@k2b/ui 0.31.0 → 0.33.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/base.css +2 -0
- package/dist/browser/actions/Dropdown.js +159 -99
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +113 -56
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +8 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -1
- package/dist/browser/actions/filter-messages.js +26 -2
- package/dist/browser/actions/filter-messages.js.map +1 -1
- package/dist/browser/actions/menu-rows.js +23 -3
- package/dist/browser/actions/menu-rows.js.map +1 -1
- package/dist/browser/actions/menu-sheet.js +2 -7
- package/dist/browser/actions/menu-sheet.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +480 -403
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/chat-messages.js +14 -0
- package/dist/browser/chat/chat-messages.js.map +1 -1
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/content/Calendar.js +39 -29
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/CalendarDayView.js +159 -165
- package/dist/browser/content/CalendarDayView.js.map +1 -1
- package/dist/browser/content/Chart.js +104 -55
- package/dist/browser/content/Chart.js.map +1 -1
- package/dist/browser/content/ChartExplorer.js +5 -3
- package/dist/browser/content/ChartExplorer.js.map +1 -1
- package/dist/browser/content/MapView.js +4 -4
- package/dist/browser/content/MapView.js.map +1 -1
- package/dist/browser/content/MessageRow.js +607 -343
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/ReactionBar.js +17 -0
- package/dist/browser/content/ReactionBar.js.map +1 -1
- package/dist/browser/content/chart-inspection-core.js +4 -1
- package/dist/browser/content/chart-inspection-core.js.map +1 -1
- package/dist/browser/content/code-highlight.js +66 -0
- package/dist/browser/content/code-highlight.js.map +1 -1
- package/dist/browser/content/index.js +2 -1
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/map-view-live.js +47 -10
- package/dist/browser/content/map-view-live.js.map +1 -1
- package/dist/browser/content/map-view-types.js.map +1 -1
- package/dist/browser/feedback/dialog-core.js +6 -0
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/inputs/AutocompleteEditor.js +167 -104
- package/dist/browser/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/browser/inputs/RichSelect.js +1474 -0
- package/dist/browser/inputs/RichSelect.js.map +1 -0
- package/dist/browser/inputs/TextInput.js +99 -61
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/choice.js +19 -8
- package/dist/browser/inputs/choice.js.map +1 -1
- package/dist/browser/inputs/index.js +1 -0
- package/dist/browser/inputs/index.js.map +1 -1
- package/dist/browser/inputs/rich-select-messages.js +46 -0
- package/dist/browser/inputs/rich-select-messages.js.map +1 -0
- package/dist/browser/intl/shortcut-messages.js +4 -0
- package/dist/browser/intl/shortcut-messages.js.map +1 -1
- package/dist/browser/intl/shortcut.js +4 -0
- package/dist/browser/intl/shortcut.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +624 -565
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/DetailPanel.js +10 -1
- package/dist/browser/layout/DetailPanel.js.map +1 -1
- package/dist/browser/layout/MobileShell.js +213 -38
- package/dist/browser/layout/MobileShell.js.map +1 -1
- package/dist/browser/layout/Navigation.js +14 -5
- package/dist/browser/layout/Navigation.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +9 -3
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/ResizeSeparator.js +8 -2
- package/dist/browser/layout/ResizeSeparator.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +192 -31
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/index.js +1 -0
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/layout/navigation-model.js.map +1 -1
- package/dist/browser/layout/resize-separator.js +25 -1
- package/dist/browser/layout/resize-separator.js.map +1 -1
- package/dist/browser/layout/scroll-fade.js +32 -5
- package/dist/browser/layout/scroll-fade.js.map +1 -1
- package/dist/browser/surfaces/NoticeCard.js +46 -30
- package/dist/browser/surfaces/NoticeCard.js.map +1 -1
- package/dist/browser/surfaces/Skeleton.js +14 -0
- package/dist/browser/surfaces/Skeleton.js.map +1 -0
- package/dist/browser/surfaces/index.js +1 -0
- package/dist/browser/surfaces/index.js.map +1 -1
- package/dist/browser/surfaces/resource-messages.js +2 -2
- package/dist/browser/surfaces/resource-messages.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +51 -27
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +67 -16
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +8 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -1
- package/dist/ssr/actions/filter-messages.js +26 -2
- package/dist/ssr/actions/filter-messages.js.map +1 -1
- package/dist/ssr/actions/menu-rows.js +23 -3
- package/dist/ssr/actions/menu-rows.js.map +1 -1
- package/dist/ssr/actions/menu-sheet.js +2 -7
- package/dist/ssr/actions/menu-sheet.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +96 -67
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/chat-messages.js +14 -0
- package/dist/ssr/chat/chat-messages.js.map +1 -1
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/content/Calendar.js +40 -31
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/CalendarDayView.js +40 -48
- package/dist/ssr/content/CalendarDayView.js.map +1 -1
- package/dist/ssr/content/Chart.js +27 -7
- package/dist/ssr/content/Chart.js.map +1 -1
- package/dist/ssr/content/ChartExplorer.js +5 -3
- package/dist/ssr/content/ChartExplorer.js.map +1 -1
- package/dist/ssr/content/MapView.js +4 -4
- package/dist/ssr/content/MapView.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +264 -91
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/ReactionBar.js +17 -0
- package/dist/ssr/content/ReactionBar.js.map +1 -1
- package/dist/ssr/content/chart-inspection-core.js +4 -1
- package/dist/ssr/content/chart-inspection-core.js.map +1 -1
- package/dist/ssr/content/code-highlight.js +66 -0
- package/dist/ssr/content/code-highlight.js.map +1 -1
- package/dist/ssr/content/index.js +2 -1
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/map-view-types.js.map +1 -1
- package/dist/ssr/feedback/dialog-core.js +6 -0
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/inputs/AutocompleteEditor.js +64 -24
- package/dist/ssr/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/ssr/inputs/RichSelect.js +950 -0
- package/dist/ssr/inputs/RichSelect.js.map +1 -0
- package/dist/ssr/inputs/TextInput.js +44 -17
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/choice.js +19 -8
- package/dist/ssr/inputs/choice.js.map +1 -1
- package/dist/ssr/inputs/index.js +1 -0
- package/dist/ssr/inputs/index.js.map +1 -1
- package/dist/ssr/inputs/rich-select-messages.js +46 -0
- package/dist/ssr/inputs/rich-select-messages.js.map +1 -0
- package/dist/ssr/intl/shortcut-messages.js +4 -0
- package/dist/ssr/intl/shortcut-messages.js.map +1 -1
- package/dist/ssr/intl/shortcut.js +4 -0
- package/dist/ssr/intl/shortcut.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +166 -125
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/DetailPanel.js +1 -1
- package/dist/ssr/layout/DetailPanel.js.map +1 -1
- package/dist/ssr/layout/MobileShell.js +100 -20
- package/dist/ssr/layout/MobileShell.js.map +1 -1
- package/dist/ssr/layout/Navigation.js +1 -1
- package/dist/ssr/layout/Navigation.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +9 -3
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/ResizeSeparator.js +4 -1
- package/dist/ssr/layout/ResizeSeparator.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +192 -31
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/index.js +1 -0
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/layout/navigation-model.js.map +1 -1
- package/dist/ssr/layout/resize-separator.js +25 -1
- package/dist/ssr/layout/resize-separator.js.map +1 -1
- package/dist/ssr/layout/scroll-fade.js +32 -5
- package/dist/ssr/layout/scroll-fade.js.map +1 -1
- package/dist/ssr/surfaces/NoticeCard.js +21 -12
- package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
- package/dist/ssr/surfaces/Skeleton.js +8 -0
- package/dist/ssr/surfaces/Skeleton.js.map +1 -0
- package/dist/ssr/surfaces/index.js +1 -0
- package/dist/ssr/surfaces/index.js.map +1 -1
- package/dist/ssr/surfaces/resource-messages.js +2 -2
- package/dist/ssr/surfaces/resource-messages.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +14 -0
- package/dist/types/actions/FilterChip.d.ts +11 -0
- package/dist/types/actions/filter-messages.d.ts +12 -0
- package/dist/types/actions/menu-rows.d.ts +7 -2
- package/dist/types/chat/ChatComposer.d.ts +13 -0
- package/dist/types/chat/chat-messages.d.ts +7 -0
- package/dist/types/chat/index.d.ts +1 -1
- package/dist/types/content/Calendar.d.ts +7 -0
- package/dist/types/content/Chart.d.ts +4 -2
- package/dist/types/content/ChartExplorer.d.ts +0 -1
- package/dist/types/content/MessageRow.d.ts +64 -2
- package/dist/types/content/code-highlight.d.ts +3 -1
- package/dist/types/content/index.d.ts +3 -2
- package/dist/types/content/map-view-live.d.ts +1 -1
- package/dist/types/content/map-view-types.d.ts +2 -0
- package/dist/types/feedback/dialog-core.d.ts +3 -0
- package/dist/types/inputs/AutocompleteEditor.d.ts +6 -1
- package/dist/types/inputs/RichSelect.d.ts +93 -0
- package/dist/types/inputs/TextInput.d.ts +5 -2
- package/dist/types/inputs/choice.d.ts +24 -4
- package/dist/types/inputs/index.d.ts +2 -0
- package/dist/types/inputs/rich-select-messages.d.ts +21 -0
- package/dist/types/intl/shortcut-messages.d.ts +2 -0
- package/dist/types/layout/AppWorkspace.d.ts +9 -0
- package/dist/types/layout/DetailPanel.d.ts +2 -0
- package/dist/types/layout/MobileShell.d.ts +52 -5
- package/dist/types/layout/app-workspace-controller.d.ts +2 -0
- package/dist/types/layout/index.d.ts +2 -1
- package/dist/types/layout/navigation-model.d.ts +2 -0
- package/dist/types/layout/resize-separator.d.ts +1 -0
- package/dist/types/layout/scroll-fade.d.ts +5 -0
- package/dist/types/surfaces/NoticeCard.d.ts +3 -0
- package/dist/types/surfaces/Skeleton.d.ts +6 -0
- package/dist/types/surfaces/index.d.ts +1 -0
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/PageTitle.tsx"],"sourcesContent":["import { createContext, createRoot, createSignal, createUniqueId, For, type JSX, onCleanup, onMount, Show, useContext } from \"solid-js\";\nimport { Dynamic, insert, isServer } from \"solid-js/web\";\nimport { IconButtonLink } from \"../actions/Button\";\nimport { installPageTitleController, PAGE_TITLE_CONTROLLER_ATTRIBUTE } from \"./page-title-controller\";\nimport { useWorkspaceMessages } from \"./workspace-messages\";\n\n/** A page above the current one. Only pages someone can go back to are levels, so each has a link. */\nexport type PageTrailLink = { title: string; href: string };\n/** The current page and the pages above it, nearest last. */\nexport type PageTrailState = { title: string; ancestors: PageTrailLink[] };\n\nexport type PageTitleProps = {\n trail: PageTrailState;\n /** Quiet text after the title, for example a count. */\n subtitle?: JSX.Element;\n /** Defaults to `\"h1\"`. Chrome that repeats a title the page already shows uses `\"p\"`. */\n as?: \"h1\" | \"h2\" | \"p\";\n /**\n * Marks a title drawn by surrounding chrome, such as the workspace head or a\n * host shell. It steps back when the page renders its own `PageTitle`, so a\n * page has one title with one way up.\n */\n fallback?: boolean;\n class?: string;\n};\n\n/** Window event a host dispatches after publishing a new page trail. */\nexport const PAGE_TRAIL_EVENT = \"k2b:page-trail\";\nconst SOURCE_SELECTOR = \"script[data-k2b-page-trail]\";\n\nconst isTrail = (value: unknown): value is PageTrailState => {\n if (!value || typeof value !== \"object\") return false;\n const trail = value as Partial<PageTrailState>;\n return (\n typeof trail.title === \"string\" &&\n Array.isArray(trail.ancestors) &&\n trail.ancestors.every((link) => typeof link?.title === \"string\" && typeof link?.href === \"string\")\n );\n};\n\n/** The trail a host published for this document, or `undefined` outside a browser or without one. */\nexport const readPageTrail = (): PageTrailState | undefined => {\n if (typeof document === \"undefined\") return undefined;\n const source = document.querySelector(SOURCE_SELECTOR);\n if (!source?.textContent) return undefined;\n try {\n const value: unknown = JSON.parse(source.textContent);\n return isTrail(value) ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** `<` is escaped so a title can never end the script element it travels in. */\nconst serializeTrail = (trail: PageTrailState): string => JSON.stringify(trail).replace(/</g, \"\\\\u003c\");\n\n/**\n * Publishes a new trail for the document in the browser, for example after an\n * in-page navigation. Workspace heads and page titles that read the host's\n * trail follow it.\n */\nexport const setPageTrail = (trail: PageTrailState): void => {\n if (typeof document === \"undefined\") return;\n let source = document.querySelector(SOURCE_SELECTOR);\n if (!source) {\n source = document.createElement(\"script\");\n source.setAttribute(\"type\", \"application/json\");\n source.setAttribute(\"data-k2b-page-trail\", \"\");\n document.body.append(source);\n }\n source.textContent = serializeTrail(trail);\n window.dispatchEvent(new Event(PAGE_TRAIL_EVENT));\n};\n\nconst PageTrailContext = createContext<() => PageTrailState | undefined>();\n\n/**\n * Gives a host's page trail to everything below it. During server rendering\n * the context carries it; the provider also publishes it into the document,\n * where browser-rendered parts such as islands read it. Below a provider that\n * renders in the browser, its `trail` prop is the source: update the prop.\n */\nexport function PageTrailProvider(props: { trail: PageTrailState | undefined; children: JSX.Element }): JSX.Element {\n return (\n <PageTrailContext.Provider value={() => props.trail}>\n <Show when={props.trail}>\n {(trail) => <script type=\"application/json\" data-k2b-page-trail=\"\" innerHTML={serializeTrail(trail())} />}\n </Show>\n {props.children}\n </PageTrailContext.Provider>\n );\n}\n\n/** The host's page trail: from the provider while rendering below it, otherwise from the document. */\nexport const usePageTrail = (): (() => PageTrailState | undefined) => {\n const context = useContext(PageTrailContext);\n if (context) return context;\n if (typeof window === \"undefined\") return () => undefined;\n const [trail, setTrail] = createSignal(readPageTrail());\n const update = () => setTrail(readPageTrail());\n window.addEventListener(PAGE_TRAIL_EVENT, update);\n onCleanup(() => window.removeEventListener(PAGE_TRAIL_EVENT, update));\n return trail;\n};\n\n/**\n * A page title with its way up. The number of pages above decides the form:\n * none shows the title alone; one puts \"‹ Parent\" above it; more put a path\n * row above it whose middle collapses into \"…\" only when it does not fit.\n * Both rows share the same height, so the title never moves between depths.\n * On phones a back arrow left of the title replaces the row above.\n */\nexport function PageTitle(props: PageTitleProps): JSX.Element {\n const messages = useWorkspaceMessages();\n // A title rendered on the server from the host's trail follows the host's later trails (see `followPageTrail`).\n const host = isServer ? useContext(PageTrailContext) : undefined;\n const menuId = `k2b-page-title-menu-${createUniqueId()}`;\n const ancestors = () => props.trail.ancestors;\n const parent = () => ancestors().at(-1);\n let root: HTMLDivElement | undefined;\n // A host may control every title in the document at once; a title outside\n // one still collapses its path.\n onMount(() => {\n if (!root || root.closest(`[${PAGE_TITLE_CONTROLLER_ATTRIBUTE}]`)) return;\n onCleanup(installPageTitleController(root));\n });\n return (\n <div\n ref={root}\n class={`k2b-page-title ${props.class ?? \"\"}`}\n data-depth={ancestors().length}\n data-fallback={props.fallback ? \"\" : undefined}\n data-k2b-server={isServer && props.trail === host?.() ? \"\" : undefined}\n >\n <Show when={parent()}>\n {(up) => (\n <IconButtonLink\n class=\"k2b-page-title__back\"\n href={up().href}\n label={messages().backTo({ page: up().title })}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up=\"\"\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </IconButtonLink>\n )}\n </Show>\n <div class=\"k2b-page-title__copy\">\n <Show when={ancestors().length === 1 && parent()}>\n {(up) => (\n <nav class=\"k2b-page-title__trail\" aria-label={messages().path}>\n <a\n class=\"k2b-page-title__up\"\n href={up().href}\n aria-label={messages().backTo({ page: up().title })}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up=\"\"\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n <span class=\"k2b-page-title__label\">{up().title}</span>\n </a>\n </nav>\n )}\n </Show>\n <Show when={ancestors().length > 1}>\n <nav class=\"k2b-page-title__trail\" aria-label={messages().path}>\n <ol class=\"k2b-page-title__path\">\n <For each={ancestors()}>\n {(link, index) => (\n <>\n <Show when={index() === 1}>\n <li class=\"k2b-page-title__more\" hidden>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n <button type=\"button\" class=\"k2b-page-title__more-button\" popovertarget={menuId} aria-label={messages().moreLevels}>\n …\n </button>\n </li>\n </Show>\n <li class=\"k2b-page-title__crumb\" data-index={index()}>\n <Show when={index() > 0}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </Show>\n <a\n href={link.href}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up={index() === ancestors().length - 1 ? \"\" : undefined}\n class=\"k2b-page-title__label\"\n >\n {link.title}\n </a>\n </li>\n </>\n )}\n </For>\n </ol>\n <div id={menuId} popover=\"auto\" class=\"k2b-dropdown__menu k2b-page-title__menu\">\n <For each={ancestors()}>\n {(link, index) => (\n <a class=\"k2b-dropdown__item\" href={link.href} data-index={index()} data-k2b-page-trail-link=\"\" hidden>\n {link.title}\n </a>\n )}\n </For>\n </div>\n </nav>\n </Show>\n <Dynamic component={props.as ?? \"h1\"} class=\"k2b-page-title__title\">\n <span class=\"k2b-page-title__text\">{props.trail.title}</span>\n <Show when={props.subtitle}>\n <small class=\"k2b-page-title__subtitle\">{props.subtitle}</small>\n </Show>\n </Dynamic>\n </div>\n </div>\n );\n}\n\n/**\n * Keeps server-rendered titles of the host's trail current: a workspace head\n * that never hydrates, its heading, and an app's own header title from the\n * host's trail.\n * When the host publishes a new trail, for example after `setPageTrail()`,\n * each such title is replaced in place by a live one with the same element,\n * class, and subtitle, and a head learns whether it now has a page above it.\n * Titles inside islands render in the browser anyway. Returns the disposer.\n */\nexport const followPageTrail = (root: ParentNode = document): (() => void) => {\n const disposers: Array<() => void> = [];\n const upgrade = () => {\n const trail = readPageTrail();\n for (const element of root.querySelectorAll<HTMLElement>(\".k2b-page-title[data-k2b-server]\")) {\n const row = element.parentElement;\n if (!row || element.closest(\"solid-island, solid-client\")) continue;\n const tag = element.querySelector(\".k2b-page-title__title\")?.localName;\n const as = tag === \"p\" || tag === \"h2\" ? tag : \"h1\";\n const fallback = element.hasAttribute(\"data-fallback\");\n const extra = [...element.classList].filter((name) => name !== \"k2b-page-title\").join(\" \") || undefined;\n const subtitle = element.querySelector(\".k2b-page-title__subtitle\");\n const subtitleNodes = subtitle ? [...subtitle.childNodes] : undefined;\n // A link focused in the server title keeps focus in the live one: same kind, then same address.\n const active = document.activeElement;\n const focused =\n active && element.contains(active)\n ? {\n kind: [\"k2b-page-title__up\", \"k2b-page-title__back\"].find((name) => active.classList.contains(name)),\n href: active.getAttribute(\"href\"),\n }\n : undefined;\n // The live title takes the server title's place, before whatever follows it in the row.\n const place = document.createTextNode(\"\");\n row.insertBefore(place, element);\n element.remove();\n disposers.push(\n createRoot((dispose) => {\n const live = usePageTrail();\n insert(\n row,\n () => (\n <Show when={live()}>\n {(current) => <PageTitle trail={current()} as={as} fallback={fallback} class={extra} subtitle={subtitleNodes} />}\n </Show>\n ),\n place,\n );\n return dispose;\n }),\n );\n const upgraded = place.previousSibling;\n if (focused && upgraded instanceof HTMLElement && upgraded.classList.contains(\"k2b-page-title\")) {\n const links = focused.kind ? [...upgraded.querySelectorAll<HTMLElement>(`a.${focused.kind}`)] : [];\n const link =\n links.find((candidate) => candidate.getAttribute(\"href\") === focused.href) ??\n links[0] ??\n upgraded.querySelector<HTMLElement>(\"a[data-k2b-page-trail-up]\");\n link?.focus({ preventScroll: true });\n }\n }\n // A server-rendered workspace heading carries only the title.\n for (const heading of root.querySelectorAll<HTMLElement>(\"[data-k2b-page-trail-title][data-k2b-server]\"))\n if (trail && !heading.closest(\"solid-island, solid-client\")) heading.textContent = trail.title;\n // The row's `data-trail` decides whether it shows; it follows the depth. A\n // workspace head holds its title in its lead.\n for (const row of root.querySelectorAll<HTMLElement>(\n \"[data-trail]:has(> .k2b-page-title[data-fallback]), [data-trail]:has(> .k2b-app-workspace__head-lead > .k2b-page-title[data-fallback])\",\n ))\n if (!row.closest(\"solid-island, solid-client\")) row.dataset.trail = trail?.ancestors.length ? \"up\" : \"title\";\n };\n window.addEventListener(PAGE_TRAIL_EVENT, upgrade);\n return () => {\n window.removeEventListener(PAGE_TRAIL_EVENT, upgrade);\n for (const dispose of disposers.splice(0)) dispose();\n };\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/PageTitle.tsx"],"sourcesContent":["import { createContext, createRoot, createSignal, createUniqueId, For, type JSX, onCleanup, onMount, Show, useContext } from \"solid-js\";\nimport { Dynamic, insert, isServer } from \"solid-js/web\";\nimport { IconButtonLink } from \"../actions/Button\";\nimport { installPageTitleController, PAGE_TITLE_CONTROLLER_ATTRIBUTE } from \"./page-title-controller\";\nimport { useWorkspaceMessages } from \"./workspace-messages\";\n\n/** A page above the current one. Only pages someone can go back to are levels, so each has a link. */\nexport type PageTrailLink = { title: string; href: string };\n/** The current page and the pages above it, nearest last. */\nexport type PageTrailState = { title: string; ancestors: PageTrailLink[] };\n\nexport type PageTitleProps = {\n trail: PageTrailState;\n /** Quiet text after the title, for example a count. */\n subtitle?: JSX.Element;\n /** Defaults to `\"h1\"`. Chrome that repeats a title the page already shows uses `\"p\"`. */\n as?: \"h1\" | \"h2\" | \"p\";\n /**\n * Marks a title drawn by surrounding chrome, such as the workspace head or a\n * host shell. It steps back when the page renders its own `PageTitle`, so a\n * page has one title with one way up.\n */\n fallback?: boolean;\n class?: string;\n};\n\n/** Window event a host dispatches after publishing a new page trail. */\nexport const PAGE_TRAIL_EVENT = \"k2b:page-trail\";\nconst SOURCE_SELECTOR = \"script[data-k2b-page-trail]\";\n\nconst isTrail = (value: unknown): value is PageTrailState => {\n if (!value || typeof value !== \"object\") return false;\n const trail = value as Partial<PageTrailState>;\n return (\n typeof trail.title === \"string\" &&\n Array.isArray(trail.ancestors) &&\n trail.ancestors.every((link) => typeof link?.title === \"string\" && typeof link?.href === \"string\")\n );\n};\n\n/** The trail a host published for this document, or `undefined` outside a browser or without one. */\nexport const readPageTrail = (): PageTrailState | undefined => {\n if (typeof document === \"undefined\") return undefined;\n const source = document.querySelector(SOURCE_SELECTOR);\n if (!source?.textContent) return undefined;\n try {\n const value: unknown = JSON.parse(source.textContent);\n return isTrail(value) ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** `<` is escaped so a title can never end the script element it travels in. */\nconst serializeTrail = (trail: PageTrailState): string => JSON.stringify(trail).replace(/</g, \"\\\\u003c\");\n\n/**\n * Publishes a new trail for the document in the browser, for example after an\n * in-page navigation. Workspace heads and page titles that read the host's\n * trail follow it.\n */\nexport const setPageTrail = (trail: PageTrailState): void => {\n if (typeof document === \"undefined\") return;\n let source = document.querySelector(SOURCE_SELECTOR);\n if (!source) {\n source = document.createElement(\"script\");\n source.setAttribute(\"type\", \"application/json\");\n source.setAttribute(\"data-k2b-page-trail\", \"\");\n document.body.append(source);\n }\n source.textContent = serializeTrail(trail);\n window.dispatchEvent(new Event(PAGE_TRAIL_EVENT));\n};\n\nconst PageTrailContext = createContext<() => PageTrailState | undefined>();\n\n/**\n * Gives a host's page trail to everything below it. During server rendering\n * the context carries it; the provider also publishes it into the document,\n * where browser-rendered parts such as islands read it. Below a provider that\n * renders in the browser, its `trail` prop is the source: update the prop.\n */\nexport function PageTrailProvider(props: { trail: PageTrailState | undefined; children: JSX.Element }): JSX.Element {\n return (\n <PageTrailContext.Provider value={() => props.trail}>\n <Show when={props.trail}>\n {(trail) => <script type=\"application/json\" data-k2b-page-trail=\"\" innerHTML={serializeTrail(trail())} />}\n </Show>\n {props.children}\n </PageTrailContext.Provider>\n );\n}\n\n/** The host's page trail: from the provider while rendering below it, otherwise from the document. */\nexport const usePageTrail = (): (() => PageTrailState | undefined) => {\n const context = useContext(PageTrailContext);\n if (context) return context;\n if (typeof window === \"undefined\") return () => undefined;\n const [trail, setTrail] = createSignal(readPageTrail());\n const update = () => setTrail(readPageTrail());\n window.addEventListener(PAGE_TRAIL_EVENT, update);\n onCleanup(() => window.removeEventListener(PAGE_TRAIL_EVENT, update));\n return trail;\n};\n\n/**\n * A page title with its way up. The number of pages above decides the form:\n * none shows the title alone; one puts \"‹ Parent\" above it; more put a path\n * row above it whose middle collapses into \"…\" only when it does not fit.\n * Both rows share the same height, so the title never moves between depths.\n * On phones a back arrow left of the title replaces the row above.\n */\nexport function PageTitle(props: PageTitleProps): JSX.Element {\n const messages = useWorkspaceMessages();\n // A title rendered on the server from the host's trail follows the host's later trails (see `followPageTrail`).\n const host = isServer ? useContext(PageTrailContext) : undefined;\n const menuId = `k2b-page-title-menu-${createUniqueId()}`;\n const ancestors = () => props.trail.ancestors;\n const parent = () => ancestors().at(-1);\n let root: HTMLDivElement | undefined;\n // A host may control every title in the document at once; a title outside\n // one still collapses its path.\n onMount(() => {\n if (!root || root.closest(`[${PAGE_TITLE_CONTROLLER_ATTRIBUTE}]`)) return;\n onCleanup(installPageTitleController(root));\n });\n return (\n <div\n ref={root}\n class={`k2b-page-title ${props.class ?? \"\"}`}\n data-depth={ancestors().length}\n data-fallback={props.fallback ? \"\" : undefined}\n data-k2b-server={isServer && props.trail === host?.() ? \"\" : undefined}\n >\n <Show when={parent()}>\n {(up) => (\n <IconButtonLink\n class=\"k2b-page-title__back\"\n href={up().href}\n label={messages().backTo({ page: up().title })}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up=\"\"\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </IconButtonLink>\n )}\n </Show>\n <div class=\"k2b-page-title__copy\">\n <Show when={ancestors().length === 1 && parent()}>\n {(up) => (\n <nav class=\"k2b-page-title__trail\" aria-label={messages().path}>\n <a\n class=\"k2b-page-title__up\"\n href={up().href}\n aria-label={messages().backTo({ page: up().title })}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up=\"\"\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n <span class=\"k2b-page-title__label\">{up().title}</span>\n </a>\n </nav>\n )}\n </Show>\n <Show when={ancestors().length > 1}>\n <nav class=\"k2b-page-title__trail\" aria-label={messages().path}>\n <ol class=\"k2b-page-title__path\">\n <For each={ancestors()}>\n {(link, index) => (\n <>\n <Show when={index() === 1}>\n <li class=\"k2b-page-title__more\" hidden>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n <button type=\"button\" class=\"k2b-page-title__more-button\" popovertarget={menuId} aria-label={messages().moreLevels}>\n …\n </button>\n </li>\n </Show>\n <li class=\"k2b-page-title__crumb\" data-index={index()}>\n <Show when={index() > 0}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </Show>\n <a\n href={link.href}\n data-k2b-page-trail-link=\"\"\n data-k2b-page-trail-up={index() === ancestors().length - 1 ? \"\" : undefined}\n class=\"k2b-page-title__label\"\n >\n {link.title}\n </a>\n </li>\n </>\n )}\n </For>\n </ol>\n <div id={menuId} popover=\"auto\" class=\"k2b-dropdown__menu k2b-page-title__menu\">\n <For each={ancestors()}>\n {(link, index) => (\n <a class=\"k2b-dropdown__item\" href={link.href} data-index={index()} data-k2b-page-trail-link=\"\" hidden>\n {link.title}\n </a>\n )}\n </For>\n </div>\n </nav>\n </Show>\n <Dynamic component={props.as ?? \"h1\"} class=\"k2b-page-title__title\">\n <span class=\"k2b-page-title__text\">{props.trail.title}</span>\n <Show when={props.subtitle}>\n <small class=\"k2b-page-title__subtitle\">{props.subtitle}</small>\n </Show>\n </Dynamic>\n </div>\n </div>\n );\n}\n\n/**\n * Keeps server-rendered titles of the host's trail current: a workspace head\n * that never hydrates, its heading, and an app's own header title from the\n * host's trail.\n * When the host publishes a new trail, for example after `setPageTrail()`,\n * each such title is replaced in place by a live one with the same element,\n * class, and subtitle, and a head learns whether it now has a page above it.\n * Titles inside islands render in the browser anyway. Returns the disposer.\n */\nexport const followPageTrail = (root: ParentNode = document): (() => void) => {\n const disposers = new Map<Text, () => void>();\n const upgrade = () => {\n for (const [place, dispose] of disposers) {\n if (place.isConnected) continue;\n dispose();\n disposers.delete(place);\n }\n const trail = readPageTrail();\n for (const element of root.querySelectorAll<HTMLElement>(\".k2b-page-title[data-k2b-server]\")) {\n const row = element.parentElement;\n if (!row || element.closest(\"solid-island, solid-client\")) continue;\n const tag = element.querySelector(\".k2b-page-title__title\")?.localName;\n const as = tag === \"p\" || tag === \"h2\" ? tag : \"h1\";\n const fallback = element.hasAttribute(\"data-fallback\");\n const extra = [...element.classList].filter((name) => name !== \"k2b-page-title\").join(\" \") || undefined;\n const subtitle = element.querySelector(\".k2b-page-title__subtitle\");\n const subtitleNodes = subtitle ? [...subtitle.childNodes] : undefined;\n // A link focused in the server title keeps focus in the live one: same kind, then same address.\n const active = document.activeElement;\n const focused =\n active && element.contains(active)\n ? {\n kind: [\"k2b-page-title__up\", \"k2b-page-title__back\"].find((name) => active.classList.contains(name)),\n href: active.getAttribute(\"href\"),\n }\n : undefined;\n // The live title takes the server title's place, before whatever follows it in the row.\n const place = document.createTextNode(\"\");\n row.insertBefore(place, element);\n element.remove();\n disposers.set(\n place,\n createRoot((dispose) => {\n const live = usePageTrail();\n insert(\n row,\n () => (\n <Show when={live()}>\n {(current) => <PageTitle trail={current()} as={as} fallback={fallback} class={extra} subtitle={subtitleNodes} />}\n </Show>\n ),\n place,\n );\n return dispose;\n }),\n );\n const upgraded = place.previousSibling;\n if (focused && upgraded instanceof HTMLElement && upgraded.classList.contains(\"k2b-page-title\")) {\n const links = focused.kind ? [...upgraded.querySelectorAll<HTMLElement>(`a.${focused.kind}`)] : [];\n const link =\n links.find((candidate) => candidate.getAttribute(\"href\") === focused.href) ??\n links[0] ??\n upgraded.querySelector<HTMLElement>(\"a[data-k2b-page-trail-up]\");\n link?.focus({ preventScroll: true });\n }\n }\n // A server-rendered workspace heading carries only the title.\n for (const heading of root.querySelectorAll<HTMLElement>(\"[data-k2b-page-trail-title][data-k2b-server]\"))\n if (trail && !heading.closest(\"solid-island, solid-client\")) heading.textContent = trail.title;\n // The row's `data-trail` decides whether it shows; it follows the depth. A\n // workspace head holds its title in its lead.\n for (const row of root.querySelectorAll<HTMLElement>(\n \"[data-trail]:has(> .k2b-page-title[data-fallback]), [data-trail]:has(> .k2b-app-workspace__head-lead > .k2b-page-title[data-fallback])\",\n ))\n if (!row.closest(\"solid-island, solid-client\")) row.dataset.trail = trail?.ancestors.length ? \"up\" : \"title\";\n };\n window.addEventListener(PAGE_TRAIL_EVENT, upgrade);\n return () => {\n window.removeEventListener(PAGE_TRAIL_EVENT, upgrade);\n for (const dispose of disposers.values()) dispose();\n disposers.clear();\n };\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -32,6 +32,9 @@ export default function ResizeSeparator(props) {
|
|
|
32
32
|
},
|
|
33
33
|
onResize: value => props.onResize(value),
|
|
34
34
|
onResizeEnd: value => props.onResizeEnd?.(value),
|
|
35
|
+
get onReset() {
|
|
36
|
+
return props.onReset;
|
|
37
|
+
},
|
|
35
38
|
onCollapseIntent: armed => props.onCollapseIntent?.(armed),
|
|
36
39
|
onCollapse: () => props.onCollapse?.(),
|
|
37
40
|
onDragChange: dragging => props.onDragChange?.(dragging)
|
|
@@ -40,6 +43,6 @@ export default function ResizeSeparator(props) {
|
|
|
40
43
|
createEffect(() => {
|
|
41
44
|
if (element) element.setAttribute("aria-valuenow", String(Math.round(props.value * 100) / 100));
|
|
42
45
|
});
|
|
43
|
-
return _$ssr(_tmpl$, `k2b-resize-separator${props.class ? ` ${_$escape(props.class, true)}` : ""}`, _$ssrAttribute("aria-orientation", _$escape(props.orientation, true), false) + _$ssrAttribute("aria-valuemin", _$escape(props.min, true), false) + _$ssrAttribute("aria-valuemax", _$escape(props.max, true), false) + _$ssrAttribute("aria-valuenow", _$escape(Math.round(props.value * 100), true) / 100, false) + _$ssrAttribute("aria-label", _$escape(props.label, true), false) + _$ssrAttribute("aria-controls", _$escape(props.controls, true), false) + _$ssrAttribute("data-orientation", _$escape(props.orientation, true), false));
|
|
46
|
+
return _$ssr(_tmpl$, `k2b-resize-separator${props.class ? ` ${_$escape(props.class, true)}` : ""}`, _$ssrAttribute("aria-orientation", _$escape(props.orientation, true), false) + _$ssrAttribute("aria-valuemin", _$escape(props.min, true), false) + _$ssrAttribute("aria-valuemax", _$escape(props.max, true), false) + _$ssrAttribute("aria-valuenow", _$escape(Math.round(props.value * 100), true) / 100, false) + _$ssrAttribute("aria-label", _$escape(props.label, true), false) + _$ssrAttribute("aria-controls", _$escape(props.controls, true), false) + _$ssrAttribute("data-orientation", _$escape(props.orientation, true), false) + _$ssrAttribute("data-resize-edge", props.grow === "start" ? "start" : "end", false));
|
|
44
47
|
}
|
|
45
48
|
//# sourceMappingURL=ResizeSeparator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/ResizeSeparator.tsx"],"sourcesContent":["import { createEffect, onCleanup, onMount } from \"solid-js\";\nimport { attachResizeSeparator, type ResizeSeparatorOptions } from \"./resize-separator\";\n\nexport type ResizeSeparatorProps = Omit<ResizeSeparatorOptions, \"value\"> & {\n label: string;\n orientation: \"vertical\" | \"horizontal\";\n value: number;\n controls?: string;\n class?: string;\n};\n\nexport default function ResizeSeparator(props: ResizeSeparatorProps) {\n let element: HTMLDivElement | undefined;\n onMount(() => {\n if (element)\n onCleanup(\n attachResizeSeparator(element, {\n get min() {\n return props.min;\n },\n get max() {\n return props.max;\n },\n value: () => props.value,\n get unit() {\n return props.unit;\n },\n get step() {\n return props.step;\n },\n get largeStep() {\n return props.largeStep;\n },\n get grow() {\n return props.grow;\n },\n get collapseBelow() {\n return props.collapseBelow;\n },\n onResize: (value) => props.onResize(value),\n onResizeEnd: (value) => props.onResizeEnd?.(value),\n onCollapseIntent: (armed) => props.onCollapseIntent?.(armed),\n onCollapse: () => props.onCollapse?.(),\n onDragChange: (dragging) => props.onDragChange?.(dragging),\n }),\n );\n });\n createEffect(() => {\n if (element) element.setAttribute(\"aria-valuenow\", String(Math.round(props.value * 100) / 100));\n });\n return (\n <div\n ref={element}\n role=\"separator\"\n tabindex=\"0\"\n class={`k2b-resize-separator${props.class ? ` ${props.class}` : \"\"}`}\n aria-orientation={props.orientation}\n aria-valuemin={props.min}\n aria-valuemax={props.max}\n aria-valuenow={Math.round(props.value * 100) / 100}\n aria-label={props.label}\n aria-controls={props.controls}\n data-orientation={props.orientation}\n >\n <span aria-hidden=\"true\" />\n </div>\n );\n}\n"],"mappings":";;;;AAAA,SAAS,YAAY,EAAE,SAAS,EAAE,OAAO,QAAQ,UAAU;AAC3D,SAAS,qBAAqB,QAAqC,oBAAoB;AAUvF,eAAe,SAAS,eAAe,CAAC,KAA2B,EAAE;EACnE,IAAI,OAAmC;EACvC,OAAO,CAAC,MAAM;IACZ,IAAI,OAAO,EACT,SAAS,CACP,qBAAqB,CAAC,OAAO,EAAE;MAC7B,IAAI,GAAG,GAAG;QACR,OAAO,KAAK,CAAC,GAAG;MAClB,CAAC;MACD,IAAI,GAAG,GAAG;QACR,OAAO,KAAK,CAAC,GAAG;MAClB,CAAC;MACD,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK;MACxB,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,SAAS,GAAG;QACd,OAAO,KAAK,CAAC,SAAS;MACxB,CAAC;MACD,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,aAAa,GAAG;QAClB,OAAO,KAAK,CAAC,aAAa;MAC5B,CAAC;MACD,QAAQ,EAAG,KAAK,IAAK,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;MAC1C,WAAW,EAAG,KAAK,IAAK,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;MAClD,gBAAgB,EAAG,KAAK,IAAK,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC;MAC5D,UAAU,EAAE,MAAM,KAAK,CAAC,UAAU,GAAG,CAAC;MACtC,YAAY,EAAG,QAAQ,IAAK,KAAK,CAAC,YAAY,GAAG,QAAQ;IAC3D,CAAC,CACH,CAAC;EACL,CAAC,CAAC;EACF,YAAY,CAAC,MAAM;IACjB,IAAI,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;EACjG,CAAC,CAAC;EACF,qBAKW,uBAAuB,KAAK,CAAC,KAAK,GAAG,aAAI,KAAK,CAAC,KAAK,SAAE,GAAG,EAAE,EAAE,8CAClD,KAAK,CAAC,WAAW,2DACpB,KAAK,CAAC,GAAG,2DACT,KAAK,CAAC,GAAG,kDACT,aAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,UAAG,GAAG,iDACtC,KAAK,CAAC,KAAK,2DACR,KAAK,CAAC,QAAQ,8DACX,KAAK,CAAC,WAAW;
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/ResizeSeparator.tsx"],"sourcesContent":["import { createEffect, onCleanup, onMount } from \"solid-js\";\nimport { attachResizeSeparator, type ResizeSeparatorOptions } from \"./resize-separator\";\n\nexport type ResizeSeparatorProps = Omit<ResizeSeparatorOptions, \"value\"> & {\n label: string;\n orientation: \"vertical\" | \"horizontal\";\n value: number;\n controls?: string;\n class?: string;\n};\n\nexport default function ResizeSeparator(props: ResizeSeparatorProps) {\n let element: HTMLDivElement | undefined;\n onMount(() => {\n if (element)\n onCleanup(\n attachResizeSeparator(element, {\n get min() {\n return props.min;\n },\n get max() {\n return props.max;\n },\n value: () => props.value,\n get unit() {\n return props.unit;\n },\n get step() {\n return props.step;\n },\n get largeStep() {\n return props.largeStep;\n },\n get grow() {\n return props.grow;\n },\n get collapseBelow() {\n return props.collapseBelow;\n },\n onResize: (value) => props.onResize(value),\n onResizeEnd: (value) => props.onResizeEnd?.(value),\n get onReset() {\n return props.onReset;\n },\n onCollapseIntent: (armed) => props.onCollapseIntent?.(armed),\n onCollapse: () => props.onCollapse?.(),\n onDragChange: (dragging) => props.onDragChange?.(dragging),\n }),\n );\n });\n createEffect(() => {\n if (element) element.setAttribute(\"aria-valuenow\", String(Math.round(props.value * 100) / 100));\n });\n return (\n <div\n ref={element}\n role=\"separator\"\n tabindex=\"0\"\n class={`k2b-resize-separator${props.class ? ` ${props.class}` : \"\"}`}\n aria-orientation={props.orientation}\n aria-valuemin={props.min}\n aria-valuemax={props.max}\n aria-valuenow={Math.round(props.value * 100) / 100}\n aria-label={props.label}\n aria-controls={props.controls}\n data-orientation={props.orientation}\n // A separator that grows toward the start sits on the start edge of the region it resizes; the drag cue shades that region.\n data-resize-edge={props.grow === \"start\" ? \"start\" : \"end\"}\n >\n <span aria-hidden=\"true\" />\n </div>\n );\n}\n"],"mappings":";;;;AAAA,SAAS,YAAY,EAAE,SAAS,EAAE,OAAO,QAAQ,UAAU;AAC3D,SAAS,qBAAqB,QAAqC,oBAAoB;AAUvF,eAAe,SAAS,eAAe,CAAC,KAA2B,EAAE;EACnE,IAAI,OAAmC;EACvC,OAAO,CAAC,MAAM;IACZ,IAAI,OAAO,EACT,SAAS,CACP,qBAAqB,CAAC,OAAO,EAAE;MAC7B,IAAI,GAAG,GAAG;QACR,OAAO,KAAK,CAAC,GAAG;MAClB,CAAC;MACD,IAAI,GAAG,GAAG;QACR,OAAO,KAAK,CAAC,GAAG;MAClB,CAAC;MACD,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK;MACxB,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,SAAS,GAAG;QACd,OAAO,KAAK,CAAC,SAAS;MACxB,CAAC;MACD,IAAI,IAAI,GAAG;QACT,OAAO,KAAK,CAAC,IAAI;MACnB,CAAC;MACD,IAAI,aAAa,GAAG;QAClB,OAAO,KAAK,CAAC,aAAa;MAC5B,CAAC;MACD,QAAQ,EAAG,KAAK,IAAK,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;MAC1C,WAAW,EAAG,KAAK,IAAK,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;MAClD,IAAI,OAAO,GAAG;QACZ,OAAO,KAAK,CAAC,OAAO;MACtB,CAAC;MACD,gBAAgB,EAAG,KAAK,IAAK,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC;MAC5D,UAAU,EAAE,MAAM,KAAK,CAAC,UAAU,GAAG,CAAC;MACtC,YAAY,EAAG,QAAQ,IAAK,KAAK,CAAC,YAAY,GAAG,QAAQ;IAC3D,CAAC,CACH,CAAC;EACL,CAAC,CAAC;EACF,YAAY,CAAC,MAAM;IACjB,IAAI,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;EACjG,CAAC,CAAC;EACF,qBAKW,uBAAuB,KAAK,CAAC,KAAK,GAAG,aAAI,KAAK,CAAC,KAAK,SAAE,GAAG,EAAE,EAAE,8CAClD,KAAK,CAAC,WAAW,2DACpB,KAAK,CAAC,GAAG,2DACT,KAAK,CAAC,GAAG,kDACT,aAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,UAAG,GAAG,iDACtC,KAAK,CAAC,KAAK,2DACR,KAAK,CAAC,QAAQ,8DACX,KAAK,CAAC,WAAW,qDAEjB,KAAK,CAAC,IAAI,KAAK,OAAO,GAAG,OAAO,GAAG,KAAK;AAKhE","ignoreList":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { suppressTextSelection } from "../internal/text-selection";
|
|
2
2
|
import { prefersReducedMotion } from "../motion";
|
|
3
|
-
import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_DETAIL_MIN, APP_WORKSPACE_DRAWER_MAX, APP_WORKSPACE_DRAWER_MIN, APP_WORKSPACE_MAIN_MIN, APP_WORKSPACE_PANE_DEFAULT, APP_WORKSPACE_PANE_MAX, APP_WORKSPACE_PANE_MIN, APP_WORKSPACE_SIDEBAR_COLLAPSED, APP_WORKSPACE_SIDEBAR_MAX, APP_WORKSPACE_SIDEBAR_MIN, appWorkspacePanelVariable, appWorkspaceResizeLimits, fitAppWorkspacePaneSizes, normalizeAppWorkspaceLayoutState, resolveAppWorkspaceSidebarWidth, safeAppWorkspacePanelId, shouldCollapseAppWorkspaceSidebar } from "./app-workspace-state";
|
|
3
|
+
import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_DETAIL_MIN, APP_WORKSPACE_DRAWER_DEFAULT, APP_WORKSPACE_DRAWER_MAX, APP_WORKSPACE_DRAWER_MIN, APP_WORKSPACE_MAIN_MIN, APP_WORKSPACE_PANE_DEFAULT, APP_WORKSPACE_PANE_MAX, APP_WORKSPACE_PANE_MIN, APP_WORKSPACE_SIDEBAR_COLLAPSED, APP_WORKSPACE_SIDEBAR_DEFAULT, APP_WORKSPACE_SIDEBAR_MAX, APP_WORKSPACE_SIDEBAR_MIN, appWorkspacePanelVariable, appWorkspaceResizeLimits, fitAppWorkspacePaneSizes, normalizeAppWorkspaceLayoutState, resolveAppWorkspaceSidebarWidth, safeAppWorkspacePanelId, shouldCollapseAppWorkspaceSidebar } from "./app-workspace-state";
|
|
4
4
|
import { readAppWorkspaceWidth, resolveAppWorkspaceWidth } from "./app-workspace-width";
|
|
5
5
|
import { installScrollFades } from "./scroll-fade";
|
|
6
6
|
const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
@@ -26,7 +26,7 @@ const parkDetailDrawer = (aside, focus = false) => {
|
|
|
26
26
|
detailDrawerContent(aside)?.style.removeProperty("--k2b-detail-space");
|
|
27
27
|
const toggle = detailToggle(aside);
|
|
28
28
|
toggle?.setAttribute("aria-expanded", "false");
|
|
29
|
-
if (focus)
|
|
29
|
+
if (focus) detailReturnControl(aside)?.focus({
|
|
30
30
|
preventScroll: true
|
|
31
31
|
});
|
|
32
32
|
};
|
|
@@ -66,6 +66,8 @@ const sidebarHideable = root => sidebarElement(root)?.dataset.workspaceHideable
|
|
|
66
66
|
* stays application state, so the controller only requests the change.
|
|
67
67
|
*/
|
|
68
68
|
export const APP_WORKSPACE_SIDEBAR_HIDE_EVENT = "k2b-app-workspace-sidebar-hide";
|
|
69
|
+
/** Asks the owning Detail to reopen its current available object. */
|
|
70
|
+
export const APP_WORKSPACE_DETAIL_OPEN_EVENT = "k2b-app-workspace-detail-open";
|
|
69
71
|
/** Asks the owning `AppWorkspace.Detail` to close; its `onClose` decides. */
|
|
70
72
|
export const APP_WORKSPACE_DETAIL_CLOSE_EVENT = "k2b-app-workspace-detail-close";
|
|
71
73
|
/**
|
|
@@ -80,6 +82,15 @@ const previewSidebarHidden = (root, hidden) => {
|
|
|
80
82
|
if (hidden) sidebar.dataset.workspaceHidePreview = "true";else delete sidebar.dataset.workspaceHidePreview;
|
|
81
83
|
};
|
|
82
84
|
const isVisible = element => Boolean(element && !element.hidden && getComputedStyle(element).display !== "none");
|
|
85
|
+
// A pane-bound head can be absent on a phone. The visible control in Main
|
|
86
|
+
// that names this same detail remains its return target, within the same root.
|
|
87
|
+
const detailReturnControl = aside => {
|
|
88
|
+
const root = workspaceRoot(aside);
|
|
89
|
+
if (!root) return undefined;
|
|
90
|
+
const shared = detailToggle(aside);
|
|
91
|
+
if (isVisible(shared ?? null) && shared && !shared.matches(":disabled")) return shared;
|
|
92
|
+
return rootElements(root, DETAIL_REQUEST_CONTROL_SELECTOR).find(control => control.matches(FOCUSABLE_SELECTOR) && !aside.contains(control) && !control.matches(":disabled, [inert]") && !control.closest("[inert]") && control.getAttribute("aria-controls")?.split(/\s+/).includes(aside.id) && control.getClientRects().length > 0);
|
|
93
|
+
};
|
|
83
94
|
const elementSize = (element, kind) => {
|
|
84
95
|
if (!isVisible(element)) return 0;
|
|
85
96
|
const rect = element.getBoundingClientRect();
|
|
@@ -336,9 +347,20 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
336
347
|
if (detailButton) {
|
|
337
348
|
const root = workspaceRoot(detailButton);
|
|
338
349
|
const aside = root ? controlledPanel(root, detailButton) : null;
|
|
339
|
-
if (!aside ||
|
|
350
|
+
if (!aside || detailButton.matches(":disabled") || claimedToggleClicks.has(event)) return;
|
|
351
|
+
const reopenable = aside.dataset.workspaceDetailReopen === "true";
|
|
352
|
+
if (!reopenable && (aside.hidden || !detailWaiting(aside))) return;
|
|
340
353
|
claimedToggleClicks.add(event);
|
|
341
|
-
if (
|
|
354
|
+
if (aside.hidden) {
|
|
355
|
+
aside.dispatchEvent(new CustomEvent(APP_WORKSPACE_DETAIL_OPEN_EVENT));
|
|
356
|
+
// The app may reject opening (for example while navigating). Never
|
|
357
|
+
// override hidden ourselves or reveal retained, unavailable contents.
|
|
358
|
+
if (aside.hidden) return;
|
|
359
|
+
if (wide?.matches && detailWaiting(aside)) openDetailDrawer(aside);
|
|
360
|
+
if (event.detail === 0 || phone?.matches) requestTitle(aside);else detailButton.focus({
|
|
361
|
+
preventScroll: true
|
|
362
|
+
});
|
|
363
|
+
} else if (reopenable && (!wide?.matches || !detailWaiting(aside))) requestClose(aside);else if (detailDrawer(aside)) parkDetailDrawer(aside);else {
|
|
342
364
|
openDetailDrawer(aside);
|
|
343
365
|
if (event.detail === 0) requestTitle(aside);else detailButton.focus({
|
|
344
366
|
preventScroll: true
|
|
@@ -435,6 +457,15 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
435
457
|
return rootElements(root, detailSelector).find(aside => !aside.hidden && ids?.includes(aside.id) && detailWaiting(aside)) ?? null;
|
|
436
458
|
};
|
|
437
459
|
const syncDetailRequestControls = () => {
|
|
460
|
+
for (const root of allRoots()) {
|
|
461
|
+
for (const aside of rootElements(root, detailSelector)) {
|
|
462
|
+
const toggle = detailToggle(aside);
|
|
463
|
+
if (toggle?.dataset.workspaceDetailReopen !== "true") continue;
|
|
464
|
+
const expanded = !aside.hidden && (!wide?.matches || !detailWaiting(aside) || detailDrawer(aside));
|
|
465
|
+
const value = String(expanded);
|
|
466
|
+
if (toggle.getAttribute("aria-expanded") !== value) toggle.setAttribute("aria-expanded", value);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
438
469
|
const waitingControls = new Map();
|
|
439
470
|
for (const root of allRoots()) {
|
|
440
471
|
const waitingDetails = wide?.matches ? rootElements(root, detailSelector).filter(aside => !aside.hidden && detailWaiting(aside)) : [];
|
|
@@ -509,7 +540,63 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
509
540
|
if (args[2] === "sidebar") scheduleHostSync();
|
|
510
541
|
return size;
|
|
511
542
|
};
|
|
543
|
+
// A responsive application control and the shared control can name the same
|
|
544
|
+
// Detail. Remember actual focus, not an old selection, before CSS hides it.
|
|
545
|
+
let detailControlFocus = null;
|
|
546
|
+
const controlledDetail = control => {
|
|
547
|
+
if (!control.matches(FOCUSABLE_SELECTOR) || control.matches(HANDLE_SELECTOR) || !eventRoot.contains(control)) return null;
|
|
548
|
+
const root = workspaceRoot(control);
|
|
549
|
+
if (!root) return null;
|
|
550
|
+
const ids = control.getAttribute("aria-controls")?.split(/\s+/);
|
|
551
|
+
const aside = rootElements(root, detailSelector).find(aside => ids?.includes(aside.id));
|
|
552
|
+
return aside ? {
|
|
553
|
+
control,
|
|
554
|
+
aside,
|
|
555
|
+
root
|
|
556
|
+
} : null;
|
|
557
|
+
};
|
|
558
|
+
const detailControlVisible = control => control.isConnected && control.getClientRects().length > 0 && getComputedStyle(control).visibility === "visible";
|
|
559
|
+
const detailControlUsable = control => detailControlVisible(control) && !control.matches(":disabled, [aria-disabled='true']") && !control.closest("[inert], [hidden], [aria-hidden='true']");
|
|
560
|
+
const onDetailControlFocusOut = () => {
|
|
561
|
+
// Explicit blur of a visible control is not a responsive focus loss.
|
|
562
|
+
if (detailControlFocus && detailControlVisible(detailControlFocus.control)) detailControlFocus = null;
|
|
563
|
+
};
|
|
564
|
+
const rescueDetailControlFocus = () => {
|
|
565
|
+
const previous = detailControlFocus;
|
|
566
|
+
if (!previous) return;
|
|
567
|
+
const {
|
|
568
|
+
control,
|
|
569
|
+
aside,
|
|
570
|
+
root
|
|
571
|
+
} = previous;
|
|
572
|
+
const active = ownerDocument.activeElement;
|
|
573
|
+
if (!control.isConnected || !aside.isConnected || workspaceRoot(control) !== root || workspaceRoot(aside) !== root || active !== ownerDocument.body && !control.contains(active)) {
|
|
574
|
+
detailControlFocus = null;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const shared = detailToggle(aside);
|
|
578
|
+
if (shared?.dataset.workspaceDetailReopen === "true" && phone?.matches && phonePage(aside) && !aside.hidden && !aside.contains(control)) {
|
|
579
|
+
// WebKit can update matchMedia after resize has already focused the now
|
|
580
|
+
// visible application control. Move into the page before Main becomes inert.
|
|
581
|
+
detailControlFocus = null;
|
|
582
|
+
requestTitle(aside);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (detailControlVisible(control)) return;
|
|
586
|
+
detailControlFocus = null;
|
|
587
|
+
if (shared?.dataset.workspaceDetailReopen !== "true") {
|
|
588
|
+
// Selection-only details keep their existing returning-column title target.
|
|
589
|
+
if (control === shared && !aside.hidden && !detailWaiting(aside)) requestTitle(aside);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const candidates = shared ? [shared, ...rootElements(root, "[aria-controls]")] : rootElements(root, "[aria-controls]");
|
|
593
|
+
const replacement = candidates.find(candidate => candidate !== control && detailControlUsable(candidate) && controlledDetail(candidate)?.aside === aside);
|
|
594
|
+
replacement?.focus({
|
|
595
|
+
preventScroll: true
|
|
596
|
+
});
|
|
597
|
+
};
|
|
512
598
|
const syncHostState = () => {
|
|
599
|
+
rescueDetailControlFocus();
|
|
513
600
|
if (pendingDetail && !canOpenPendingDetail(pendingDetail.aside)) cancelPendingDetail();
|
|
514
601
|
if (overlay && sidebarForm(overlay.root) !== "overlay") closeOverlay("none");
|
|
515
602
|
for (const root of allRoots()) {
|
|
@@ -525,11 +612,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
525
612
|
const before = detailWasWaiting.get(aside);
|
|
526
613
|
detailWasWaiting.set(aside, waiting);
|
|
527
614
|
if (!waiting) {
|
|
528
|
-
//
|
|
529
|
-
|
|
615
|
+
// Rescue focus only when the legacy waiting-only button disappears.
|
|
616
|
+
// A persistent head control stays a valid focus target after resizing.
|
|
617
|
+
const toggle = detailToggle(aside);
|
|
618
|
+
const toggleFocused = toggle?.contains(ownerDocument.activeElement) ?? false;
|
|
530
619
|
parkDetailDrawer(aside);
|
|
531
|
-
if (toggleFocused) focusTitle(aside);
|
|
532
|
-
} else if (detailDrawer(aside)) syncDetailDrawerSpace(aside);else if (before !== true && aside.contains(ownerDocument.activeElement))
|
|
620
|
+
if (toggle && toggleFocused && !isVisible(toggle)) focusTitle(aside);
|
|
621
|
+
} else if (detailDrawer(aside)) syncDetailDrawerSpace(aside);else if (before !== true && aside.contains(ownerDocument.activeElement)) detailReturnControl(aside)?.focus({
|
|
533
622
|
preventScroll: true
|
|
534
623
|
});
|
|
535
624
|
}
|
|
@@ -648,6 +737,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
648
737
|
const root = target?.closest(ROOT_SELECTOR);
|
|
649
738
|
if (!target || !root || !observedRoots.has(root)) return;
|
|
650
739
|
lastFocus.set(root, target);
|
|
740
|
+
const control = target.closest("[aria-controls]");
|
|
741
|
+
detailControlFocus = control ? controlledDetail(control) : null;
|
|
651
742
|
const previous = remembered.get(root);
|
|
652
743
|
// Pointer focus follows pointerdown; keeping that snapshot also keeps the
|
|
653
744
|
// interaction that opened the panel rather than treating focus as a new one.
|
|
@@ -694,6 +785,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
694
785
|
// that opens later than that does not take the drawer or the focus.
|
|
695
786
|
let lastChosen = null;
|
|
696
787
|
const cancelTitleOnInteraction = event => {
|
|
788
|
+
if (detailControlFocus && !(event.target instanceof Node && detailControlFocus.control.contains(event.target))) detailControlFocus = null;
|
|
697
789
|
if (event.type === "pointerdown" || event.type === "keydown") clearActivation();
|
|
698
790
|
if (lastChosen && !(event.target instanceof Node && lastChosen.item.contains(event.target))) {
|
|
699
791
|
lastChosen.chosen = false;
|
|
@@ -955,7 +1047,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
955
1047
|
pendingAnchors.set(root, cancel);
|
|
956
1048
|
} else anchorItem(root, aside, snapshot);
|
|
957
1049
|
const focused = ownerDocument.activeElement;
|
|
958
|
-
|
|
1050
|
+
const toggle = detailToggle(aside);
|
|
1051
|
+
const returnControl = detailReturnControl(aside);
|
|
1052
|
+
if (toggle?.dataset.workspaceDetailReopen === "true" && returnControl && (aside.contains(focused) || focused === ownerDocument.body && aside.contains(lastFocus.get(root) ?? null))) {
|
|
1053
|
+
returnControl.focus({
|
|
1054
|
+
preventScroll: true
|
|
1055
|
+
});
|
|
1056
|
+
} else if (snapshot?.item.isConnected && (aside.contains(lastFocus.get(root) ?? null) || aside.contains(focused) || focused === ownerDocument.body)) snapshot.item.focus({
|
|
959
1057
|
preventScroll: true
|
|
960
1058
|
});
|
|
961
1059
|
clearDrag(aside);
|
|
@@ -1232,34 +1330,86 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1232
1330
|
const handle = resizeHandle(event);
|
|
1233
1331
|
const root = handle?.closest(ROOT_SELECTOR);
|
|
1234
1332
|
const kind = handle ? resizeKind(handle) : null;
|
|
1235
|
-
if (!handle || !root || kind
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1333
|
+
if (!handle || !root || !kind || !workspaceResizable(root) || !controlsResizableRegion(root, handle, kind)) return;
|
|
1334
|
+
if (active) return;
|
|
1335
|
+
if (kind === "sidebar") {
|
|
1336
|
+
const {
|
|
1337
|
+
sidebarWidth: _width,
|
|
1338
|
+
sidebarCollapsed: _collapsed,
|
|
1339
|
+
...remaining
|
|
1340
|
+
} = layoutState;
|
|
1341
|
+
layoutState = remaining;
|
|
1342
|
+
} else {
|
|
1343
|
+
const key = kind === "pane" ? "paneWidths" : kind === "drawer" ? "drawerHeights" : "detailWidths";
|
|
1344
|
+
const widths = {
|
|
1345
|
+
...layoutState[key]
|
|
1346
|
+
};
|
|
1347
|
+
delete widths[panelId(handle)];
|
|
1348
|
+
layoutState = {
|
|
1349
|
+
...layoutState,
|
|
1350
|
+
[key]: widths
|
|
1351
|
+
};
|
|
1253
1352
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1353
|
+
const regions = resetRegions.get(root) ?? new Map();
|
|
1354
|
+
regions.set(resizeVariable(handle, kind), {
|
|
1355
|
+
kind,
|
|
1356
|
+
id: panelId(handle),
|
|
1357
|
+
configuration: handle
|
|
1358
|
+
});
|
|
1359
|
+
resetRegions.set(root, regions);
|
|
1360
|
+
writeState();
|
|
1361
|
+
if (kind === "sidebar" || kind === "pane") reconcilePanes(root, layoutState);
|
|
1256
1362
|
updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
|
|
1257
|
-
options.writeState?.(layoutState);
|
|
1258
1363
|
};
|
|
1259
1364
|
const stopScrollFades = installScrollFades(eventRoot);
|
|
1260
1365
|
let layoutState = normalizeAppWorkspaceLayoutState(options.readState?.()) ?? {
|
|
1261
1366
|
version: 2
|
|
1262
1367
|
};
|
|
1368
|
+
const resetRegions = new WeakMap();
|
|
1369
|
+
const resizeVariable = (handle, kind) => kind === "sidebar" ? "--k2b-workspace-sidebar-width" : appWorkspacePanelVariable(kind, panelId(handle));
|
|
1370
|
+
const defaultSize = (handle, kind) => readAppWorkspaceWidth(handle.dataset.workspaceDefaultSize) ?? (kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_DEFAULT : kind === "pane" ? APP_WORKSPACE_PANE_DEFAULT : kind === "drawer" ? APP_WORKSPACE_DRAWER_DEFAULT : APP_WORKSPACE_DETAIL_DEFAULT);
|
|
1371
|
+
const restoreResetDefaults = root => {
|
|
1372
|
+
for (const [variable, region] of resetRegions.get(root) ?? []) {
|
|
1373
|
+
const {
|
|
1374
|
+
kind,
|
|
1375
|
+
id
|
|
1376
|
+
} = region;
|
|
1377
|
+
const persisted = kind === "sidebar" ? layoutState.sidebarWidth : layoutState[kind === "pane" ? "paneWidths" : kind === "drawer" ? "drawerHeights" : "detailWidths"]?.[id];
|
|
1378
|
+
if (persisted !== undefined || kind === "sidebar" && layoutState.sidebarCollapsed) continue;
|
|
1379
|
+
const handle = rootElements(root, HANDLE_SELECTOR).find(handle => resizeKind(handle) === kind && panelId(handle) === id);
|
|
1380
|
+
// Keep the latest handle's configuration across unmounts. Limits still
|
|
1381
|
+
// measure today's workspace, so a missing handle cannot freeze its fit.
|
|
1382
|
+
if (handle) region.configuration = handle;
|
|
1383
|
+
const spec = defaultSize(region.configuration, kind);
|
|
1384
|
+
// Store the configured default, not a temporary fit. CSS/relayout owns
|
|
1385
|
+
// the available space, so a numeric level can grow back when room returns.
|
|
1386
|
+
if (typeof spec === "number") {
|
|
1387
|
+
// Sidebar and drawer have no CSS fit. Keep their configured default
|
|
1388
|
+
// in the map, but render within today's bounds and recover it later.
|
|
1389
|
+
if (kind === "sidebar" || kind === "drawer") {
|
|
1390
|
+
const {
|
|
1391
|
+
min,
|
|
1392
|
+
max
|
|
1393
|
+
} = sizeLimits(root, region.configuration, kind);
|
|
1394
|
+
root.style.setProperty(variable, `${clamp(spec, min, max)}px`);
|
|
1395
|
+
} else root.style.setProperty(variable, `${spec}px`);
|
|
1396
|
+
} else {
|
|
1397
|
+
root.style.removeProperty(variable);
|
|
1398
|
+
if (getComputedStyle(root).getPropertyValue(variable).trim()) root.style.setProperty(variable, "initial");
|
|
1399
|
+
}
|
|
1400
|
+
if (kind === "sidebar") delete root.dataset.sidebarCollapsed;
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
const writeState = () => {
|
|
1404
|
+
options.writeState?.(layoutState);
|
|
1405
|
+
// Keep reset defaults through reactive writes and temporary handle unmounts,
|
|
1406
|
+
// without saving a width that would mask a changed application default.
|
|
1407
|
+
for (const root of roots()) {
|
|
1408
|
+
if (!workspaceResizable(root)) continue;
|
|
1409
|
+
restoreResetDefaults(root);
|
|
1410
|
+
reconcilePanes(root, layoutState);
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1263
1413
|
let active = null;
|
|
1264
1414
|
let resizeFrame = null;
|
|
1265
1415
|
let pendingClient = null;
|
|
@@ -1281,7 +1431,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1281
1431
|
}
|
|
1282
1432
|
};
|
|
1283
1433
|
}
|
|
1284
|
-
|
|
1434
|
+
const root = workspaceRoot(handle);
|
|
1435
|
+
if (root) resetRegions.get(root)?.delete(resizeVariable(handle, kind));
|
|
1436
|
+
writeState();
|
|
1285
1437
|
};
|
|
1286
1438
|
|
|
1287
1439
|
// A level resize starts from what is on screen. When the stored widths do
|
|
@@ -1422,6 +1574,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1422
1574
|
const kind = handle ? resizeKind(handle) : null;
|
|
1423
1575
|
const root = handle ? workspaceRoot(handle) : null;
|
|
1424
1576
|
if (!handle || !kind || !root || !workspaceResizable(root) || !controlsResizableRegion(root, handle, kind)) return;
|
|
1577
|
+
if (event.key === "Enter" && !event.defaultPrevented && !event.isComposing && !event.altKey && !event.ctrlKey && !event.metaKey && !active) {
|
|
1578
|
+
event.preventDefault();
|
|
1579
|
+
onPanelDoubleClick(event);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1425
1582
|
const current = currentSize(root, handle, kind);
|
|
1426
1583
|
const {
|
|
1427
1584
|
min,
|
|
@@ -1500,6 +1657,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1500
1657
|
// resizing; without this the same workspace still had persisted sizes
|
|
1501
1658
|
// forced onto it on load and on every window resize.
|
|
1502
1659
|
if (!workspaceResizable(root)) return;
|
|
1660
|
+
restoreResetDefaults(root);
|
|
1503
1661
|
const handles = rootElements(root, HANDLE_SELECTOR);
|
|
1504
1662
|
// Chrome establishes the space available to the work area. Auxiliary
|
|
1505
1663
|
// panes are then fitted together into what remains, so DOM order cannot
|
|
@@ -1654,6 +1812,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1654
1812
|
ownerDocument.addEventListener("keydown", onOverlayKeyDown);
|
|
1655
1813
|
ownerWindow.addEventListener("keydown", onDetailKeyDown);
|
|
1656
1814
|
ownerDocument.addEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1815
|
+
ownerDocument.addEventListener("focusout", onDetailControlFocusOut, true);
|
|
1657
1816
|
ownerDocument.addEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1658
1817
|
ownerDocument.addEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1659
1818
|
ownerDocument.addEventListener("click", onDetailRequestClick, true);
|
|
@@ -1714,6 +1873,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1714
1873
|
titleTabIndices.forEach(title => title.removeAttribute("tabindex"));
|
|
1715
1874
|
ownerWindow.removeEventListener("keydown", onDetailKeyDown);
|
|
1716
1875
|
ownerDocument.removeEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1876
|
+
ownerDocument.removeEventListener("focusout", onDetailControlFocusOut, true);
|
|
1877
|
+
detailControlFocus = null;
|
|
1717
1878
|
ownerDocument.removeEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1718
1879
|
ownerDocument.removeEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1719
1880
|
clearActivation();
|