@k2b/ui 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base.css +2 -0
- package/dist/browser/actions/Dropdown.js +159 -99
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +113 -56
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +8 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -1
- package/dist/browser/actions/filter-messages.js +26 -2
- package/dist/browser/actions/filter-messages.js.map +1 -1
- package/dist/browser/actions/menu-rows.js +23 -3
- package/dist/browser/actions/menu-rows.js.map +1 -1
- package/dist/browser/actions/menu-sheet.js +2 -7
- package/dist/browser/actions/menu-sheet.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +480 -403
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/chat-messages.js +14 -0
- package/dist/browser/chat/chat-messages.js.map +1 -1
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/content/Calendar.js +39 -29
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/CalendarDayView.js +159 -165
- package/dist/browser/content/CalendarDayView.js.map +1 -1
- package/dist/browser/content/Chart.js +104 -55
- package/dist/browser/content/Chart.js.map +1 -1
- package/dist/browser/content/ChartExplorer.js +5 -3
- package/dist/browser/content/ChartExplorer.js.map +1 -1
- package/dist/browser/content/MapView.js +4 -4
- package/dist/browser/content/MapView.js.map +1 -1
- package/dist/browser/content/MessageRow.js +607 -343
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/ReactionBar.js +17 -0
- package/dist/browser/content/ReactionBar.js.map +1 -1
- package/dist/browser/content/chart-inspection-core.js +4 -1
- package/dist/browser/content/chart-inspection-core.js.map +1 -1
- package/dist/browser/content/code-highlight.js +66 -0
- package/dist/browser/content/code-highlight.js.map +1 -1
- package/dist/browser/content/index.js +2 -1
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/map-view-live.js +47 -10
- package/dist/browser/content/map-view-live.js.map +1 -1
- package/dist/browser/content/map-view-types.js.map +1 -1
- package/dist/browser/feedback/dialog-core.js +6 -0
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/inputs/AutocompleteEditor.js +167 -104
- package/dist/browser/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/browser/inputs/RichSelect.js +1474 -0
- package/dist/browser/inputs/RichSelect.js.map +1 -0
- package/dist/browser/inputs/TextInput.js +99 -61
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/choice.js +19 -8
- package/dist/browser/inputs/choice.js.map +1 -1
- package/dist/browser/inputs/index.js +1 -0
- package/dist/browser/inputs/index.js.map +1 -1
- package/dist/browser/inputs/rich-select-messages.js +46 -0
- package/dist/browser/inputs/rich-select-messages.js.map +1 -0
- package/dist/browser/intl/shortcut-messages.js +4 -0
- package/dist/browser/intl/shortcut-messages.js.map +1 -1
- package/dist/browser/intl/shortcut.js +4 -0
- package/dist/browser/intl/shortcut.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +624 -565
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/DetailPanel.js +10 -1
- package/dist/browser/layout/DetailPanel.js.map +1 -1
- package/dist/browser/layout/MobileShell.js +213 -38
- package/dist/browser/layout/MobileShell.js.map +1 -1
- package/dist/browser/layout/Navigation.js +14 -5
- package/dist/browser/layout/Navigation.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +9 -3
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/ResizeSeparator.js +8 -2
- package/dist/browser/layout/ResizeSeparator.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +192 -31
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/index.js +1 -0
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/layout/navigation-model.js.map +1 -1
- package/dist/browser/layout/resize-separator.js +25 -1
- package/dist/browser/layout/resize-separator.js.map +1 -1
- package/dist/browser/layout/scroll-fade.js +32 -5
- package/dist/browser/layout/scroll-fade.js.map +1 -1
- package/dist/browser/surfaces/NoticeCard.js +46 -30
- package/dist/browser/surfaces/NoticeCard.js.map +1 -1
- package/dist/browser/surfaces/Skeleton.js +14 -0
- package/dist/browser/surfaces/Skeleton.js.map +1 -0
- package/dist/browser/surfaces/index.js +1 -0
- package/dist/browser/surfaces/index.js.map +1 -1
- package/dist/browser/surfaces/resource-messages.js +2 -2
- package/dist/browser/surfaces/resource-messages.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +51 -27
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +67 -16
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +8 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -1
- package/dist/ssr/actions/filter-messages.js +26 -2
- package/dist/ssr/actions/filter-messages.js.map +1 -1
- package/dist/ssr/actions/menu-rows.js +23 -3
- package/dist/ssr/actions/menu-rows.js.map +1 -1
- package/dist/ssr/actions/menu-sheet.js +2 -7
- package/dist/ssr/actions/menu-sheet.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +96 -67
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/chat-messages.js +14 -0
- package/dist/ssr/chat/chat-messages.js.map +1 -1
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/content/Calendar.js +40 -31
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/CalendarDayView.js +40 -48
- package/dist/ssr/content/CalendarDayView.js.map +1 -1
- package/dist/ssr/content/Chart.js +27 -7
- package/dist/ssr/content/Chart.js.map +1 -1
- package/dist/ssr/content/ChartExplorer.js +5 -3
- package/dist/ssr/content/ChartExplorer.js.map +1 -1
- package/dist/ssr/content/MapView.js +4 -4
- package/dist/ssr/content/MapView.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +264 -91
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/ReactionBar.js +17 -0
- package/dist/ssr/content/ReactionBar.js.map +1 -1
- package/dist/ssr/content/chart-inspection-core.js +4 -1
- package/dist/ssr/content/chart-inspection-core.js.map +1 -1
- package/dist/ssr/content/code-highlight.js +66 -0
- package/dist/ssr/content/code-highlight.js.map +1 -1
- package/dist/ssr/content/index.js +2 -1
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/map-view-types.js.map +1 -1
- package/dist/ssr/feedback/dialog-core.js +6 -0
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/inputs/AutocompleteEditor.js +64 -24
- package/dist/ssr/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/ssr/inputs/RichSelect.js +950 -0
- package/dist/ssr/inputs/RichSelect.js.map +1 -0
- package/dist/ssr/inputs/TextInput.js +44 -17
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/choice.js +19 -8
- package/dist/ssr/inputs/choice.js.map +1 -1
- package/dist/ssr/inputs/index.js +1 -0
- package/dist/ssr/inputs/index.js.map +1 -1
- package/dist/ssr/inputs/rich-select-messages.js +46 -0
- package/dist/ssr/inputs/rich-select-messages.js.map +1 -0
- package/dist/ssr/intl/shortcut-messages.js +4 -0
- package/dist/ssr/intl/shortcut-messages.js.map +1 -1
- package/dist/ssr/intl/shortcut.js +4 -0
- package/dist/ssr/intl/shortcut.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +166 -125
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/DetailPanel.js +1 -1
- package/dist/ssr/layout/DetailPanel.js.map +1 -1
- package/dist/ssr/layout/MobileShell.js +100 -20
- package/dist/ssr/layout/MobileShell.js.map +1 -1
- package/dist/ssr/layout/Navigation.js +1 -1
- package/dist/ssr/layout/Navigation.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +9 -3
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/ResizeSeparator.js +4 -1
- package/dist/ssr/layout/ResizeSeparator.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +192 -31
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/index.js +1 -0
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/layout/navigation-model.js.map +1 -1
- package/dist/ssr/layout/resize-separator.js +25 -1
- package/dist/ssr/layout/resize-separator.js.map +1 -1
- package/dist/ssr/layout/scroll-fade.js +32 -5
- package/dist/ssr/layout/scroll-fade.js.map +1 -1
- package/dist/ssr/surfaces/NoticeCard.js +21 -12
- package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
- package/dist/ssr/surfaces/Skeleton.js +8 -0
- package/dist/ssr/surfaces/Skeleton.js.map +1 -0
- package/dist/ssr/surfaces/index.js +1 -0
- package/dist/ssr/surfaces/index.js.map +1 -1
- package/dist/ssr/surfaces/resource-messages.js +2 -2
- package/dist/ssr/surfaces/resource-messages.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +14 -0
- package/dist/types/actions/FilterChip.d.ts +11 -0
- package/dist/types/actions/filter-messages.d.ts +12 -0
- package/dist/types/actions/menu-rows.d.ts +7 -2
- package/dist/types/chat/ChatComposer.d.ts +13 -0
- package/dist/types/chat/chat-messages.d.ts +7 -0
- package/dist/types/chat/index.d.ts +1 -1
- package/dist/types/content/Calendar.d.ts +7 -0
- package/dist/types/content/Chart.d.ts +4 -2
- package/dist/types/content/ChartExplorer.d.ts +0 -1
- package/dist/types/content/MessageRow.d.ts +64 -2
- package/dist/types/content/code-highlight.d.ts +3 -1
- package/dist/types/content/index.d.ts +3 -2
- package/dist/types/content/map-view-live.d.ts +1 -1
- package/dist/types/content/map-view-types.d.ts +2 -0
- package/dist/types/feedback/dialog-core.d.ts +3 -0
- package/dist/types/inputs/AutocompleteEditor.d.ts +6 -1
- package/dist/types/inputs/RichSelect.d.ts +93 -0
- package/dist/types/inputs/TextInput.d.ts +5 -2
- package/dist/types/inputs/choice.d.ts +24 -4
- package/dist/types/inputs/index.d.ts +2 -0
- package/dist/types/inputs/rich-select-messages.d.ts +21 -0
- package/dist/types/intl/shortcut-messages.d.ts +2 -0
- package/dist/types/layout/AppWorkspace.d.ts +9 -0
- package/dist/types/layout/DetailPanel.d.ts +2 -0
- package/dist/types/layout/MobileShell.d.ts +52 -5
- package/dist/types/layout/app-workspace-controller.d.ts +2 -0
- package/dist/types/layout/index.d.ts +2 -1
- package/dist/types/layout/navigation-model.d.ts +2 -0
- package/dist/types/layout/resize-separator.d.ts +1 -0
- package/dist/types/layout/scroll-fade.d.ts +5 -0
- package/dist/types/surfaces/NoticeCard.d.ts +3 -0
- package/dist/types/surfaces/Skeleton.d.ts +6 -0
- package/dist/types/surfaces/index.d.ts +1 -0
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { suppressTextSelection } from "../internal/text-selection";
|
|
2
2
|
import { prefersReducedMotion } from "../motion";
|
|
3
|
-
import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_DETAIL_MIN, APP_WORKSPACE_DRAWER_MAX, APP_WORKSPACE_DRAWER_MIN, APP_WORKSPACE_MAIN_MIN, APP_WORKSPACE_PANE_DEFAULT, APP_WORKSPACE_PANE_MAX, APP_WORKSPACE_PANE_MIN, APP_WORKSPACE_SIDEBAR_COLLAPSED, APP_WORKSPACE_SIDEBAR_MAX, APP_WORKSPACE_SIDEBAR_MIN, appWorkspacePanelVariable, appWorkspaceResizeLimits, fitAppWorkspacePaneSizes, normalizeAppWorkspaceLayoutState, resolveAppWorkspaceSidebarWidth, safeAppWorkspacePanelId, shouldCollapseAppWorkspaceSidebar } from "./app-workspace-state";
|
|
3
|
+
import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_DETAIL_MIN, APP_WORKSPACE_DRAWER_DEFAULT, APP_WORKSPACE_DRAWER_MAX, APP_WORKSPACE_DRAWER_MIN, APP_WORKSPACE_MAIN_MIN, APP_WORKSPACE_PANE_DEFAULT, APP_WORKSPACE_PANE_MAX, APP_WORKSPACE_PANE_MIN, APP_WORKSPACE_SIDEBAR_COLLAPSED, APP_WORKSPACE_SIDEBAR_DEFAULT, APP_WORKSPACE_SIDEBAR_MAX, APP_WORKSPACE_SIDEBAR_MIN, appWorkspacePanelVariable, appWorkspaceResizeLimits, fitAppWorkspacePaneSizes, normalizeAppWorkspaceLayoutState, resolveAppWorkspaceSidebarWidth, safeAppWorkspacePanelId, shouldCollapseAppWorkspaceSidebar } from "./app-workspace-state";
|
|
4
4
|
import { readAppWorkspaceWidth, resolveAppWorkspaceWidth } from "./app-workspace-width";
|
|
5
5
|
import { installScrollFades } from "./scroll-fade";
|
|
6
6
|
const HANDLE_SELECTOR = "[data-app-workspace-resize]";
|
|
@@ -26,7 +26,7 @@ const parkDetailDrawer = (aside, focus = false) => {
|
|
|
26
26
|
detailDrawerContent(aside)?.style.removeProperty("--k2b-detail-space");
|
|
27
27
|
const toggle = detailToggle(aside);
|
|
28
28
|
toggle?.setAttribute("aria-expanded", "false");
|
|
29
|
-
if (focus)
|
|
29
|
+
if (focus) detailReturnControl(aside)?.focus({
|
|
30
30
|
preventScroll: true
|
|
31
31
|
});
|
|
32
32
|
};
|
|
@@ -66,6 +66,8 @@ const sidebarHideable = root => sidebarElement(root)?.dataset.workspaceHideable
|
|
|
66
66
|
* stays application state, so the controller only requests the change.
|
|
67
67
|
*/
|
|
68
68
|
export const APP_WORKSPACE_SIDEBAR_HIDE_EVENT = "k2b-app-workspace-sidebar-hide";
|
|
69
|
+
/** Asks the owning Detail to reopen its current available object. */
|
|
70
|
+
export const APP_WORKSPACE_DETAIL_OPEN_EVENT = "k2b-app-workspace-detail-open";
|
|
69
71
|
/** Asks the owning `AppWorkspace.Detail` to close; its `onClose` decides. */
|
|
70
72
|
export const APP_WORKSPACE_DETAIL_CLOSE_EVENT = "k2b-app-workspace-detail-close";
|
|
71
73
|
/**
|
|
@@ -80,6 +82,15 @@ const previewSidebarHidden = (root, hidden) => {
|
|
|
80
82
|
if (hidden) sidebar.dataset.workspaceHidePreview = "true";else delete sidebar.dataset.workspaceHidePreview;
|
|
81
83
|
};
|
|
82
84
|
const isVisible = element => Boolean(element && !element.hidden && getComputedStyle(element).display !== "none");
|
|
85
|
+
// A pane-bound head can be absent on a phone. The visible control in Main
|
|
86
|
+
// that names this same detail remains its return target, within the same root.
|
|
87
|
+
const detailReturnControl = aside => {
|
|
88
|
+
const root = workspaceRoot(aside);
|
|
89
|
+
if (!root) return undefined;
|
|
90
|
+
const shared = detailToggle(aside);
|
|
91
|
+
if (isVisible(shared ?? null) && shared && !shared.matches(":disabled")) return shared;
|
|
92
|
+
return rootElements(root, DETAIL_REQUEST_CONTROL_SELECTOR).find(control => control.matches(FOCUSABLE_SELECTOR) && !aside.contains(control) && !control.matches(":disabled, [inert]") && !control.closest("[inert]") && control.getAttribute("aria-controls")?.split(/\s+/).includes(aside.id) && control.getClientRects().length > 0);
|
|
93
|
+
};
|
|
83
94
|
const elementSize = (element, kind) => {
|
|
84
95
|
if (!isVisible(element)) return 0;
|
|
85
96
|
const rect = element.getBoundingClientRect();
|
|
@@ -336,9 +347,20 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
336
347
|
if (detailButton) {
|
|
337
348
|
const root = workspaceRoot(detailButton);
|
|
338
349
|
const aside = root ? controlledPanel(root, detailButton) : null;
|
|
339
|
-
if (!aside ||
|
|
350
|
+
if (!aside || detailButton.matches(":disabled") || claimedToggleClicks.has(event)) return;
|
|
351
|
+
const reopenable = aside.dataset.workspaceDetailReopen === "true";
|
|
352
|
+
if (!reopenable && (aside.hidden || !detailWaiting(aside))) return;
|
|
340
353
|
claimedToggleClicks.add(event);
|
|
341
|
-
if (
|
|
354
|
+
if (aside.hidden) {
|
|
355
|
+
aside.dispatchEvent(new CustomEvent(APP_WORKSPACE_DETAIL_OPEN_EVENT));
|
|
356
|
+
// The app may reject opening (for example while navigating). Never
|
|
357
|
+
// override hidden ourselves or reveal retained, unavailable contents.
|
|
358
|
+
if (aside.hidden) return;
|
|
359
|
+
if (wide?.matches && detailWaiting(aside)) openDetailDrawer(aside);
|
|
360
|
+
if (event.detail === 0 || phone?.matches) requestTitle(aside);else detailButton.focus({
|
|
361
|
+
preventScroll: true
|
|
362
|
+
});
|
|
363
|
+
} else if (reopenable && (!wide?.matches || !detailWaiting(aside))) requestClose(aside);else if (detailDrawer(aside)) parkDetailDrawer(aside);else {
|
|
342
364
|
openDetailDrawer(aside);
|
|
343
365
|
if (event.detail === 0) requestTitle(aside);else detailButton.focus({
|
|
344
366
|
preventScroll: true
|
|
@@ -435,6 +457,15 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
435
457
|
return rootElements(root, detailSelector).find(aside => !aside.hidden && ids?.includes(aside.id) && detailWaiting(aside)) ?? null;
|
|
436
458
|
};
|
|
437
459
|
const syncDetailRequestControls = () => {
|
|
460
|
+
for (const root of allRoots()) {
|
|
461
|
+
for (const aside of rootElements(root, detailSelector)) {
|
|
462
|
+
const toggle = detailToggle(aside);
|
|
463
|
+
if (toggle?.dataset.workspaceDetailReopen !== "true") continue;
|
|
464
|
+
const expanded = !aside.hidden && (!wide?.matches || !detailWaiting(aside) || detailDrawer(aside));
|
|
465
|
+
const value = String(expanded);
|
|
466
|
+
if (toggle.getAttribute("aria-expanded") !== value) toggle.setAttribute("aria-expanded", value);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
438
469
|
const waitingControls = new Map();
|
|
439
470
|
for (const root of allRoots()) {
|
|
440
471
|
const waitingDetails = wide?.matches ? rootElements(root, detailSelector).filter(aside => !aside.hidden && detailWaiting(aside)) : [];
|
|
@@ -509,7 +540,63 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
509
540
|
if (args[2] === "sidebar") scheduleHostSync();
|
|
510
541
|
return size;
|
|
511
542
|
};
|
|
543
|
+
// A responsive application control and the shared control can name the same
|
|
544
|
+
// Detail. Remember actual focus, not an old selection, before CSS hides it.
|
|
545
|
+
let detailControlFocus = null;
|
|
546
|
+
const controlledDetail = control => {
|
|
547
|
+
if (!control.matches(FOCUSABLE_SELECTOR) || control.matches(HANDLE_SELECTOR) || !eventRoot.contains(control)) return null;
|
|
548
|
+
const root = workspaceRoot(control);
|
|
549
|
+
if (!root) return null;
|
|
550
|
+
const ids = control.getAttribute("aria-controls")?.split(/\s+/);
|
|
551
|
+
const aside = rootElements(root, detailSelector).find(aside => ids?.includes(aside.id));
|
|
552
|
+
return aside ? {
|
|
553
|
+
control,
|
|
554
|
+
aside,
|
|
555
|
+
root
|
|
556
|
+
} : null;
|
|
557
|
+
};
|
|
558
|
+
const detailControlVisible = control => control.isConnected && control.getClientRects().length > 0 && getComputedStyle(control).visibility === "visible";
|
|
559
|
+
const detailControlUsable = control => detailControlVisible(control) && !control.matches(":disabled, [aria-disabled='true']") && !control.closest("[inert], [hidden], [aria-hidden='true']");
|
|
560
|
+
const onDetailControlFocusOut = () => {
|
|
561
|
+
// Explicit blur of a visible control is not a responsive focus loss.
|
|
562
|
+
if (detailControlFocus && detailControlVisible(detailControlFocus.control)) detailControlFocus = null;
|
|
563
|
+
};
|
|
564
|
+
const rescueDetailControlFocus = () => {
|
|
565
|
+
const previous = detailControlFocus;
|
|
566
|
+
if (!previous) return;
|
|
567
|
+
const {
|
|
568
|
+
control,
|
|
569
|
+
aside,
|
|
570
|
+
root
|
|
571
|
+
} = previous;
|
|
572
|
+
const active = ownerDocument.activeElement;
|
|
573
|
+
if (!control.isConnected || !aside.isConnected || workspaceRoot(control) !== root || workspaceRoot(aside) !== root || active !== ownerDocument.body && !control.contains(active)) {
|
|
574
|
+
detailControlFocus = null;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const shared = detailToggle(aside);
|
|
578
|
+
if (shared?.dataset.workspaceDetailReopen === "true" && phone?.matches && phonePage(aside) && !aside.hidden && !aside.contains(control)) {
|
|
579
|
+
// WebKit can update matchMedia after resize has already focused the now
|
|
580
|
+
// visible application control. Move into the page before Main becomes inert.
|
|
581
|
+
detailControlFocus = null;
|
|
582
|
+
requestTitle(aside);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (detailControlVisible(control)) return;
|
|
586
|
+
detailControlFocus = null;
|
|
587
|
+
if (shared?.dataset.workspaceDetailReopen !== "true") {
|
|
588
|
+
// Selection-only details keep their existing returning-column title target.
|
|
589
|
+
if (control === shared && !aside.hidden && !detailWaiting(aside)) requestTitle(aside);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const candidates = shared ? [shared, ...rootElements(root, "[aria-controls]")] : rootElements(root, "[aria-controls]");
|
|
593
|
+
const replacement = candidates.find(candidate => candidate !== control && detailControlUsable(candidate) && controlledDetail(candidate)?.aside === aside);
|
|
594
|
+
replacement?.focus({
|
|
595
|
+
preventScroll: true
|
|
596
|
+
});
|
|
597
|
+
};
|
|
512
598
|
const syncHostState = () => {
|
|
599
|
+
rescueDetailControlFocus();
|
|
513
600
|
if (pendingDetail && !canOpenPendingDetail(pendingDetail.aside)) cancelPendingDetail();
|
|
514
601
|
if (overlay && sidebarForm(overlay.root) !== "overlay") closeOverlay("none");
|
|
515
602
|
for (const root of allRoots()) {
|
|
@@ -525,11 +612,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
525
612
|
const before = detailWasWaiting.get(aside);
|
|
526
613
|
detailWasWaiting.set(aside, waiting);
|
|
527
614
|
if (!waiting) {
|
|
528
|
-
//
|
|
529
|
-
|
|
615
|
+
// Rescue focus only when the legacy waiting-only button disappears.
|
|
616
|
+
// A persistent head control stays a valid focus target after resizing.
|
|
617
|
+
const toggle = detailToggle(aside);
|
|
618
|
+
const toggleFocused = toggle?.contains(ownerDocument.activeElement) ?? false;
|
|
530
619
|
parkDetailDrawer(aside);
|
|
531
|
-
if (toggleFocused) focusTitle(aside);
|
|
532
|
-
} else if (detailDrawer(aside)) syncDetailDrawerSpace(aside);else if (before !== true && aside.contains(ownerDocument.activeElement))
|
|
620
|
+
if (toggle && toggleFocused && !isVisible(toggle)) focusTitle(aside);
|
|
621
|
+
} else if (detailDrawer(aside)) syncDetailDrawerSpace(aside);else if (before !== true && aside.contains(ownerDocument.activeElement)) detailReturnControl(aside)?.focus({
|
|
533
622
|
preventScroll: true
|
|
534
623
|
});
|
|
535
624
|
}
|
|
@@ -648,6 +737,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
648
737
|
const root = target?.closest(ROOT_SELECTOR);
|
|
649
738
|
if (!target || !root || !observedRoots.has(root)) return;
|
|
650
739
|
lastFocus.set(root, target);
|
|
740
|
+
const control = target.closest("[aria-controls]");
|
|
741
|
+
detailControlFocus = control ? controlledDetail(control) : null;
|
|
651
742
|
const previous = remembered.get(root);
|
|
652
743
|
// Pointer focus follows pointerdown; keeping that snapshot also keeps the
|
|
653
744
|
// interaction that opened the panel rather than treating focus as a new one.
|
|
@@ -694,6 +785,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
694
785
|
// that opens later than that does not take the drawer or the focus.
|
|
695
786
|
let lastChosen = null;
|
|
696
787
|
const cancelTitleOnInteraction = event => {
|
|
788
|
+
if (detailControlFocus && !(event.target instanceof Node && detailControlFocus.control.contains(event.target))) detailControlFocus = null;
|
|
697
789
|
if (event.type === "pointerdown" || event.type === "keydown") clearActivation();
|
|
698
790
|
if (lastChosen && !(event.target instanceof Node && lastChosen.item.contains(event.target))) {
|
|
699
791
|
lastChosen.chosen = false;
|
|
@@ -955,7 +1047,13 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
955
1047
|
pendingAnchors.set(root, cancel);
|
|
956
1048
|
} else anchorItem(root, aside, snapshot);
|
|
957
1049
|
const focused = ownerDocument.activeElement;
|
|
958
|
-
|
|
1050
|
+
const toggle = detailToggle(aside);
|
|
1051
|
+
const returnControl = detailReturnControl(aside);
|
|
1052
|
+
if (toggle?.dataset.workspaceDetailReopen === "true" && returnControl && (aside.contains(focused) || focused === ownerDocument.body && aside.contains(lastFocus.get(root) ?? null))) {
|
|
1053
|
+
returnControl.focus({
|
|
1054
|
+
preventScroll: true
|
|
1055
|
+
});
|
|
1056
|
+
} else if (snapshot?.item.isConnected && (aside.contains(lastFocus.get(root) ?? null) || aside.contains(focused) || focused === ownerDocument.body)) snapshot.item.focus({
|
|
959
1057
|
preventScroll: true
|
|
960
1058
|
});
|
|
961
1059
|
clearDrag(aside);
|
|
@@ -1232,34 +1330,86 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1232
1330
|
const handle = resizeHandle(event);
|
|
1233
1331
|
const root = handle?.closest(ROOT_SELECTOR);
|
|
1234
1332
|
const kind = handle ? resizeKind(handle) : null;
|
|
1235
|
-
if (!handle || !root || kind
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1333
|
+
if (!handle || !root || !kind || !workspaceResizable(root) || !controlsResizableRegion(root, handle, kind)) return;
|
|
1334
|
+
if (active) return;
|
|
1335
|
+
if (kind === "sidebar") {
|
|
1336
|
+
const {
|
|
1337
|
+
sidebarWidth: _width,
|
|
1338
|
+
sidebarCollapsed: _collapsed,
|
|
1339
|
+
...remaining
|
|
1340
|
+
} = layoutState;
|
|
1341
|
+
layoutState = remaining;
|
|
1342
|
+
} else {
|
|
1343
|
+
const key = kind === "pane" ? "paneWidths" : kind === "drawer" ? "drawerHeights" : "detailWidths";
|
|
1344
|
+
const widths = {
|
|
1345
|
+
...layoutState[key]
|
|
1346
|
+
};
|
|
1347
|
+
delete widths[panelId(handle)];
|
|
1348
|
+
layoutState = {
|
|
1349
|
+
...layoutState,
|
|
1350
|
+
[key]: widths
|
|
1351
|
+
};
|
|
1253
1352
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1353
|
+
const regions = resetRegions.get(root) ?? new Map();
|
|
1354
|
+
regions.set(resizeVariable(handle, kind), {
|
|
1355
|
+
kind,
|
|
1356
|
+
id: panelId(handle),
|
|
1357
|
+
configuration: handle
|
|
1358
|
+
});
|
|
1359
|
+
resetRegions.set(root, regions);
|
|
1360
|
+
writeState();
|
|
1361
|
+
if (kind === "sidebar" || kind === "pane") reconcilePanes(root, layoutState);
|
|
1256
1362
|
updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
|
|
1257
|
-
options.writeState?.(layoutState);
|
|
1258
1363
|
};
|
|
1259
1364
|
const stopScrollFades = installScrollFades(eventRoot);
|
|
1260
1365
|
let layoutState = normalizeAppWorkspaceLayoutState(options.readState?.()) ?? {
|
|
1261
1366
|
version: 2
|
|
1262
1367
|
};
|
|
1368
|
+
const resetRegions = new WeakMap();
|
|
1369
|
+
const resizeVariable = (handle, kind) => kind === "sidebar" ? "--k2b-workspace-sidebar-width" : appWorkspacePanelVariable(kind, panelId(handle));
|
|
1370
|
+
const defaultSize = (handle, kind) => readAppWorkspaceWidth(handle.dataset.workspaceDefaultSize) ?? (kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_DEFAULT : kind === "pane" ? APP_WORKSPACE_PANE_DEFAULT : kind === "drawer" ? APP_WORKSPACE_DRAWER_DEFAULT : APP_WORKSPACE_DETAIL_DEFAULT);
|
|
1371
|
+
const restoreResetDefaults = root => {
|
|
1372
|
+
for (const [variable, region] of resetRegions.get(root) ?? []) {
|
|
1373
|
+
const {
|
|
1374
|
+
kind,
|
|
1375
|
+
id
|
|
1376
|
+
} = region;
|
|
1377
|
+
const persisted = kind === "sidebar" ? layoutState.sidebarWidth : layoutState[kind === "pane" ? "paneWidths" : kind === "drawer" ? "drawerHeights" : "detailWidths"]?.[id];
|
|
1378
|
+
if (persisted !== undefined || kind === "sidebar" && layoutState.sidebarCollapsed) continue;
|
|
1379
|
+
const handle = rootElements(root, HANDLE_SELECTOR).find(handle => resizeKind(handle) === kind && panelId(handle) === id);
|
|
1380
|
+
// Keep the latest handle's configuration across unmounts. Limits still
|
|
1381
|
+
// measure today's workspace, so a missing handle cannot freeze its fit.
|
|
1382
|
+
if (handle) region.configuration = handle;
|
|
1383
|
+
const spec = defaultSize(region.configuration, kind);
|
|
1384
|
+
// Store the configured default, not a temporary fit. CSS/relayout owns
|
|
1385
|
+
// the available space, so a numeric level can grow back when room returns.
|
|
1386
|
+
if (typeof spec === "number") {
|
|
1387
|
+
// Sidebar and drawer have no CSS fit. Keep their configured default
|
|
1388
|
+
// in the map, but render within today's bounds and recover it later.
|
|
1389
|
+
if (kind === "sidebar" || kind === "drawer") {
|
|
1390
|
+
const {
|
|
1391
|
+
min,
|
|
1392
|
+
max
|
|
1393
|
+
} = sizeLimits(root, region.configuration, kind);
|
|
1394
|
+
root.style.setProperty(variable, `${clamp(spec, min, max)}px`);
|
|
1395
|
+
} else root.style.setProperty(variable, `${spec}px`);
|
|
1396
|
+
} else {
|
|
1397
|
+
root.style.removeProperty(variable);
|
|
1398
|
+
if (getComputedStyle(root).getPropertyValue(variable).trim()) root.style.setProperty(variable, "initial");
|
|
1399
|
+
}
|
|
1400
|
+
if (kind === "sidebar") delete root.dataset.sidebarCollapsed;
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
const writeState = () => {
|
|
1404
|
+
options.writeState?.(layoutState);
|
|
1405
|
+
// Keep reset defaults through reactive writes and temporary handle unmounts,
|
|
1406
|
+
// without saving a width that would mask a changed application default.
|
|
1407
|
+
for (const root of roots()) {
|
|
1408
|
+
if (!workspaceResizable(root)) continue;
|
|
1409
|
+
restoreResetDefaults(root);
|
|
1410
|
+
reconcilePanes(root, layoutState);
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1263
1413
|
let active = null;
|
|
1264
1414
|
let resizeFrame = null;
|
|
1265
1415
|
let pendingClient = null;
|
|
@@ -1281,7 +1431,9 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1281
1431
|
}
|
|
1282
1432
|
};
|
|
1283
1433
|
}
|
|
1284
|
-
|
|
1434
|
+
const root = workspaceRoot(handle);
|
|
1435
|
+
if (root) resetRegions.get(root)?.delete(resizeVariable(handle, kind));
|
|
1436
|
+
writeState();
|
|
1285
1437
|
};
|
|
1286
1438
|
|
|
1287
1439
|
// A level resize starts from what is on screen. When the stored widths do
|
|
@@ -1422,6 +1574,11 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1422
1574
|
const kind = handle ? resizeKind(handle) : null;
|
|
1423
1575
|
const root = handle ? workspaceRoot(handle) : null;
|
|
1424
1576
|
if (!handle || !kind || !root || !workspaceResizable(root) || !controlsResizableRegion(root, handle, kind)) return;
|
|
1577
|
+
if (event.key === "Enter" && !event.defaultPrevented && !event.isComposing && !event.altKey && !event.ctrlKey && !event.metaKey && !active) {
|
|
1578
|
+
event.preventDefault();
|
|
1579
|
+
onPanelDoubleClick(event);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1425
1582
|
const current = currentSize(root, handle, kind);
|
|
1426
1583
|
const {
|
|
1427
1584
|
min,
|
|
@@ -1500,6 +1657,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1500
1657
|
// resizing; without this the same workspace still had persisted sizes
|
|
1501
1658
|
// forced onto it on load and on every window resize.
|
|
1502
1659
|
if (!workspaceResizable(root)) return;
|
|
1660
|
+
restoreResetDefaults(root);
|
|
1503
1661
|
const handles = rootElements(root, HANDLE_SELECTOR);
|
|
1504
1662
|
// Chrome establishes the space available to the work area. Auxiliary
|
|
1505
1663
|
// panes are then fitted together into what remains, so DOM order cannot
|
|
@@ -1654,6 +1812,7 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1654
1812
|
ownerDocument.addEventListener("keydown", onOverlayKeyDown);
|
|
1655
1813
|
ownerWindow.addEventListener("keydown", onDetailKeyDown);
|
|
1656
1814
|
ownerDocument.addEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1815
|
+
ownerDocument.addEventListener("focusout", onDetailControlFocusOut, true);
|
|
1657
1816
|
ownerDocument.addEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1658
1817
|
ownerDocument.addEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1659
1818
|
ownerDocument.addEventListener("click", onDetailRequestClick, true);
|
|
@@ -1714,6 +1873,8 @@ export const installAppWorkspaceController = (options = {}) => {
|
|
|
1714
1873
|
titleTabIndices.forEach(title => title.removeAttribute("tabindex"));
|
|
1715
1874
|
ownerWindow.removeEventListener("keydown", onDetailKeyDown);
|
|
1716
1875
|
ownerDocument.removeEventListener("focusin", cancelTitleOnInteraction, true);
|
|
1876
|
+
ownerDocument.removeEventListener("focusout", onDetailControlFocusOut, true);
|
|
1877
|
+
detailControlFocus = null;
|
|
1717
1878
|
ownerDocument.removeEventListener("pointerdown", cancelTitleOnInteraction, true);
|
|
1718
1879
|
ownerDocument.removeEventListener("keydown", cancelTitleOnInteraction, true);
|
|
1719
1880
|
clearActivation();
|