@k2b/ui 0.30.0 → 0.31.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.
Files changed (96) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/actions/Toolbar.js +2 -0
  3. package/dist/browser/actions/Toolbar.js.map +1 -1
  4. package/dist/browser/chat/ChatDecision.js +28 -2
  5. package/dist/browser/chat/ChatDecision.js.map +1 -1
  6. package/dist/browser/content/MapAttribution.js +27 -0
  7. package/dist/browser/content/MapAttribution.js.map +1 -0
  8. package/dist/browser/content/MapView.js +553 -0
  9. package/dist/browser/content/MapView.js.map +1 -0
  10. package/dist/browser/content/MapViewFrame.js +395 -0
  11. package/dist/browser/content/MapViewFrame.js.map +1 -0
  12. package/dist/browser/content/index.js +5 -0
  13. package/dist/browser/content/index.js.map +1 -1
  14. package/dist/browser/content/map-palette.js +39 -0
  15. package/dist/browser/content/map-palette.js.map +1 -0
  16. package/dist/browser/content/map-style.js +276 -0
  17. package/dist/browser/content/map-style.js.map +1 -0
  18. package/dist/browser/content/map-view-geometry.js +269 -0
  19. package/dist/browser/content/map-view-geometry.js.map +1 -0
  20. package/dist/browser/content/map-view-live.js +28 -0
  21. package/dist/browser/content/map-view-live.js.map +1 -0
  22. package/dist/browser/content/map-view-messages.js +53 -0
  23. package/dist/browser/content/map-view-messages.js.map +1 -0
  24. package/dist/browser/content/map-view-types.js +2 -0
  25. package/dist/browser/content/map-view-types.js.map +1 -0
  26. package/dist/browser/content/map-view-worker.js +10 -0
  27. package/dist/browser/feedback/dialog-core.js +19 -0
  28. package/dist/browser/feedback/dialog-core.js.map +1 -1
  29. package/dist/browser/layout/AppWorkspace.js +17 -9
  30. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  31. package/dist/browser/layout/app-workspace-controller.js +114 -8
  32. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  33. package/dist/browser/layout/app-workspace-width.js +26 -0
  34. package/dist/browser/layout/app-workspace-width.js.map +1 -0
  35. package/dist/browser/layout/index.js +1 -1
  36. package/dist/browser/layout/index.js.map +1 -1
  37. package/dist/licenses/maplibre-gl.txt +116 -0
  38. package/dist/ssr/actions/Toolbar.js +2 -0
  39. package/dist/ssr/actions/Toolbar.js.map +1 -1
  40. package/dist/ssr/chat/ChatDecision.js +11 -0
  41. package/dist/ssr/chat/ChatDecision.js.map +1 -1
  42. package/dist/ssr/content/MapAttribution.js +19 -0
  43. package/dist/ssr/content/MapAttribution.js.map +1 -0
  44. package/dist/ssr/content/MapView.js +474 -0
  45. package/dist/ssr/content/MapView.js.map +1 -0
  46. package/dist/ssr/content/MapViewFrame.js +193 -0
  47. package/dist/ssr/content/MapViewFrame.js.map +1 -0
  48. package/dist/ssr/content/index.js +5 -0
  49. package/dist/ssr/content/index.js.map +1 -1
  50. package/dist/ssr/content/map-palette.js +39 -0
  51. package/dist/ssr/content/map-palette.js.map +1 -0
  52. package/dist/ssr/content/map-style.js +276 -0
  53. package/dist/ssr/content/map-style.js.map +1 -0
  54. package/dist/ssr/content/map-view-geometry.js +269 -0
  55. package/dist/ssr/content/map-view-geometry.js.map +1 -0
  56. package/dist/ssr/content/map-view-live.js +1 -0
  57. package/dist/ssr/content/map-view-messages.js +53 -0
  58. package/dist/ssr/content/map-view-messages.js.map +1 -0
  59. package/dist/ssr/content/map-view-types.js +2 -0
  60. package/dist/ssr/content/map-view-types.js.map +1 -0
  61. package/dist/ssr/feedback/dialog-core.js +19 -0
  62. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  63. package/dist/ssr/layout/AppWorkspace.js +17 -9
  64. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  65. package/dist/ssr/layout/app-workspace-controller.js +114 -8
  66. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  67. package/dist/ssr/layout/app-workspace-width.js +26 -0
  68. package/dist/ssr/layout/app-workspace-width.js.map +1 -0
  69. package/dist/ssr/layout/index.js +1 -1
  70. package/dist/ssr/layout/index.js.map +1 -1
  71. package/dist/styles.css +1 -1
  72. package/dist/types/content/MapAttribution.d.ts +6 -0
  73. package/dist/types/content/MapView.d.ts +5 -0
  74. package/dist/types/content/MapViewFrame.d.ts +31 -0
  75. package/dist/types/content/index.d.ts +6 -0
  76. package/dist/types/content/map-palette.d.ts +58 -0
  77. package/dist/types/content/map-style.d.ts +29 -0
  78. package/dist/types/content/map-view-geometry.d.ts +55 -0
  79. package/dist/types/content/map-view-live.d.ts +2 -0
  80. package/dist/types/content/map-view-messages.d.ts +21 -0
  81. package/dist/types/content/map-view-types.d.ts +61 -0
  82. package/dist/types/feedback/dialog-core.d.ts +1 -0
  83. package/dist/types/layout/AppWorkspace.d.ts +7 -3
  84. package/dist/types/layout/app-workspace-width.d.ts +13 -0
  85. package/dist/types/layout/index.d.ts +2 -2
  86. package/map-glyphs/IBM Plex Sans Medium/0-255.pbf +0 -0
  87. package/map-glyphs/IBM Plex Sans Medium/1024-1279.pbf +0 -0
  88. package/map-glyphs/IBM Plex Sans Medium/256-511.pbf +0 -0
  89. package/map-glyphs/IBM Plex Sans Medium/512-767.pbf +0 -0
  90. package/map-glyphs/IBM Plex Sans Medium/768-1023.pbf +0 -0
  91. package/map-glyphs/IBM Plex Sans Medium/7680-7935.pbf +0 -0
  92. package/map-glyphs/IBM Plex Sans Medium/8192-8447.pbf +0 -0
  93. package/map-glyphs/IBM Plex Sans Medium/8448-8703.pbf +0 -0
  94. package/map-glyphs/IBM Plex Sans Medium/8704-8959.pbf +0 -0
  95. package/map-glyphs/OFL.txt +93 -0
  96. package/package.json +10 -5
@@ -1,6 +1,7 @@
1
1
  import { suppressTextSelection } from "../internal/text-selection";
2
2
  import { prefersReducedMotion } from "../motion";
3
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";
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]";
@@ -121,8 +122,9 @@ const sizeLimits = (root, handle, kind, reserveMainMinimum = true) => {
121
122
  const content = kind === "detail" ? controlled?.closest(".k2b-app-workspace__content") : null;
122
123
  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
124
  const defaultMax = kind === "sidebar" ? APP_WORKSPACE_SIDEBAR_MAX : kind === "pane" ? APP_WORKSPACE_PANE_MAX : kind === "detail" ? APP_WORKSPACE_DETAIL_MAX : APP_WORKSPACE_DRAWER_MAX;
125
+ const mainSpec = readAppWorkspaceWidth(mainElement(root)?.dataset.workspaceMainMin);
124
126
  const mainMinValue = cssState(root, "--k2b-workspace-main-min");
125
- const mainMin = mainMinValue.endsWith("px") ? Number.parseFloat(mainMinValue) : APP_WORKSPACE_MAIN_MIN;
127
+ const mainMin = typeof mainSpec === "object" ? resolveAppWorkspaceWidth(mainSpec, root.getBoundingClientRect().width) : mainMinValue.endsWith("px") ? Number.parseFloat(mainMinValue) : APP_WORKSPACE_MAIN_MIN;
126
128
  const mainExtra = Math.max(0, (Number.isFinite(mainMin) ? mainMin : APP_WORKSPACE_MAIN_MIN) - APP_WORKSPACE_MAIN_MIN);
127
129
  const limits = appWorkspaceResizeLimits({
128
130
  kind,
@@ -213,18 +215,39 @@ const reconcilePanes = (root, layoutState, active = null) => {
213
215
  if (!isVisible(panel)) return [];
214
216
  const min = numberData(handle, "workspaceMinSize", APP_WORKSPACE_PANE_MIN);
215
217
  const max = Math.max(min, numberData(handle, "workspaceMaxSize", APP_WORKSPACE_PANE_MAX));
216
- const preferred = active?.moved && active.root === root && active.kind === "pane" && active.handle === handle ? active.liveSize : layoutState.paneWidths?.[panelId(handle)] ?? numberData(handle, "workspaceDefaultSize", elementSize(panel, "pane"));
217
- const desired = clamp(preferred, min, max);
218
+ const spec = readAppWorkspaceWidth(handle.dataset.workspaceDefaultSize) ?? elementSize(panel, "pane");
219
+ const preferred = active?.moved && active.root === root && active.kind === "pane" && active.handle === handle ? active.liveSize : layoutState.paneWidths?.[panelId(handle)];
220
+ const cssDefault = preferred === undefined && typeof spec !== "number";
221
+ if (cssDefault && root.style.getPropertyValue(appWorkspacePanelVariable("pane", panelId(handle))))
222
+ // Drop a previous live width (or an inherited SSR preference after reset).
223
+ root.style.setProperty(appWorkspacePanelVariable("pane", panelId(handle)), "initial");
224
+ // CSS has already applied this default and the pane-fit clamp, including
225
+ // subpixels. Reserve exactly that box when fitting other preferences;
226
+ // measuring must not turn a responsive default into a stored px width.
227
+ const requested = preferred ?? resolveAppWorkspaceWidth(spec, root.getBoundingClientRect().width);
228
+ const desired = cssDefault ? elementSize(panel, "pane") : clamp(requested, min, max);
218
229
  return [{
219
230
  desired,
220
231
  handle,
221
- min
232
+ min: cssDefault ? desired : min,
233
+ cssDefault,
234
+ requested: clamp(requested, min, max),
235
+ configuredMin: min
222
236
  }];
223
237
  });
224
238
  if (!fits.length) return;
225
- const sizes = fitAppWorkspacePaneSizes(fits, main.getBoundingClientRect().width - fixedWidth - APP_WORKSPACE_MAIN_MIN);
239
+ const budget = main.getBoundingClientRect().width - fixedWidth - APP_WORKSPACE_MAIN_MIN;
240
+ // Each share default clamps only against Main, not its resizable or non-resizable siblings. When panes
241
+ // together leave Main's primary region less than its minimum, fit them like stored widths, allowing subpixels.
242
+ if (fits.reduce((total, fit) => total + fit.desired, 0) > budget + 0.5) for (const fit of fits) if (fit.cssDefault) Object.assign(fit, {
243
+ cssDefault: false,
244
+ desired: fit.requested,
245
+ min: fit.configuredMin
246
+ });
247
+ const sizes = fitAppWorkspacePaneSizes(fits, budget);
226
248
  fits.forEach((fit, index) => {
227
- root.style.setProperty(appWorkspacePanelVariable("pane", panelId(fit.handle)), `${sizes[index]}px`);
249
+ // Defaults stay CSS-owned; fit only saved or live preferences around them.
250
+ if (!fit.cssDefault) root.style.setProperty(appWorkspacePanelVariable("pane", panelId(fit.handle)), `${sizes[index]}px`);
228
251
  });
229
252
  fits.forEach((fit, index) => updateHandleValue(root, fit.handle, "pane", sizes[index]));
230
253
  };
@@ -321,6 +344,7 @@ export const installAppWorkspaceController = (options = {}) => {
321
344
  preventScroll: true
322
345
  });
323
346
  }
347
+ syncDetailRequestControls();
324
348
  return;
325
349
  }
326
350
  const toggle = target?.closest(TOGGLE_SELECTOR);
@@ -396,10 +420,79 @@ export const installAppWorkspaceController = (options = {}) => {
396
420
  // Whether each open detail waited at the last check, so a column that starts
397
421
  // waiting under focus hands that focus to its Details button.
398
422
  const detailWasWaiting = new WeakMap();
423
+ const detailRequestExpanded = new Map();
424
+ const restoreDetailRequestExpanded = (control, state) => {
425
+ // A newer application render already owns the current value.
426
+ if (control.getAttribute("aria-expanded") !== state.written) return;
427
+ if (state.rendered === null) control.removeAttribute("aria-expanded");else control.setAttribute("aria-expanded", state.aside.hidden || !state.aside.isConnected ? "false" : state.rendered);
428
+ };
429
+ const requestedWaitingDetail = control => {
430
+ if (!wide?.matches || control.matches(DETAIL_TOGGLE_SELECTOR) || !eventRoot.contains(control)) return null;
431
+ const root = workspaceRoot(control);
432
+ const region = control.closest(".k2b-app-workspace__main, .k2b-app-workspace__detail");
433
+ if (!root || !region || region.closest(ROOT_SELECTOR) !== root) return null;
434
+ const ids = control.getAttribute("aria-controls")?.split(/\s+/);
435
+ return rootElements(root, detailSelector).find(aside => !aside.hidden && ids?.includes(aside.id) && detailWaiting(aside)) ?? null;
436
+ };
437
+ const syncDetailRequestControls = () => {
438
+ const waitingControls = new Map();
439
+ for (const root of allRoots()) {
440
+ const waitingDetails = wide?.matches ? rootElements(root, detailSelector).filter(aside => !aside.hidden && detailWaiting(aside)) : [];
441
+ if (!waitingDetails.length) continue;
442
+ // Only a control that names a Detail can request it.
443
+ for (const control of rootElements(root, "[aria-controls]")) {
444
+ if (control.matches(DETAIL_TOGGLE_SELECTOR) || !eventRoot.contains(control)) continue;
445
+ const region = control.closest(".k2b-app-workspace__main, .k2b-app-workspace__detail");
446
+ if (!region || region.closest(ROOT_SELECTOR) !== root) continue;
447
+ const ids = control.getAttribute("aria-controls")?.split(/\s+/);
448
+ const aside = waitingDetails.find(aside => ids?.includes(aside.id));
449
+ if (aside) waitingControls.set(control, aside);
450
+ }
451
+ }
452
+ detailRequestExpanded.forEach((state, control) => {
453
+ if (waitingControls.has(control)) return;
454
+ restoreDetailRequestExpanded(control, state);
455
+ detailRequestExpanded.delete(control);
456
+ });
457
+ waitingControls.forEach((aside, control) => {
458
+ const current = control.getAttribute("aria-expanded");
459
+ const state = detailRequestExpanded.get(control);
460
+ const written = String(detailDrawer(aside));
461
+ // Keep the application's latest value to restore when waiting ends.
462
+ const rendered = !state || current !== state.written ? current : state.rendered;
463
+ detailRequestExpanded.set(control, {
464
+ aside,
465
+ rendered,
466
+ written
467
+ });
468
+ if (current !== written) control.setAttribute("aria-expanded", written);
469
+ });
470
+ };
471
+ const onDetailRequestClick = event => {
472
+ if (event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
473
+ const control = eventElement(event)?.closest(DETAIL_REQUEST_CONTROL_SELECTOR);
474
+ const aside = control ? requestedWaitingDetail(control) : null;
475
+ if (!control || !aside) return;
476
+ // The application keeps its Detail open; only its waiting drawer changes.
477
+ event.preventDefault();
478
+ event.stopImmediatePropagation();
479
+ cancelPendingDetail();
480
+ cancelTitle();
481
+ clearActivation();
482
+ // A control inside the drawer leaves with it, so focus goes to Details.
483
+ if (detailDrawer(aside)) parkDetailDrawer(aside, aside.contains(ownerDocument.activeElement));else {
484
+ openDetailDrawer(aside);
485
+ if (event.detail === 0) requestTitle(aside);else control.focus({
486
+ preventScroll: true
487
+ });
488
+ }
489
+ syncDetailRequestControls();
490
+ };
399
491
  const openDetailDrawer = aside => {
400
492
  aside.dataset.workspaceDetailDrawer = "open";
401
493
  detailToggle(aside)?.setAttribute("aria-expanded", "true");
402
494
  syncDetailDrawerSpace(aside);
495
+ syncDetailRequestControls();
403
496
  };
404
497
  // A sidebar drag changes the room beside it without resizing the workspace,
405
498
  // so it rechecks waiting once per frame.
@@ -441,6 +534,7 @@ export const installAppWorkspaceController = (options = {}) => {
441
534
  });
442
535
  }
443
536
  }
537
+ syncDetailRequestControls();
444
538
  };
445
539
  const phone = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 47.999rem)") : null;
446
540
  const wide = typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 48rem)") : null;
@@ -903,6 +997,7 @@ export const installAppWorkspaceController = (options = {}) => {
903
997
  if (!aside || !owningRoot || !isVisible(aside)) return;
904
998
  if (event.key === "Escape" && detailDrawer(aside)) {
905
999
  parkDetailDrawer(aside, true);
1000
+ syncDetailRequestControls();
906
1001
  event.preventDefault();
907
1002
  return;
908
1003
  }
@@ -1150,7 +1245,12 @@ export const installAppWorkspaceController = (options = {}) => {
1150
1245
  const variable = appWorkspacePanelVariable(kind, panelId(handle));
1151
1246
  root.style.removeProperty(variable);
1152
1247
  // An SSR ancestor may still supply the old preference until the next request.
1153
- if (getComputedStyle(root).getPropertyValue(variable).trim()) root.style.setProperty(variable, `${numberData(handle, "workspaceDefaultSize", kind === "pane" ? APP_WORKSPACE_PANE_DEFAULT : APP_WORKSPACE_DETAIL_DEFAULT)}px`);
1248
+ if (getComputedStyle(root).getPropertyValue(variable).trim()) {
1249
+ const spec = readAppWorkspaceWidth(handle.dataset.workspaceDefaultSize) ?? (kind === "pane" ? APP_WORKSPACE_PANE_DEFAULT : APP_WORKSPACE_DETAIL_DEFAULT);
1250
+ // A share must resolve at the pane, where the workspace length exists.
1251
+ // `initial` clears an inherited SSR preference and restores its CSS fallback.
1252
+ root.style.setProperty(variable, typeof spec === "number" ? `${spec}px` : "initial");
1253
+ }
1154
1254
  // The siblings fit again around the default width, also in a Main without levels.
1155
1255
  if (kind === "pane") reconcilePanes(root, layoutState);
1156
1256
  updateHandleValue(root, handle, kind, currentSize(root, handle, kind));
@@ -1529,6 +1629,7 @@ export const installAppWorkspaceController = (options = {}) => {
1529
1629
  };
1530
1630
  const layoutNode = node => node instanceof Element && (node.matches(LAYOUT_REGION_SELECTOR) || Boolean(node.querySelector(LAYOUT_REGION_SELECTOR)));
1531
1631
  const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(records => {
1632
+ if (records.some(record => record.type === "childList" || ["hidden", "aria-controls", "aria-expanded", "data-workspace-detail-drawer"].includes(record.attributeName ?? ""))) syncDetailRequestControls();
1532
1633
  syncPhonePages();
1533
1634
  if (pendingTitle && (pendingTitle.aside.hidden || !pendingTitle.aside.isConnected)) cancelTitle();
1534
1635
  if (pendingDetail && (!canOpenPendingDetail(pendingDetail.aside) || !pendingDetail.snapshot.chosen || !pendingDetail.snapshot.item.isConnected)) cancelPendingDetail();
@@ -1555,6 +1656,7 @@ export const installAppWorkspaceController = (options = {}) => {
1555
1656
  ownerDocument.addEventListener("focusin", cancelTitleOnInteraction, true);
1556
1657
  ownerDocument.addEventListener("pointerdown", cancelTitleOnInteraction, true);
1557
1658
  ownerDocument.addEventListener("keydown", cancelTitleOnInteraction, true);
1659
+ ownerDocument.addEventListener("click", onDetailRequestClick, true);
1558
1660
  ownerDocument.addEventListener("click", onChoiceClick, true);
1559
1661
  ownerDocument.addEventListener("keyup", endActivation, true);
1560
1662
  ownerDocument.addEventListener("pointerup", endActivation, true);
@@ -1581,10 +1683,11 @@ export const installAppWorkspaceController = (options = {}) => {
1581
1683
  window.addEventListener("resize", onResize);
1582
1684
  syncPhonePages();
1583
1685
  syncObservedRoots();
1686
+ syncDetailRequestControls();
1584
1687
  mutationObserver?.observe(eventRoot, {
1585
1688
  attributes: true,
1586
1689
  attributeOldValue: true,
1587
- attributeFilter: ["hidden", "data-workspace-level-shown", "data-width", "data-workspace-main-min"],
1690
+ 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
1691
  childList: true,
1589
1692
  subtree: true
1590
1693
  });
@@ -1638,6 +1741,9 @@ export const installAppWorkspaceController = (options = {}) => {
1638
1741
  if (reconcileFrame !== null) cancelAnimationFrame(reconcileFrame);
1639
1742
  if (hostSyncFrame !== null) cancelAnimationFrame(hostSyncFrame);
1640
1743
  mutationObserver?.disconnect();
1744
+ detailRequestExpanded.forEach((state, control) => restoreDetailRequestExpanded(control, state));
1745
+ detailRequestExpanded.clear();
1746
+ ownerDocument.removeEventListener("click", onDetailRequestClick, true);
1641
1747
  resizeObserver?.disconnect();
1642
1748
  observedRoots.clear();
1643
1749
  observedHosts.clear();