@k2b/ui 0.38.1 → 0.40.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 (41) hide show
  1. package/dist/browser/actions/Dropdown.js +42 -6
  2. package/dist/browser/actions/Dropdown.js.map +1 -1
  3. package/dist/browser/actions/SelectionToolbar.js +11 -6
  4. package/dist/browser/actions/SelectionToolbar.js.map +1 -1
  5. package/dist/browser/actions/Toolbar.js +5 -1
  6. package/dist/browser/actions/Toolbar.js.map +1 -1
  7. package/dist/browser/actions/index.js.map +1 -1
  8. package/dist/browser/chat/ChatComposer.js +11 -2
  9. package/dist/browser/chat/ChatComposer.js.map +1 -1
  10. package/dist/browser/layout/AppWorkspace.js +31 -17
  11. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  12. package/dist/browser/layout/app-workspace-controller.js +49 -5
  13. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  14. package/dist/browser/text-editor/TextEditor.js +331 -214
  15. package/dist/browser/text-editor/TextEditor.js.map +1 -1
  16. package/dist/browser/text-editor/table.js +43 -6
  17. package/dist/browser/text-editor/table.js.map +1 -1
  18. package/dist/ssr/actions/Dropdown.js +38 -6
  19. package/dist/ssr/actions/Dropdown.js.map +1 -1
  20. package/dist/ssr/actions/SelectionToolbar.js +11 -6
  21. package/dist/ssr/actions/SelectionToolbar.js.map +1 -1
  22. package/dist/ssr/actions/Toolbar.js +5 -1
  23. package/dist/ssr/actions/Toolbar.js.map +1 -1
  24. package/dist/ssr/actions/index.js.map +1 -1
  25. package/dist/ssr/chat/ChatComposer.js +10 -2
  26. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  27. package/dist/ssr/layout/AppWorkspace.js +31 -17
  28. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  29. package/dist/ssr/layout/app-workspace-controller.js +49 -5
  30. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  31. package/dist/ssr/text-editor/TextEditor.js +225 -96
  32. package/dist/ssr/text-editor/TextEditor.js.map +1 -1
  33. package/dist/ssr/text-editor/table.js +43 -6
  34. package/dist/ssr/text-editor/table.js.map +1 -1
  35. package/dist/styles.css +1 -1
  36. package/dist/types/actions/Dropdown.d.ts +15 -1
  37. package/dist/types/actions/Toolbar.d.ts +6 -1
  38. package/dist/types/actions/index.d.ts +1 -1
  39. package/dist/types/layout/AppWorkspace.d.ts +14 -0
  40. package/dist/types/layout/app-workspace-controller.d.ts +2 -0
  41. package/package.json +1 -1
@@ -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 `AppWorkspace.Sidebar` to show again; its `onHiddenChange` decides. */
70
+ export const APP_WORKSPACE_SIDEBAR_SHOW_EVENT = "k2b-app-workspace-sidebar-show";
69
71
  /** Asks the owning Detail to reopen its current available object. */
70
72
  export const APP_WORKSPACE_DETAIL_OPEN_EVENT = "k2b-app-workspace-detail-open";
71
73
  /** Asks the owning `AppWorkspace.Detail` to close; its `onClose` decides. */
@@ -303,6 +305,12 @@ const overlayTarget = toggle => {
303
305
  };
304
306
  };
305
307
  const overlayRegions = (root, sidebar) => Array.from(root.children).flatMap(child => child instanceof HTMLElement && (child.localName === "solid-island" || child.localName === "solid-client") ? Array.from(child.children) : [child]).filter(child => child instanceof HTMLElement && child !== sidebar && !child.contains(sidebar));
308
+
309
+ /**
310
+ * A sidebar button tells whether its sidebar shows: as an open overlay where the sidebar is one, otherwise as a
311
+ * column that is not hidden.
312
+ */
313
+ const sidebarShown = (root, sidebar) => sidebarForm(root) === "overlay" ? root.dataset.sidebarOverlay === "open" : !sidebar.hidden;
306
314
  export const installAppWorkspaceController = (options = {}) => {
307
315
  const eventRoot = options.root ?? document;
308
316
  // A document has no owner document; an element root listens on its own.
@@ -311,6 +319,17 @@ export const installAppWorkspaceController = (options = {}) => {
311
319
  let overlay = null;
312
320
  // A server-rendered head names the sidebar before the sidebar renders; the button learns its real id now.
313
321
  for (const toggle of eventRoot.querySelectorAll(TOGGLE_SELECTOR)) overlayTarget(toggle);
322
+ /** Every sidebar button, the workspace head's and an application's own, states whether its sidebar shows. */
323
+ const syncSidebarToggles = () => {
324
+ for (const toggle of eventRoot.querySelectorAll(TOGGLE_SELECTOR)) {
325
+ const root = workspaceRoot(toggle);
326
+ const sidebar = root ? sidebarElement(root) : null;
327
+ if (!root || !sidebar) continue;
328
+ // The head's button only opens the overlay; an application's button also shows and hides the column.
329
+ const expanded = String(toggle.closest(".k2b-app-workspace__head") ? root.dataset.sidebarOverlay === "open" : sidebarShown(root, sidebar));
330
+ if (toggle.getAttribute("aria-expanded") !== expanded) toggle.setAttribute("aria-expanded", expanded);
331
+ }
332
+ };
314
333
  const openOverlay = target => {
315
334
  if (overlay) closeOverlay("none");
316
335
  const inerted = overlayRegions(target.root, target.sidebar).filter(region => !region.hasAttribute("inert"));
@@ -320,6 +339,7 @@ export const installAppWorkspaceController = (options = {}) => {
320
339
  };
321
340
  target.root.dataset.sidebarOverlay = "open";
322
341
  target.toggle.setAttribute("aria-expanded", "true");
342
+ syncSidebarToggles();
323
343
  inerted.forEach(region => region.setAttribute("inert", ""));
324
344
  // Focus starts on the current destination, so the reader knows where they
325
345
  // are: on its own tab stop, or on the tree item around it.
@@ -342,6 +362,8 @@ export const installAppWorkspaceController = (options = {}) => {
342
362
  overlay = null;
343
363
  delete closing.root.dataset.sidebarOverlay;
344
364
  closing.toggle.setAttribute("aria-expanded", "false");
365
+ // Escape, the scrim or a chosen destination also close it: every sidebar button of the workspace says so.
366
+ syncSidebarToggles();
345
367
  closing.inerted.forEach(region => region.removeAttribute("inert"));
346
368
  if (focus === "none") return;
347
369
  const active = document.activeElement;
@@ -377,10 +399,27 @@ export const installAppWorkspaceController = (options = {}) => {
377
399
  }
378
400
  const toggle = target?.closest(TOGGLE_SELECTOR);
379
401
  if (toggle) {
402
+ if (claimedToggleClicks.has(event)) return;
403
+ const root = workspaceRoot(toggle);
404
+ const sidebar = root ? sidebarElement(root) : null;
405
+ // A sidebar the person hid comes back first; where it is an overlay, it then opens.
406
+ if (root && sidebar?.hidden && sidebarHideable(root)) {
407
+ claimedToggleClicks.add(event);
408
+ sidebar.dispatchEvent(new CustomEvent(APP_WORKSPACE_SIDEBAR_SHOW_EVENT));
409
+ ownerWindow.requestAnimationFrame(() => {
410
+ const next = !sidebar.hidden && sidebarForm(root) === "overlay" ? overlayTarget(toggle) : null;
411
+ if (next) openOverlay(next);
412
+ syncSidebarToggles();
413
+ });
414
+ return;
415
+ }
380
416
  const next = overlayTarget(toggle);
381
- if (!next || claimedToggleClicks.has(event)) return;
417
+ if (!next) return;
382
418
  claimedToggleClicks.add(event);
383
419
  if (overlay?.sidebar === next.sidebar) closeOverlay("button");else if (sidebarForm(next.root) === "overlay") openOverlay(next);
420
+ // A column: an application's own button hides a sidebar it lets people hide.
421
+ else if (sidebarHideable(next.root)) next.sidebar.dispatchEvent(new CustomEvent(APP_WORKSPACE_SIDEBAR_HIDE_EVENT));
422
+ syncSidebarToggles();
384
423
  return;
385
424
  }
386
425
  if (!overlay || !target) return;
@@ -466,7 +505,8 @@ export const installAppWorkspaceController = (options = {}) => {
466
505
  for (const root of allRoots()) {
467
506
  for (const aside of rootElements(root, detailSelector)) {
468
507
  const toggle = detailToggle(aside);
469
- if (toggle?.dataset.workspaceDetailReopen !== "true") continue;
508
+ // The panel decides whether it reopens; its button may be the workspace's own or an application's.
509
+ if (!toggle || aside.dataset.workspaceDetailReopen !== "true") continue;
470
510
  const expanded = !aside.hidden && (!wide?.matches || !detailWaiting(aside) || detailDrawer(aside));
471
511
  const value = String(expanded);
472
512
  if (toggle.getAttribute("aria-expanded") !== value) toggle.setAttribute("aria-expanded", value);
@@ -581,7 +621,7 @@ export const installAppWorkspaceController = (options = {}) => {
581
621
  return;
582
622
  }
583
623
  const shared = detailToggle(aside);
584
- if (shared?.dataset.workspaceDetailReopen === "true" && phone?.matches && phonePage(aside) && !aside.hidden && !aside.contains(control)) {
624
+ if (shared && aside.dataset.workspaceDetailReopen === "true" && phone?.matches && phonePage(aside) && !aside.hidden && !aside.contains(control)) {
585
625
  // WebKit can update matchMedia after resize has already focused the now
586
626
  // visible application control. Move into the page before Main becomes inert.
587
627
  detailControlFocus = null;
@@ -590,7 +630,7 @@ export const installAppWorkspaceController = (options = {}) => {
590
630
  }
591
631
  if (detailControlVisible(control)) return;
592
632
  detailControlFocus = null;
593
- if (shared?.dataset.workspaceDetailReopen !== "true") {
633
+ if (!shared || aside.dataset.workspaceDetailReopen !== "true") {
594
634
  // Selection-only details keep their existing returning-column title target.
595
635
  if (control === shared && !aside.hidden && !detailWaiting(aside)) requestTitle(aside);
596
636
  return;
@@ -602,6 +642,7 @@ export const installAppWorkspaceController = (options = {}) => {
602
642
  });
603
643
  };
604
644
  const syncHostState = () => {
645
+ syncSidebarToggles();
605
646
  rescueDetailControlFocus();
606
647
  if (pendingDetail && !canOpenPendingDetail(pendingDetail.aside)) cancelPendingDetail();
607
648
  if (overlay && sidebarForm(overlay.root) !== "overlay") closeOverlay("none");
@@ -1055,7 +1096,7 @@ export const installAppWorkspaceController = (options = {}) => {
1055
1096
  const focused = ownerDocument.activeElement;
1056
1097
  const toggle = detailToggle(aside);
1057
1098
  const returnControl = detailReturnControl(aside);
1058
- if (toggle?.dataset.workspaceDetailReopen === "true" && returnControl && (aside.contains(focused) || focused === ownerDocument.body && aside.contains(lastFocus.get(root) ?? null))) {
1099
+ if (toggle && aside.dataset.workspaceDetailReopen === "true" && returnControl && (aside.contains(focused) || focused === ownerDocument.body && aside.contains(lastFocus.get(root) ?? null))) {
1059
1100
  returnControl.focus({
1060
1101
  preventScroll: true
1061
1102
  });
@@ -1795,6 +1836,8 @@ export const installAppWorkspaceController = (options = {}) => {
1795
1836
  const layoutNode = node => node instanceof Element && (node.matches(LAYOUT_REGION_SELECTOR) || Boolean(node.querySelector(LAYOUT_REGION_SELECTOR)));
1796
1837
  const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(records => {
1797
1838
  if (records.some(record => record.type === "childList" || ["hidden", "aria-controls", "aria-expanded", "data-workspace-detail-drawer"].includes(record.attributeName ?? ""))) syncDetailRequestControls();
1839
+ // A sidebar shown or hidden, or a sidebar button added: the buttons state it.
1840
+ if (records.some(record => record.type === "attributes" && record.attributeName === "hidden" && record.target instanceof Element && record.target.matches(".k2b-app-workspace__sidebar") || [...record.addedNodes].some(node => node instanceof Element && (node.matches(TOGGLE_SELECTOR) || node.querySelector(TOGGLE_SELECTOR))))) syncSidebarToggles();
1798
1841
  syncPhonePages();
1799
1842
  if (pendingTitle && (pendingTitle.aside.hidden || !pendingTitle.aside.isConnected)) cancelTitle();
1800
1843
  if (pendingDetail && (!canOpenPendingDetail(pendingDetail.aside) || !pendingDetail.snapshot.chosen || !pendingDetail.snapshot.item.isConnected)) cancelPendingDetail();
@@ -1850,6 +1893,7 @@ export const installAppWorkspaceController = (options = {}) => {
1850
1893
  syncPhonePages();
1851
1894
  syncObservedRoots();
1852
1895
  syncDetailRequestControls();
1896
+ syncSidebarToggles();
1853
1897
  mutationObserver?.observe(eventRoot, {
1854
1898
  attributes: true,
1855
1899
  attributeOldValue: true,