@k2b/ui 0.25.0 → 0.27.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/GestureMenu.js +2 -2
- package/dist/browser/actions/GestureMenu.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +811 -406
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/chat-behavior.js +4 -0
- package/dist/browser/chat/chat-behavior.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/command-search.js +170 -0
- package/dist/browser/chat/command-search.js.map +1 -0
- package/dist/browser/chat/composer-document.js +1 -1
- package/dist/browser/chat/composer-document.js.map +1 -1
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/content/Calendar.js +8 -7
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/CalendarDayView.js +32 -6
- package/dist/browser/content/CalendarDayView.js.map +1 -1
- package/dist/browser/content/CalendarWeekView.js +4 -3
- package/dist/browser/content/CalendarWeekView.js.map +1 -1
- package/dist/browser/content/Timeline.js +3 -3
- package/dist/browser/content/Timeline.js.map +1 -1
- package/dist/browser/content/calendar-time-view.js +0 -1
- package/dist/browser/content/calendar-time-view.js.map +1 -1
- package/dist/browser/index.js +1 -0
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/inputs/Combobox.js +26 -5
- package/dist/browser/inputs/Combobox.js.map +1 -1
- package/dist/browser/inputs/QrScanner.js +2 -1
- package/dist/browser/inputs/QrScanner.js.map +1 -1
- package/dist/browser/inputs/SelectChip.js +43 -12
- package/dist/browser/inputs/SelectChip.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +720 -528
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/BottomSheet.js +8 -0
- package/dist/browser/layout/BottomSheet.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +159 -27
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/app-workspace-state.js +5 -1
- package/dist/browser/layout/app-workspace-state.js.map +1 -1
- package/dist/browser/layout/index.js +1 -1
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/layout/scroll-fade.js +14 -9
- package/dist/browser/layout/scroll-fade.js.map +1 -1
- package/dist/browser/layout/sidebar-scroll-memory.js +38 -0
- package/dist/browser/layout/sidebar-scroll-memory.js.map +1 -0
- package/dist/browser/motion.js +7 -0
- package/dist/browser/motion.js.map +1 -0
- package/dist/ssr/actions/GestureMenu.js +2 -2
- package/dist/ssr/actions/GestureMenu.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +410 -93
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/chat-behavior.js +4 -0
- package/dist/ssr/chat/chat-behavior.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/command-search.js +170 -0
- package/dist/ssr/chat/command-search.js.map +1 -0
- package/dist/ssr/chat/composer-document.js +1 -1
- package/dist/ssr/chat/composer-document.js.map +1 -1
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/content/Calendar.js +8 -7
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/CalendarDayView.js +32 -6
- package/dist/ssr/content/CalendarDayView.js.map +1 -1
- package/dist/ssr/content/CalendarWeekView.js +4 -3
- package/dist/ssr/content/CalendarWeekView.js.map +1 -1
- package/dist/ssr/content/Timeline.js +3 -3
- package/dist/ssr/content/Timeline.js.map +1 -1
- package/dist/ssr/content/calendar-time-view.js +0 -1
- package/dist/ssr/content/calendar-time-view.js.map +1 -1
- package/dist/ssr/index.js +1 -0
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/inputs/Combobox.js +22 -5
- package/dist/ssr/inputs/Combobox.js.map +1 -1
- package/dist/ssr/inputs/QrScanner.js +2 -1
- package/dist/ssr/inputs/QrScanner.js.map +1 -1
- package/dist/ssr/inputs/SelectChip.js +35 -11
- package/dist/ssr/inputs/SelectChip.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +254 -118
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/BottomSheet.js +8 -0
- package/dist/ssr/layout/BottomSheet.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +159 -27
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/app-workspace-state.js +5 -1
- package/dist/ssr/layout/app-workspace-state.js.map +1 -1
- package/dist/ssr/layout/index.js +1 -1
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/layout/scroll-fade.js +14 -9
- package/dist/ssr/layout/scroll-fade.js.map +1 -1
- package/dist/ssr/layout/sidebar-scroll-memory.js +38 -0
- package/dist/ssr/layout/sidebar-scroll-memory.js.map +1 -0
- package/dist/ssr/motion.js +7 -0
- package/dist/ssr/motion.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/types/chat/ChatComposer.d.ts +44 -2
- package/dist/types/chat/chat-behavior.d.ts +4 -0
- package/dist/types/chat/chat-messages.d.ts +7 -0
- package/dist/types/chat/command-search.d.ts +39 -0
- package/dist/types/chat/index.d.ts +1 -1
- package/dist/types/content/calendar-time-view.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/inputs/Combobox.d.ts +1 -0
- package/dist/types/inputs/SelectChip.d.ts +4 -0
- package/dist/types/layout/AppWorkspace.d.ts +13 -1
- package/dist/types/layout/BottomSheet.d.ts +8 -0
- package/dist/types/layout/index.d.ts +1 -1
- package/dist/types/layout/scroll-fade.d.ts +2 -0
- package/dist/types/layout/sidebar-scroll-memory.d.ts +6 -0
- package/dist/types/motion.d.ts +6 -0
- package/package.json +4 -4
|
@@ -23,6 +23,14 @@ export const bottomSheetOptions = {
|
|
|
23
23
|
contentClassName: "k2b-panel-dialog-viewport k2b-bottom-sheet-viewport",
|
|
24
24
|
initialFocus: sheetInitialFocus
|
|
25
25
|
};
|
|
26
|
+
|
|
27
|
+
/** A sheet on phones and a centered dialog from 48rem up, for decisions and forms; menus keep
|
|
28
|
+
* `bottomSheetOptions`. The stylesheet picks the mode with the phone media query, so the first
|
|
29
|
+
* frame is already right and a rotation or resize switches it in place, without a remount. */
|
|
30
|
+
export const bottomSheetAdaptiveOptions = {
|
|
31
|
+
...bottomSheetOptions,
|
|
32
|
+
panelClassName: `${bottomSheetOptions.panelClassName} is-adaptive`
|
|
33
|
+
};
|
|
26
34
|
function BottomSheetRoot(props) {
|
|
27
35
|
const messages = useUiMessages();
|
|
28
36
|
let pointer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/BottomSheet.tsx"],"sourcesContent":["import { type JSX, Show } from \"solid-js\";\nimport type { OpenDialogOptions } from \"../feedback/dialog-core\";\nimport { suppressTextSelection } from \"../internal/text-selection\";\nimport { useUiMessages } from \"../intl/messages\";\nimport PanelDialog from \"./PanelDialog\";\n\n/** Initial focus goes to the first input of the top sheet, otherwise to the\n * dialog element itself. Native `showModal()` focuses the first focusable\n * descendant, which is the drag handle; iOS shows a ring for that programmatic\n * focus, so the sheet moves it to the ring-less dialog before the next paint. */\nconst sheetInitialFocus = (dialog: HTMLDialogElement): HTMLElement => {\n const input = dialog.lastElementChild?.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), [role='combobox']:not([disabled]):not([aria-disabled='true'])\",\n );\n if (input) return input;\n dialog.tabIndex = -1;\n return dialog;\n};\n\nexport const bottomSheetOptions = {\n panelClassName: \"k2b-dialog k2b-bottom-sheet-frame\",\n contentClassName: \"k2b-panel-dialog-viewport k2b-bottom-sheet-viewport\",\n initialFocus: sheetInitialFocus,\n} satisfies OpenDialogOptions;\n\nexport type BottomSheetProps = {\n children: JSX.Element;\n /** Pass dialogCore's render context requestDismiss, never the completion callback. */\n onDismiss: () => void | Promise<void>;\n dismissDisabled?: boolean;\n dismissLabel?: string;\n handle?: boolean;\n};\n\nfunction BottomSheetRoot(props: BottomSheetProps) {\n const messages = useUiMessages();\n let pointer: { id: number; y: number; releaseTextSelection: () => void } | undefined;\n let suppressClick = false;\n const dismiss = () => {\n if (!props.dismissDisabled) void props.onDismiss();\n };\n return (\n <PanelDialog>\n <Show when={props.handle !== false}>\n <button\n type=\"button\"\n class=\"k2b-bottom-sheet__handle\"\n aria-label={props.dismissLabel ?? messages().close}\n disabled={props.dismissDisabled}\n onPointerDown={(event) => {\n if (!event.isPrimary || event.button !== 0 || props.dismissDisabled) return;\n suppressClick = false;\n pointer = { id: event.pointerId, y: event.clientY, releaseTextSelection: suppressTextSelection(event.pointerId) };\n event.currentTarget.setPointerCapture(event.pointerId);\n }}\n onPointerUp={(event) => {\n if (pointer?.id !== event.pointerId) return;\n const distance = event.clientY - pointer.y;\n pointer.releaseTextSelection();\n pointer = undefined;\n // A drag must not turn into a second dismiss request through click.\n suppressClick = Math.abs(distance) > 8;\n if (distance > 60) dismiss();\n }}\n onPointerCancel={() => {\n pointer?.releaseTextSelection();\n pointer = undefined;\n suppressClick = true;\n }}\n onLostPointerCapture={() => {\n if (pointer) {\n pointer.releaseTextSelection();\n pointer = undefined;\n suppressClick = true;\n }\n }}\n onClick={(event) => {\n if (event.detail !== 0 && suppressClick) {\n suppressClick = false;\n return;\n }\n dismiss();\n }}\n >\n <span aria-hidden=\"true\" />\n </button>\n </Show>\n {props.children}\n </PanelDialog>\n );\n}\n\n/** Bottom-edge presentation; dialogCore continues to own all modal state. */\nconst BottomSheet = Object.assign(BottomSheetRoot, {\n Header: PanelDialog.Header,\n Body: PanelDialog.Body,\n Footer: PanelDialog.Footer,\n Section: PanelDialog.Section,\n Tabs: PanelDialog.Tabs,\n});\nexport default BottomSheet;\n"],"mappings":";;;;;AAAA,SAAmB,IAAI,QAAQ,UAAU;AAEzC,SAAS,qBAAqB,QAAQ,4BAA4B;AAClE,SAAS,aAAa,QAAQ,kBAAkB;AAChD,OAAO,WAAW,MAAM,eAAe;;AAEvC;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAI,MAAyB,IAAkB;EACpE,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE,aAAa,CAClD,6JACF,CAAC;EACD,IAAI,KAAK,EAAE,OAAO,KAAK;EACvB,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;EACpB,OAAO,MAAM;AACf,CAAC;AAED,OAAO,MAAM,kBAAkB,GAAG;EAChC,cAAc,EAAE,mCAAmC;EACnD,gBAAgB,EAAE,qDAAqD;EACvE,YAAY,EAAE;AAChB,CAA6B;AAW7B,SAAS,eAAe,CAAC,KAAuB,EAAE;EAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,IAAI,OAAgF;EACpF,IAAI,aAAa,GAAG,KAAK;EACzB,MAAM,OAAO,GAAG,MAAM;IACpB,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;EACpD,CAAC;EACD,yBACG,WAAW;IAAA;MAAA,0BACT,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,KAAK,CAAC,MAAM,KAAK,KAAK;QAAA;QAAA;UAAA,2DAIlB,KAAK,CAAC,YAAY,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,4CACxC,KAAK,CAAC,eAAe;QAAA;MAAA,IAuClC,KAAK,CAAC,QAAQ;IAAA;EAAA;AAGrB;;AAEA;AACA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE;EACjD,MAAM,EAAE,WAAW,CAAC,MAAM;EAC1B,IAAI,EAAE,WAAW,CAAC,IAAI;EACtB,MAAM,EAAE,WAAW,CAAC,MAAM;EAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;EAC5B,IAAI,EAAE,WAAW,CAAC;AACpB,CAAC,CAAC;AACF,eAAe,WAAW","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/BottomSheet.tsx"],"sourcesContent":["import { type JSX, Show } from \"solid-js\";\nimport type { OpenDialogOptions } from \"../feedback/dialog-core\";\nimport { suppressTextSelection } from \"../internal/text-selection\";\nimport { useUiMessages } from \"../intl/messages\";\nimport PanelDialog from \"./PanelDialog\";\n\n/** Initial focus goes to the first input of the top sheet, otherwise to the\n * dialog element itself. Native `showModal()` focuses the first focusable\n * descendant, which is the drag handle; iOS shows a ring for that programmatic\n * focus, so the sheet moves it to the ring-less dialog before the next paint. */\nconst sheetInitialFocus = (dialog: HTMLDialogElement): HTMLElement => {\n const input = dialog.lastElementChild?.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), [role='combobox']:not([disabled]):not([aria-disabled='true'])\",\n );\n if (input) return input;\n dialog.tabIndex = -1;\n return dialog;\n};\n\nexport const bottomSheetOptions = {\n panelClassName: \"k2b-dialog k2b-bottom-sheet-frame\",\n contentClassName: \"k2b-panel-dialog-viewport k2b-bottom-sheet-viewport\",\n initialFocus: sheetInitialFocus,\n} satisfies OpenDialogOptions;\n\n/** A sheet on phones and a centered dialog from 48rem up, for decisions and forms; menus keep\n * `bottomSheetOptions`. The stylesheet picks the mode with the phone media query, so the first\n * frame is already right and a rotation or resize switches it in place, without a remount. */\nexport const bottomSheetAdaptiveOptions = {\n ...bottomSheetOptions,\n panelClassName: `${bottomSheetOptions.panelClassName} is-adaptive`,\n} satisfies OpenDialogOptions;\n\nexport type BottomSheetProps = {\n children: JSX.Element;\n /** Pass dialogCore's render context requestDismiss, never the completion callback. */\n onDismiss: () => void | Promise<void>;\n dismissDisabled?: boolean;\n dismissLabel?: string;\n handle?: boolean;\n};\n\nfunction BottomSheetRoot(props: BottomSheetProps) {\n const messages = useUiMessages();\n let pointer: { id: number; y: number; releaseTextSelection: () => void } | undefined;\n let suppressClick = false;\n const dismiss = () => {\n if (!props.dismissDisabled) void props.onDismiss();\n };\n return (\n <PanelDialog>\n <Show when={props.handle !== false}>\n <button\n type=\"button\"\n class=\"k2b-bottom-sheet__handle\"\n aria-label={props.dismissLabel ?? messages().close}\n disabled={props.dismissDisabled}\n onPointerDown={(event) => {\n if (!event.isPrimary || event.button !== 0 || props.dismissDisabled) return;\n suppressClick = false;\n pointer = { id: event.pointerId, y: event.clientY, releaseTextSelection: suppressTextSelection(event.pointerId) };\n event.currentTarget.setPointerCapture(event.pointerId);\n }}\n onPointerUp={(event) => {\n if (pointer?.id !== event.pointerId) return;\n const distance = event.clientY - pointer.y;\n pointer.releaseTextSelection();\n pointer = undefined;\n // A drag must not turn into a second dismiss request through click.\n suppressClick = Math.abs(distance) > 8;\n if (distance > 60) dismiss();\n }}\n onPointerCancel={() => {\n pointer?.releaseTextSelection();\n pointer = undefined;\n suppressClick = true;\n }}\n onLostPointerCapture={() => {\n if (pointer) {\n pointer.releaseTextSelection();\n pointer = undefined;\n suppressClick = true;\n }\n }}\n onClick={(event) => {\n if (event.detail !== 0 && suppressClick) {\n suppressClick = false;\n return;\n }\n dismiss();\n }}\n >\n <span aria-hidden=\"true\" />\n </button>\n </Show>\n {props.children}\n </PanelDialog>\n );\n}\n\n/** Bottom-edge presentation; dialogCore continues to own all modal state. */\nconst BottomSheet = Object.assign(BottomSheetRoot, {\n Header: PanelDialog.Header,\n Body: PanelDialog.Body,\n Footer: PanelDialog.Footer,\n Section: PanelDialog.Section,\n Tabs: PanelDialog.Tabs,\n});\nexport default BottomSheet;\n"],"mappings":";;;;;AAAA,SAAmB,IAAI,QAAQ,UAAU;AAEzC,SAAS,qBAAqB,QAAQ,4BAA4B;AAClE,SAAS,aAAa,QAAQ,kBAAkB;AAChD,OAAO,WAAW,MAAM,eAAe;;AAEvC;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAI,MAAyB,IAAkB;EACpE,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE,aAAa,CAClD,6JACF,CAAC;EACD,IAAI,KAAK,EAAE,OAAO,KAAK;EACvB,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;EACpB,OAAO,MAAM;AACf,CAAC;AAED,OAAO,MAAM,kBAAkB,GAAG;EAChC,cAAc,EAAE,mCAAmC;EACnD,gBAAgB,EAAE,qDAAqD;EACvE,YAAY,EAAE;AAChB,CAA6B;;AAE7B;AACA;AACA;AACA,OAAO,MAAM,0BAA0B,GAAG;EACxC,GAAG,kBAAkB;EACrB,cAAc,EAAE,GAAG,kBAAkB,CAAC,cAAc;AACtD,CAA6B;AAW7B,SAAS,eAAe,CAAC,KAAuB,EAAE;EAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,IAAI,OAAgF;EACpF,IAAI,aAAa,GAAG,KAAK;EACzB,MAAM,OAAO,GAAG,MAAM;IACpB,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;EACpD,CAAC;EACD,yBACG,WAAW;IAAA;MAAA,0BACT,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,KAAK,CAAC,MAAM,KAAK,KAAK;QAAA;QAAA;UAAA,2DAIlB,KAAK,CAAC,YAAY,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,4CACxC,KAAK,CAAC,eAAe;QAAA;MAAA,IAuClC,KAAK,CAAC,QAAQ;IAAA;EAAA;AAGrB;;AAEA;AACA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE;EACjD,MAAM,EAAE,WAAW,CAAC,MAAM;EAC1B,IAAI,EAAE,WAAW,CAAC,IAAI;EACtB,MAAM,EAAE,WAAW,CAAC,MAAM;EAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;EAC5B,IAAI,EAAE,WAAW,CAAC;AACpB,CAAC,CAAC;AACF,eAAe,WAAW","ignoreList":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { suppressTextSelection } from "../internal/text-selection";
|
|
2
|
-
import {
|
|
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
4
|
import { installScrollFades } from "./scroll-fade";
|
|
4
5
|
const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
5
6
|
const ROOT_SELECTOR = "[data-k2b-app-workspace]";
|
|
@@ -56,7 +57,16 @@ const controlledPanel = (root, handle) => {
|
|
|
56
57
|
const panel = document.getElementById(controls);
|
|
57
58
|
return panel?.closest(ROOT_SELECTOR) === root ? panel : null;
|
|
58
59
|
};
|
|
59
|
-
const
|
|
60
|
+
const flexibleLevel = pane => {
|
|
61
|
+
if (!pane.hasAttribute("data-workspace-level")) return false;
|
|
62
|
+
const widthClass = window.matchMedia("(min-width: 80rem)").matches ? "3" : window.matchMedia("(min-width: 48rem)").matches ? "2" : "1";
|
|
63
|
+
return pane.dataset.workspaceLevelLast?.split(" ").includes(widthClass) ?? false;
|
|
64
|
+
};
|
|
65
|
+
const levelsMain = handle => handle.closest(".k2b-app-workspace__main")?.hasAttribute("data-workspace-levels") ?? false;
|
|
66
|
+
const controlsResizableRegion = (root, handle, kind) => {
|
|
67
|
+
const panel = kind === "sidebar" ? sidebarElement(root) : controlledPanel(root, handle);
|
|
68
|
+
return panel?.dataset.workspaceResizable === "true" && (!panel.hasAttribute("data-workspace-level") || isVisible(panel) && isVisible(handle) && !flexibleLevel(panel));
|
|
69
|
+
};
|
|
60
70
|
const panelId = handle => safeAppWorkspacePanelId(handle.dataset.workspacePanelId ?? "primary") || "primary";
|
|
61
71
|
const numberData = (handle, key, fallback) => {
|
|
62
72
|
const value = Number(handle.dataset[key]);
|
|
@@ -65,7 +75,11 @@ const numberData = (handle, key, fallback) => {
|
|
|
65
75
|
const sizeLimits = (root, handle, kind) => {
|
|
66
76
|
const controlled = controlledPanel(root, handle);
|
|
67
77
|
const main = kind === "pane" ? handle.closest(".k2b-app-workspace__main") : mainElement(root);
|
|
68
|
-
const panes = main ? Array.from(main.querySelectorAll(".k2b-app-workspace__main-pane")).filter(pane => pane.closest(".k2b-app-workspace__main") === main && !pane.classList.contains("is-primary") && isVisible(pane)) : [];
|
|
78
|
+
const panes = main ? Array.from(main.querySelectorAll(".k2b-app-workspace__main-pane")).filter(pane => pane.closest(".k2b-app-workspace__main") === main && !pane.classList.contains("is-primary") && !flexibleLevel(pane) && isVisible(pane)) : [];
|
|
79
|
+
const separatorSize = kind === "pane" && main ? Array.from(main.querySelectorAll(`.k2b-app-workspace__resize${HANDLE_SELECTOR}`)).filter(separator => separator.closest(".k2b-app-workspace__main") === main && resizeKind(separator) === "pane" && isVisible(separator)).reduce((total, separator) => {
|
|
80
|
+
const style = getComputedStyle(separator);
|
|
81
|
+
return total + Math.max(0, separator.getBoundingClientRect().width + (Number.parseFloat(style.marginLeft) || 0) + (Number.parseFloat(style.marginRight) || 0));
|
|
82
|
+
}, 0) : 0;
|
|
69
83
|
// Content ends before the detail track, so its width alone would shrink a
|
|
70
84
|
// persisted panel again on every reconciliation. Measure from Content's
|
|
71
85
|
// start to the workspace's inner end: this includes the detail column and
|
|
@@ -76,7 +90,7 @@ const sizeLimits = (root, handle, kind) => {
|
|
|
76
90
|
return appWorkspaceResizeLimits({
|
|
77
91
|
kind,
|
|
78
92
|
workspaceSize: kind === "drawer" ? root.getBoundingClientRect().height : kind === "pane" && main ? main.getBoundingClientRect().width : content ? root.getBoundingClientRect().right - root.clientLeft - content.getBoundingClientRect().left : root.getBoundingClientRect().width,
|
|
79
|
-
reservedSize: kind === "detail" ? 8 : kind === "pane" ? panes.reduce((total, pane) => total + (pane === controlled ? 0 : elementSize(pane, "pane")), 0) : 0,
|
|
93
|
+
reservedSize: kind === "detail" ? 8 : kind === "pane" ? panes.reduce((total, pane) => total + (pane === controlled ? 0 : elementSize(pane, "pane")), 0) + separatorSize : 0,
|
|
80
94
|
min: numberData(handle, "workspaceMinSize", defaultMin),
|
|
81
95
|
max: numberData(handle, "workspaceMaxSize", defaultMax),
|
|
82
96
|
sidebarCollapsible: kind === "sidebar" && sidebarCollapsible(root)
|
|
@@ -139,6 +153,18 @@ const reconcilePanes = (root, layoutState, active = null) => {
|
|
|
139
153
|
if (!main || !isVisible(main)) return;
|
|
140
154
|
const handles = rootElements(root, HANDLE_SELECTOR).filter(handle => resizeKind(handle) === "pane" && controlsResizableRegion(root, handle, "pane"));
|
|
141
155
|
if (!handles.length) return;
|
|
156
|
+
if (main.hasAttribute("data-workspace-levels")) {
|
|
157
|
+
// CSS owns the multi-level fit, including a flexible shown-last pane.
|
|
158
|
+
// Keep preferences intact when the window temporarily has less room.
|
|
159
|
+
for (const handle of handles) {
|
|
160
|
+
const min = numberData(handle, "workspaceMinSize", APP_WORKSPACE_PANE_MIN);
|
|
161
|
+
const max = Math.max(min, numberData(handle, "workspaceMaxSize", APP_WORKSPACE_PANE_MAX));
|
|
162
|
+
const preferred = active?.moved && active.handle === handle ? active.liveSize : layoutState.paneWidths?.[panelId(handle)];
|
|
163
|
+
if (preferred !== undefined) root.style.setProperty(appWorkspacePanelVariable("pane", panelId(handle)), `${clamp(preferred, min, max)}px`);
|
|
164
|
+
updateHandleValue(root, handle, "pane", currentSize(root, handle, "pane"));
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
142
168
|
const controlled = new Set(handles.map(handle => controlledPanel(root, handle)).filter(Boolean));
|
|
143
169
|
const fixedWidth = Array.from(main.querySelectorAll(".k2b-app-workspace__main-pane")).filter(pane => pane.closest(".k2b-app-workspace__main") === main && !pane.classList.contains("is-primary") && isVisible(pane) && !controlled.has(pane)).reduce((total, pane) => total + elementSize(pane, "pane"), 0);
|
|
144
170
|
const fits = handles.flatMap(handle => {
|
|
@@ -267,27 +293,43 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
267
293
|
if (control.matches("a[href]") || control.closest(ITEM_SELECTOR)) closeOverlay("kept");
|
|
268
294
|
};
|
|
269
295
|
const onOverlayKeyDown = event => {
|
|
270
|
-
if (
|
|
296
|
+
if (event.defaultPrevented) return;
|
|
271
297
|
const target = eventElement(event);
|
|
272
298
|
if (!target) return;
|
|
273
299
|
// Escape also closes it when a click left focus on nothing, so on the body.
|
|
274
|
-
if (event.key === "Escape" && (overlay.sidebar.contains(target) || target === ownerDocument.body)) {
|
|
300
|
+
if (overlay && event.key === "Escape" && (overlay.sidebar.contains(target) || target === ownerDocument.body)) {
|
|
275
301
|
event.preventDefault();
|
|
276
302
|
closeOverlay("button");
|
|
277
303
|
return;
|
|
278
304
|
}
|
|
279
|
-
|
|
280
|
-
const
|
|
305
|
+
const aside = target.closest(detailSelector);
|
|
306
|
+
const panel = overlay?.sidebar ?? (phone?.matches && aside && !aside.hidden && phonePage(aside) && eventRoot.contains(aside) ? aside : null);
|
|
307
|
+
if (event.key !== "Tab") return;
|
|
308
|
+
if (!panel) {
|
|
309
|
+
// Focus outside an open phone page, on the body after a load or on chrome the page covers, enters the page.
|
|
310
|
+
// Dialogs and popovers keep their own Tab order.
|
|
311
|
+
const page = phone?.matches ? allRoots().flatMap(openDetails).find(phonePage) : undefined;
|
|
312
|
+
if (!page || ownerDocument.querySelector("dialog[open], [aria-modal='true']") || target.closest("[popover]")) return;
|
|
313
|
+
const stops = tabStopsIn(page);
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
(event.shiftKey ? stops.at(-1) : stops[0])?.focus();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!panel.contains(target)) return;
|
|
319
|
+
const stops = tabStopsIn(panel);
|
|
281
320
|
const first = stops[0];
|
|
282
321
|
const last = stops.at(-1);
|
|
283
322
|
if (!first || !last) {
|
|
284
323
|
event.preventDefault();
|
|
285
324
|
return;
|
|
286
325
|
}
|
|
287
|
-
|
|
326
|
+
// Focus can also rest on an element that is no tab stop, such as a title focused as the panel opened.
|
|
327
|
+
const active = document.activeElement;
|
|
328
|
+
const before = (element, other) => Boolean(element && other && element.compareDocumentPosition(other) & Node.DOCUMENT_POSITION_FOLLOWING);
|
|
329
|
+
if (event.shiftKey && !stops.some(stop => before(stop, active))) {
|
|
288
330
|
event.preventDefault();
|
|
289
331
|
last.focus();
|
|
290
|
-
} else if (!event.shiftKey &&
|
|
332
|
+
} else if (!event.shiftKey && !stops.some(stop => before(active, stop))) {
|
|
291
333
|
event.preventDefault();
|
|
292
334
|
first.focus();
|
|
293
335
|
}
|
|
@@ -298,7 +340,6 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
298
340
|
const phone = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 47.999rem)") : null;
|
|
299
341
|
const wide = typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 48rem)") : null;
|
|
300
342
|
const touchDrawer = typeof window.matchMedia === "function" ? window.matchMedia(`${COMPACT_QUERY} and (pointer: coarse)`) : null;
|
|
301
|
-
const reducedMotion = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
302
343
|
const detailSelector = ".k2b-app-workspace__detail";
|
|
303
344
|
const grabberSelector = ".k2b-app-workspace__detail-grabber";
|
|
304
345
|
const detailHidden = new WeakMap();
|
|
@@ -445,7 +486,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
445
486
|
subtree: true
|
|
446
487
|
});
|
|
447
488
|
};
|
|
489
|
+
const phonePage = aside => aside.dataset.workspaceDetailPhone === "page";
|
|
448
490
|
const setDetent = (aside, full) => {
|
|
491
|
+
if (phonePage(aside)) return;
|
|
449
492
|
touchedDetails.add(aside);
|
|
450
493
|
if (full) aside.dataset.workspaceDetailSheet = "full";else delete aside.dataset.workspaceDetailSheet;
|
|
451
494
|
aside.querySelector(grabberSelector)?.setAttribute("aria-pressed", String(full));
|
|
@@ -466,7 +509,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
466
509
|
// edge, so the workspace clips while the panel moves (see the stylesheet).
|
|
467
510
|
const slide = (root, aside) => {
|
|
468
511
|
slides.get(root)?.();
|
|
469
|
-
if (!root.hasAttribute("data-workspace-motion") ||
|
|
512
|
+
if (!root.hasAttribute("data-workspace-motion") || prefersReducedMotion() || !wide?.matches) return;
|
|
470
513
|
const end = () => {
|
|
471
514
|
aside.removeEventListener("transitionend", onEnd);
|
|
472
515
|
aside.removeEventListener("transitioncancel", onEnd);
|
|
@@ -489,12 +532,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
489
532
|
root.dataset.workspaceDetailSliding = "";
|
|
490
533
|
};
|
|
491
534
|
const revealItem = (root, aside, snapshot) => {
|
|
535
|
+
if (phone?.matches && phonePage(aside)) return;
|
|
492
536
|
const pending = revealFrames.get(root);
|
|
493
537
|
if (pending !== undefined) cancelAnimationFrame(pending);
|
|
494
538
|
revealFrames.set(root, requestAnimationFrame(() => {
|
|
495
539
|
revealFrames.delete(root);
|
|
496
540
|
if (!aside.hidden && aside.isConnected && snapshot.item.isConnected && mainElement(root)?.contains(snapshot.item) && snapshot.item.closest(ROOT_SELECTOR) === root) {
|
|
497
|
-
const behavior =
|
|
541
|
+
const behavior = prefersReducedMotion() ? "auto" : "smooth";
|
|
498
542
|
const main = mainElement(root);
|
|
499
543
|
const cell = snapshot.cell;
|
|
500
544
|
const target = cell?.isConnected && main?.contains(cell) ? cell : snapshot.item;
|
|
@@ -563,7 +607,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
563
607
|
if (aside.hidden) {
|
|
564
608
|
if (pendingTitle?.aside === aside) cancelTitle();
|
|
565
609
|
if (panelStep === aside) panelStep = null;
|
|
566
|
-
if (root.hasAttribute("data-workspace-motion") && !
|
|
610
|
+
if (root.hasAttribute("data-workspace-motion") && !prefersReducedMotion() && wide?.matches) {
|
|
567
611
|
// Closing holds the detail column until the slide ends; anchor after Main widens.
|
|
568
612
|
const cancel = () => {
|
|
569
613
|
root.removeEventListener("transitionend", onTransitionEnd);
|
|
@@ -734,6 +778,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
734
778
|
const aside = target?.closest(detailSelector);
|
|
735
779
|
const root = aside?.closest(ROOT_SELECTOR);
|
|
736
780
|
if (!target || !aside || !root || aside.hidden || !observedRoots.has(root) || target.closest(ROOT_SELECTOR) !== root) return;
|
|
781
|
+
if (phonePage(aside)) return;
|
|
737
782
|
const grabber = target.closest(grabberSelector);
|
|
738
783
|
if (!grabber && (!target.closest(".k2b-detail-panel__header") || target.closest("a, button, input, select, textarea, [role='button'], [contenteditable]:not([contenteditable='false'])"))) return;
|
|
739
784
|
claimedDetailGestures.add(event);
|
|
@@ -759,7 +804,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
759
804
|
const target = eventElement(event);
|
|
760
805
|
const grabber = target?.closest(grabberSelector);
|
|
761
806
|
const aside = grabber?.closest(detailSelector);
|
|
762
|
-
if (!aside || aside.hidden || !phone?.matches || claimedToggleClicks.has(event)) return;
|
|
807
|
+
if (!aside || aside.hidden || phonePage(aside) || !phone?.matches || claimedToggleClicks.has(event)) return;
|
|
763
808
|
claimedToggleClicks.add(event);
|
|
764
809
|
if (suppressedClick === aside && event instanceof MouseEvent && event.detail !== 0) {
|
|
765
810
|
event.preventDefault();
|
|
@@ -789,6 +834,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
789
834
|
const touch = event.touches[0];
|
|
790
835
|
const root = aside?.closest(ROOT_SELECTOR);
|
|
791
836
|
if (!target || !aside || !root || aside.hidden || !touch || !observedRoots.has(root) || target.closest(ROOT_SELECTOR) !== root) return;
|
|
837
|
+
if (phone?.matches && phonePage(aside)) return;
|
|
792
838
|
if (phone?.matches && (verticalScroller(target, aside) ?? aside).scrollTop <= 0) {
|
|
793
839
|
if (!ownsTouchGesture(target, aside, "sheet")) beginDetailDrag(aside, target, "sheet", touch.clientX, touch.clientY, touch.identifier);
|
|
794
840
|
} else if (touchDrawer?.matches && closable(aside)) {
|
|
@@ -840,22 +886,27 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
840
886
|
const onDetailTouchEnd = event => {
|
|
841
887
|
if (detailDrag && detailDrag.source !== "pointer") finishDetailDrag(event.type === "touchcancel");
|
|
842
888
|
};
|
|
843
|
-
const
|
|
889
|
+
const onPanelDoubleClick = event => {
|
|
844
890
|
const handle = resizeHandle(event);
|
|
845
891
|
const root = handle?.closest(ROOT_SELECTOR);
|
|
846
|
-
|
|
892
|
+
const kind = handle ? resizeKind(handle) : null;
|
|
893
|
+
if (!handle || !root || kind !== "detail" && kind !== "pane" || !workspaceResizable(root) || !controlsResizableRegion(root, handle, kind)) return;
|
|
894
|
+
const key = kind === "pane" ? "paneWidths" : "detailWidths";
|
|
847
895
|
const widths = {
|
|
848
|
-
...layoutState
|
|
896
|
+
...layoutState[key]
|
|
849
897
|
};
|
|
850
898
|
delete widths[panelId(handle)];
|
|
851
899
|
layoutState = {
|
|
852
900
|
...layoutState,
|
|
853
|
-
|
|
901
|
+
[key]: widths
|
|
854
902
|
};
|
|
855
|
-
const variable = appWorkspacePanelVariable(
|
|
903
|
+
const variable = appWorkspacePanelVariable(kind, panelId(handle));
|
|
856
904
|
root.style.removeProperty(variable);
|
|
857
|
-
|
|
858
|
-
|
|
905
|
+
// An SSR ancestor may still supply the old preference until the next request.
|
|
906
|
+
if (getComputedStyle(root).getPropertyValue(variable).trim()) root.style.setProperty(variable, `${numberData(handle, "workspaceDefaultSize", kind === "pane" ? APP_WORKSPACE_PANE_DEFAULT : APP_WORKSPACE_DETAIL_DEFAULT)}px`);
|
|
907
|
+
// The siblings fit again around the default width, also in a Main without levels.
|
|
908
|
+
if (kind === "pane") reconcilePanes(root, layoutState);
|
|
909
|
+
updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
|
|
859
910
|
options.writeState?.(layoutState);
|
|
860
911
|
};
|
|
861
912
|
const stopScrollFades = installScrollFades(eventRoot);
|
|
@@ -885,6 +936,40 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
885
936
|
}
|
|
886
937
|
options.writeState?.(layoutState);
|
|
887
938
|
};
|
|
939
|
+
|
|
940
|
+
// A level resize starts from what is on screen. When the stored widths do
|
|
941
|
+
// not fit beside the flexible level, CSS shrinks the resizable levels (a
|
|
942
|
+
// level without a separator does not shrink), so a step from a shrunk width
|
|
943
|
+
// would land on a preference that shrinks again and jump. The visible
|
|
944
|
+
// resizable levels therefore take their rendered widths as preferences when
|
|
945
|
+
// a step really moves; then they fit, and the step moves exactly by its
|
|
946
|
+
// size. All widths are measured before any is written, because each write
|
|
947
|
+
// reflows the others.
|
|
948
|
+
const settleLevels = (root, handle) => {
|
|
949
|
+
const main = handle.closest(".k2b-app-workspace__main");
|
|
950
|
+
if (!main?.hasAttribute("data-workspace-levels")) return;
|
|
951
|
+
const widths = Array.from(main.querySelectorAll(HANDLE_SELECTOR)).filter(other => other.closest(".k2b-app-workspace__main") === main && resizeKind(other) === "pane").flatMap(other => {
|
|
952
|
+
const panel = controlledPanel(root, other);
|
|
953
|
+
return isVisible(panel) && !flexibleLevel(panel) && panel.dataset.workspaceResizable === "true" ? [{
|
|
954
|
+
id: panelId(other),
|
|
955
|
+
width: Math.round(elementSize(panel, "pane"))
|
|
956
|
+
}] : [];
|
|
957
|
+
});
|
|
958
|
+
for (const {
|
|
959
|
+
id,
|
|
960
|
+
width
|
|
961
|
+
} of widths) {
|
|
962
|
+
if (layoutState.paneWidths?.[id] === width) continue;
|
|
963
|
+
root.style.setProperty(appWorkspacePanelVariable("pane", id), `${width}px`);
|
|
964
|
+
layoutState = {
|
|
965
|
+
...layoutState,
|
|
966
|
+
paneWidths: {
|
|
967
|
+
...layoutState.paneWidths,
|
|
968
|
+
[id]: width
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
};
|
|
888
973
|
const flushPointerMove = () => {
|
|
889
974
|
if (resizeFrame !== null) {
|
|
890
975
|
cancelAnimationFrame(resizeFrame);
|
|
@@ -893,8 +978,18 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
893
978
|
if (!active || pendingClient === null) return;
|
|
894
979
|
const client = pendingClient;
|
|
895
980
|
pendingClient = null;
|
|
896
|
-
active.moved ||= client !== active.startClient;
|
|
897
981
|
const requested = active.startSize + (client - active.startClient) * active.direction;
|
|
982
|
+
// A press without movement, or a drag that the limits stop, changes no
|
|
983
|
+
// level preference, also not a sibling's.
|
|
984
|
+
if (!active.moved && client !== active.startClient && active.kind === "pane" && levelsMain(active.handle)) {
|
|
985
|
+
const {
|
|
986
|
+
min,
|
|
987
|
+
max
|
|
988
|
+
} = sizeLimits(active.root, active.handle, "pane");
|
|
989
|
+
if (clamp(requested, min, max) === Math.round(currentSize(active.root, active.handle, "pane"))) return;
|
|
990
|
+
settleLevels(active.root, active.handle);
|
|
991
|
+
}
|
|
992
|
+
active.moved ||= client !== active.startClient;
|
|
898
993
|
// Below half the smallest visible width a hideable sidebar stays at its
|
|
899
994
|
// minimum and previews hiding; dragging back out resumes the live width.
|
|
900
995
|
const hiding = active.kind === "sidebar" && sidebarHideable(active.root) && requested < sizeLimits(active.root, active.handle, active.kind).min / 2;
|
|
@@ -1000,6 +1095,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1000
1095
|
else if (kind !== "drawer" && event.key === "ArrowLeft") requested = collapsibleSidebar && current <= APP_WORKSPACE_SIDEBAR_MIN ? APP_WORKSPACE_SIDEBAR_COLLAPSED : current - step * direction;else if (kind !== "drawer" && event.key === "ArrowRight") requested = collapsibleSidebar && current <= APP_WORKSPACE_SIDEBAR_COLLAPSED ? layoutState.sidebarWidth ?? APP_WORKSPACE_SIDEBAR_MIN : current + step * direction;
|
|
1001
1096
|
if (requested === null) return;
|
|
1002
1097
|
event.preventDefault();
|
|
1098
|
+
if (kind === "pane" && levelsMain(handle)) {
|
|
1099
|
+
// A step that the limits stop changes no preference, also not a sibling's.
|
|
1100
|
+
if (clamp(requested, min, max) === Math.round(current)) return;
|
|
1101
|
+
settleLevels(root, handle);
|
|
1102
|
+
}
|
|
1003
1103
|
persistSize(handle, kind, applySize(root, handle, kind, requested));
|
|
1004
1104
|
if (kind === "sidebar" || kind === "detail") reconcilePanes(root, layoutState);
|
|
1005
1105
|
};
|
|
@@ -1007,6 +1107,32 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1007
1107
|
const nested = Array.from(eventRoot.querySelectorAll(ROOT_SELECTOR));
|
|
1008
1108
|
return eventRoot instanceof HTMLElement && eventRoot.matches(ROOT_SELECTOR) ? [eventRoot, ...nested] : nested;
|
|
1009
1109
|
};
|
|
1110
|
+
// Remember only attributes we own; application-set inert survives every transition.
|
|
1111
|
+
const phonePageInerted = new Set();
|
|
1112
|
+
const syncPhonePages = () => {
|
|
1113
|
+
const regions = new Set();
|
|
1114
|
+
if (phone?.matches) {
|
|
1115
|
+
for (const root of allRoots()) {
|
|
1116
|
+
if (!root.isConnected) continue;
|
|
1117
|
+
for (const aside of openDetails(root).filter(phonePage)) {
|
|
1118
|
+
for (let node = aside; node && node !== root; node = node.parentElement) {
|
|
1119
|
+
const parent = node.parentElement;
|
|
1120
|
+
if (parent) overlayRegions(parent, aside).forEach(region => regions.add(region));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
phonePageInerted.forEach(region => {
|
|
1126
|
+
if (regions.has(region)) return;
|
|
1127
|
+
region.removeAttribute("inert");
|
|
1128
|
+
phonePageInerted.delete(region);
|
|
1129
|
+
});
|
|
1130
|
+
regions.forEach(region => {
|
|
1131
|
+
if (region.hasAttribute("inert")) return;
|
|
1132
|
+
region.setAttribute("inert", "");
|
|
1133
|
+
phonePageInerted.add(region);
|
|
1134
|
+
});
|
|
1135
|
+
};
|
|
1010
1136
|
const roots = () => {
|
|
1011
1137
|
// A hidden or zero-width workspace measures 0, which drives every limit
|
|
1012
1138
|
// down to its minimum — reconciling one would overwrite its variables and
|
|
@@ -1125,12 +1251,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1125
1251
|
};
|
|
1126
1252
|
const layoutNode = node => node instanceof Element && (node.matches(LAYOUT_REGION_SELECTOR) || Boolean(node.querySelector(LAYOUT_REGION_SELECTOR)));
|
|
1127
1253
|
const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(records => {
|
|
1254
|
+
syncPhonePages();
|
|
1128
1255
|
if (pendingTitle && (pendingTitle.aside.hidden || !pendingTitle.aside.isConnected)) cancelTitle();
|
|
1129
1256
|
// Hiding or removing the sidebar, or its button, ends the overlay.
|
|
1130
1257
|
if (overlay && (overlay.sidebar.hidden || !overlay.sidebar.isConnected || !overlay.toggle.isConnected)) closeOverlay("kept");
|
|
1131
1258
|
const details = new Set();
|
|
1132
1259
|
for (const record of records) {
|
|
1133
|
-
if (record.type === "attributes" && record.target instanceof HTMLElement && record.target.matches(detailSelector) && !details.has(record.target)) {
|
|
1260
|
+
if (record.type === "attributes" && record.attributeName === "hidden" && record.target instanceof HTMLElement && record.target.matches(detailSelector) && !details.has(record.target)) {
|
|
1134
1261
|
details.add(record.target);
|
|
1135
1262
|
detailTransition(record.target, detailHidden.get(record.target) ?? record.oldValue !== null);
|
|
1136
1263
|
}
|
|
@@ -1152,7 +1279,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1152
1279
|
eventRoot.addEventListener("focusin", onDetailFocusIn);
|
|
1153
1280
|
eventRoot.addEventListener("pointerdown", onSheetPointerDown);
|
|
1154
1281
|
eventRoot.addEventListener("click", onGrabberClick);
|
|
1155
|
-
eventRoot.addEventListener("dblclick",
|
|
1282
|
+
eventRoot.addEventListener("dblclick", onPanelDoubleClick);
|
|
1156
1283
|
window.addEventListener("pointermove", onSheetPointerMove);
|
|
1157
1284
|
window.addEventListener("pointerup", onSheetPointerEnd);
|
|
1158
1285
|
window.addEventListener("pointercancel", onSheetPointerEnd);
|
|
@@ -1163,19 +1290,24 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1163
1290
|
eventRoot.addEventListener("touchend", onDetailTouchEnd);
|
|
1164
1291
|
eventRoot.addEventListener("touchcancel", onDetailTouchEnd);
|
|
1165
1292
|
compact?.addEventListener("change", onCompactChange);
|
|
1293
|
+
phone?.addEventListener("change", syncPhonePages);
|
|
1166
1294
|
eventRoot.addEventListener("keydown", onKeyDown);
|
|
1167
1295
|
eventRoot.addEventListener("focusin", onFocusIn);
|
|
1168
1296
|
window.addEventListener("resize", onResize);
|
|
1297
|
+
syncPhonePages();
|
|
1169
1298
|
syncObservedRoots();
|
|
1170
1299
|
mutationObserver?.observe(eventRoot, {
|
|
1171
1300
|
attributes: true,
|
|
1172
1301
|
attributeOldValue: true,
|
|
1173
|
-
attributeFilter: ["hidden"],
|
|
1302
|
+
attributeFilter: ["hidden", "data-workspace-level-shown"],
|
|
1174
1303
|
childList: true,
|
|
1175
1304
|
subtree: true
|
|
1176
1305
|
});
|
|
1177
1306
|
scheduleReconcile();
|
|
1178
1307
|
return () => {
|
|
1308
|
+
phonePageInerted.forEach(region => region.removeAttribute("inert"));
|
|
1309
|
+
phonePageInerted.clear();
|
|
1310
|
+
phone?.removeEventListener("change", syncPhonePages);
|
|
1179
1311
|
cancelTitle();
|
|
1180
1312
|
pendingAnchors.forEach(cancel => cancel());
|
|
1181
1313
|
slides.forEach(end => end());
|
|
@@ -1200,7 +1332,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1200
1332
|
eventRoot.removeEventListener("focusin", onDetailFocusIn);
|
|
1201
1333
|
eventRoot.removeEventListener("pointerdown", onSheetPointerDown);
|
|
1202
1334
|
eventRoot.removeEventListener("click", onGrabberClick);
|
|
1203
|
-
eventRoot.removeEventListener("dblclick",
|
|
1335
|
+
eventRoot.removeEventListener("dblclick", onPanelDoubleClick);
|
|
1204
1336
|
window.removeEventListener("pointermove", onSheetPointerMove);
|
|
1205
1337
|
window.removeEventListener("pointerup", onSheetPointerEnd);
|
|
1206
1338
|
window.removeEventListener("pointercancel", onSheetPointerEnd);
|