@k2b/ui 0.30.0 → 0.32.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/README.md +1 -1
- package/dist/base.css +2 -0
- package/dist/browser/actions/Toolbar.js +2 -0
- package/dist/browser/actions/Toolbar.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +8 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +28 -2
- package/dist/browser/chat/ChatDecision.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/MapAttribution.js +27 -0
- package/dist/browser/content/MapAttribution.js.map +1 -0
- package/dist/browser/content/MapView.js +553 -0
- package/dist/browser/content/MapView.js.map +1 -0
- package/dist/browser/content/MapViewFrame.js +395 -0
- package/dist/browser/content/MapViewFrame.js.map +1 -0
- 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 +6 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/map-palette.js +39 -0
- package/dist/browser/content/map-palette.js.map +1 -0
- package/dist/browser/content/map-style.js +276 -0
- package/dist/browser/content/map-style.js.map +1 -0
- package/dist/browser/content/map-view-geometry.js +269 -0
- package/dist/browser/content/map-view-geometry.js.map +1 -0
- package/dist/browser/content/map-view-live.js +65 -0
- package/dist/browser/content/map-view-live.js.map +1 -0
- package/dist/browser/content/map-view-messages.js +53 -0
- package/dist/browser/content/map-view-messages.js.map +1 -0
- package/dist/browser/content/map-view-types.js +2 -0
- package/dist/browser/content/map-view-types.js.map +1 -0
- package/dist/browser/content/map-view-worker.js +10 -0
- package/dist/browser/feedback/dialog-core.js +19 -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 +95 -60
- 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/layout/AppWorkspace.js +194 -144
- 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/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 +301 -34
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/app-workspace-width.js +26 -0
- package/dist/browser/layout/app-workspace-width.js.map +1 -0
- package/dist/browser/layout/index.js +1 -1
- 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/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/licenses/maplibre-gl.txt +116 -0
- package/dist/ssr/actions/Toolbar.js +2 -0
- package/dist/ssr/actions/Toolbar.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +8 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +11 -0
- package/dist/ssr/chat/ChatDecision.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/MapAttribution.js +19 -0
- package/dist/ssr/content/MapAttribution.js.map +1 -0
- package/dist/ssr/content/MapView.js +474 -0
- package/dist/ssr/content/MapView.js.map +1 -0
- package/dist/ssr/content/MapViewFrame.js +193 -0
- package/dist/ssr/content/MapViewFrame.js.map +1 -0
- 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 +6 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/map-palette.js +39 -0
- package/dist/ssr/content/map-palette.js.map +1 -0
- package/dist/ssr/content/map-style.js +276 -0
- package/dist/ssr/content/map-style.js.map +1 -0
- package/dist/ssr/content/map-view-geometry.js +269 -0
- package/dist/ssr/content/map-view-geometry.js.map +1 -0
- package/dist/ssr/content/map-view-live.js +1 -0
- package/dist/ssr/content/map-view-messages.js +53 -0
- package/dist/ssr/content/map-view-messages.js.map +1 -0
- package/dist/ssr/content/map-view-types.js +2 -0
- package/dist/ssr/content/map-view-types.js.map +1 -0
- package/dist/ssr/feedback/dialog-core.js +19 -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 +40 -16
- 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/layout/AppWorkspace.js +53 -16
- 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/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 +301 -34
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/app-workspace-width.js +26 -0
- package/dist/ssr/layout/app-workspace-width.js.map +1 -0
- package/dist/ssr/layout/index.js +1 -1
- 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/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/styles.css +1 -1
- package/dist/types/content/Chart.d.ts +4 -2
- package/dist/types/content/ChartExplorer.d.ts +0 -1
- package/dist/types/content/MapAttribution.d.ts +6 -0
- package/dist/types/content/MapView.d.ts +5 -0
- package/dist/types/content/MapViewFrame.d.ts +31 -0
- package/dist/types/content/code-highlight.d.ts +3 -1
- package/dist/types/content/index.d.ts +7 -0
- package/dist/types/content/map-palette.d.ts +58 -0
- package/dist/types/content/map-style.d.ts +29 -0
- package/dist/types/content/map-view-geometry.d.ts +55 -0
- package/dist/types/content/map-view-live.d.ts +2 -0
- package/dist/types/content/map-view-messages.d.ts +21 -0
- package/dist/types/content/map-view-types.d.ts +63 -0
- package/dist/types/feedback/dialog-core.d.ts +1 -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/layout/AppWorkspace.d.ts +14 -3
- package/dist/types/layout/DetailPanel.d.ts +2 -0
- package/dist/types/layout/app-workspace-controller.d.ts +2 -0
- package/dist/types/layout/app-workspace-width.d.ts +13 -0
- package/dist/types/layout/index.d.ts +2 -2
- package/dist/types/layout/navigation-model.d.ts +2 -0
- package/dist/types/layout/resize-separator.d.ts +1 -0
- package/dist/types/surfaces/Skeleton.d.ts +6 -0
- package/dist/types/surfaces/index.d.ts +1 -0
- package/map-glyphs/IBM Plex Sans Medium/0-255.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/1024-1279.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/256-511.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/512-767.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/768-1023.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/7680-7935.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8192-8447.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8448-8703.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8704-8959.pbf +0 -0
- package/map-glyphs/OFL.txt +93 -0
- package/package.json +11 -6
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
import { readAppWorkspaceWidth, resolveAppWorkspaceWidth } from "./app-workspace-width";
|
|
4
5
|
import { installScrollFades } from "./scroll-fade";
|
|
5
6
|
const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
6
7
|
const ROOT_SELECTOR = "[data-k2b-app-workspace]";
|
|
@@ -25,7 +26,7 @@ const parkDetailDrawer = (aside, focus = false) => {
|
|
|
25
26
|
detailDrawerContent(aside)?.style.removeProperty("--k2b-detail-space");
|
|
26
27
|
const toggle = detailToggle(aside);
|
|
27
28
|
toggle?.setAttribute("aria-expanded", "false");
|
|
28
|
-
if (focus)
|
|
29
|
+
if (focus) detailReturnControl(aside)?.focus({
|
|
29
30
|
preventScroll: true
|
|
30
31
|
});
|
|
31
32
|
};
|
|
@@ -65,6 +66,8 @@ const sidebarHideable = root => sidebarElement(root)?.dataset.workspaceHideable
|
|
|
65
66
|
* stays application state, so the controller only requests the change.
|
|
66
67
|
*/
|
|
67
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";
|
|
68
71
|
/** Asks the owning `AppWorkspace.Detail` to close; its `onClose` decides. */
|
|
69
72
|
export const APP_WORKSPACE_DETAIL_CLOSE_EVENT = "k2b-app-workspace-detail-close";
|
|
70
73
|
/**
|
|
@@ -79,6 +82,15 @@ const previewSidebarHidden = (root, hidden) => {
|
|
|
79
82
|
if (hidden) sidebar.dataset.workspaceHidePreview = "true";else delete sidebar.dataset.workspaceHidePreview;
|
|
80
83
|
};
|
|
81
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
|
+
};
|
|
82
94
|
const elementSize = (element, kind) => {
|
|
83
95
|
if (!isVisible(element)) return 0;
|
|
84
96
|
const rect = element.getBoundingClientRect();
|
|
@@ -121,8 +133,9 @@ const sizeLimits = (root, handle, kind, reserveMainMinimum = true) => {
|
|
|
121
133
|
const content = kind === "detail" ? controlled?.closest(".k2b-app-workspace__content") : null;
|
|
122
134
|
const defaultMin = kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_MIN : kind === "pane" ? APP_WORKSPACE_PANE_MIN : kind === "detail" ? APP_WORKSPACE_DETAIL_MIN : APP_WORKSPACE_DRAWER_MIN;
|
|
123
135
|
const defaultMax = kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_MAX : kind === "pane" ? APP_WORKSPACE_PANE_MAX : kind === "detail" ? APP_WORKSPACE_DETAIL_MAX : APP_WORKSPACE_DRAWER_MAX;
|
|
136
|
+
const mainSpec = readAppWorkspaceWidth(mainElement(root)?.dataset.workspaceMainMin);
|
|
124
137
|
const mainMinValue = cssState(root, "--k2b-workspace-main-min");
|
|
125
|
-
const mainMin = mainMinValue.endsWith("px") ? Number.parseFloat(mainMinValue) : APP_WORKSPACE_MAIN_MIN;
|
|
138
|
+
const mainMin = typeof mainSpec === "object" ? resolveAppWorkspaceWidth(mainSpec, root.getBoundingClientRect().width) : mainMinValue.endsWith("px") ? Number.parseFloat(mainMinValue) : APP_WORKSPACE_MAIN_MIN;
|
|
126
139
|
const mainExtra = Math.max(0, (Number.isFinite(mainMin) ? mainMin : APP_WORKSPACE_MAIN_MIN) - APP_WORKSPACE_MAIN_MIN);
|
|
127
140
|
const limits = appWorkspaceResizeLimits({
|
|
128
141
|
kind,
|
|
@@ -213,18 +226,39 @@ const reconcilePanes = (root, layoutState, active = null) => {
|
|
|
213
226
|
if (!isVisible(panel)) return [];
|
|
214
227
|
const min = numberData(handle, "workspaceMinSize", APP_WORKSPACE_PANE_MIN);
|
|
215
228
|
const max = Math.max(min, numberData(handle, "workspaceMaxSize", APP_WORKSPACE_PANE_MAX));
|
|
216
|
-
const
|
|
217
|
-
const
|
|
229
|
+
const spec = readAppWorkspaceWidth(handle.dataset.workspaceDefaultSize) ?? elementSize(panel, "pane");
|
|
230
|
+
const preferred = active?.moved && active.root === root && active.kind === "pane" && active.handle === handle ? active.liveSize : layoutState.paneWidths?.[panelId(handle)];
|
|
231
|
+
const cssDefault = preferred === undefined && typeof spec !== "number";
|
|
232
|
+
if (cssDefault && root.style.getPropertyValue(appWorkspacePanelVariable("pane", panelId(handle))))
|
|
233
|
+
// Drop a previous live width (or an inherited SSR preference after reset).
|
|
234
|
+
root.style.setProperty(appWorkspacePanelVariable("pane", panelId(handle)), "initial");
|
|
235
|
+
// CSS has already applied this default and the pane-fit clamp, including
|
|
236
|
+
// subpixels. Reserve exactly that box when fitting other preferences;
|
|
237
|
+
// measuring must not turn a responsive default into a stored px width.
|
|
238
|
+
const requested = preferred ?? resolveAppWorkspaceWidth(spec, root.getBoundingClientRect().width);
|
|
239
|
+
const desired = cssDefault ? elementSize(panel, "pane") : clamp(requested, min, max);
|
|
218
240
|
return [{
|
|
219
241
|
desired,
|
|
220
242
|
handle,
|
|
221
|
-
min
|
|
243
|
+
min: cssDefault ? desired : min,
|
|
244
|
+
cssDefault,
|
|
245
|
+
requested: clamp(requested, min, max),
|
|
246
|
+
configuredMin: min
|
|
222
247
|
}];
|
|
223
248
|
});
|
|
224
249
|
if (!fits.length) return;
|
|
225
|
-
const
|
|
250
|
+
const budget = main.getBoundingClientRect().width - fixedWidth - APP_WORKSPACE_MAIN_MIN;
|
|
251
|
+
// Each share default clamps only against Main, not its resizable or non-resizable siblings. When panes
|
|
252
|
+
// together leave Main's primary region less than its minimum, fit them like stored widths, allowing subpixels.
|
|
253
|
+
if (fits.reduce((total, fit) => total + fit.desired, 0) > budget + 0.5) for (const fit of fits) if (fit.cssDefault) Object.assign(fit, {
|
|
254
|
+
cssDefault: false,
|
|
255
|
+
desired: fit.requested,
|
|
256
|
+
min: fit.configuredMin
|
|
257
|
+
});
|
|
258
|
+
const sizes = fitAppWorkspacePaneSizes(fits, budget);
|
|
226
259
|
fits.forEach((fit, index) => {
|
|
227
|
-
|
|
260
|
+
// Defaults stay CSS-owned; fit only saved or live preferences around them.
|
|
261
|
+
if (!fit.cssDefault) root.style.setProperty(appWorkspacePanelVariable("pane", panelId(fit.handle)), `${sizes[index]}px`);
|
|
228
262
|
});
|
|
229
263
|
fits.forEach((fit, index) => updateHandleValue(root, fit.handle, "pane", sizes[index]));
|
|
230
264
|
};
|
|
@@ -313,14 +347,26 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
313
347
|
if (detailButton) {
|
|
314
348
|
const root = workspaceRoot(detailButton);
|
|
315
349
|
const aside = root ? controlledPanel(root, detailButton) : null;
|
|
316
|
-
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;
|
|
317
353
|
claimedToggleClicks.add(event);
|
|
318
|
-
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 {
|
|
319
364
|
openDetailDrawer(aside);
|
|
320
365
|
if (event.detail === 0) requestTitle(aside);else detailButton.focus({
|
|
321
366
|
preventScroll: true
|
|
322
367
|
});
|
|
323
368
|
}
|
|
369
|
+
syncDetailRequestControls();
|
|
324
370
|
return;
|
|
325
371
|
}
|
|
326
372
|
const toggle = target?.closest(TOGGLE_SELECTOR);
|
|
@@ -396,10 +442,88 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
396
442
|
// Whether each open detail waited at the last check, so a column that starts
|
|
397
443
|
// waiting under focus hands that focus to its Details button.
|
|
398
444
|
const detailWasWaiting = new WeakMap();
|
|
445
|
+
const detailRequestExpanded = new Map();
|
|
446
|
+
const restoreDetailRequestExpanded = (control, state) => {
|
|
447
|
+
// A newer application render already owns the current value.
|
|
448
|
+
if (control.getAttribute("aria-expanded") !== state.written) return;
|
|
449
|
+
if (state.rendered === null) control.removeAttribute("aria-expanded");else control.setAttribute("aria-expanded", state.aside.hidden || !state.aside.isConnected ? "false" : state.rendered);
|
|
450
|
+
};
|
|
451
|
+
const requestedWaitingDetail = control => {
|
|
452
|
+
if (!wide?.matches || control.matches(DETAIL_TOGGLE_SELECTOR) || !eventRoot.contains(control)) return null;
|
|
453
|
+
const root = workspaceRoot(control);
|
|
454
|
+
const region = control.closest(".k2b-app-workspace__main, .k2b-app-workspace__detail");
|
|
455
|
+
if (!root || !region || region.closest(ROOT_SELECTOR) !== root) return null;
|
|
456
|
+
const ids = control.getAttribute("aria-controls")?.split(/\s+/);
|
|
457
|
+
return rootElements(root, detailSelector).find(aside => !aside.hidden && ids?.includes(aside.id) && detailWaiting(aside)) ?? null;
|
|
458
|
+
};
|
|
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
|
+
}
|
|
469
|
+
const waitingControls = new Map();
|
|
470
|
+
for (const root of allRoots()) {
|
|
471
|
+
const waitingDetails = wide?.matches ? rootElements(root, detailSelector).filter(aside => !aside.hidden && detailWaiting(aside)) : [];
|
|
472
|
+
if (!waitingDetails.length) continue;
|
|
473
|
+
// Only a control that names a Detail can request it.
|
|
474
|
+
for (const control of rootElements(root, "[aria-controls]")) {
|
|
475
|
+
if (control.matches(DETAIL_TOGGLE_SELECTOR) || !eventRoot.contains(control)) continue;
|
|
476
|
+
const region = control.closest(".k2b-app-workspace__main, .k2b-app-workspace__detail");
|
|
477
|
+
if (!region || region.closest(ROOT_SELECTOR) !== root) continue;
|
|
478
|
+
const ids = control.getAttribute("aria-controls")?.split(/\s+/);
|
|
479
|
+
const aside = waitingDetails.find(aside => ids?.includes(aside.id));
|
|
480
|
+
if (aside) waitingControls.set(control, aside);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
detailRequestExpanded.forEach((state, control) => {
|
|
484
|
+
if (waitingControls.has(control)) return;
|
|
485
|
+
restoreDetailRequestExpanded(control, state);
|
|
486
|
+
detailRequestExpanded.delete(control);
|
|
487
|
+
});
|
|
488
|
+
waitingControls.forEach((aside, control) => {
|
|
489
|
+
const current = control.getAttribute("aria-expanded");
|
|
490
|
+
const state = detailRequestExpanded.get(control);
|
|
491
|
+
const written = String(detailDrawer(aside));
|
|
492
|
+
// Keep the application's latest value to restore when waiting ends.
|
|
493
|
+
const rendered = !state || current !== state.written ? current : state.rendered;
|
|
494
|
+
detailRequestExpanded.set(control, {
|
|
495
|
+
aside,
|
|
496
|
+
rendered,
|
|
497
|
+
written
|
|
498
|
+
});
|
|
499
|
+
if (current !== written) control.setAttribute("aria-expanded", written);
|
|
500
|
+
});
|
|
501
|
+
};
|
|
502
|
+
const onDetailRequestClick = event => {
|
|
503
|
+
if (event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
|
|
504
|
+
const control = eventElement(event)?.closest(DETAIL_REQUEST_CONTROL_SELECTOR);
|
|
505
|
+
const aside = control ? requestedWaitingDetail(control) : null;
|
|
506
|
+
if (!control || !aside) return;
|
|
507
|
+
// The application keeps its Detail open; only its waiting drawer changes.
|
|
508
|
+
event.preventDefault();
|
|
509
|
+
event.stopImmediatePropagation();
|
|
510
|
+
cancelPendingDetail();
|
|
511
|
+
cancelTitle();
|
|
512
|
+
clearActivation();
|
|
513
|
+
// A control inside the drawer leaves with it, so focus goes to Details.
|
|
514
|
+
if (detailDrawer(aside)) parkDetailDrawer(aside, aside.contains(ownerDocument.activeElement));else {
|
|
515
|
+
openDetailDrawer(aside);
|
|
516
|
+
if (event.detail === 0) requestTitle(aside);else control.focus({
|
|
517
|
+
preventScroll: true
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
syncDetailRequestControls();
|
|
521
|
+
};
|
|
399
522
|
const openDetailDrawer = aside => {
|
|
400
523
|
aside.dataset.workspaceDetailDrawer = "open";
|
|
401
524
|
detailToggle(aside)?.setAttribute("aria-expanded", "true");
|
|
402
525
|
syncDetailDrawerSpace(aside);
|
|
526
|
+
syncDetailRequestControls();
|
|
403
527
|
};
|
|
404
528
|
// A sidebar drag changes the room beside it without resizing the workspace,
|
|
405
529
|
// so it rechecks waiting once per frame.
|
|
@@ -416,7 +540,63 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
416
540
|
if (args[2] === "sidebar") scheduleHostSync();
|
|
417
541
|
return size;
|
|
418
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
|
+
};
|
|
419
598
|
const syncHostState = () => {
|
|
599
|
+
rescueDetailControlFocus();
|
|
420
600
|
if (pendingDetail && !canOpenPendingDetail(pendingDetail.aside)) cancelPendingDetail();
|
|
421
601
|
if (overlay && sidebarForm(overlay.root) !== "overlay") closeOverlay("none");
|
|
422
602
|
for (const root of allRoots()) {
|
|
@@ -432,15 +612,18 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
432
612
|
const before = detailWasWaiting.get(aside);
|
|
433
613
|
detailWasWaiting.set(aside, waiting);
|
|
434
614
|
if (!waiting) {
|
|
435
|
-
//
|
|
436
|
-
|
|
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;
|
|
437
619
|
parkDetailDrawer(aside);
|
|
438
|
-
if (toggleFocused) focusTitle(aside);
|
|
439
|
-
} 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({
|
|
440
622
|
preventScroll: true
|
|
441
623
|
});
|
|
442
624
|
}
|
|
443
625
|
}
|
|
626
|
+
syncDetailRequestControls();
|
|
444
627
|
};
|
|
445
628
|
const phone = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 47.999rem)") : null;
|
|
446
629
|
const wide = typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 48rem)") : null;
|
|
@@ -554,6 +737,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
554
737
|
const root = target?.closest(ROOT_SELECTOR);
|
|
555
738
|
if (!target || !root || !observedRoots.has(root)) return;
|
|
556
739
|
lastFocus.set(root, target);
|
|
740
|
+
const control = target.closest("[aria-controls]");
|
|
741
|
+
detailControlFocus = control ? controlledDetail(control) : null;
|
|
557
742
|
const previous = remembered.get(root);
|
|
558
743
|
// Pointer focus follows pointerdown; keeping that snapshot also keeps the
|
|
559
744
|
// interaction that opened the panel rather than treating focus as a new one.
|
|
@@ -600,6 +785,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
600
785
|
// that opens later than that does not take the drawer or the focus.
|
|
601
786
|
let lastChosen = null;
|
|
602
787
|
const cancelTitleOnInteraction = event => {
|
|
788
|
+
if (detailControlFocus && !(event.target instanceof Node && detailControlFocus.control.contains(event.target))) detailControlFocus = null;
|
|
603
789
|
if (event.type === "pointerdown" || event.type === "keydown") clearActivation();
|
|
604
790
|
if (lastChosen && !(event.target instanceof Node && lastChosen.item.contains(event.target))) {
|
|
605
791
|
lastChosen.chosen = false;
|
|
@@ -861,7 +1047,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
861
1047
|
pendingAnchors.set(root, cancel);
|
|
862
1048
|
} else anchorItem(root, aside, snapshot);
|
|
863
1049
|
const focused = ownerDocument.activeElement;
|
|
864
|
-
|
|
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({
|
|
865
1057
|
preventScroll: true
|
|
866
1058
|
});
|
|
867
1059
|
clearDrag(aside);
|
|
@@ -903,6 +1095,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
903
1095
|
if (!aside || !owningRoot || !isVisible(aside)) return;
|
|
904
1096
|
if (event.key === "Escape" && detailDrawer(aside)) {
|
|
905
1097
|
parkDetailDrawer(aside, true);
|
|
1098
|
+
syncDetailRequestControls();
|
|
906
1099
|
event.preventDefault();
|
|
907
1100
|
return;
|
|
908
1101
|
}
|
|
@@ -1137,29 +1330,86 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1137
1330
|
const handle = resizeHandle(event);
|
|
1138
1331
|
const root = handle?.closest(ROOT_SELECTOR);
|
|
1139
1332
|
const kind = handle ? resizeKind(handle) : null;
|
|
1140
|
-
if (!handle || !root || kind
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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
|
+
};
|
|
1352
|
+
}
|
|
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);
|
|
1156
1362
|
updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
|
|
1157
|
-
options.writeState?.(layoutState);
|
|
1158
1363
|
};
|
|
1159
1364
|
const stopScrollFades = installScrollFades(eventRoot);
|
|
1160
1365
|
let layoutState = normalizeAppWorkspaceLayoutState(options.readState?.()) ?? {
|
|
1161
1366
|
version: 2
|
|
1162
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
|
+
};
|
|
1163
1413
|
let active = null;
|
|
1164
1414
|
let resizeFrame = null;
|
|
1165
1415
|
let pendingClient = null;
|
|
@@ -1181,7 +1431,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1181
1431
|
}
|
|
1182
1432
|
};
|
|
1183
1433
|
}
|
|
1184
|
-
|
|
1434
|
+
const root = workspaceRoot(handle);
|
|
1435
|
+
if (root) resetRegions.get(root)?.delete(resizeVariable(handle, kind));
|
|
1436
|
+
writeState();
|
|
1185
1437
|
};
|
|
1186
1438
|
|
|
1187
1439
|
// A level resize starts from what is on screen. When the stored widths do
|
|
@@ -1322,6 +1574,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1322
1574
|
const kind = handle ? resizeKind(handle) : null;
|
|
1323
1575
|
const root = handle ? workspaceRoot(handle) : null;
|
|
1324
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
|
+
}
|
|
1325
1582
|
const current = currentSize(root, handle, kind);
|
|
1326
1583
|
const {
|
|
1327
1584
|
min,
|
|
@@ -1400,6 +1657,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1400
1657
|
// resizing; without this the same workspace still had persisted sizes
|
|
1401
1658
|
// forced onto it on load and on every window resize.
|
|
1402
1659
|
if (!workspaceResizable(root)) return;
|
|
1660
|
+
restoreResetDefaults(root);
|
|
1403
1661
|
const handles = rootElements(root, HANDLE_SELECTOR);
|
|
1404
1662
|
// Chrome establishes the space available to the work area. Auxiliary
|
|
1405
1663
|
// panes are then fitted together into what remains, so DOM order cannot
|
|
@@ -1529,6 +1787,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1529
1787
|
};
|
|
1530
1788
|
const layoutNode = node => node instanceof Element && (node.matches(LAYOUT_REGION_SELECTOR) || Boolean(node.querySelector(LAYOUT_REGION_SELECTOR)));
|
|
1531
1789
|
const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(records => {
|
|
1790
|
+
if (records.some(record => record.type === "childList" || ["hidden", "aria-controls", "aria-expanded", "data-workspace-detail-drawer"].includes(record.attributeName ?? ""))) syncDetailRequestControls();
|
|
1532
1791
|
syncPhonePages();
|
|
1533
1792
|
if (pendingTitle && (pendingTitle.aside.hidden || !pendingTitle.aside.isConnected)) cancelTitle();
|
|
1534
1793
|
if (pendingDetail && (!canOpenPendingDetail(pendingDetail.aside) || !pendingDetail.snapshot.chosen || !pendingDetail.snapshot.item.isConnected)) cancelPendingDetail();
|
|
@@ -1553,8 +1812,10 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1553
1812
|
ownerDocument.addEventListener("keydown", onOverlayKeyDown);
|
|
1554
1813
|
ownerWindow.addEventListener("keydown", onDetailKeyDown);
|
|
1555
1814
|
ownerDocument.addEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1815
|
+
ownerDocument.addEventListener("focusout", onDetailControlFocusOut, true);
|
|
1556
1816
|
ownerDocument.addEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1557
1817
|
ownerDocument.addEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1818
|
+
ownerDocument.addEventListener("click", onDetailRequestClick, true);
|
|
1558
1819
|
ownerDocument.addEventListener("click", onChoiceClick, true);
|
|
1559
1820
|
ownerDocument.addEventListener("keyup", endActivation, true);
|
|
1560
1821
|
ownerDocument.addEventListener("pointerup", endActivation, true);
|
|
@@ -1581,10 +1842,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1581
1842
|
window.addEventListener("resize", onResize);
|
|
1582
1843
|
syncPhonePages();
|
|
1583
1844
|
syncObservedRoots();
|
|
1845
|
+
syncDetailRequestControls();
|
|
1584
1846
|
mutationObserver?.observe(eventRoot, {
|
|
1585
1847
|
attributes: true,
|
|
1586
1848
|
attributeOldValue: true,
|
|
1587
|
-
attributeFilter: ["hidden", "data-workspace-level-shown", "data-width", "data-workspace-main-min"],
|
|
1849
|
+
attributeFilter: ["hidden", "aria-controls", "aria-expanded", "data-workspace-detail-drawer", "data-workspace-level-shown", "data-width", "data-workspace-main-min", "data-workspace-default-size"],
|
|
1588
1850
|
childList: true,
|
|
1589
1851
|
subtree: true
|
|
1590
1852
|
});
|
|
@@ -1611,6 +1873,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1611
1873
|
titleTabIndices.forEach(title => title.removeAttribute("tabindex"));
|
|
1612
1874
|
ownerWindow.removeEventListener("keydown", onDetailKeyDown);
|
|
1613
1875
|
ownerDocument.removeEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1876
|
+
ownerDocument.removeEventListener("focusout", onDetailControlFocusOut, true);
|
|
1877
|
+
detailControlFocus = null;
|
|
1614
1878
|
ownerDocument.removeEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1615
1879
|
ownerDocument.removeEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1616
1880
|
clearActivation();
|
|
@@ -1638,6 +1902,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1638
1902
|
if (reconcileFrame !== null) cancelAnimationFrame(reconcileFrame);
|
|
1639
1903
|
if (hostSyncFrame !== null) cancelAnimationFrame(hostSyncFrame);
|
|
1640
1904
|
mutationObserver?.disconnect();
|
|
1905
|
+
detailRequestExpanded.forEach((state, control) => restoreDetailRequestExpanded(control, state));
|
|
1906
|
+
detailRequestExpanded.clear();
|
|
1907
|
+
ownerDocument.removeEventListener("click", onDetailRequestClick, true);
|
|
1641
1908
|
resizeObserver?.disconnect();
|
|
1642
1909
|
observedRoots.clear();
|
|
1643
1910
|
observedHosts.clear();
|