feat(design): the channel menu's Tags and Priority submenus (C1, E28 S3e3b)
Tags A-Z as in-place checks (the menu stays open for the next); Priority as the fixed steps 10 5 3 2 1 0, an off-scale current value ticked in its place. A pick re-sorts the list at once, moving only that row (placeByPriority, the server's order), and the table / card pager and the focus follow it (DataTable and useCardPager take a reveal seq, listNav.focusNavItem lands on the row's own "…", a virtualized card revealed first). ActionMenu rows take check: many | one. The menu reads the row fresh from the cache and keys its target by id.
This commit is contained in:
@@ -213,20 +213,44 @@ function focusItem(el: HTMLElement, reveal = true): void {
|
||||
if (reveal) el.scrollIntoView?.({ block: "nearest" });
|
||||
}
|
||||
|
||||
function focusIndex(list: NavList, index: number): void {
|
||||
function focusIndex(
|
||||
list: NavList,
|
||||
index: number,
|
||||
land: (el: HTMLElement) => void = (el) => focusItem(el),
|
||||
): void {
|
||||
const el = itemAt(list, index);
|
||||
if (el) return focusItem(el);
|
||||
if (el) return land(el);
|
||||
// Virtualized and not mounted: have the list scroll it in, then take it as soon as it renders.
|
||||
list.reveal(index);
|
||||
let frames = 0;
|
||||
const wait = () => {
|
||||
const got = itemAt(list, index);
|
||||
if (got) focusItem(got);
|
||||
if (got) land(got);
|
||||
else if (++frames < REVEAL_FRAMES) requestAnimationFrame(wait);
|
||||
};
|
||||
requestAnimationFrame(wait);
|
||||
}
|
||||
|
||||
/** Hand the focus to item `index` of the list registered inside `within` — or, with `pick`, to the
|
||||
* control `pick` finds in it (the row's own "…") — bringing a virtualized item into the DOM first
|
||||
* and scrolling it just far enough to be seen. For a caller that MOVED an item and follows it: a
|
||||
* channel re-sorted by the priority its menu set, whose node may have been re-inserted (the focus
|
||||
* then fell to <body>) or scrolled off. */
|
||||
export function focusNavItem(
|
||||
within: HTMLElement,
|
||||
index: number,
|
||||
pick?: (item: HTMLElement) => HTMLElement | null,
|
||||
): void {
|
||||
const list = [...lists.values()].find((l) => within.contains(l.el));
|
||||
if (!list) return;
|
||||
focusIndex(list, index, (item) => {
|
||||
const to = pick?.(item);
|
||||
if (!to) return focusItem(item);
|
||||
to.focus({ preventScroll: true });
|
||||
item.scrollIntoView?.({ block: "nearest" });
|
||||
});
|
||||
}
|
||||
|
||||
function step(delta: 1 | -1): void {
|
||||
const cur = current();
|
||||
if (!cur) {
|
||||
|
||||
Reference in New Issue
Block a user