@k2b/ui 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/actions/ContextMenu.js +5 -0
- package/dist/browser/actions/ContextMenu.js.map +1 -1
- package/dist/browser/actions/DragAndDrop.js +89 -0
- package/dist/browser/actions/DragAndDrop.js.map +1 -0
- package/dist/browser/actions/GestureMenu.js +26 -3
- package/dist/browser/actions/GestureMenu.js.map +1 -1
- package/dist/browser/actions/drag-and-drop-messages.js +75 -0
- package/dist/browser/actions/drag-and-drop-messages.js.map +1 -0
- package/dist/browser/actions/drag-and-drop.js +692 -0
- package/dist/browser/actions/drag-and-drop.js.map +1 -0
- package/dist/browser/actions/index.js +1 -0
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +385 -265
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +778 -0
- package/dist/browser/chat/ChatDecision.js.map +1 -0
- package/dist/browser/chat/ChatPrimitives.js +34 -28
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/ChatTimeline.js.map +1 -1
- package/dist/browser/chat/chat-messages.js +6 -0
- package/dist/browser/chat/chat-messages.js.map +1 -1
- package/dist/browser/chat/command-search.js +10 -3
- package/dist/browser/chat/command-search.js.map +1 -1
- package/dist/browser/chat/index.js +6 -1
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/chat/types.js.map +1 -1
- package/dist/browser/content/Calendar.js +3 -3
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/Chart.js +54 -71
- package/dist/browser/content/Chart.js.map +1 -1
- package/dist/browser/content/FileBrowser.js +2 -2
- package/dist/browser/content/FileBrowser.js.map +1 -1
- package/dist/browser/content/FileTree.js +42 -36
- package/dist/browser/content/FileTree.js.map +1 -1
- package/dist/browser/content/FileTypeIcon.js +72 -0
- package/dist/browser/content/FileTypeIcon.js.map +1 -0
- package/dist/browser/content/FileView.js +2 -2
- package/dist/browser/content/FileView.js.map +1 -1
- package/dist/browser/content/ReferenceChip.js +162 -0
- package/dist/browser/content/ReferenceChip.js.map +1 -0
- package/dist/browser/content/calendar-pointer.js +9 -0
- package/dist/browser/content/calendar-pointer.js.map +1 -1
- package/dist/browser/content/calendar-time-view.js +6 -3
- package/dist/browser/content/calendar-time-view.js.map +1 -1
- package/dist/browser/content/index.js +2 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/layout/AppOverview.js +1 -1
- package/dist/browser/layout/AppOverview.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +644 -445
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/PanelDialog.js +88 -86
- package/dist/browser/layout/PanelDialog.js.map +1 -1
- package/dist/browser/layout/ResizeSeparator.js +81 -0
- package/dist/browser/layout/ResizeSeparator.js.map +1 -0
- package/dist/browser/layout/SidebarSwitcher.js +9 -2
- package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
- package/dist/browser/layout/TabBar.js +15 -12
- package/dist/browser/layout/TabBar.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +130 -17
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/index.js +3 -1
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/layout/resize-separator.js +90 -0
- package/dist/browser/layout/resize-separator.js.map +1 -0
- package/dist/browser/layout/workspace-messages.js +12 -2
- package/dist/browser/layout/workspace-messages.js.map +1 -1
- package/dist/browser/surfaces/LinkCard.js +1 -1
- package/dist/browser/surfaces/LinkCard.js.map +1 -1
- package/dist/browser/surfaces/Placeholder.js +14 -3
- package/dist/browser/surfaces/Placeholder.js.map +1 -1
- package/dist/browser/surfaces/StatCell.js +23 -0
- package/dist/browser/surfaces/StatCell.js.map +1 -1
- package/dist/browser/widgets/Widget.js +1 -1
- package/dist/browser/widgets/Widget.js.map +1 -1
- package/dist/ssr/actions/ContextMenu.js +5 -0
- package/dist/ssr/actions/ContextMenu.js.map +1 -1
- package/dist/ssr/actions/DragAndDrop.js +63 -0
- package/dist/ssr/actions/DragAndDrop.js.map +1 -0
- package/dist/ssr/actions/GestureMenu.js +26 -3
- package/dist/ssr/actions/GestureMenu.js.map +1 -1
- package/dist/ssr/actions/drag-and-drop-messages.js +75 -0
- package/dist/ssr/actions/drag-and-drop-messages.js.map +1 -0
- package/dist/ssr/actions/drag-and-drop.js +692 -0
- package/dist/ssr/actions/drag-and-drop.js.map +1 -0
- package/dist/ssr/actions/index.js +1 -0
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +131 -14
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +465 -0
- package/dist/ssr/chat/ChatDecision.js.map +1 -0
- package/dist/ssr/chat/ChatPrimitives.js +2 -2
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/ChatTimeline.js.map +1 -1
- package/dist/ssr/chat/chat-messages.js +6 -0
- package/dist/ssr/chat/chat-messages.js.map +1 -1
- package/dist/ssr/chat/command-search.js +10 -3
- package/dist/ssr/chat/command-search.js.map +1 -1
- package/dist/ssr/chat/index.js +6 -1
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/chat/types.js.map +1 -1
- package/dist/ssr/content/Calendar.js +3 -3
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/Chart.js +19 -23
- package/dist/ssr/content/Chart.js.map +1 -1
- package/dist/ssr/content/FileBrowser.js +2 -2
- package/dist/ssr/content/FileBrowser.js.map +1 -1
- package/dist/ssr/content/FileTree.js +21 -14
- package/dist/ssr/content/FileTree.js.map +1 -1
- package/dist/ssr/content/FileTypeIcon.js +50 -0
- package/dist/ssr/content/FileTypeIcon.js.map +1 -0
- package/dist/ssr/content/FileView.js +2 -2
- package/dist/ssr/content/FileView.js.map +1 -1
- package/dist/ssr/content/ReferenceChip.js +121 -0
- package/dist/ssr/content/ReferenceChip.js.map +1 -0
- package/dist/ssr/content/calendar-pointer.js +9 -0
- package/dist/ssr/content/calendar-pointer.js.map +1 -1
- package/dist/ssr/content/calendar-time-view.js +6 -3
- package/dist/ssr/content/calendar-time-view.js.map +1 -1
- package/dist/ssr/content/index.js +2 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/layout/AppOverview.js +1 -1
- package/dist/ssr/layout/AppOverview.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +272 -95
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/PanelDialog.js +7 -6
- package/dist/ssr/layout/PanelDialog.js.map +1 -1
- package/dist/ssr/layout/ResizeSeparator.js +45 -0
- package/dist/ssr/layout/ResizeSeparator.js.map +1 -0
- package/dist/ssr/layout/SidebarSwitcher.js +9 -2
- package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
- package/dist/ssr/layout/TabBar.js +1 -1
- package/dist/ssr/layout/TabBar.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +130 -17
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/index.js +3 -1
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/layout/resize-separator.js +90 -0
- package/dist/ssr/layout/resize-separator.js.map +1 -0
- package/dist/ssr/layout/workspace-messages.js +12 -2
- package/dist/ssr/layout/workspace-messages.js.map +1 -1
- package/dist/ssr/surfaces/LinkCard.js +1 -1
- package/dist/ssr/surfaces/LinkCard.js.map +1 -1
- package/dist/ssr/surfaces/Placeholder.js +4 -2
- package/dist/ssr/surfaces/Placeholder.js.map +1 -1
- package/dist/ssr/surfaces/StatCell.js +23 -0
- package/dist/ssr/surfaces/StatCell.js.map +1 -1
- package/dist/ssr/widgets/Widget.js +1 -1
- package/dist/ssr/widgets/Widget.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/ContextMenu.d.ts +1 -0
- package/dist/types/actions/DragAndDrop.d.ts +29 -0
- package/dist/types/actions/drag-and-drop-messages.d.ts +35 -0
- package/dist/types/actions/drag-and-drop.d.ts +36 -0
- package/dist/types/actions/index.d.ts +2 -0
- package/dist/types/chat/ChatComposer.d.ts +19 -1
- package/dist/types/chat/ChatDecision.d.ts +133 -0
- package/dist/types/chat/ChatPrimitives.d.ts +4 -1
- package/dist/types/chat/ChatTimeline.d.ts +2 -1
- package/dist/types/chat/chat-messages.d.ts +3 -0
- package/dist/types/chat/command-search.d.ts +3 -0
- package/dist/types/chat/index.d.ts +23 -1
- package/dist/types/chat/types.d.ts +54 -1
- package/dist/types/content/Chart.d.ts +9 -3
- package/dist/types/content/FileTypeIcon.d.ts +27 -0
- package/dist/types/content/ReferenceChip.d.ts +49 -0
- package/dist/types/content/calendar-pointer.d.ts +6 -0
- package/dist/types/content/index.d.ts +3 -0
- package/dist/types/layout/AppWorkspace.d.ts +5 -3
- package/dist/types/layout/ResizeSeparator.d.ts +9 -0
- package/dist/types/layout/TabBar.d.ts +7 -0
- package/dist/types/layout/app-workspace-controller.d.ts +2 -0
- package/dist/types/layout/index.d.ts +3 -1
- package/dist/types/layout/resize-separator.d.ts +18 -0
- package/dist/types/layout/workspace-messages.d.ts +5 -0
- package/dist/types/surfaces/StatCell.d.ts +6 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/SidebarSwitcher.tsx"],"sourcesContent":["import { documentNavigate } from \"@k2b/ssr/nav\";\nimport { createMemo, createSignal, createUniqueId, For, type JSX, onCleanup, Show } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport { dropdownPosition } from \"../actions/Dropdown\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\n/** One container a person can switch to, such as a notebook, a space, or a mailbox. */\nexport type AppWorkspaceSwitcherOption = {\n id: string;\n label: string;\n /** One secondary fact, such as a mailbox's address. */\n description?: string;\n /** Tabler icon class; without it the row takes the switcher's icon. */\n icon?: string;\n /** Tints the icon; without it the app accent does. */\n color?: string;\n href: string;\n};\n\n/** A row below the containers, such as \"New notebook\" or \"All notebooks\". */\nexport type AppWorkspaceSwitcherAction = {\n id: string;\n label: string;\n icon: string;\n disabled?: boolean;\n} & ({ href: string; action?: never } | { action: () => void; href?: never });\n\nexport type AppWorkspaceSidebarSwitcherProps = {\n /** The current container's ID among `options`. */\n currentId: string;\n /** The current container's name. */\n label: string;\n /** One secondary fact under the name, such as a mailbox's address. */\n description?: string;\n icon: string;\n /** Tints the icon; without it the app accent does. */\n color?: string;\n /** Names the button's purpose and its panel, such as \"Switch notebook\". */\n switchLabel: string;\n /** Every container the person can switch to, the current one included; `undefined` until they are loaded. */\n options?: readonly AppWorkspaceSwitcherOption[];\n /** Shown in place of the options when loading them failed; the panel offers to try again through `onLoad`. */\n error?: string;\n /**\n * Asks for `options` while they are `undefined`: when a pointer or the focus first reaches the button, so the list\n * is usually there when the panel opens, on every opening, and from the panel's retry. Ignore repeated calls while a\n * request runs.\n */\n onLoad?: () => void;\n actions?: readonly AppWorkspaceSwitcherAction[];\n /**\n * A host menu around the switcher, such as the phone menu, finishes dismissing itself before a chosen container\n * opens or an action runs; `false` cancels the choice. Navigation passes its own.\n */\n beforeSelect?: () => boolean | void | Promise<boolean | void>;\n /** Sizes the switcher in its place, such as `flex-1` beside a button in one row. */\n class?: string;\n};\n\n/** From this many containers the panel scrolls, so it offers a search field. */\nexport const APP_WORKSPACE_SWITCHER_SEARCH_FROM = 8;\n\n/** The space between trigger and panel, and the panel's distance from the viewport edges, as for every dropdown. */\nconst SWITCHER_GAP = 4;\nconst SWITCHER_INSET = 8;\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\nconst tint = (color: string | undefined) => (color ? { \"--k2b-sidebar-switcher-color\": color } : undefined);\nconst plainClick = (event: MouseEvent) => event.button === 0 && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey);\n\n/**\n * The current container at the top of a sidebar: its icon, name, and an optional fact, with a chevron that opens the\n * other containers, a search field when there are many, and actions such as \"New …\" and \"All …\". The rows are links,\n * so a container also opens in a new tab. In a collapsed rail it shows only the icon and opens beside the rail.\n */\nexport function AppWorkspaceSidebarSwitcher(props: AppWorkspaceSidebarSwitcherProps): JSX.Element {\n const messages = useUiMessages();\n const id = `k2b-sidebar-switcher-${createUniqueId().replace(/[^a-zA-Z0-9_-]/g, \"-\")}`;\n const [open, setOpen] = createSignal(false);\n const [query, setQuery] = createSignal(\"\");\n let trigger: HTMLButtonElement | undefined;\n let panel: HTMLDivElement | undefined;\n let search: HTMLInputElement | undefined;\n let keyboardOpened = true;\n /** Whether the panel was open when a press on the button began; the press itself light-dismisses it first. */\n let openAtPress: boolean | undefined;\n\n const searchable = () => (props.options?.length ?? 0) >= APP_WORKSPACE_SWITCHER_SEARCH_FROM;\n const visible = createMemo(() => {\n const needle = query().trim().toLocaleLowerCase();\n const options = props.options ?? [];\n if (!needle) return options;\n return options.filter((option) => [option.label, option.description].some((part) => part?.toLocaleLowerCase().includes(needle)));\n });\n const load = () => {\n if (props.options === undefined) props.onLoad?.();\n };\n\n // The collapsed rail is a narrow column from 64 rem, so the panel opens beside it instead of below.\n const inRail = () =>\n Boolean(\n trigger?.closest('[data-sidebar-collapsed=\"true\"] .k2b-app-workspace__sidebar[data-workspace-collapsible=\"true\"]') &&\n window.matchMedia(\"(min-width: 64rem)\").matches,\n );\n /** Whether the panel opens above its trigger; chosen on opening and on a viewport resize only. */\n let above = false;\n /**\n * Places the panel beside its trigger. `choose` picks its side again; otherwise it keeps the side, so arriving\n * options, an error, or a narrowed search never flip it to the other side while it is open.\n */\n const place = (choose = false) => {\n if (!trigger || !panel) return;\n const rect = trigger.getBoundingClientRect();\n const viewport = { width: window.innerWidth, height: window.innerHeight };\n const rail = inRail();\n // As wide as the sidebar row and never narrower than a menu, so long names keep their words; fixed while open,\n // so filtering never changes the panel's width.\n panel.style.setProperty(\"--k2b-sidebar-switcher-width\", `${rail ? 0 : Math.round(rect.width)}px`);\n if (rail) {\n panel.style.removeProperty(\"--k2b-sidebar-switcher-room\");\n panel.style.removeProperty(\"min-height\");\n panel.style.bottom = \"auto\";\n const position = dropdownPosition(rect, panel.getBoundingClientRect(), \"right-start\", viewport);\n panel.style.left = `${position.left}px`;\n panel.style.top = `${position.top}px`;\n return;\n }\n // Below the trigger, or above it when the panel at its tallest does not fit below and there is more room above,\n // as in the lower half of the phone menu. Its tallest is its CSS max-height, min(28rem, 80dvh), so the side does\n // not depend on what the panel shows at the moment. It never covers its trigger: it takes at most the room on its\n // side, and its list scrolls.\n const roomBelow = viewport.height - rect.bottom - SWITCHER_GAP - SWITCHER_INSET;\n const roomAbove = rect.top - SWITCHER_GAP - SWITCHER_INSET;\n if (choose) {\n const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n above = Math.min(28 * rem, 0.8 * viewport.height) > roomBelow && roomAbove > roomBelow;\n panel.style.removeProperty(\"min-height\");\n }\n const room = Math.max(0, above ? roomAbove : roomBelow);\n panel.style.setProperty(\"--k2b-sidebar-switcher-room\", `${room}px`);\n panel.style.left = `${dropdownPosition(rect, panel.getBoundingClientRect(), \"bottom-right\", viewport).left}px`;\n if (above) {\n // Its bottom edge stays beside the trigger, and it does not shrink while open, so a narrowed search keeps the\n // search field where the person is typing.\n const height = Math.min(room, Math.max(panel.getBoundingClientRect().height, Number.parseFloat(panel.style.minHeight) || 0));\n panel.style.minHeight = `${height}px`;\n panel.style.top = \"auto\";\n panel.style.bottom = `${document.documentElement.clientHeight - rect.top + SWITCHER_GAP}px`;\n } else {\n panel.style.top = `${rect.bottom + SWITCHER_GAP}px`;\n panel.style.bottom = \"auto\";\n }\n };\n const reposition = () => open() && place();\n const resize = () => open() && place(true);\n // Arriving options, an error, or the search field grow the panel after it opened, so its size places it too.\n let resizing: ResizeObserver | undefined;\n const listen = (on: boolean) => {\n const method = on ? \"addEventListener\" : \"removeEventListener\";\n window[method](\"resize\", resize);\n window[method](\"scroll\", reposition, true);\n if (on && panel) {\n resizing ??= new ResizeObserver(reposition);\n resizing.observe(panel);\n } else resizing?.disconnect();\n };\n // Server rendering cleans up too, where no window exists.\n onCleanup(() => isServer || listen(false));\n /** Lets the host dismiss its menu first, then opens the link or runs the action unless the host cancels. */\n const choose = (run: () => void) => {\n hide(false);\n if (!props.beforeSelect) return run();\n void Promise.resolve(props.beforeSelect()).then((allowed) => {\n if (allowed !== false) run();\n });\n };\n const follow = (event: MouseEvent & { currentTarget: HTMLAnchorElement }) => {\n if (!plainClick(event)) return;\n if (!props.beforeSelect) return hide(false);\n event.preventDefault();\n const href = event.currentTarget.href;\n choose(() => documentNavigate(href));\n };\n\n const rows = () => (panel ? Array.from(panel.querySelectorAll<HTMLElement>(\"[data-switcher-row]:not([aria-disabled='true'])\")) : []);\n const focusRow = (index: number) => {\n const all = rows();\n if (all.length) all[(index + all.length) % all.length]?.focus();\n };\n const show = (focus: \"start\" | \"end\" = \"start\") => {\n if (!panel || panel.matches(\":popover-open\")) return;\n load();\n keyboardOpened = ringOnReturn(trigger);\n setQuery(\"\");\n panel.showPopover();\n setOpen(true);\n listen(true);\n place(true);\n queueMicrotask(() => {\n // On touch the search field waits for a tap, so the keyboard does not cover the list on opening.\n if (searchable() && focus === \"start\" && !window.matchMedia(\"(pointer: coarse)\").matches) search?.focus();\n else if (focus === \"end\") focusRow(-1);\n // While the list loads, the panel holds focus, so arriving rows do not move it.\n else (panel?.querySelector<HTMLElement>('[aria-current=\"true\"]') ?? (props.options ? rows()[0] : panel))?.focus();\n });\n };\n const hide = (restoreFocus: boolean) => {\n if (restoreFocus) returnFocus(trigger, keyboardOpened);\n if (panel?.matches(\":popover-open\")) panel.hidePopover();\n };\n\n const onPanelKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n const all = rows();\n const index = all.indexOf(target);\n if (event.key === \"Escape\") {\n // The panel closes alone; an overlay sidebar around it stays open.\n event.preventDefault();\n event.stopPropagation();\n hide(true);\n } else if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusRow(index + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if ((event.key === \"Home\" || event.key === \"End\") && target !== search) {\n event.preventDefault();\n focusRow(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Enter\" && target === search) {\n event.preventDefault();\n panel?.querySelector<HTMLElement>(\"[data-switcher-option]\")?.click();\n } else if (\n search &&\n target !== search &&\n event.key.length === 1 &&\n event.key !== \" \" &&\n !(event.metaKey || event.ctrlKey || event.altKey)\n ) {\n // Typing anywhere in a long list searches it.\n event.preventDefault();\n setQuery(query() + event.key);\n search.focus();\n }\n };\n\n return (\n <div class={`k2b-sidebar-switcher ${props.class ?? \"\"}`} data-description={props.description ? \"\" : undefined}>\n <button\n ref={trigger}\n type=\"button\"\n class=\"k2b-sidebar-switcher__trigger\"\n aria-label={props.label}\n aria-describedby={`${id}-purpose`}\n aria-haspopup=\"dialog\"\n aria-expanded={open()}\n aria-controls={id}\n // The full name for a truncated title and for the icon in a collapsed rail.\n title={props.label}\n onPointerEnter={load}\n onFocus={load}\n onPointerDown={() => (openAtPress = panel?.matches(\":popover-open\"))}\n onClick={() => {\n const wasOpen = openAtPress ?? panel?.matches(\":popover-open\");\n openAtPress = undefined;\n if (wasOpen) hide(false);\n else show();\n }}\n onKeyDown={(event) => {\n if (event.key !== \"ArrowDown\" && event.key !== \"ArrowUp\") return;\n event.preventDefault();\n if (open()) focusRow(event.key === \"ArrowDown\" ? 0 : -1);\n else show(event.key === \"ArrowDown\" ? \"start\" : \"end\");\n }}\n >\n <span class=\"k2b-sidebar-switcher__icon\" style={tint(props.color)} aria-hidden=\"true\">\n <i class={iconClass(props.icon)} />\n </span>\n <span class=\"k2b-sidebar-switcher__copy\" aria-hidden=\"true\">\n <span>{props.label}</span>\n <Show when={props.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n <i class=\"ti ti-selector k2b-sidebar-switcher__chevron\" aria-hidden=\"true\" />\n <span id={`${id}-purpose`} class=\"k2b-sr-only\">\n {[props.description, props.switchLabel].filter(Boolean).join(\", \")}\n </span>\n </button>\n <div\n ref={panel}\n id={id}\n popover=\"auto\"\n tabIndex={-1}\n role=\"dialog\"\n aria-label={props.switchLabel}\n class=\"k2b-sidebar-switcher__panel\"\n onKeyDown={onPanelKeyDown}\n onFocusOut={(event) => {\n const next = event.relatedTarget as Node | null;\n // Tabbing out closes the panel; focus that leaves the window keeps it.\n if (next && !panel?.contains(next) && next !== trigger) hide(false);\n }}\n onToggle={(event) => {\n if ((event as ToggleEvent).newState === \"open\") return;\n setOpen(false);\n listen(false);\n }}\n >\n <Show when={searchable()}>\n <div class=\"k2b-choice-search k2b-sidebar-switcher__search\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n <input\n ref={search}\n type=\"search\"\n data-switcher-row\n value={query()}\n placeholder={messages().search}\n aria-label={messages().searchLabel}\n aria-controls={`${id}-options`}\n onInput={(event) => setQuery(event.currentTarget.value)}\n />\n </div>\n </Show>\n <ul id={`${id}-options`} class=\"k2b-sidebar-switcher__options\">\n <Show\n when={props.options}\n fallback={\n <li class=\"k2b-choice-status\" role=\"status\" data-tone={props.error ? \"danger\" : undefined}>\n <Show\n when={props.error}\n fallback={\n <>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n <span>{messages().loading}</span>\n </>\n }\n >\n {(error) => (\n <>\n <span>{error()}</span>\n <button type=\"button\" data-switcher-row onClick={() => props.onLoad?.()}>\n {messages().retry}\n </button>\n </>\n )}\n </Show>\n </li>\n }\n >\n <For\n each={visible()}\n fallback={\n <li class=\"k2b-choice-status\" role=\"status\">\n {messages().noResults}\n </li>\n }\n >\n {(option) => {\n const current = () => option.id === props.currentId;\n return (\n <li>\n <a\n href={option.href}\n class=\"k2b-dropdown__item k2b-sidebar-switcher__option\"\n data-switcher-row\n data-switcher-option\n aria-current={current() ? \"true\" : undefined}\n onClick={(event) => {\n // The open container is already here: choosing it only closes the panel.\n if (current() && plainClick(event)) {\n event.preventDefault();\n hide(true);\n } else follow(event);\n }}\n >\n <span class=\"k2b-sidebar-switcher__option-icon\" style={tint(option.color ?? props.color)} aria-hidden=\"true\">\n <i class={iconClass(option.icon ?? props.icon)} />\n </span>\n <span class=\"k2b-dropdown__copy\">\n <span>{option.label}</span>\n <Show when={option.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n <i class=\"ti ti-check k2b-dropdown__check\" data-hidden={current() ? undefined : \"true\"} aria-hidden=\"true\" />\n </a>\n </li>\n );\n }}\n </For>\n </Show>\n </ul>\n <Show when={props.actions?.length}>\n <ul class=\"k2b-sidebar-switcher__actions\">\n <For each={props.actions}>\n {(item) => (\n <li>\n <Show\n when={item.href}\n fallback={\n <button\n type=\"button\"\n class=\"k2b-dropdown__item\"\n data-switcher-row\n disabled={item.disabled}\n aria-disabled={item.disabled ? \"true\" : undefined}\n onClick={() => choose(() => item.action?.())}\n >\n <i class={iconClass(item.icon)} aria-hidden=\"true\" />\n <span class=\"k2b-dropdown__copy\">\n <span>{item.label}</span>\n </span>\n </button>\n }\n >\n {(href) => (\n <a\n href={item.disabled ? undefined : href()}\n class=\"k2b-dropdown__item\"\n data-switcher-row\n aria-disabled={item.disabled ? \"true\" : undefined}\n onClick={(event) => item.disabled || follow(event)}\n >\n <i class={iconClass(item.icon)} aria-hidden=\"true\" />\n <span class=\"k2b-dropdown__copy\">\n <span>{item.label}</span>\n </span>\n </a>\n )}\n </Show>\n </li>\n )}\n </For>\n </ul>\n </Show>\n </div>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/SidebarSwitcher.tsx"],"sourcesContent":["import { documentNavigate } from \"@k2b/ssr/nav\";\nimport { createMemo, createSignal, createUniqueId, For, type JSX, onCleanup, Show } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport { dropdownPosition } from \"../actions/Dropdown\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\n/** One container a person can switch to, such as a notebook, a space, or a mailbox. */\nexport type AppWorkspaceSwitcherOption = {\n id: string;\n label: string;\n /** One secondary fact, such as a mailbox's address. */\n description?: string;\n /** Tabler icon class; without it the row takes the switcher's icon. */\n icon?: string;\n /** Tints the icon; without it the app accent does. */\n color?: string;\n href: string;\n};\n\n/** A row below the containers, such as \"New notebook\" or \"All notebooks\". */\nexport type AppWorkspaceSwitcherAction = {\n id: string;\n label: string;\n icon: string;\n disabled?: boolean;\n} & ({ href: string; action?: never } | { action: () => void; href?: never });\n\nexport type AppWorkspaceSidebarSwitcherProps = {\n /** The current container's ID among `options`. */\n currentId: string;\n /** The current container's name. */\n label: string;\n /** One secondary fact under the name, such as a mailbox's address. */\n description?: string;\n icon: string;\n /** Tints the icon; without it the app accent does. */\n color?: string;\n /** Names the button's purpose and its panel, such as \"Switch notebook\". */\n switchLabel: string;\n /** Every container the person can switch to, the current one included; `undefined` until they are loaded. */\n options?: readonly AppWorkspaceSwitcherOption[];\n /** Shown in place of the options when loading them failed; the panel offers to try again through `onLoad`. */\n error?: string;\n /**\n * Asks for `options` while they are `undefined`: when a pointer or the focus first reaches the button, so the list\n * is usually there when the panel opens, on every opening, and from the panel's retry. Ignore repeated calls while a\n * request runs.\n */\n onLoad?: () => void;\n actions?: readonly AppWorkspaceSwitcherAction[];\n /**\n * A host menu around the switcher, such as the phone menu, finishes dismissing itself before a chosen container\n * opens or an action runs; `false` cancels the choice. Navigation passes its own.\n */\n beforeSelect?: () => boolean | void | Promise<boolean | void>;\n /** Sizes the switcher in its place, such as `flex-1` beside a button in one row. */\n class?: string;\n};\n\n/** From this many containers the panel scrolls, so it offers a search field. */\nexport const APP_WORKSPACE_SWITCHER_SEARCH_FROM = 8;\n\n/** The space between trigger and panel, and the panel's distance from the viewport edges, as for every dropdown. */\nconst SWITCHER_GAP = 4;\nconst SWITCHER_INSET = 8;\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\nconst tint = (color: string | undefined) => (color ? { \"--k2b-sidebar-switcher-color\": color } : undefined);\nconst plainClick = (event: MouseEvent) => event.button === 0 && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey);\n\n/**\n * The current container at the top of a sidebar: its icon, name, and an optional fact, with a chevron that opens the\n * other containers, a search field when there are many, and actions such as \"New …\" and \"All …\". The rows are links,\n * so a container also opens in a new tab. In a collapsed rail it shows only the icon and opens beside the rail.\n */\nexport function AppWorkspaceSidebarSwitcher(props: AppWorkspaceSidebarSwitcherProps): JSX.Element {\n const messages = useUiMessages();\n const id = `k2b-sidebar-switcher-${createUniqueId().replace(/[^a-zA-Z0-9_-]/g, \"-\")}`;\n const [open, setOpen] = createSignal(false);\n const [query, setQuery] = createSignal(\"\");\n let trigger: HTMLButtonElement | undefined;\n let panel: HTMLDivElement | undefined;\n let search: HTMLInputElement | undefined;\n let keyboardOpened = true;\n /** Whether the panel was open when a press on the button began; the press itself light-dismisses it first. */\n let openAtPress: boolean | undefined;\n\n const searchable = () => (props.options?.length ?? 0) >= APP_WORKSPACE_SWITCHER_SEARCH_FROM;\n const visible = createMemo(() => {\n const needle = query().trim().toLocaleLowerCase();\n const options = props.options ?? [];\n if (!needle) return options;\n return options.filter((option) => [option.label, option.description].some((part) => part?.toLocaleLowerCase().includes(needle)));\n });\n const load = () => {\n if (props.options === undefined) props.onLoad?.();\n };\n\n // The collapsed rail is a narrow column from a 64 rem host, so the panel opens beside it instead of below.\n // The workspace's stylesheet states which form its sidebar has.\n const inRail = () => {\n const sidebar = trigger?.closest('[data-sidebar-collapsed=\"true\"] .k2b-app-workspace__sidebar[data-workspace-collapsible=\"true\"]');\n const root = sidebar?.closest<HTMLElement>(\"[data-k2b-app-workspace]\");\n return Boolean(root && getComputedStyle(root).getPropertyValue(\"--k2b-workspace-sidebar-form\").trim() === \"column\");\n };\n /** Whether the panel opens above its trigger; chosen on opening and on a viewport resize only. */\n let above = false;\n /**\n * Places the panel beside its trigger. `choose` picks its side again; otherwise it keeps the side, so arriving\n * options, an error, or a narrowed search never flip it to the other side while it is open.\n */\n const place = (choose = false) => {\n if (!trigger || !panel) return;\n const rect = trigger.getBoundingClientRect();\n const viewport = { width: window.innerWidth, height: window.innerHeight };\n const rail = inRail();\n // As wide as the sidebar row and never narrower than a menu, so long names keep their words; fixed while open,\n // so filtering never changes the panel's width.\n panel.style.setProperty(\"--k2b-sidebar-switcher-width\", `${rail ? 0 : Math.round(rect.width)}px`);\n if (rail) {\n panel.style.removeProperty(\"--k2b-sidebar-switcher-room\");\n panel.style.removeProperty(\"min-height\");\n panel.style.bottom = \"auto\";\n const position = dropdownPosition(rect, panel.getBoundingClientRect(), \"right-start\", viewport);\n panel.style.left = `${position.left}px`;\n panel.style.top = `${position.top}px`;\n return;\n }\n // Below the trigger, or above it when the panel at its tallest does not fit below and there is more room above,\n // as in the lower half of the phone menu. Its tallest is its CSS max-height, min(28rem, 80dvh), so the side does\n // not depend on what the panel shows at the moment. It never covers its trigger: it takes at most the room on its\n // side, and its list scrolls.\n const roomBelow = viewport.height - rect.bottom - SWITCHER_GAP - SWITCHER_INSET;\n const roomAbove = rect.top - SWITCHER_GAP - SWITCHER_INSET;\n if (choose) {\n const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n above = Math.min(28 * rem, 0.8 * viewport.height) > roomBelow && roomAbove > roomBelow;\n panel.style.removeProperty(\"min-height\");\n }\n const room = Math.max(0, above ? roomAbove : roomBelow);\n panel.style.setProperty(\"--k2b-sidebar-switcher-room\", `${room}px`);\n panel.style.left = `${dropdownPosition(rect, panel.getBoundingClientRect(), \"bottom-right\", viewport).left}px`;\n if (above) {\n // Its bottom edge stays beside the trigger, and it does not shrink while open, so a narrowed search keeps the\n // search field where the person is typing.\n const height = Math.min(room, Math.max(panel.getBoundingClientRect().height, Number.parseFloat(panel.style.minHeight) || 0));\n panel.style.minHeight = `${height}px`;\n panel.style.top = \"auto\";\n panel.style.bottom = `${document.documentElement.clientHeight - rect.top + SWITCHER_GAP}px`;\n } else {\n panel.style.top = `${rect.bottom + SWITCHER_GAP}px`;\n panel.style.bottom = \"auto\";\n }\n };\n const reposition = () => open() && place();\n const resize = () => open() && place(true);\n // Arriving options, an error, or the search field grow the panel after it opened, so its size places it too.\n // The trigger changes size when a host turns the rail into the expanded overlay, without a window resize.\n let resizing: ResizeObserver | undefined;\n const listen = (on: boolean) => {\n const method = on ? \"addEventListener\" : \"removeEventListener\";\n window[method](\"resize\", resize);\n window[method](\"scroll\", reposition, true);\n if (on && panel) {\n resizing ??= new ResizeObserver(reposition);\n resizing.observe(panel);\n if (trigger) resizing.observe(trigger);\n } else resizing?.disconnect();\n };\n // Server rendering cleans up too, where no window exists.\n onCleanup(() => isServer || listen(false));\n /** Lets the host dismiss its menu first, then opens the link or runs the action unless the host cancels. */\n const choose = (run: () => void) => {\n hide(false);\n if (!props.beforeSelect) return run();\n void Promise.resolve(props.beforeSelect()).then((allowed) => {\n if (allowed !== false) run();\n });\n };\n const follow = (event: MouseEvent & { currentTarget: HTMLAnchorElement }) => {\n if (!plainClick(event)) return;\n if (!props.beforeSelect) return hide(false);\n event.preventDefault();\n const href = event.currentTarget.href;\n choose(() => documentNavigate(href));\n };\n\n const rows = () => (panel ? Array.from(panel.querySelectorAll<HTMLElement>(\"[data-switcher-row]:not([aria-disabled='true'])\")) : []);\n const focusRow = (index: number) => {\n const all = rows();\n if (all.length) all[(index + all.length) % all.length]?.focus();\n };\n const show = (focus: \"start\" | \"end\" = \"start\") => {\n if (!panel || panel.matches(\":popover-open\")) return;\n load();\n keyboardOpened = ringOnReturn(trigger);\n setQuery(\"\");\n panel.showPopover();\n setOpen(true);\n listen(true);\n place(true);\n queueMicrotask(() => {\n // On touch the search field waits for a tap, so the keyboard does not cover the list on opening.\n if (searchable() && focus === \"start\" && !window.matchMedia(\"(pointer: coarse)\").matches) search?.focus();\n else if (focus === \"end\") focusRow(-1);\n // While the list loads, the panel holds focus, so arriving rows do not move it.\n else (panel?.querySelector<HTMLElement>('[aria-current=\"true\"]') ?? (props.options ? rows()[0] : panel))?.focus();\n });\n };\n const hide = (restoreFocus: boolean) => {\n if (restoreFocus) returnFocus(trigger, keyboardOpened);\n if (panel?.matches(\":popover-open\")) panel.hidePopover();\n };\n\n const onPanelKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n const all = rows();\n const index = all.indexOf(target);\n if (event.key === \"Escape\") {\n // The panel closes alone; an overlay sidebar around it stays open.\n event.preventDefault();\n event.stopPropagation();\n hide(true);\n } else if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusRow(index + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if ((event.key === \"Home\" || event.key === \"End\") && target !== search) {\n event.preventDefault();\n focusRow(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Enter\" && target === search) {\n event.preventDefault();\n panel?.querySelector<HTMLElement>(\"[data-switcher-option]\")?.click();\n } else if (\n search &&\n target !== search &&\n event.key.length === 1 &&\n event.key !== \" \" &&\n !(event.metaKey || event.ctrlKey || event.altKey)\n ) {\n // Typing anywhere in a long list searches it.\n event.preventDefault();\n setQuery(query() + event.key);\n search.focus();\n }\n };\n\n return (\n <div class={`k2b-sidebar-switcher ${props.class ?? \"\"}`} data-description={props.description ? \"\" : undefined}>\n <button\n ref={trigger}\n type=\"button\"\n class=\"k2b-sidebar-switcher__trigger\"\n aria-label={props.label}\n aria-describedby={`${id}-purpose`}\n aria-haspopup=\"dialog\"\n aria-expanded={open()}\n aria-controls={id}\n // The full name for a truncated title and for the icon in a collapsed rail.\n title={props.label}\n onPointerEnter={load}\n onFocus={load}\n onPointerDown={() => (openAtPress = panel?.matches(\":popover-open\"))}\n onClick={() => {\n const wasOpen = openAtPress ?? panel?.matches(\":popover-open\");\n openAtPress = undefined;\n if (wasOpen) hide(false);\n else show();\n }}\n onKeyDown={(event) => {\n if (event.key !== \"ArrowDown\" && event.key !== \"ArrowUp\") return;\n event.preventDefault();\n if (open()) focusRow(event.key === \"ArrowDown\" ? 0 : -1);\n else show(event.key === \"ArrowDown\" ? \"start\" : \"end\");\n }}\n >\n <span class=\"k2b-sidebar-switcher__icon\" style={tint(props.color)} aria-hidden=\"true\">\n <i class={iconClass(props.icon)} />\n </span>\n <span class=\"k2b-sidebar-switcher__copy\" aria-hidden=\"true\">\n <span>{props.label}</span>\n <Show when={props.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n <i class=\"ti ti-selector k2b-sidebar-switcher__chevron\" aria-hidden=\"true\" />\n <span id={`${id}-purpose`} class=\"k2b-sr-only\">\n {[props.description, props.switchLabel].filter(Boolean).join(\", \")}\n </span>\n </button>\n <div\n ref={panel}\n id={id}\n popover=\"auto\"\n tabIndex={-1}\n role=\"dialog\"\n aria-label={props.switchLabel}\n class=\"k2b-sidebar-switcher__panel\"\n onKeyDown={onPanelKeyDown}\n onFocusOut={(event) => {\n const next = event.relatedTarget as Node | null;\n // Tabbing out closes the panel; focus that leaves the window keeps it.\n if (next && !panel?.contains(next) && next !== trigger) hide(false);\n }}\n onToggle={(event) => {\n if ((event as ToggleEvent).newState === \"open\") return;\n setOpen(false);\n listen(false);\n }}\n >\n <Show when={searchable()}>\n <div class=\"k2b-choice-search k2b-sidebar-switcher__search\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n <input\n ref={search}\n type=\"search\"\n data-switcher-row\n value={query()}\n placeholder={messages().search}\n aria-label={messages().searchLabel}\n aria-controls={`${id}-options`}\n onInput={(event) => setQuery(event.currentTarget.value)}\n />\n </div>\n </Show>\n <ul id={`${id}-options`} class=\"k2b-sidebar-switcher__options\">\n <Show\n when={props.options}\n fallback={\n <li class=\"k2b-choice-status\" role=\"status\" data-tone={props.error ? \"danger\" : undefined}>\n <Show\n when={props.error}\n fallback={\n <>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n <span>{messages().loading}</span>\n </>\n }\n >\n {(error) => (\n <>\n <span>{error()}</span>\n <button type=\"button\" data-switcher-row onClick={() => props.onLoad?.()}>\n {messages().retry}\n </button>\n </>\n )}\n </Show>\n </li>\n }\n >\n <For\n each={visible()}\n fallback={\n <li class=\"k2b-choice-status\" role=\"status\">\n {messages().noResults}\n </li>\n }\n >\n {(option) => {\n const current = () => option.id === props.currentId;\n return (\n <li>\n <a\n href={option.href}\n class=\"k2b-dropdown__item k2b-sidebar-switcher__option\"\n data-switcher-row\n data-switcher-option\n aria-current={current() ? \"true\" : undefined}\n onClick={(event) => {\n // The open container is already here: choosing it only closes the panel.\n if (current() && plainClick(event)) {\n event.preventDefault();\n hide(true);\n } else follow(event);\n }}\n >\n <span class=\"k2b-sidebar-switcher__option-icon\" style={tint(option.color ?? props.color)} aria-hidden=\"true\">\n <i class={iconClass(option.icon ?? props.icon)} />\n </span>\n <span class=\"k2b-dropdown__copy\">\n <span>{option.label}</span>\n <Show when={option.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n <i class=\"ti ti-check k2b-dropdown__check\" data-hidden={current() ? undefined : \"true\"} aria-hidden=\"true\" />\n </a>\n </li>\n );\n }}\n </For>\n </Show>\n </ul>\n <Show when={props.actions?.length}>\n <ul class=\"k2b-sidebar-switcher__actions\">\n <For each={props.actions}>\n {(item) => (\n <li>\n <Show\n when={item.href}\n fallback={\n <button\n type=\"button\"\n class=\"k2b-dropdown__item\"\n data-switcher-row\n disabled={item.disabled}\n aria-disabled={item.disabled ? \"true\" : undefined}\n onClick={() => choose(() => item.action?.())}\n >\n <i class={iconClass(item.icon)} aria-hidden=\"true\" />\n <span class=\"k2b-dropdown__copy\">\n <span>{item.label}</span>\n </span>\n </button>\n }\n >\n {(href) => (\n <a\n href={item.disabled ? undefined : href()}\n class=\"k2b-dropdown__item\"\n data-switcher-row\n aria-disabled={item.disabled ? \"true\" : undefined}\n onClick={(event) => item.disabled || follow(event)}\n >\n <i class={iconClass(item.icon)} aria-hidden=\"true\" />\n <span class=\"k2b-dropdown__copy\">\n <span>{item.label}</span>\n </span>\n </a>\n )}\n </Show>\n </li>\n )}\n </For>\n </ul>\n </Show>\n </div>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -31,16 +31,16 @@ export function TabBar(props) {
|
|
|
31
31
|
_el$6 = _el$5.nextSibling;
|
|
32
32
|
_$insert(_el$6, () => item.label);
|
|
33
33
|
_$effect(_p$ => {
|
|
34
|
-
var _v$
|
|
35
|
-
_v$
|
|
36
|
-
_v$
|
|
37
|
-
_v$
|
|
38
|
-
_v$
|
|
39
|
-
_v$
|
|
40
|
-
_v$
|
|
41
|
-
_v$
|
|
42
|
-
_v$
|
|
43
|
-
_v$
|
|
34
|
+
var _v$4 = item.href,
|
|
35
|
+
_v$5 = item.current ? "page" : undefined,
|
|
36
|
+
_v$6 = item.id,
|
|
37
|
+
_v$7 = item.title ?? item.label,
|
|
38
|
+
_v$8 = item.icon;
|
|
39
|
+
_v$4 !== _p$.e && _$setAttribute(_el$4, "href", _p$.e = _v$4);
|
|
40
|
+
_v$5 !== _p$.t && _$setAttribute(_el$4, "aria-current", _p$.t = _v$5);
|
|
41
|
+
_v$6 !== _p$.a && _$setAttribute(_el$4, "data-tab", _p$.a = _v$6);
|
|
42
|
+
_v$7 !== _p$.o && _$setAttribute(_el$4, "data-k2b-title", _p$.o = _v$7);
|
|
43
|
+
_v$8 !== _p$.i && _$className(_el$5, _p$.i = _v$8);
|
|
44
44
|
return _p$;
|
|
45
45
|
}, {
|
|
46
46
|
e: undefined,
|
|
@@ -54,13 +54,16 @@ export function TabBar(props) {
|
|
|
54
54
|
}));
|
|
55
55
|
_$effect(_p$ => {
|
|
56
56
|
var _v$ = props.class ? `k2b-tab-bar ${props.class}` : "k2b-tab-bar",
|
|
57
|
-
_v$2 = props.label
|
|
57
|
+
_v$2 = props.label,
|
|
58
|
+
_v$3 = props.variant === "floating" ? "floating" : undefined;
|
|
58
59
|
_v$ !== _p$.e && _$className(_el$, _p$.e = _v$);
|
|
59
60
|
_v$2 !== _p$.t && _$setAttribute(_el$, "aria-label", _p$.t = _v$2);
|
|
61
|
+
_v$3 !== _p$.a && _$setAttribute(_el$, "data-variant", _p$.a = _v$3);
|
|
60
62
|
return _p$;
|
|
61
63
|
}, {
|
|
62
64
|
e: undefined,
|
|
63
|
-
t: undefined
|
|
65
|
+
t: undefined,
|
|
66
|
+
a: undefined
|
|
64
67
|
});
|
|
65
68
|
return _el$;
|
|
66
69
|
})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/TabBar.tsx"],"sourcesContent":["import { For, type JSX } from \"solid-js\";\n\nexport type TabBarItem = {\n id: string;\n label: string;\n /** Icon class, for example `ti ti-home`. */\n icon: string;\n href: string;\n /** The header title of the page this item opens, shown while that page loads. Defaults to `label`. */\n title?: string;\n /** The page this item leads to is open. */\n current?: boolean;\n};\n\nexport type TabBarProps = {\n /** Accessible name of the navigation landmark. */\n label: string;\n /** Up to five destinations; further items are not rendered. Put the rest behind a \"More\" destination. */\n items: readonly TabBarItem[];\n class?: string;\n};\n\n/** A phone app's bottom navigation holds at most five destinations. */\nconst TAB_BAR_MAX_ITEMS = 5;\n\n/**\n * Bottom navigation between a phone app's top-level pages: native links with equal widths, an icon above each label,\n * and `aria-current=\"page\"` on the open page. Place it in `MobileShell`'s footer; it pads the bottom safe area. There a\n * tab switches at the first touch: the shell shows the item's `title` while its page loads.\n */\nexport function TabBar(props: TabBarProps): JSX.Element {\n return (\n <nav
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/TabBar.tsx"],"sourcesContent":["import { For, type JSX } from \"solid-js\";\n\nexport type TabBarItem = {\n id: string;\n label: string;\n /** Icon class, for example `ti ti-home`. */\n icon: string;\n href: string;\n /** The header title of the page this item opens, shown while that page loads. Defaults to `label`. */\n title?: string;\n /** The page this item leads to is open. */\n current?: boolean;\n};\n\nexport type TabBarProps = {\n /** Accessible name of the navigation landmark. */\n label: string;\n /** Up to five destinations; further items are not rendered. Put the rest behind a \"More\" destination. */\n items: readonly TabBarItem[];\n /**\n * `bar` (the default) is flat on the page canvas and pads the bottom safe area. `floating` is a rounded bar that the\n * host places above the content, away from the safe areas; its open item is a raised chip. The host may paint it\n * with `--k2b-tab-bar-background`, color its items with `--k2b-tab-bar-color`, and color the open item with\n * `--k2b-tab-bar-selected-background` and `--k2b-tab-bar-selected-color`.\n */\n variant?: \"bar\" | \"floating\";\n class?: string;\n};\n\n/** A phone app's bottom navigation holds at most five destinations. */\nconst TAB_BAR_MAX_ITEMS = 5;\n\n/**\n * Bottom navigation between a phone app's top-level pages: native links with equal widths, an icon above each label,\n * and `aria-current=\"page\"` on the open page. Place it in `MobileShell`'s footer; it pads the bottom safe area. There a\n * tab switches at the first touch: the shell shows the item's `title` while its page loads.\n */\nexport function TabBar(props: TabBarProps): JSX.Element {\n return (\n <nav\n class={props.class ? `k2b-tab-bar ${props.class}` : \"k2b-tab-bar\"}\n aria-label={props.label}\n data-variant={props.variant === \"floating\" ? \"floating\" : undefined}\n >\n <ul>\n <For each={props.items.slice(0, TAB_BAR_MAX_ITEMS)}>\n {(item) => (\n <li>\n <a\n href={item.href}\n aria-current={item.current ? \"page\" : undefined}\n data-tab={item.id}\n data-k2b-title={item.title ?? item.label}\n >\n <i class={item.icon} aria-hidden=\"true\" />\n <span>{item.label}</span>\n </a>\n </li>\n )}\n </For>\n </ul>\n </nav>\n );\n}\n\nexport default TabBar;\n"],"mappings":";;;;;;;;;AAAA,SAAS,GAAG,QAAkB,UAAU;AA6BxC;AACA,MAAM,iBAAiB,GAAG,CAAC;;AAE3B;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS,MAAM,CAAC,KAAkB,EAAe;EACtD;IAAA;MAAA;IAAA,kCAOO,GAAG;MAAA,IAAC,IAAI;QAAA,OAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC;MAAA;MAAA,UAC9C,IAAI;QAAA;UAAA;UAAA;UAAA;QAAA,sBASO,IAAI,CAAC,KAAK;QAAA;UAAA,WANX,IAAI,CAAC,IAAI;YAAA,OACD,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,SAAS;YAAA,OACrC,IAAI,CAAC,EAAE;YAAA,OACD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK;YAAA,OAE9B,IAAI,CAAC,IAAI;UAAA;UAAA;UAAA;UAAA;UAAA;UAAA;QAAA;UAAA;UAAA;UAAA;UAAA;UAAA;QAAA;QAAA;MAAA;IAIxB;IAAA;MAAA,UAlBE,KAAK,CAAC,KAAK,GAAG,eAAe,KAAK,CAAC,KAAK,EAAE,GAAG,aAAa;QAAA,OACrD,KAAK,CAAC,KAAK;QAAA,OACT,KAAK,CAAC,OAAO,KAAK,UAAU,GAAG,UAAU,GAAG,SAAS;MAAA;MAAA;MAAA;MAAA;IAAA;MAAA;MAAA;MAAA;IAAA;IAAA;EAAA;AAqBzE;AAEA,eAAe,MAAM","ignoreList":[]}
|
|
@@ -6,8 +6,36 @@ const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
|
6
6
|
const ROOT_SELECTOR = "[data-k2b-app-workspace]";
|
|
7
7
|
const LAYOUT_REGION_SELECTOR = [ROOT_SELECTOR, ".k2b-app-workspace__sidebar", ".k2b-app-workspace__main-pane", ".k2b-app-workspace__detail", ".k2b-app-workspace__drawer", HANDLE_SELECTOR].join(",");
|
|
8
8
|
const TOGGLE_SELECTOR = "[data-app-workspace-sidebar-toggle]";
|
|
9
|
-
/**
|
|
10
|
-
const
|
|
9
|
+
/** Touch-tablet gestures remain viewport decisions. */
|
|
10
|
+
const TOUCH_DRAWER_QUERY = "(min-width: 48rem) and (max-width: 63.999rem) and (pointer: coarse)";
|
|
11
|
+
const DETAIL_TOGGLE_SELECTOR = "[data-app-workspace-detail-toggle]";
|
|
12
|
+
const DETAIL_DRAWER_SELECTOR = '.k2b-app-workspace__detail[data-workspace-detail-drawer="open"]';
|
|
13
|
+
const cssState = (element, name) => getComputedStyle(element).getPropertyValue(name).trim();
|
|
14
|
+
const detailWaiting = aside => cssState(aside, "--k2b-workspace-detail-waiting") === "1";
|
|
15
|
+
const detailDrawer = aside => aside.dataset.workspaceDetailDrawer === "open";
|
|
16
|
+
const detailToggle = aside => {
|
|
17
|
+
const root = workspaceRoot(aside);
|
|
18
|
+
return rootElements(root ?? aside, DETAIL_TOGGLE_SELECTOR).find(toggle => toggle.getAttribute("aria-controls") === aside.id);
|
|
19
|
+
};
|
|
20
|
+
// The trail's registered property accepts a length, not the drawer's percentage
|
|
21
|
+
// cap. Only a user-opened drawer needs this measurement; the first frame stays CSS-owned.
|
|
22
|
+
const detailDrawerContent = aside => aside.closest(".k2b-app-workspace__content");
|
|
23
|
+
const syncDetailDrawerSpace = aside => detailDrawerContent(aside)?.style.setProperty("--k2b-detail-space", `${aside.getBoundingClientRect().width}px`);
|
|
24
|
+
const parkDetailDrawer = (aside, focus = false) => {
|
|
25
|
+
if (!detailDrawer(aside)) return;
|
|
26
|
+
delete aside.dataset.workspaceDetailDrawer;
|
|
27
|
+
detailDrawerContent(aside)?.style.removeProperty("--k2b-detail-space");
|
|
28
|
+
const toggle = detailToggle(aside);
|
|
29
|
+
toggle?.setAttribute("aria-expanded", "false");
|
|
30
|
+
if (focus) toggle?.focus({
|
|
31
|
+
preventScroll: true
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Parks open waiting-detail drawers; focus follows only when it was inside one. */
|
|
36
|
+
export const parkWorkspaceDetailDrawers = (root = document) => {
|
|
37
|
+
for (const aside of root.querySelectorAll(DETAIL_DRAWER_SELECTOR)) parkDetailDrawer(aside, aside.contains(aside.ownerDocument.activeElement));
|
|
38
|
+
};
|
|
11
39
|
const FOCUSABLE_SELECTOR = ["a[href]", "button:not([disabled])", "input:not([disabled]):not([type='hidden'])", "select:not([disabled])", "textarea:not([disabled])", "[tabindex]:not([tabindex='-1'])"].join(",");
|
|
12
40
|
/** Text entry, menus, dialogs, and open popups keep their own keys. */
|
|
13
41
|
const KEY_OWNER_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false']), [role='textbox'], [role='combobox'], [role='searchbox'], [role='spinbutton'], [role='menu'], [role='listbox'], [role='dialog'], dialog, [aria-haspopup]:not([aria-haspopup='false'])[aria-expanded='true']";
|
|
@@ -59,8 +87,9 @@ const controlledPanel = (root, handle) => {
|
|
|
59
87
|
};
|
|
60
88
|
const flexibleLevel = pane => {
|
|
61
89
|
if (!pane.hasAttribute("data-workspace-level")) return false;
|
|
62
|
-
const
|
|
63
|
-
|
|
90
|
+
const root = workspaceRoot(pane);
|
|
91
|
+
const widthClass = root ? cssState(root, "--k2b-workspace-level-class") : "";
|
|
92
|
+
return Boolean(widthClass && pane.dataset.workspaceLevelLast?.split(" ").includes(widthClass));
|
|
64
93
|
};
|
|
65
94
|
const levelsMain = handle => handle.closest(".k2b-app-workspace__main")?.hasAttribute("data-workspace-levels") ?? false;
|
|
66
95
|
const controlsResizableRegion = (root, handle, kind) => {
|
|
@@ -228,7 +257,6 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
228
257
|
const ownerDocument = eventRoot.ownerDocument ?? document;
|
|
229
258
|
const ownerWindow = ownerDocument.defaultView ?? window;
|
|
230
259
|
let overlay = null;
|
|
231
|
-
const compact = typeof window.matchMedia === "function" ? window.matchMedia(COMPACT_QUERY) : null;
|
|
232
260
|
// A server-rendered head names the sidebar before the sidebar renders; the button learns its real id now.
|
|
233
261
|
for (const toggle of eventRoot.querySelectorAll(TOGGLE_SELECTOR)) overlayTarget(toggle);
|
|
234
262
|
const openOverlay = target => {
|
|
@@ -269,12 +297,28 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
269
297
|
};
|
|
270
298
|
const onClick = event => {
|
|
271
299
|
const target = eventElement(event);
|
|
300
|
+
const detailButton = target?.closest(DETAIL_TOGGLE_SELECTOR);
|
|
301
|
+
if (detailButton) {
|
|
302
|
+
const root = workspaceRoot(detailButton);
|
|
303
|
+
const aside = root ? controlledPanel(root, detailButton) : null;
|
|
304
|
+
if (!aside || aside.hidden || !detailWaiting(aside) || claimedToggleClicks.has(event)) return;
|
|
305
|
+
claimedToggleClicks.add(event);
|
|
306
|
+
if (detailDrawer(aside)) parkDetailDrawer(aside);else {
|
|
307
|
+
aside.dataset.workspaceDetailDrawer = "open";
|
|
308
|
+
detailButton.setAttribute("aria-expanded", "true");
|
|
309
|
+
syncDetailDrawerSpace(aside);
|
|
310
|
+
if (event.detail === 0) requestTitle(aside);else detailButton.focus({
|
|
311
|
+
preventScroll: true
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
272
316
|
const toggle = target?.closest(TOGGLE_SELECTOR);
|
|
273
317
|
if (toggle) {
|
|
274
318
|
const next = overlayTarget(toggle);
|
|
275
319
|
if (!next || claimedToggleClicks.has(event)) return;
|
|
276
320
|
claimedToggleClicks.add(event);
|
|
277
|
-
if (overlay?.sidebar === next.sidebar) closeOverlay("button");else if (
|
|
321
|
+
if (overlay?.sidebar === next.sidebar) closeOverlay("button");else if (cssState(next.root, "--k2b-workspace-sidebar-form") === "overlay") openOverlay(next);
|
|
278
322
|
return;
|
|
279
323
|
}
|
|
280
324
|
if (!overlay || !target) return;
|
|
@@ -310,6 +354,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
310
354
|
// Dialogs and popovers keep their own Tab order.
|
|
311
355
|
const page = phone?.matches ? allRoots().flatMap(openDetails).find(phonePage) : undefined;
|
|
312
356
|
if (!page || ownerDocument.querySelector("dialog[open], [aria-modal='true']") || target.closest("[popover]")) return;
|
|
357
|
+
if (target !== ownerDocument.body) {
|
|
358
|
+
const rect = target.getBoundingClientRect();
|
|
359
|
+
const hit = ownerDocument.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
360
|
+
if (hit && target.contains(hit)) return;
|
|
361
|
+
}
|
|
313
362
|
const stops = tabStopsIn(page);
|
|
314
363
|
event.preventDefault();
|
|
315
364
|
(event.shiftKey ? stops.at(-1) : stops[0])?.focus();
|
|
@@ -334,12 +383,37 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
334
383
|
first.focus();
|
|
335
384
|
}
|
|
336
385
|
};
|
|
337
|
-
|
|
338
|
-
|
|
386
|
+
// Whether each open detail waited at the last check, so a column that starts
|
|
387
|
+
// waiting under focus hands that focus to its Details button.
|
|
388
|
+
const detailWasWaiting = new WeakMap();
|
|
389
|
+
const syncHostState = () => {
|
|
390
|
+
if (overlay && cssState(overlay.root, "--k2b-workspace-sidebar-form") !== "overlay") closeOverlay("none");
|
|
391
|
+
for (const root of allRoots()) {
|
|
392
|
+
// A drawer removed with its panel leaves no space behind in Content.
|
|
393
|
+
for (const content of rootElements(root, ".k2b-app-workspace__content")) if (content.style.getPropertyValue("--k2b-detail-space") && !content.querySelector(DETAIL_DRAWER_SELECTOR)) content.style.removeProperty("--k2b-detail-space");
|
|
394
|
+
for (const aside of rootElements(root, detailSelector)) {
|
|
395
|
+
if (aside.hidden) {
|
|
396
|
+
detailWasWaiting.delete(aside);
|
|
397
|
+
parkDetailDrawer(aside);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const waiting = detailWaiting(aside);
|
|
401
|
+
const before = detailWasWaiting.get(aside);
|
|
402
|
+
detailWasWaiting.set(aside, waiting);
|
|
403
|
+
if (!waiting) {
|
|
404
|
+
// The Details button hides with the waiting; its focus moves to the returning column's title.
|
|
405
|
+
const toggleFocused = detailToggle(aside)?.contains(ownerDocument.activeElement) ?? false;
|
|
406
|
+
parkDetailDrawer(aside);
|
|
407
|
+
if (toggleFocused) focusTitle(aside);
|
|
408
|
+
} else if (detailDrawer(aside)) syncDetailDrawerSpace(aside);else if (before !== true && aside.contains(ownerDocument.activeElement)) detailToggle(aside)?.focus({
|
|
409
|
+
preventScroll: true
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
339
413
|
};
|
|
340
414
|
const phone = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 47.999rem)") : null;
|
|
341
415
|
const wide = typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 48rem)") : null;
|
|
342
|
-
const touchDrawer = typeof window.matchMedia === "function" ? window.matchMedia(
|
|
416
|
+
const touchDrawer = typeof window.matchMedia === "function" ? window.matchMedia(TOUCH_DRAWER_QUERY) : null;
|
|
343
417
|
const detailSelector = ".k2b-app-workspace__detail";
|
|
344
418
|
const grabberSelector = ".k2b-app-workspace__detail-grabber";
|
|
345
419
|
const detailHidden = new WeakMap();
|
|
@@ -499,7 +573,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
499
573
|
delete aside.dataset.workspaceDetailDragging;
|
|
500
574
|
};
|
|
501
575
|
const anchorItem = (root, aside, snapshot) => {
|
|
502
|
-
if (!wide?.matches || touchDrawer?.matches && closable(aside) || !snapshot?.item.isConnected) return;
|
|
576
|
+
if (!wide?.matches || touchDrawer?.matches && closable(aside) || detailDrawer(aside) || !snapshot?.item.isConnected) return;
|
|
503
577
|
if (!mainElement(root)?.contains(snapshot.item) || snapshot.item.closest(ROOT_SELECTOR) !== root) return;
|
|
504
578
|
const scroller = snapshot.scroller;
|
|
505
579
|
if (scroller?.isConnected && scroller.scrollTop === snapshot.scrollTop) scroller.scrollTop += snapshot.item.getBoundingClientRect().top - snapshot.top;
|
|
@@ -605,6 +679,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
605
679
|
const followedInteraction = snapshot?.fresh ?? false;
|
|
606
680
|
if (snapshot) snapshot.fresh = false;
|
|
607
681
|
if (aside.hidden) {
|
|
682
|
+
parkDetailDrawer(aside);
|
|
608
683
|
if (pendingTitle?.aside === aside) cancelTitle();
|
|
609
684
|
if (panelStep === aside) panelStep = null;
|
|
610
685
|
if (root.hasAttribute("data-workspace-motion") && !prefersReducedMotion() && wide?.matches) {
|
|
@@ -633,6 +708,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
633
708
|
if (detailDrag?.aside === aside) finishDetailDrag(true);
|
|
634
709
|
return;
|
|
635
710
|
}
|
|
711
|
+
if (detailWaiting(aside) && !detailDrawer(aside)) return;
|
|
636
712
|
anchorItem(root, aside, snapshot);
|
|
637
713
|
if (phone?.matches) setDetent(aside, false);
|
|
638
714
|
if (followedInteraction && snapshot) {
|
|
@@ -653,13 +729,18 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
653
729
|
const root = target.closest(ROOT_SELECTOR);
|
|
654
730
|
let aside = target.closest(detailSelector);
|
|
655
731
|
if (aside?.hidden || aside && (!eventRoot.contains(aside) || aside.closest(ROOT_SELECTOR) !== root)) aside = null;
|
|
656
|
-
if (!aside && root && observedRoots.has(root) && mainElement(root)?.contains(target)) aside = openDetails(root)
|
|
732
|
+
if (!aside && root && observedRoots.has(root) && (mainElement(root)?.contains(target) || target.closest(DETAIL_TOGGLE_SELECTOR))) aside = openDetails(root).find(isVisible) ?? null;
|
|
657
733
|
if (!aside && target === ownerDocument.body) {
|
|
658
|
-
const open = allRoots().flatMap(openDetails);
|
|
734
|
+
const open = allRoots().flatMap(openDetails).filter(isVisible);
|
|
659
735
|
if (open.length === 1) aside = open[0];
|
|
660
736
|
}
|
|
661
737
|
const owningRoot = aside?.closest(ROOT_SELECTOR);
|
|
662
|
-
if (!aside || !owningRoot) return;
|
|
738
|
+
if (!aside || !owningRoot || !isVisible(aside)) return;
|
|
739
|
+
if (event.key === "Escape" && detailDrawer(aside)) {
|
|
740
|
+
parkDetailDrawer(aside, true);
|
|
741
|
+
event.preventDefault();
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
663
744
|
if (event.key === "Escape" && closable(aside)) requestClose(aside);else if ((event.key.toLowerCase() === "j" || event.key.toLowerCase() === "k") && aside.hasAttribute("data-workspace-detail-steppable")) {
|
|
664
745
|
const main = mainElement(owningRoot);
|
|
665
746
|
const inMain = remembered.get(owningRoot)?.item.contains(target) || main?.contains(target);
|
|
@@ -1196,14 +1277,43 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1196
1277
|
reconcileFrame = requestAnimationFrame(() => {
|
|
1197
1278
|
reconcileFrame = null;
|
|
1198
1279
|
reconcile();
|
|
1280
|
+
// Reconciling can restore a drawer's persisted width; its trail space follows.
|
|
1281
|
+
for (const root of allRoots()) for (const aside of rootElements(root, DETAIL_DRAWER_SELECTOR)) syncDetailDrawerSpace(aside);
|
|
1199
1282
|
});
|
|
1200
1283
|
};
|
|
1201
|
-
const onResize = () =>
|
|
1284
|
+
const onResize = () => {
|
|
1285
|
+
syncHostState();
|
|
1286
|
+
scheduleReconcile();
|
|
1287
|
+
};
|
|
1202
1288
|
const observedRoots = new Set();
|
|
1203
1289
|
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
1290
|
+
syncHostState();
|
|
1204
1291
|
scheduleReconcile();
|
|
1205
1292
|
});
|
|
1293
|
+
// A host can change its width while a workspace with a fixed width keeps
|
|
1294
|
+
// its own, so the controller also watches each root's nearest named host.
|
|
1295
|
+
const observedHosts = new Set();
|
|
1296
|
+
// Found once per root: reading styles on every mutation would cost a style pass.
|
|
1297
|
+
const rootHosts = new WeakMap();
|
|
1298
|
+
const hostOf = root => {
|
|
1299
|
+
if (rootHosts.has(root)) return rootHosts.get(root);
|
|
1300
|
+
let host = null;
|
|
1301
|
+
for (let element = root.parentElement; element && !host; element = element.parentElement) if (getComputedStyle(element).containerName.split(/\s+/).includes("k2b-app-area")) host = element;
|
|
1302
|
+
rootHosts.set(root, host);
|
|
1303
|
+
return host;
|
|
1304
|
+
};
|
|
1206
1305
|
const syncObservedRoots = () => {
|
|
1306
|
+
const hosts = new Set(allRoots().map(hostOf).filter(host => host !== null));
|
|
1307
|
+
observedHosts.forEach(host => {
|
|
1308
|
+
if (hosts.has(host)) return;
|
|
1309
|
+
resizeObserver?.unobserve(host);
|
|
1310
|
+
observedHosts.delete(host);
|
|
1311
|
+
});
|
|
1312
|
+
hosts.forEach(host => {
|
|
1313
|
+
if (observedHosts.has(host)) return;
|
|
1314
|
+
observedHosts.add(host);
|
|
1315
|
+
resizeObserver?.observe(host);
|
|
1316
|
+
});
|
|
1207
1317
|
for (const elements of [touchedDetails, titleTabIndices, motionRoots]) {
|
|
1208
1318
|
elements.forEach(element => {
|
|
1209
1319
|
if (!element.isConnected) elements.delete(element);
|
|
@@ -1265,6 +1375,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1265
1375
|
const changed = records.some(record => record.type === "attributes" ? record.target instanceof Element && record.target.matches(LAYOUT_REGION_SELECTOR) : [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(layoutNode));
|
|
1266
1376
|
if (!changed) return;
|
|
1267
1377
|
syncObservedRoots();
|
|
1378
|
+
// A new width token or a removed panel can end waiting without any box resizing.
|
|
1379
|
+
if (records.some(record => record.type === "childList" || record.attributeName === "data-width")) syncHostState();
|
|
1268
1380
|
scheduleReconcile();
|
|
1269
1381
|
});
|
|
1270
1382
|
eventRoot.addEventListener("pointerdown", onPointerDown);
|
|
@@ -1289,7 +1401,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1289
1401
|
});
|
|
1290
1402
|
eventRoot.addEventListener("touchend", onDetailTouchEnd);
|
|
1291
1403
|
eventRoot.addEventListener("touchcancel", onDetailTouchEnd);
|
|
1292
|
-
|
|
1404
|
+
phone?.addEventListener("change", syncHostState);
|
|
1293
1405
|
phone?.addEventListener("change", syncPhonePages);
|
|
1294
1406
|
eventRoot.addEventListener("keydown", onKeyDown);
|
|
1295
1407
|
eventRoot.addEventListener("focusin", onFocusIn);
|
|
@@ -1299,7 +1411,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1299
1411
|
mutationObserver?.observe(eventRoot, {
|
|
1300
1412
|
attributes: true,
|
|
1301
1413
|
attributeOldValue: true,
|
|
1302
|
-
attributeFilter: ["hidden", "data-workspace-level-shown"],
|
|
1414
|
+
attributeFilter: ["hidden", "data-workspace-level-shown", "data-width"],
|
|
1303
1415
|
childList: true,
|
|
1304
1416
|
subtree: true
|
|
1305
1417
|
});
|
|
@@ -1348,11 +1460,12 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1348
1460
|
mutationObserver?.disconnect();
|
|
1349
1461
|
resizeObserver?.disconnect();
|
|
1350
1462
|
observedRoots.clear();
|
|
1463
|
+
observedHosts.clear();
|
|
1351
1464
|
closeOverlay("none");
|
|
1352
1465
|
eventRoot.removeEventListener("pointerdown", onPointerDown);
|
|
1353
1466
|
eventRoot.removeEventListener("click", onClick);
|
|
1354
1467
|
ownerDocument.removeEventListener("keydown", onOverlayKeyDown);
|
|
1355
|
-
|
|
1468
|
+
phone?.removeEventListener("change", syncHostState);
|
|
1356
1469
|
eventRoot.removeEventListener("keydown", onKeyDown);
|
|
1357
1470
|
eventRoot.removeEventListener("focusin", onFocusIn);
|
|
1358
1471
|
window.removeEventListener("resize", onResize);
|