Yugen UI

Sortable

Sortable sắp xếp lại danh sách bằng kéo thả hoặc bàn phím; mỗi bước được đọc cho trình đọc màn hình.

Sortable là danh sách sắp xếp được: SortableItem là từng mục, SortableHandle là tay nắm. Kéo tay nắm bằng chuột, bút hoặc ngón tay; mục đi quá nửa mục bên cạnh thì đổi chỗ, sát mép khung cuộn thì khung tự cuộn. Với bàn phím: focus tay nắm, Space hoặc Enter để nhấc mục lên, phím mũi tên để di chuyển, Space hoặc Enter để thả, Esc để huỷ; mỗi bước được đọc qua useAnnounce. Không kéo được thì bấm tay nắm để nhấc mục, rồi bấm tay nắm của mục khác để đặt vào chỗ đó. onValueChange chạy cả trong lúc kéo để giao diện theo kịp; lưu thứ tự trong onSortEnd. SortableGroup nối nhiều danh sách (cột Kanban): mục sang được danh sách khác, với bàn phím là phím mũi tên vuông góc với danh sách. Mục đã dùng Space / Enter cho việc khác (nút, menu item, Tag) thì đặt handle để cả mục là tay nắm và shortcuts để Alt + phím mũi tên di chuyển; DataGridColumnPicker và TagPicker (reorderable) làm như vậy. Hướng ngang và lưới theo RTL; khi bật giảm chuyển động, các mục đổi chỗ không trượt. Không phụ thuộc thư viện kéo thả.
  • Màn hình đăng nhậpHạn thứ Sáu
  • Danh sách đơn hàngChờ API
  • Thanh toánƯu tiên cao
  • Tài liệu phát hànhSau khi phát hành

Import

Cài từ npm, chép mã nguồn vào dự án bằng @yugen-ui/cli hoặc shadcn CLI, hoặc import thẳng khi làm việc trong repo này.

TSX
// npm install yugen-uiimport { Sortable, SortableGroup, SortableItem, SortableHandle, arrayMove } from "yugen-ui";

Biến thể

Mỗi ô là một mẫu độc lập: hover để Copy, hoặc bấm Code để xem mã nguồn.

1Nhanh
2Rẻ
3Bền

Ngang

orientation="horizontal": ← → di chuyển, đảo chiều trong RTL.

Sakura
Fuji
Kyoto
Nara
Nikko
Osaka

Lưới

orientation="grid": mục xuống dòng; ← → sang mục kế bên, ↑ ↓ sang hàng trên / dưới.

Cần làm

Màn hình đăng nhập
Danh sách đơn hàng
Tìm kiếm

Đang làm

Thanh toán

Xong

Tài liệu phát hành

Kanban: SortableGroup

Mỗi cột là một Sortable có listId; SortableGroup giữ thứ tự của mọi cột. Với bàn phím, ← → chuyển thẻ sang cột kế bên. Cột đang nhận thẻ có data-over. onSortEnd chạy một lần khi thả: chỗ để lưu.

  • Hoa anh đào
  • Mưa tháng Sáu
  • Biển đêm
  • Gió mùa

Lệnh Lên / Xuống

Thêm lệnh trong menu cho người không nhận ra tay nắm. arrayMove trả về mảng mới; useAnnounce đọc vị trí mới như khi kéo.

Cả mục là tay nắm

Nút đã dùng Space / Enter để mở: handle cho kéo cả nút (bấm không kéo vẫn là bấm; trên màn hình cảm ứng thì giữ một lúc rồi kéo), shortcuts cho Alt + phím mũi tên di chuyển. Nút xuống dòng khi khung hẹp nên dùng orientation="grid".

Cách dùng

Nên
  • Lưu thứ tự trong onSortEnd; onValueChange chạy cả trong lúc kéo.
  • Đặt label cho SortableItem khi mục có nhiều chữ, để thông báo ngắn gọn.
  • Thêm lệnh Lên / Xuống khi người dùng có thể không nhận ra tay nắm.
Không nên
  • Không đặt SortableHandle trong mục có role option hay menuitem: các mục này không chứa được nút. Dùng handle và shortcuts.
  • Không cho kéo danh sách đã có thứ tự tự động (theo ngày, theo tên).
  • Không đổi id của mục giữa các lần render: key và value phải ổn định.

Props

Prop · KiểuMặc địnhMô tả
valuestring[]Mặc định: —Thứ tự id các mục. Bỏ trống khi nằm trong SortableGroup.
onValueChange(value: string[]) => voidMặc định: —Gọi mỗi lần thứ tự đổi, kể cả giữa lúc kéo; Esc gọi lại với thứ tự lúc nhấc lên.
onSortStart(data: { item, from }) => voidMặc định: —Lúc nhấc mục lên.
onSortEnd(data: { item, from, to, canceled }) => voidMặc định: —Một lần mỗi lần thả, kể cả khi di chuyển bằng phím tắt. from / to là { list, index }; canceled khi Esc. Lưu thứ tự ở đây.
orientation"vertical" | "horizontal" | "grid"Mặc định: "vertical"Hướng của danh sách: phím mũi tên nào di chuyển mục và cách tính chỗ thả khi kéo.
listIdstringMặc định: —Khoá của danh sách trong value của SortableGroup.
labelstringMặc định: —Tên danh sách trong thông báo khi mục chuyển danh sách. Mặc định aria-label / aria-labelledby.
disabledbooleanMặc định: falseKhông kéo mục trong danh sách và không thả mục khác vào.
asChildbooleanMặc định: falseGắn vào phần tử con (List, ul, tbody) thay vì render div.
SortableGroup.valueRecord<string, string[]>Mặc định: —Thứ tự của mọi danh sách, theo listId.
SortableGroup.onValueChange(value: Record<string, string[]>) => voidMặc định: —Như onValueChange của Sortable, cho cả nhóm.
SortableGroup.onSortStart / onSortEnd(data) => voidMặc định: —Như của Sortable; from.list / to.list cho biết mục ở danh sách nào.
SortableItem.valuestringMặc định: —Id của mục, có trong value của danh sách.
SortableItem.labelstringMặc định: —Tên mục trong thông báo và nhãn tay nắm. Mặc định là chữ trong mục.
SortableItem.disabledbooleanMặc định: falseKhông nhấc được; mục vẫn bị đẩy chỗ khi mục khác đi qua.
SortableItem.handlebooleanMặc định: falseCả mục là tay nắm khi kéo bằng chuột / cảm ứng.
SortableItem.shortcutsbooleanMặc định: falseAlt + phím mũi tên trên mục di chuyển ngay, không cần nhấc lên.
SortableItem.asChildbooleanMặc định: falseGắn vào phần tử con (ListItem, li, tr, Card) thay vì render div.
SortableHandle.asChildbooleanMặc định: falseDùng phần tử con làm tay nắm thay cho nút icon mặc định.
arrayMove(array, from, to)T[]Mặc định: —Mảng mới với phần tử ở from chuyển sang to.

Messages

Chữ có sẵn của component: nhãn, aria-label, thông báo cho trình đọc màn hình.

Mặc định là tiếng Anh. Đổi qua prop messages của YugenProvider, theo namespace bên dưới; messagesVi chứa bản tiếng Việt. Xem Ngôn ngữ giao diện.

messages.sortable

Khoá · KiểuMặc định (tiếng Anh)messagesVi
handlestring

Nhãn tay nắm khi mục không có label.

Mặc định (tiếng Anh)"Reorder"messagesVi"Sắp xếp lại"
handleItemstring

Nhãn tay nắm của mục có label.

Mặc định (tiếng Anh)"Reorder {item}"messagesVi"Sắp xếp lại {item}"
instructionsstring

Hướng dẫn dùng bàn phím, là mô tả (aria-describedby) của tay nắm.

Mặc định (tiếng Anh)"Press Space or Enter to pick up the item, arrow keys to move it, Space or Enter to drop it, Escape to cancel."messagesVi"Nhấn Space hoặc Enter để nhấc mục lên, dùng phím mũi tên để di chuyển, Space hoặc Enter để thả, Esc để huỷ."
liftedstringMặc định (tiếng Anh)"Picked up {item}, position {position} of {total}."messagesVi"Đã nhấc {item}, vị trí {position} trên {total}."
movedstringMặc định (tiếng Anh)"{item}: position {position} of {total}."messagesVi"{item}: vị trí {position} trên {total}."
movedTostring

Mục vừa sang danh sách khác (SortableGroup).

Mặc định (tiếng Anh)"{item}: moved to {list}, position {position} of {total}."messagesVi"{item}: sang {list}, vị trí {position} trên {total}."
droppedstringMặc định (tiếng Anh)"Dropped {item} at position {position} of {total}."messagesVi"Đã thả {item} ở vị trí {position} trên {total}."
droppedInstringMặc định (tiếng Anh)"Dropped {item} in {list}, position {position} of {total}."messagesVi"Đã thả {item} vào {list}, vị trí {position} trên {total}."
canceledstringMặc định (tiếng Anh)"Canceled. {item} is back at position {position} of {total}."messagesVi"Đã huỷ. {item} về lại vị trí {position} trên {total}."