@k2b/ui 0.29.0 → 0.30.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 -2
- package/dist/browser/actions/Dropdown.js +6 -3
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +137 -7
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/SegmentedControl.js +5 -9
- package/dist/browser/actions/SegmentedControl.js.map +1 -1
- package/dist/browser/actions/SpotlightSearch.js +6 -3
- package/dist/browser/actions/SpotlightSearch.js.map +1 -1
- package/dist/browser/actions/Tabs.js +5 -2
- package/dist/browser/actions/Tabs.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +294 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -0
- package/dist/browser/actions/filter-messages.js +6 -2
- package/dist/browser/actions/filter-messages.js.map +1 -1
- package/dist/browser/actions/index.js +2 -1
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +1108 -604
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +120 -84
- package/dist/browser/chat/ChatDecision.js.map +1 -1
- package/dist/browser/chat/ChatPrimitives.js +292 -159
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/chat-messages.js +8 -0
- package/dist/browser/chat/chat-messages.js.map +1 -1
- package/dist/browser/chat/command-search.js +26 -15
- package/dist/browser/chat/command-search.js.map +1 -1
- package/dist/browser/chat/index.js +4 -2
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/content/Calendar.js +143 -116
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/CalendarWeekView.js +291 -266
- package/dist/browser/content/CalendarWeekView.js.map +1 -1
- package/dist/browser/content/DataTable.js +224 -155
- package/dist/browser/content/DataTable.js.map +1 -1
- package/dist/browser/content/data-table-messages.js +4 -2
- package/dist/browser/content/data-table-messages.js.map +1 -1
- package/dist/browser/feedback/MovedHint.js +197 -0
- package/dist/browser/feedback/MovedHint.js.map +1 -0
- package/dist/browser/feedback/dialog-core.js +66 -9
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/feedback/index.js +1 -0
- package/dist/browser/feedback/index.js.map +1 -1
- package/dist/browser/feedback/prompts.js +6 -1
- package/dist/browser/feedback/prompts.js.map +1 -1
- package/dist/browser/inputs/DatePicker.js +337 -212
- package/dist/browser/inputs/DatePicker.js.map +1 -1
- package/dist/browser/inputs/date-picker.js +59 -5
- package/dist/browser/inputs/date-picker.js.map +1 -1
- package/dist/browser/intl/messages.js +12 -0
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/browser/intl/weekend.js +39 -0
- package/dist/browser/intl/weekend.js.map +1 -0
- package/dist/browser/layout/AppOverview.js +0 -1
- package/dist/browser/layout/AppOverview.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +688 -471
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +3 -2
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/SettingsModal.js +2 -2
- package/dist/browser/layout/SettingsModal.js.map +1 -1
- package/dist/browser/layout/SidebarSwitcher.js +13 -12
- package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
- package/dist/browser/layout/TabBar.js +1 -1
- package/dist/browser/layout/TabBar.js.map +1 -1
- package/dist/browser/layout/app-host-width.js +44 -0
- package/dist/browser/layout/app-host-width.js.map +1 -0
- package/dist/browser/layout/app-workspace-controller.js +219 -39
- 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/surfaces/Placeholder.js +36 -13
- package/dist/browser/surfaces/Placeholder.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +5 -2
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +59 -7
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/SegmentedControl.js +1 -2
- package/dist/ssr/actions/SegmentedControl.js.map +1 -1
- package/dist/ssr/actions/SpotlightSearch.js +2 -2
- package/dist/ssr/actions/SpotlightSearch.js.map +1 -1
- package/dist/ssr/actions/Tabs.js +2 -2
- package/dist/ssr/actions/Tabs.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +211 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -0
- package/dist/ssr/actions/filter-messages.js +6 -2
- package/dist/ssr/actions/filter-messages.js.map +1 -1
- package/dist/ssr/actions/index.js +2 -1
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +546 -131
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +33 -19
- package/dist/ssr/chat/ChatDecision.js.map +1 -1
- package/dist/ssr/chat/ChatPrimitives.js +95 -24
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/chat-messages.js +8 -0
- package/dist/ssr/chat/chat-messages.js.map +1 -1
- package/dist/ssr/chat/command-search.js +26 -15
- package/dist/ssr/chat/command-search.js.map +1 -1
- package/dist/ssr/chat/index.js +4 -2
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/content/Calendar.js +109 -100
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/CalendarWeekView.js +42 -28
- package/dist/ssr/content/CalendarWeekView.js.map +1 -1
- package/dist/ssr/content/DataTable.js +78 -31
- package/dist/ssr/content/DataTable.js.map +1 -1
- package/dist/ssr/content/data-table-messages.js +4 -2
- package/dist/ssr/content/data-table-messages.js.map +1 -1
- package/dist/ssr/feedback/MovedHint.js +166 -0
- package/dist/ssr/feedback/MovedHint.js.map +1 -0
- package/dist/ssr/feedback/dialog-core.js +66 -9
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/feedback/index.js +1 -0
- package/dist/ssr/feedback/index.js.map +1 -1
- package/dist/ssr/feedback/prompts.js +6 -1
- package/dist/ssr/feedback/prompts.js.map +1 -1
- package/dist/ssr/inputs/DatePicker.js +138 -69
- package/dist/ssr/inputs/DatePicker.js.map +1 -1
- package/dist/ssr/inputs/date-picker.js +59 -5
- package/dist/ssr/inputs/date-picker.js.map +1 -1
- package/dist/ssr/intl/messages.js +12 -0
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/ssr/intl/weekend.js +39 -0
- package/dist/ssr/intl/weekend.js.map +1 -0
- package/dist/ssr/layout/AppOverview.js +0 -1
- package/dist/ssr/layout/AppOverview.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +203 -39
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +3 -2
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/SettingsModal.js +2 -2
- package/dist/ssr/layout/SettingsModal.js.map +1 -1
- package/dist/ssr/layout/SidebarSwitcher.js +13 -12
- package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
- package/dist/ssr/layout/TabBar.js +1 -1
- package/dist/ssr/layout/TabBar.js.map +1 -1
- package/dist/ssr/layout/app-host-width.js +44 -0
- package/dist/ssr/layout/app-host-width.js.map +1 -0
- package/dist/ssr/layout/app-workspace-controller.js +219 -39
- 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/surfaces/Placeholder.js +24 -7
- package/dist/ssr/surfaces/Placeholder.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +4 -0
- package/dist/types/actions/FilterChip.d.ts +22 -0
- package/dist/types/actions/ViewTabs.d.ts +30 -0
- package/dist/types/actions/filter-messages.d.ts +2 -0
- package/dist/types/actions/index.d.ts +4 -2
- package/dist/types/chat/ChatComposer.d.ts +52 -0
- package/dist/types/chat/ChatDecision.d.ts +18 -2
- package/dist/types/chat/ChatPrimitives.d.ts +10 -0
- package/dist/types/chat/chat-messages.d.ts +4 -0
- package/dist/types/chat/command-search.d.ts +2 -0
- package/dist/types/chat/index.d.ts +9 -8
- package/dist/types/content/DataTable.d.ts +3 -0
- package/dist/types/content/data-table-messages.d.ts +1 -0
- package/dist/types/feedback/MovedHint.d.ts +25 -0
- package/dist/types/feedback/dialog-core.d.ts +5 -2
- package/dist/types/feedback/index.d.ts +2 -0
- package/dist/types/feedback/prompts.d.ts +2 -0
- package/dist/types/inputs/DatePicker.d.ts +7 -0
- package/dist/types/inputs/date-picker.d.ts +25 -1
- package/dist/types/intl/messages.d.ts +6 -0
- package/dist/types/intl/weekend.d.ts +12 -0
- package/dist/types/layout/AppWorkspace.d.ts +43 -7
- package/dist/types/layout/app-host-width.d.ts +6 -0
- package/dist/types/layout/index.d.ts +2 -1
- package/dist/types/surfaces/Placeholder.d.ts +2 -1
- package/package.json +4 -4
|
@@ -4,10 +4,8 @@ import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_D
|
|
|
4
4
|
import { installScrollFades } from "./scroll-fade";
|
|
5
5
|
const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
6
6
|
const ROOT_SELECTOR = "[data-k2b-app-workspace]";
|
|
7
|
-
const LAYOUT_REGION_SELECTOR = [ROOT_SELECTOR, ".k2b-app-workspace__sidebar", ".k2b-app-workspace__main-pane", ".k2b-app-workspace__detail", ".k2b-app-workspace__drawer", HANDLE_SELECTOR].join(",");
|
|
7
|
+
const LAYOUT_REGION_SELECTOR = [ROOT_SELECTOR, ".k2b-app-workspace__sidebar", ".k2b-app-workspace__main", ".k2b-app-workspace__main-pane", ".k2b-app-workspace__detail", ".k2b-app-workspace__drawer", HANDLE_SELECTOR].join(",");
|
|
8
8
|
const TOGGLE_SELECTOR = "[data-app-workspace-sidebar-toggle]";
|
|
9
|
-
/** Touch-tablet gestures remain viewport decisions. */
|
|
10
|
-
const TOUCH_DRAWER_QUERY = "(min-width: 48rem) and (max-width: 63.999rem) and (pointer: coarse)";
|
|
11
9
|
const DETAIL_TOGGLE_SELECTOR = "[data-app-workspace-detail-toggle]";
|
|
12
10
|
const DETAIL_DRAWER_SELECTOR = '.k2b-app-workspace__detail[data-workspace-detail-drawer="open"]';
|
|
13
11
|
const cssState = (element, name) => getComputedStyle(element).getPropertyValue(name).trim();
|
|
@@ -40,6 +38,12 @@ const FOCUSABLE_SELECTOR = ["a[href]", "button:not([disabled])", "input:not([dis
|
|
|
40
38
|
/** Text entry, menus, dialogs, and open popups keep their own keys. */
|
|
41
39
|
const KEY_OWNER_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false']), [role='textbox'], [role='combobox'], [role='searchbox'], [role='spinbutton'], [role='menu'], [role='listbox'], [role='dialog'], dialog, [aria-haspopup]:not([aria-haspopup='false'])[aria-expanded='true']";
|
|
42
40
|
const ITEM_SELECTOR = ".k2b-app-workspace__sidebar-item";
|
|
41
|
+
/** Controls inside a selected row that act on their own instead of choosing the row. */
|
|
42
|
+
const NESTED_CONTROL_SELECTOR = "[aria-expanded], [aria-haspopup]:not([aria-haspopup='false']), [aria-pressed], [role='checkbox'], [role='switch'], [role='menuitem'], input, select, textarea, summary";
|
|
43
|
+
const DETAIL_REQUEST_CONTROL_SELECTOR = "[aria-pressed], [aria-expanded], [aria-controls]";
|
|
44
|
+
const SELECTION_MARK_SELECTOR = '[aria-selected="true"], [aria-current]:not([aria-current="false"]):not([aria-current="date"]):not([aria-current="time"]), [data-selected="true"]';
|
|
45
|
+
// Calendar day marks select a date range, not the Detail's subject.
|
|
46
|
+
const IGNORED_SELECTION_MARK_SELECTOR = "[data-calendar-day-key]";
|
|
43
47
|
const clamp = (value, min, max) => Math.round(Math.min(max, Math.max(min, value)));
|
|
44
48
|
const eventElement = event => event.target instanceof Element ? event.target : null;
|
|
45
49
|
const resizeHandle = event => eventElement(event)?.closest(HANDLE_SELECTOR) ?? null;
|
|
@@ -52,6 +56,7 @@ const workspaceRoot = handle => handle.closest(ROOT_SELECTOR);
|
|
|
52
56
|
const workspaceResizable = root => root.dataset.workspaceResizable !== "false";
|
|
53
57
|
const rootElements = (root, selector) => Array.from(root.querySelectorAll(selector)).filter(element => element.closest(ROOT_SELECTOR) === root);
|
|
54
58
|
const sidebarElement = root => rootElements(root, ".k2b-app-workspace__sidebar")[0] ?? null;
|
|
59
|
+
const sidebarForm = root => cssState(sidebarElement(root) ?? root, "--k2b-workspace-sidebar-form");
|
|
55
60
|
const mainElement = root => rootElements(root, ".k2b-app-workspace__main")[0] ?? null;
|
|
56
61
|
const sidebarCollapsible = root => sidebarElement(root)?.dataset.workspaceCollapsible === "true";
|
|
57
62
|
const sidebarHideable = root => sidebarElement(root)?.dataset.workspaceHideable === "true";
|
|
@@ -101,7 +106,7 @@ const numberData = (handle, key, fallback) => {
|
|
|
101
106
|
const value = Number(handle.dataset[key]);
|
|
102
107
|
return Number.isFinite(value) ? value : fallback;
|
|
103
108
|
};
|
|
104
|
-
const sizeLimits = (root, handle, kind) => {
|
|
109
|
+
const sizeLimits = (root, handle, kind, reserveMainMinimum = true) => {
|
|
105
110
|
const controlled = controlledPanel(root, handle);
|
|
106
111
|
const main = kind === "pane" ? handle.closest(".k2b-app-workspace__main") : mainElement(root);
|
|
107
112
|
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)) : [];
|
|
@@ -116,14 +121,23 @@ const sizeLimits = (root, handle, kind) => {
|
|
|
116
121
|
const content = kind === "detail" ? controlled?.closest(".k2b-app-workspace__content") : null;
|
|
117
122
|
const defaultMin = kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_MIN : kind === "pane" ? APP_WORKSPACE_PANE_MIN : kind === "detail" ? APP_WORKSPACE_DETAIL_MIN : APP_WORKSPACE_DRAWER_MIN;
|
|
118
123
|
const defaultMax = kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_MAX : kind === "pane" ? APP_WORKSPACE_PANE_MAX : kind === "detail" ? APP_WORKSPACE_DETAIL_MAX : APP_WORKSPACE_DRAWER_MAX;
|
|
119
|
-
|
|
124
|
+
const mainMinValue = cssState(root, "--k2b-workspace-main-min");
|
|
125
|
+
const mainMin = mainMinValue.endsWith("px") ? Number.parseFloat(mainMinValue) : APP_WORKSPACE_MAIN_MIN;
|
|
126
|
+
const mainExtra = Math.max(0, (Number.isFinite(mainMin) ? mainMin : APP_WORKSPACE_MAIN_MIN) - APP_WORKSPACE_MAIN_MIN);
|
|
127
|
+
const limits = appWorkspaceResizeLimits({
|
|
120
128
|
kind,
|
|
121
|
-
workspaceSize: kind === "drawer" ? root.getBoundingClientRect().height : kind === "pane" && main ? main.getBoundingClientRect().width : content ? root.getBoundingClientRect().right - root.clientLeft - content.getBoundingClientRect().left : root.getBoundingClientRect().width,
|
|
122
|
-
|
|
129
|
+
workspaceSize: kind === "drawer" ? root.getBoundingClientRect().height : kind === "pane" && main ? main.getBoundingClientRect().width : content ? root.getBoundingClientRect().right - root.clientLeft - content.getBoundingClientRect().left : kind === "sidebar" ? root.getBoundingClientRect().width - 2 * root.clientLeft : root.getBoundingClientRect().width,
|
|
130
|
+
// Interactive sizing keeps Main's declared minimum beside the sidebar and the Detail.
|
|
131
|
+
reservedSize: kind === "sidebar" && reserveMainMinimum ? mainExtra : kind === "detail" ? 8 + (reserveMainMinimum ? mainExtra : 0) : kind === "pane" ? panes.reduce((total, pane) => total + (pane === controlled ? 0 : elementSize(pane, "pane")), 0) + separatorSize : 0,
|
|
123
132
|
min: numberData(handle, "workspaceMinSize", defaultMin),
|
|
124
133
|
max: numberData(handle, "workspaceMaxSize", defaultMax),
|
|
125
134
|
sidebarCollapsible: kind === "sidebar" && sidebarCollapsible(root)
|
|
126
135
|
});
|
|
136
|
+
// Widths are applied as whole pixels; rounding up must not cross the fit boundary.
|
|
137
|
+
return kind === "sidebar" ? {
|
|
138
|
+
min: limits.min,
|
|
139
|
+
max: Math.max(limits.min, Math.floor(limits.max))
|
|
140
|
+
} : limits;
|
|
127
141
|
};
|
|
128
142
|
const currentSize = (root, handle, kind) => kind === "sidebar" ? elementSize(sidebarElement(root), kind) : elementSize(controlledPanel(root, handle), kind);
|
|
129
143
|
const updateHandleValue = (root, handle, kind, size, limits = sizeLimits(root, handle, kind)) => {
|
|
@@ -135,7 +149,7 @@ const updateHandleValue = (root, handle, kind, size, limits = sizeLimits(root, h
|
|
|
135
149
|
handle.setAttribute("aria-valuemax", String(max));
|
|
136
150
|
handle.setAttribute("aria-valuenow", String(clamp(size, min, max)));
|
|
137
151
|
};
|
|
138
|
-
const
|
|
152
|
+
const applyRegionSize = (root, handle, kind, requestedSize,
|
|
139
153
|
/**
|
|
140
154
|
* `snapSidebar: false` tracks the pointer continuously and only *previews*
|
|
141
155
|
* the collapsed state. Snapping (the default, and what keyboard steps and
|
|
@@ -146,7 +160,7 @@ options = {}) => {
|
|
|
146
160
|
const {
|
|
147
161
|
min,
|
|
148
162
|
max
|
|
149
|
-
} = sizeLimits(root, handle, kind);
|
|
163
|
+
} = sizeLimits(root, handle, kind, options.reserveMainMinimum);
|
|
150
164
|
if (kind === "sidebar") {
|
|
151
165
|
const collapsible = sidebarCollapsible(root);
|
|
152
166
|
const sidebar = options.snapSidebar === false ? {
|
|
@@ -157,10 +171,8 @@ options = {}) => {
|
|
|
157
171
|
// Cloud removes the attribute rather than writing "false", so that a
|
|
158
172
|
// presence selector (`[data-sidebar-collapsed]`) means "collapsed".
|
|
159
173
|
if (sidebar.collapsed) root.dataset.sidebarCollapsed = "true";else delete root.dataset.sidebarCollapsed;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
max
|
|
163
|
-
});
|
|
174
|
+
// A restored width may require an overlay; the handle still advertises interactive limits.
|
|
175
|
+
updateHandleValue(root, handle, kind, sidebar.width);
|
|
164
176
|
return sidebar.width;
|
|
165
177
|
}
|
|
166
178
|
const size = clamp(requestedSize, min, max);
|
|
@@ -229,7 +241,7 @@ const claimedToggleClicks = new WeakSet();
|
|
|
229
241
|
const claimedDetailGestures = new WeakSet();
|
|
230
242
|
|
|
231
243
|
/**
|
|
232
|
-
* The
|
|
244
|
+
* The overlay sidebar opens over the work area when its column does not fit
|
|
233
245
|
* from the button in the workspace's head. While it is open the rest of
|
|
234
246
|
* the workspace is inert, Tab stays in the sidebar, and Escape, the scrim,
|
|
235
247
|
* choosing an item, hiding the sidebar, or leaving the width range close it
|
|
@@ -304,9 +316,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
304
316
|
if (!aside || aside.hidden || !detailWaiting(aside) || claimedToggleClicks.has(event)) return;
|
|
305
317
|
claimedToggleClicks.add(event);
|
|
306
318
|
if (detailDrawer(aside)) parkDetailDrawer(aside);else {
|
|
307
|
-
aside
|
|
308
|
-
detailButton.setAttribute("aria-expanded", "true");
|
|
309
|
-
syncDetailDrawerSpace(aside);
|
|
319
|
+
openDetailDrawer(aside);
|
|
310
320
|
if (event.detail === 0) requestTitle(aside);else detailButton.focus({
|
|
311
321
|
preventScroll: true
|
|
312
322
|
});
|
|
@@ -318,7 +328,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
318
328
|
const next = overlayTarget(toggle);
|
|
319
329
|
if (!next || claimedToggleClicks.has(event)) return;
|
|
320
330
|
claimedToggleClicks.add(event);
|
|
321
|
-
if (overlay?.sidebar === next.sidebar) closeOverlay("button");else if (
|
|
331
|
+
if (overlay?.sidebar === next.sidebar) closeOverlay("button");else if (sidebarForm(next.root) === "overlay") openOverlay(next);
|
|
322
332
|
return;
|
|
323
333
|
}
|
|
324
334
|
if (!overlay || !target) return;
|
|
@@ -386,8 +396,29 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
386
396
|
// Whether each open detail waited at the last check, so a column that starts
|
|
387
397
|
// waiting under focus hands that focus to its Details button.
|
|
388
398
|
const detailWasWaiting = new WeakMap();
|
|
399
|
+
const openDetailDrawer = aside => {
|
|
400
|
+
aside.dataset.workspaceDetailDrawer = "open";
|
|
401
|
+
detailToggle(aside)?.setAttribute("aria-expanded", "true");
|
|
402
|
+
syncDetailDrawerSpace(aside);
|
|
403
|
+
};
|
|
404
|
+
// A sidebar drag changes the room beside it without resizing the workspace,
|
|
405
|
+
// so it rechecks waiting once per frame.
|
|
406
|
+
let hostSyncFrame = null;
|
|
407
|
+
const scheduleHostSync = () => {
|
|
408
|
+
if (hostSyncFrame !== null) return;
|
|
409
|
+
hostSyncFrame = requestAnimationFrame(() => {
|
|
410
|
+
hostSyncFrame = null;
|
|
411
|
+
syncHostState();
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
const applySize = (...args) => {
|
|
415
|
+
const size = applyRegionSize(...args);
|
|
416
|
+
if (args[2] === "sidebar") scheduleHostSync();
|
|
417
|
+
return size;
|
|
418
|
+
};
|
|
389
419
|
const syncHostState = () => {
|
|
390
|
-
if (
|
|
420
|
+
if (pendingDetail && !canOpenPendingDetail(pendingDetail.aside)) cancelPendingDetail();
|
|
421
|
+
if (overlay && sidebarForm(overlay.root) !== "overlay") closeOverlay("none");
|
|
391
422
|
for (const root of allRoots()) {
|
|
392
423
|
// A drawer removed with its panel leaves no space behind in Content.
|
|
393
424
|
for (const content of rootElements(root, ".k2b-app-workspace__content")) if (content.style.getPropertyValue("--k2b-detail-space") && !content.querySelector(DETAIL_DRAWER_SELECTOR)) content.style.removeProperty("--k2b-detail-space");
|
|
@@ -413,7 +444,6 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
413
444
|
};
|
|
414
445
|
const phone = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 47.999rem)") : null;
|
|
415
446
|
const wide = typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 48rem)") : null;
|
|
416
|
-
const touchDrawer = typeof window.matchMedia === "function" ? window.matchMedia(TOUCH_DRAWER_QUERY) : null;
|
|
417
447
|
const detailSelector = ".k2b-app-workspace__detail";
|
|
418
448
|
const grabberSelector = ".k2b-app-workspace__detail-grabber";
|
|
419
449
|
const detailHidden = new WeakMap();
|
|
@@ -447,6 +477,36 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
447
477
|
snapshot.top = snapshot.item.getBoundingClientRect().top;
|
|
448
478
|
snapshot.scrollTop = snapshot.scroller?.scrollTop ?? 0;
|
|
449
479
|
};
|
|
480
|
+
// Native Enter/Space clicks and pointer clicks continue their initiating event.
|
|
481
|
+
let activationItem = null;
|
|
482
|
+
let activationFrame = null;
|
|
483
|
+
const clearActivation = () => {
|
|
484
|
+
if (activationFrame !== null) cancelAnimationFrame(activationFrame);
|
|
485
|
+
activationFrame = null;
|
|
486
|
+
activationItem = null;
|
|
487
|
+
};
|
|
488
|
+
const endActivation = () => {
|
|
489
|
+
if (!activationItem) return;
|
|
490
|
+
// Space's native click follows keyup before the next frame.
|
|
491
|
+
if (activationFrame !== null) cancelAnimationFrame(activationFrame);
|
|
492
|
+
activationFrame = requestAnimationFrame(clearActivation);
|
|
493
|
+
};
|
|
494
|
+
const cancelActivation = () => {
|
|
495
|
+
// A cancelled gesture must not open a Detail that appears later.
|
|
496
|
+
if (lastChosen?.interaction === "pointer" && lastChosen.item === activationItem) {
|
|
497
|
+
lastChosen.chosen = false;
|
|
498
|
+
lastChosen = null;
|
|
499
|
+
}
|
|
500
|
+
clearActivation();
|
|
501
|
+
};
|
|
502
|
+
const onChoiceClick = event => {
|
|
503
|
+
const preceded = event.target instanceof Node && Boolean(activationItem?.contains(event.target));
|
|
504
|
+
clearActivation();
|
|
505
|
+
if (event.detail === 0 && !preceded) {
|
|
506
|
+
cancelTitleOnInteraction(event);
|
|
507
|
+
rememberInteraction(event);
|
|
508
|
+
} else if (!(event.target instanceof Node && lastChosen?.item.contains(event.target))) cancelTitleOnInteraction(event);
|
|
509
|
+
};
|
|
450
510
|
// A table row can exceed its scroller's width, so keep the clicked cell for horizontal reveal.
|
|
451
511
|
const rememberInteraction = event => {
|
|
452
512
|
const target = eventElement(event);
|
|
@@ -455,24 +515,37 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
455
515
|
const previous = remembered.get(root);
|
|
456
516
|
if (previous && openDetails(root).length) refreshItem(previous);
|
|
457
517
|
const main = mainElement(root);
|
|
458
|
-
|
|
459
|
-
|
|
518
|
+
// Collection rows with a roving tab index are items too, though no tab stops.
|
|
519
|
+
const item = target.closest(`${FOCUSABLE_SELECTOR}, [tabindex]`);
|
|
520
|
+
if (!main || !item || item === main || !main.contains(item) || item.closest(ROOT_SELECTOR) !== root) return;
|
|
460
521
|
if (event instanceof KeyboardEvent && event.key !== "Enter" && event.key !== " ") return;
|
|
522
|
+
if (event.type === "pointerdown" || event.type === "keydown") activationItem = item;
|
|
523
|
+
const assistive = event instanceof MouseEvent && event.type === "click" && event.detail === 0 && event.button === 0;
|
|
461
524
|
const cell = target.closest("td, th");
|
|
462
525
|
const snapshot = {
|
|
463
526
|
item,
|
|
464
527
|
cell: cell && main.contains(cell) && cell.closest(ROOT_SELECTOR) === root ? cell : null,
|
|
465
|
-
interaction: event instanceof KeyboardEvent ? "keyboard" : "pointer",
|
|
528
|
+
interaction: event instanceof KeyboardEvent || assistive ? "keyboard" : "pointer",
|
|
466
529
|
scroller: verticalScroller(item, main),
|
|
467
530
|
scrollTop: 0,
|
|
468
531
|
top: 0,
|
|
469
|
-
fresh: true
|
|
532
|
+
fresh: true,
|
|
533
|
+
// Text entry and focus alone do not choose an item.
|
|
534
|
+
// A modified activation, such as Ctrl/Cmd-click opening a new tab, chooses nothing here.
|
|
535
|
+
chosen: (event instanceof PointerEvent && event.type === "pointerdown" && event.button === 0 || event instanceof KeyboardEvent || assistive) && !(event instanceof MouseEvent || event instanceof KeyboardEvent ? event.ctrlKey || event.metaKey || event.shiftKey || event.altKey : false) &&
|
|
536
|
+
// Space scrolls on native links; it does not activate them.
|
|
537
|
+
!(event instanceof KeyboardEvent && event.key === " " && item.matches("a[href], area[href]") && (!item.hasAttribute("role") || item.getAttribute("role") === "link")) && !target.closest(KEY_OWNER_SELECTOR) && !item.closest("[role='tab'], [role='tablist']")
|
|
470
538
|
};
|
|
471
539
|
refreshItem(snapshot);
|
|
472
540
|
remembered.set(root, snapshot);
|
|
541
|
+
if (snapshot.chosen) lastChosen = snapshot;
|
|
542
|
+
if (snapshot.chosen) {
|
|
543
|
+
const aside = openDetails(root).find(canOpenPendingDetail);
|
|
544
|
+
if (aside) checkPendingDetail(root, aside, snapshot, event instanceof KeyboardEvent && event.key === " ");
|
|
545
|
+
}
|
|
473
546
|
// Enter in an editor or a field types; only an item that chooses something moves on to its title.
|
|
474
|
-
if (
|
|
475
|
-
const aside = openDetails(root).find(isVisible);
|
|
547
|
+
if (snapshot.chosen && snapshot.interaction === "keyboard") {
|
|
548
|
+
const aside = openDetails(root).find(aside => isVisible(aside) && (!detailWaiting(aside) || detailDrawer(aside)));
|
|
476
549
|
if (aside) requestTitle(aside, true);
|
|
477
550
|
}
|
|
478
551
|
};
|
|
@@ -523,7 +596,18 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
523
596
|
// content, so focus falls to the body. The panel keeps owning the steps until
|
|
524
597
|
// focus or a pointer goes elsewhere, so a quick second J still ends on the title.
|
|
525
598
|
let panelStep = null;
|
|
599
|
+
// The last choice in Main, until another interaction replaces it: a Detail
|
|
600
|
+
// that opens later than that does not take the drawer or the focus.
|
|
601
|
+
let lastChosen = null;
|
|
526
602
|
const cancelTitleOnInteraction = event => {
|
|
603
|
+
if (event.type === "pointerdown" || event.type === "keydown") clearActivation();
|
|
604
|
+
if (lastChosen && !(event.target instanceof Node && lastChosen.item.contains(event.target))) {
|
|
605
|
+
lastChosen.chosen = false;
|
|
606
|
+
lastChosen = null;
|
|
607
|
+
}
|
|
608
|
+
if (pendingDetail &&
|
|
609
|
+
// Native pointer focus on the chosen item continues its activation.
|
|
610
|
+
!(event.type === "focusin" && event.target instanceof Node && pendingDetail.snapshot.item.contains(event.target))) cancelPendingDetail();
|
|
527
611
|
if (event.type !== "keydown" && !(event.target instanceof Node && panelStep?.contains(event.target))) panelStep = null;
|
|
528
612
|
if (event.type !== "focusin" || event.target !== focusingTitle) cancelTitle();
|
|
529
613
|
};
|
|
@@ -573,14 +657,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
573
657
|
delete aside.dataset.workspaceDetailDragging;
|
|
574
658
|
};
|
|
575
659
|
const anchorItem = (root, aside, snapshot) => {
|
|
576
|
-
if (!wide?.matches ||
|
|
660
|
+
if (!wide?.matches || detailDrawer(aside) || !snapshot?.item.isConnected) return;
|
|
577
661
|
if (!mainElement(root)?.contains(snapshot.item) || snapshot.item.closest(ROOT_SELECTOR) !== root) return;
|
|
578
662
|
const scroller = snapshot.scroller;
|
|
579
663
|
if (scroller?.isConnected && scroller.scrollTop === snapshot.scrollTop) scroller.scrollTop += snapshot.item.getBoundingClientRect().top - snapshot.top;
|
|
580
664
|
};
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
// edge, so the workspace clips while the panel moves (see the stylesheet).
|
|
665
|
+
// A moving Detail would cross a framed workspace's edge, so the workspace
|
|
666
|
+
// clips while it slides (see the stylesheet).
|
|
584
667
|
const slide = (root, aside) => {
|
|
585
668
|
slides.get(root)?.();
|
|
586
669
|
if (!root.hasAttribute("data-workspace-motion") || prefersReducedMotion() || !wide?.matches) return;
|
|
@@ -665,6 +748,82 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
665
748
|
}
|
|
666
749
|
}));
|
|
667
750
|
};
|
|
751
|
+
let pendingDetail = null;
|
|
752
|
+
const canOpenPendingDetail = aside => aside.isConnected && eventRoot.contains(aside) && !aside.hidden && detailWaiting(aside) && !detailDrawer(aside) && aside.dataset.workspaceDetailChoice !== "request";
|
|
753
|
+
const cancelPendingDetail = () => {
|
|
754
|
+
pendingDetail?.stop();
|
|
755
|
+
pendingDetail = null;
|
|
756
|
+
};
|
|
757
|
+
// Check a completed choice after two frames. If its selection mark arrives
|
|
758
|
+
// later, keep waiting until another interaction replaces the choice.
|
|
759
|
+
const checkPendingDetail = (root, aside, snapshot, space = false) => {
|
|
760
|
+
cancelPendingDetail();
|
|
761
|
+
let frame = null;
|
|
762
|
+
let observer = null;
|
|
763
|
+
const settle = () => {
|
|
764
|
+
ownerDocument.removeEventListener("click", onClick, true);
|
|
765
|
+
ownerDocument.removeEventListener("keyup", onKeyUp, true);
|
|
766
|
+
if (frame !== null) cancelAnimationFrame(frame);
|
|
767
|
+
frame = requestAnimationFrame(() => {
|
|
768
|
+
frame = requestAnimationFrame(() => {
|
|
769
|
+
frame = null;
|
|
770
|
+
const main = mainElement(root);
|
|
771
|
+
if (!canOpenPendingDetail(aside) || !snapshot.chosen || !snapshot.item.isConnected || !main?.contains(snapshot.item) || snapshot.item.closest(ROOT_SELECTOR) !== root) {
|
|
772
|
+
cancelPendingDetail();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
// ARIA marks, and `data-selected` as DataTable and Calendar mark their selection.
|
|
776
|
+
let selected = snapshot.item.closest(SELECTION_MARK_SELECTOR);
|
|
777
|
+
while (selected?.matches(IGNORED_SELECTION_MARK_SELECTOR)) selected = selected.parentElement?.closest(SELECTION_MARK_SELECTOR) ?? null;
|
|
778
|
+
if (!selected || selected === main || !main.contains(selected)) {
|
|
779
|
+
if (!observer) {
|
|
780
|
+
observer = new MutationObserver(settle);
|
|
781
|
+
observer.observe(main, {
|
|
782
|
+
subtree: true,
|
|
783
|
+
attributes: true,
|
|
784
|
+
attributeFilter: ["aria-selected", "aria-current", "data-selected"]
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
cancelPendingDetail();
|
|
790
|
+
// A control inside a selected row that discloses, toggles, or opens
|
|
791
|
+
// a menu does its own job and selects nothing.
|
|
792
|
+
if (selected !== snapshot.item && snapshot.item.matches(NESTED_CONTROL_SELECTOR)) return;
|
|
793
|
+
snapshot.fresh = false;
|
|
794
|
+
openDetailDrawer(aside);
|
|
795
|
+
revealItem(root, aside, snapshot);
|
|
796
|
+
// The same item keeps its title; another one may still be loading,
|
|
797
|
+
// so focus follows the title that replaces the current one.
|
|
798
|
+
if (snapshot.interaction === "keyboard") {
|
|
799
|
+
focusTitle(aside);
|
|
800
|
+
requestTitle(aside, true);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
};
|
|
805
|
+
const onClick = event => {
|
|
806
|
+
if (event.button === 0 && event.target instanceof Node && snapshot.item.contains(event.target)) settle();else cancelPendingDetail();
|
|
807
|
+
};
|
|
808
|
+
// Space activates a button on release, so its check waits for keyup.
|
|
809
|
+
const onKeyUp = event => {
|
|
810
|
+
if (event.key === " ") settle();
|
|
811
|
+
};
|
|
812
|
+
const stop = () => {
|
|
813
|
+
if (frame !== null) cancelAnimationFrame(frame);
|
|
814
|
+
observer?.disconnect();
|
|
815
|
+
ownerDocument.removeEventListener("click", onClick, true);
|
|
816
|
+
ownerDocument.removeEventListener("keyup", onKeyUp, true);
|
|
817
|
+
ownerDocument.removeEventListener("pointercancel", cancelPendingDetail, true);
|
|
818
|
+
};
|
|
819
|
+
pendingDetail = {
|
|
820
|
+
aside,
|
|
821
|
+
snapshot,
|
|
822
|
+
stop
|
|
823
|
+
};
|
|
824
|
+
ownerDocument.addEventListener("pointercancel", cancelPendingDetail, true);
|
|
825
|
+
if (space) ownerDocument.addEventListener("keyup", onKeyUp, true);else if (snapshot.interaction === "keyboard") settle();else ownerDocument.addEventListener("click", onClick, true);
|
|
826
|
+
};
|
|
668
827
|
const detailTransition = (aside, wasHidden) => {
|
|
669
828
|
const root = aside.closest(ROOT_SELECTOR);
|
|
670
829
|
detailHidden.set(aside, Boolean(aside.hidden));
|
|
@@ -679,6 +838,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
679
838
|
const followedInteraction = snapshot?.fresh ?? false;
|
|
680
839
|
if (snapshot) snapshot.fresh = false;
|
|
681
840
|
if (aside.hidden) {
|
|
841
|
+
if (pendingDetail?.aside === aside) cancelPendingDetail();
|
|
682
842
|
parkDetailDrawer(aside);
|
|
683
843
|
if (pendingTitle?.aside === aside) cancelTitle();
|
|
684
844
|
if (panelStep === aside) panelStep = null;
|
|
@@ -708,7 +868,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
708
868
|
if (detailDrag?.aside === aside) finishDetailDrag(true);
|
|
709
869
|
return;
|
|
710
870
|
}
|
|
711
|
-
if (detailWaiting(aside) && !detailDrawer(aside))
|
|
871
|
+
if (detailWaiting(aside) && !detailDrawer(aside)) {
|
|
872
|
+
if (!followedInteraction || !snapshot?.chosen) return;
|
|
873
|
+
if (aside.dataset.workspaceDetailChoice === "request" && !snapshot.item.matches(DETAIL_REQUEST_CONTROL_SELECTOR)) return;
|
|
874
|
+
openDetailDrawer(aside);
|
|
875
|
+
}
|
|
712
876
|
anchorItem(root, aside, snapshot);
|
|
713
877
|
if (phone?.matches) setDetent(aside, false);
|
|
714
878
|
if (followedInteraction && snapshot) {
|
|
@@ -722,7 +886,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
722
886
|
if (!target || target.closest(KEY_OWNER_SELECTOR)) return;
|
|
723
887
|
if (ownerDocument.querySelector("dialog[open], [aria-modal='true']")) return;
|
|
724
888
|
try {
|
|
725
|
-
|
|
889
|
+
// A passive hint does not own the keyboard.
|
|
890
|
+
if (ownerDocument.querySelector(":popover-open:not(.k2b-moved-hint)")) return;
|
|
726
891
|
} catch {
|
|
727
892
|
// Older engines have no popover selector; the other popup guards still apply.
|
|
728
893
|
}
|
|
@@ -764,7 +929,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
764
929
|
scroller: verticalScroller(item, main),
|
|
765
930
|
scrollTop: 0,
|
|
766
931
|
top: 0,
|
|
767
|
-
fresh: false
|
|
932
|
+
fresh: false,
|
|
933
|
+
chosen: false
|
|
768
934
|
};
|
|
769
935
|
refreshItem(snapshot);
|
|
770
936
|
remembered.set(owningRoot, snapshot);
|
|
@@ -829,7 +995,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
829
995
|
clearDrag(drag.aside);
|
|
830
996
|
if (cancelled || !drag.moved) return;
|
|
831
997
|
if (drag.source === "drawer") {
|
|
832
|
-
if (drag.dx > 80 || drag.dx > 30 && drag.velocity > 0.5)
|
|
998
|
+
if (drag.dx > 80 || drag.dx > 30 && drag.velocity > 0.5) parkDetailDrawer(drag.aside, drag.aside.contains(ownerDocument.activeElement));
|
|
833
999
|
} else if (drag.dy > 60) {
|
|
834
1000
|
setDetent(drag.aside, false);
|
|
835
1001
|
if (!drag.full && closable(drag.aside)) requestClose(drag.aside);
|
|
@@ -918,7 +1084,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
918
1084
|
if (phone?.matches && phonePage(aside)) return;
|
|
919
1085
|
if (phone?.matches && (verticalScroller(target, aside) ?? aside).scrollTop <= 0) {
|
|
920
1086
|
if (!ownsTouchGesture(target, aside, "sheet")) beginDetailDrag(aside, target, "sheet", touch.clientX, touch.clientY, touch.identifier);
|
|
921
|
-
} else if (
|
|
1087
|
+
} else if (wide?.matches && detailDrawer(aside)) {
|
|
922
1088
|
if (!ownsTouchGesture(target, aside, "drawer")) beginDetailDrag(aside, target, "drawer", touch.clientX, touch.clientY, touch.identifier);
|
|
923
1089
|
}
|
|
924
1090
|
if (detailDrag) claimedDetailGestures.add(event);
|
|
@@ -1221,7 +1387,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1221
1387
|
return allRoots().filter(root => getComputedStyle(root).display !== "none" && root.getBoundingClientRect().width > 0);
|
|
1222
1388
|
};
|
|
1223
1389
|
const reconcile = () => {
|
|
1224
|
-
if (detailDrag && (detailDrag.source === "drawer" ? !
|
|
1390
|
+
if (detailDrag && (detailDrag.source === "drawer" ? !wide?.matches || !detailDrawer(detailDrag.aside) : !phone?.matches)) finishDetailDrag(true);
|
|
1225
1391
|
allRoots().forEach(root => {
|
|
1226
1392
|
if (!phone?.matches) {
|
|
1227
1393
|
for (const aside of rootElements(root, detailSelector)) {
|
|
@@ -1248,7 +1414,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1248
1414
|
return;
|
|
1249
1415
|
}
|
|
1250
1416
|
const persisted = kind === "sidebar" ? layoutState.sidebarCollapsed && sidebarCollapsible(root) ? APP_WORKSPACE_SIDEBAR_COLLAPSED : layoutState.sidebarWidth : kind === "detail" ? layoutState.detailWidths?.[panelId(handle)] : layoutState.drawerHeights?.[panelId(handle)];
|
|
1251
|
-
if (persisted !== undefined) applySize(root, handle, kind, persisted
|
|
1417
|
+
if (persisted !== undefined) applySize(root, handle, kind, persisted, {
|
|
1418
|
+
reserveMainMinimum: false
|
|
1419
|
+
});else updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
|
|
1252
1420
|
});
|
|
1253
1421
|
reconcilePanes(root, layoutState, active?.root === root ? active : null);
|
|
1254
1422
|
});
|
|
@@ -1363,6 +1531,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1363
1531
|
const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(records => {
|
|
1364
1532
|
syncPhonePages();
|
|
1365
1533
|
if (pendingTitle && (pendingTitle.aside.hidden || !pendingTitle.aside.isConnected)) cancelTitle();
|
|
1534
|
+
if (pendingDetail && (!canOpenPendingDetail(pendingDetail.aside) || !pendingDetail.snapshot.chosen || !pendingDetail.snapshot.item.isConnected)) cancelPendingDetail();
|
|
1366
1535
|
// Hiding or removing the sidebar, or its button, ends the overlay.
|
|
1367
1536
|
if (overlay && (overlay.sidebar.hidden || !overlay.sidebar.isConnected || !overlay.toggle.isConnected)) closeOverlay("kept");
|
|
1368
1537
|
const details = new Set();
|
|
@@ -1376,7 +1545,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1376
1545
|
if (!changed) return;
|
|
1377
1546
|
syncObservedRoots();
|
|
1378
1547
|
// A new width token or a removed panel can end waiting without any box resizing.
|
|
1379
|
-
if (records.some(record => record.type === "childList" || record.attributeName === "data-width")) syncHostState();
|
|
1548
|
+
if (records.some(record => record.type === "childList" || record.attributeName === "data-width" || record.attributeName === "data-workspace-main-min")) syncHostState();
|
|
1380
1549
|
scheduleReconcile();
|
|
1381
1550
|
});
|
|
1382
1551
|
eventRoot.addEventListener("pointerdown", onPointerDown);
|
|
@@ -1386,6 +1555,10 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1386
1555
|
ownerDocument.addEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1387
1556
|
ownerDocument.addEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1388
1557
|
ownerDocument.addEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1558
|
+
ownerDocument.addEventListener("click", onChoiceClick, true);
|
|
1559
|
+
ownerDocument.addEventListener("keyup", endActivation, true);
|
|
1560
|
+
ownerDocument.addEventListener("pointerup", endActivation, true);
|
|
1561
|
+
ownerDocument.addEventListener("pointercancel", cancelActivation, true);
|
|
1389
1562
|
eventRoot.addEventListener("pointerdown", rememberInteraction, true);
|
|
1390
1563
|
eventRoot.addEventListener("keydown", rememberInteraction, true);
|
|
1391
1564
|
eventRoot.addEventListener("focusin", onDetailFocusIn);
|
|
@@ -1411,7 +1584,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1411
1584
|
mutationObserver?.observe(eventRoot, {
|
|
1412
1585
|
attributes: true,
|
|
1413
1586
|
attributeOldValue: true,
|
|
1414
|
-
attributeFilter: ["hidden", "data-workspace-level-shown", "data-width"],
|
|
1587
|
+
attributeFilter: ["hidden", "data-workspace-level-shown", "data-width", "data-workspace-main-min"],
|
|
1415
1588
|
childList: true,
|
|
1416
1589
|
subtree: true
|
|
1417
1590
|
});
|
|
@@ -1421,6 +1594,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1421
1594
|
phonePageInerted.clear();
|
|
1422
1595
|
phone?.removeEventListener("change", syncPhonePages);
|
|
1423
1596
|
cancelTitle();
|
|
1597
|
+
cancelPendingDetail();
|
|
1424
1598
|
pendingAnchors.forEach(cancel => cancel());
|
|
1425
1599
|
slides.forEach(end => end());
|
|
1426
1600
|
stopScrollFades();
|
|
@@ -1439,6 +1613,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1439
1613
|
ownerDocument.removeEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1440
1614
|
ownerDocument.removeEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1441
1615
|
ownerDocument.removeEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1616
|
+
clearActivation();
|
|
1617
|
+
ownerDocument.removeEventListener("click", onChoiceClick, true);
|
|
1618
|
+
ownerDocument.removeEventListener("keyup", endActivation, true);
|
|
1619
|
+
ownerDocument.removeEventListener("pointerup", endActivation, true);
|
|
1620
|
+
ownerDocument.removeEventListener("pointercancel", cancelActivation, true);
|
|
1442
1621
|
eventRoot.removeEventListener("pointerdown", rememberInteraction, true);
|
|
1443
1622
|
eventRoot.removeEventListener("keydown", rememberInteraction, true);
|
|
1444
1623
|
eventRoot.removeEventListener("focusin", onDetailFocusIn);
|
|
@@ -1457,6 +1636,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1457
1636
|
// torn down, so unmounting during a drag cannot lose the final size.
|
|
1458
1637
|
stopResize();
|
|
1459
1638
|
if (reconcileFrame !== null) cancelAnimationFrame(reconcileFrame);
|
|
1639
|
+
if (hostSyncFrame !== null) cancelAnimationFrame(hostSyncFrame);
|
|
1460
1640
|
mutationObserver?.disconnect();
|
|
1461
1641
|
resizeObserver?.disconnect();
|
|
1462
1642
|
observedRoots.clear();
|