@k2b/ui 0.22.0 → 0.24.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 (117) hide show
  1. package/dist/browser/content/Calendar.js +1122 -1065
  2. package/dist/browser/content/Calendar.js.map +1 -1
  3. package/dist/browser/content/CalendarDayView.js +1187 -0
  4. package/dist/browser/content/CalendarDayView.js.map +1 -0
  5. package/dist/browser/content/CalendarWeekView.js +1593 -0
  6. package/dist/browser/content/CalendarWeekView.js.map +1 -0
  7. package/dist/browser/content/ChartExplorer.js +174 -82
  8. package/dist/browser/content/ChartExplorer.js.map +1 -1
  9. package/dist/browser/content/ChartFilterControls.js +90 -33
  10. package/dist/browser/content/ChartFilterControls.js.map +1 -1
  11. package/dist/browser/content/DataTable.js +235 -197
  12. package/dist/browser/content/DataTable.js.map +1 -1
  13. package/dist/browser/content/calendar-day-scroll.js +111 -0
  14. package/dist/browser/content/calendar-day-scroll.js.map +1 -0
  15. package/dist/browser/content/calendar-days.js +408 -0
  16. package/dist/browser/content/calendar-days.js.map +1 -0
  17. package/dist/browser/content/calendar-dialogs.js +236 -0
  18. package/dist/browser/content/calendar-dialogs.js.map +1 -0
  19. package/dist/browser/content/calendar-messages.js +210 -2
  20. package/dist/browser/content/calendar-messages.js.map +1 -1
  21. package/dist/browser/content/calendar-pointer.js +15 -3
  22. package/dist/browser/content/calendar-pointer.js.map +1 -1
  23. package/dist/browser/content/calendar-shared.js +328 -0
  24. package/dist/browser/content/calendar-shared.js.map +1 -0
  25. package/dist/browser/content/calendar-time-view.js +1654 -0
  26. package/dist/browser/content/calendar-time-view.js.map +1 -0
  27. package/dist/browser/content/chart-messages.js +10 -0
  28. package/dist/browser/content/chart-messages.js.map +1 -1
  29. package/dist/browser/content/index.js.map +1 -1
  30. package/dist/browser/inputs/Combobox.js +0 -1
  31. package/dist/browser/inputs/Combobox.js.map +1 -1
  32. package/dist/browser/layout/AppWorkspace.js +125 -97
  33. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  34. package/dist/browser/layout/Navigation.js +18 -3
  35. package/dist/browser/layout/Navigation.js.map +1 -1
  36. package/dist/browser/layout/PageTitle.js +18 -1
  37. package/dist/browser/layout/PageTitle.js.map +1 -1
  38. package/dist/browser/layout/SidebarSwitcher.js +505 -0
  39. package/dist/browser/layout/SidebarSwitcher.js.map +1 -0
  40. package/dist/browser/layout/app-workspace-controller.js +174 -86
  41. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  42. package/dist/browser/layout/index.js +1 -0
  43. package/dist/browser/layout/index.js.map +1 -1
  44. package/dist/browser/layout/navigation-model.js +1 -0
  45. package/dist/browser/layout/navigation-model.js.map +1 -1
  46. package/dist/browser/surfaces/StatCell.js +18 -6
  47. package/dist/browser/surfaces/StatCell.js.map +1 -1
  48. package/dist/ssr/content/Calendar.js +553 -341
  49. package/dist/ssr/content/Calendar.js.map +1 -1
  50. package/dist/ssr/content/CalendarDayView.js +800 -0
  51. package/dist/ssr/content/CalendarDayView.js.map +1 -0
  52. package/dist/ssr/content/CalendarWeekView.js +1090 -0
  53. package/dist/ssr/content/CalendarWeekView.js.map +1 -0
  54. package/dist/ssr/content/ChartExplorer.js +93 -34
  55. package/dist/ssr/content/ChartExplorer.js.map +1 -1
  56. package/dist/ssr/content/ChartFilterControls.js +49 -7
  57. package/dist/ssr/content/ChartFilterControls.js.map +1 -1
  58. package/dist/ssr/content/DataTable.js +88 -66
  59. package/dist/ssr/content/DataTable.js.map +1 -1
  60. package/dist/ssr/content/calendar-day-scroll.js +111 -0
  61. package/dist/ssr/content/calendar-day-scroll.js.map +1 -0
  62. package/dist/ssr/content/calendar-days.js +408 -0
  63. package/dist/ssr/content/calendar-days.js.map +1 -0
  64. package/dist/ssr/content/calendar-dialogs.js +159 -0
  65. package/dist/ssr/content/calendar-dialogs.js.map +1 -0
  66. package/dist/ssr/content/calendar-messages.js +210 -2
  67. package/dist/ssr/content/calendar-messages.js.map +1 -1
  68. package/dist/ssr/content/calendar-pointer.js +15 -3
  69. package/dist/ssr/content/calendar-pointer.js.map +1 -1
  70. package/dist/ssr/content/calendar-shared.js +261 -0
  71. package/dist/ssr/content/calendar-shared.js.map +1 -0
  72. package/dist/ssr/content/calendar-time-view.js +1311 -0
  73. package/dist/ssr/content/calendar-time-view.js.map +1 -0
  74. package/dist/ssr/content/chart-messages.js +10 -0
  75. package/dist/ssr/content/chart-messages.js.map +1 -1
  76. package/dist/ssr/content/index.js.map +1 -1
  77. package/dist/ssr/inputs/Combobox.js.map +1 -1
  78. package/dist/ssr/layout/AppWorkspace.js +40 -29
  79. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  80. package/dist/ssr/layout/Navigation.js +12 -2
  81. package/dist/ssr/layout/Navigation.js.map +1 -1
  82. package/dist/ssr/layout/PageTitle.js +18 -1
  83. package/dist/ssr/layout/PageTitle.js.map +1 -1
  84. package/dist/ssr/layout/SidebarSwitcher.js +271 -0
  85. package/dist/ssr/layout/SidebarSwitcher.js.map +1 -0
  86. package/dist/ssr/layout/app-workspace-controller.js +174 -86
  87. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  88. package/dist/ssr/layout/index.js +1 -0
  89. package/dist/ssr/layout/index.js.map +1 -1
  90. package/dist/ssr/layout/navigation-model.js +1 -0
  91. package/dist/ssr/layout/navigation-model.js.map +1 -1
  92. package/dist/ssr/surfaces/StatCell.js +2 -2
  93. package/dist/ssr/surfaces/StatCell.js.map +1 -1
  94. package/dist/styles.css +1 -1
  95. package/dist/types/content/Calendar.d.ts +95 -1
  96. package/dist/types/content/CalendarDayView.d.ts +7 -0
  97. package/dist/types/content/CalendarWeekView.d.ts +7 -0
  98. package/dist/types/content/ChartExplorer.d.ts +36 -1
  99. package/dist/types/content/ChartFilterControls.d.ts +6 -1
  100. package/dist/types/content/DataTable.d.ts +14 -0
  101. package/dist/types/content/calendar-day-scroll.d.ts +47 -0
  102. package/dist/types/content/calendar-days.d.ts +192 -0
  103. package/dist/types/content/calendar-dialogs.d.ts +22 -0
  104. package/dist/types/content/calendar-messages.d.ts +99 -0
  105. package/dist/types/content/calendar-pointer.d.ts +9 -0
  106. package/dist/types/content/calendar-shared.d.ts +69 -0
  107. package/dist/types/content/calendar-time-view.d.ts +252 -0
  108. package/dist/types/content/chart-messages.d.ts +5 -0
  109. package/dist/types/content/index.d.ts +2 -2
  110. package/dist/types/layout/AppWorkspace.d.ts +27 -3
  111. package/dist/types/layout/PageTitle.d.ts +2 -1
  112. package/dist/types/layout/SidebarSwitcher.d.ts +65 -0
  113. package/dist/types/layout/app-workspace-controller.d.ts +9 -1
  114. package/dist/types/layout/index.d.ts +1 -0
  115. package/dist/types/layout/navigation-model.d.ts +4 -0
  116. package/dist/types/surfaces/StatCell.d.ts +5 -0
  117. package/package.json +3 -3
@@ -49,15 +49,18 @@ var _tmpl$ = ["<button type=\"button\" class=\"k2b-app-workspace__resize\" role=
49
49
  _tmpl$38 = ["<i", " aria-hidden=\"true\"></i>"],
50
50
  _tmpl$39 = "<i class=\"ti ti-layout-sidebar\" aria-hidden=\"true\"></i>",
51
51
  _tmpl$40 = ["<div class=\"k2b-app-workspace__head\"", ">", "", "</div>"],
52
- _tmpl$41 = ["<div class=\"", "\" data-k2b-app-workspace", " style=\"", "\">", "", "</div>"],
53
- _tmpl$42 = ["<div class=\"", "\">", "</div>"];
52
+ _tmpl$41 = ["<h1 class=\"k2b-app-workspace__head-heading\" data-k2b-page-trail-title", ">", "</h1>"],
53
+ _tmpl$42 = ["<div class=\"", "\" data-k2b-app-workspace", " style=\"", "\">", "", "</div>"],
54
+ _tmpl$43 = ["<div class=\"", "\">", "</div>"];
54
55
  import { Link } from "@k2b/ssr/nav";
55
56
  import { children, createContext, createEffect, createMemo, createSignal, createUniqueId, For, mergeProps, onCleanup, onMount, Show, useContext } from "solid-js";
57
+ import { isServer } from "solid-js/web";
56
58
  import { IconButton } from "../actions/Button";
57
59
  import { APP_WORKSPACE_DETAIL_CLOSE_EVENT, APP_WORKSPACE_DETAIL_STEP_EVENT, APP_WORKSPACE_SIDEBAR_HIDE_EVENT, installAppWorkspaceController } from "./app-workspace-controller";
58
60
  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_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, appWorkspaceLayoutStyle, appWorkspacePanelVariable } from "./app-workspace-state";
59
61
  import { PageTitle, usePageTrail } from "./PageTitle";
60
62
  import { SidebarItemPreview } from "./SidebarItemPreview";
63
+ import { AppWorkspaceSidebarSwitcher } from "./SidebarSwitcher";
61
64
  import { createScrollFade } from "./scroll-fade";
62
65
  import { assertStableUiId, assertUniqueStableUiIds } from "./stable-id";
63
66
  import { useWorkspaceMessages } from "./workspace-messages";
@@ -286,20 +289,22 @@ function AppWorkspaceDetail(props) {
286
289
  "--k2b-workspace-panel-min": `${minWidth()}px`,
287
290
  "--k2b-workspace-panel-max": `${maxWidth()}px`
288
291
  });
289
- // The panel floats over the content area, and `Main` beside it makes room
290
- // in CSS alone, so a server-rendered page arrives in its final geometry. A
291
- // sibling cannot read the panel's own variables, so this rule hands the
292
- // open panel's size to the content area around it.
293
- const contentRule = () => {
292
+ // An open panel takes a column at the workspace's end, sized in CSS alone,
293
+ // so a server-rendered page arrives in its final geometry. The workspace
294
+ // cannot read the panel's own variables, so this rule hands the open
295
+ // panel's size to its workspace.
296
+ const workspaceRule = () => {
294
297
  const open = `#${domId()}:not([hidden])`;
295
- return `.k2b-app-workspace__content:has(> ${open}, > :is(solid-island, solid-client) > ${open}){--k2b-detail-size:${size()};--k2b-detail-min:${minWidth()}px;--k2b-detail-max:${maxWidth()}px}`;
298
+ return `.k2b-app-workspace:has(> .k2b-app-workspace__content > ${open}, > .k2b-app-workspace__content > :is(solid-island, solid-client) > ${open}){--k2b-detail-size:${size()};--k2b-detail-min:${minWidth()}px;--k2b-detail-max:${maxWidth()}px}`;
296
299
  };
297
300
  let aside;
298
301
  onMount(() => {
299
302
  const close = () => props.onClose?.();
303
+ const isStep = detail => typeof detail === "object" && detail !== null && "direction" in detail && (detail.direction === 1 || detail.direction === -1);
300
304
  const step = event => {
301
- const direction = event instanceof CustomEvent ? event.detail?.direction : undefined;
302
- if (direction === 1 || direction === -1) props.onStep?.(direction);
305
+ if (!(event instanceof CustomEvent) || !isStep(event.detail)) return;
306
+ const item = props.onStep?.(event.detail.direction);
307
+ if (item instanceof HTMLElement) event.detail.item = item;
303
308
  };
304
309
  aside?.addEventListener(APP_WORKSPACE_DETAIL_CLOSE_EVENT, close);
305
310
  aside?.addEventListener(APP_WORKSPACE_DETAIL_STEP_EVENT, step);
@@ -308,7 +313,7 @@ function AppWorkspaceDetail(props) {
308
313
  aside?.removeEventListener(APP_WORKSPACE_DETAIL_STEP_EVENT, step);
309
314
  });
310
315
  });
311
- return [_$ssr(_tmpl$4, contentRule()), _$createComponent(Show, {
316
+ return [_$ssr(_tmpl$4, workspaceRule()), _$createComponent(Show, {
312
317
  get when() {
313
318
  return resizable();
314
319
  },
@@ -455,11 +460,12 @@ const AppWorkspaceSidebarDesktop = props => ({
455
460
  // hydrates. Islands load asynchronously and server-only sidebars never mount,
456
461
  // so `onMount` would be too late or never run. The memory is one
457
462
  // `sessionStorage` entry per path segment and sidebar position and ends with
458
- // the tab. A stored offset is applied first; the current item is then kept in
459
- // view, which is a no-op when the offset already shows it. The frame callback
463
+ // the tab. A stored offset is applied first; the current item, or on a deeper
464
+ // page the item that contains it, is then kept in view, which is a no-op when
465
+ // the offset already shows it. The frame callback
460
466
  // repeats the restore once the whole document is parsed, because content that
461
467
  // follows the sidebar can still change its height before the first paint.
462
- const SIDEBAR_SCROLL_MEMORY_SCRIPT = ["(function(){", "var body=document.currentScript&&document.currentScript.previousElementSibling;if(!body)return;", "var storage;try{storage=window.sessionStorage}catch(_){return}", 'var key="k2b-sidebar-scroll:"+location.pathname.split("/")[1]+":"+document.querySelectorAll(".k2b-app-workspace__sidebar-body").length;', "var restore=function(){", "var stored=storage.getItem(key);if(stored!==null)body.scrollTop=Number(stored);", 'var current=body.querySelector(\'[aria-current="page"]\');if(current)current.scrollIntoView({block:"nearest"});', "};", "restore();requestAnimationFrame(restore);", "var frame=0;", 'body.addEventListener("scroll",function(){if(frame)return;frame=requestAnimationFrame(function(){frame=0;try{storage.setItem(key,String(body.scrollTop))}catch(_){}})},{passive:true});', "})();"].join("");
468
+ const SIDEBAR_SCROLL_MEMORY_SCRIPT = ["(function(){", "var body=document.currentScript&&document.currentScript.previousElementSibling;if(!body)return;", "var storage;try{storage=window.sessionStorage}catch(_){return}", 'var key="k2b-sidebar-scroll:"+location.pathname.split("/")[1]+":"+document.querySelectorAll(".k2b-app-workspace__sidebar-body").length;', "var restore=function(){", "var stored=storage.getItem(key);if(stored!==null)body.scrollTop=Number(stored);", 'var current=body.querySelector(\'[aria-current="page"]\')||body.querySelector(\'[aria-current="true"]\');if(current)current.scrollIntoView({block:"nearest"});', "};", "restore();requestAnimationFrame(restore);", "var frame=0;", 'body.addEventListener("scroll",function(){if(frame)return;frame=requestAnimationFrame(function(){frame=0;try{storage.setItem(key,String(body.scrollTop))}catch(_){}})},{passive:true});', "})();"].join("");
463
469
  const AppWorkspaceSidebarBody = props => {
464
470
  let body;
465
471
  createScrollFade(() => body, () => props.scrollFade !== false);
@@ -551,11 +557,12 @@ const AppWorkspaceSidebarItemAction = props => mergeProps({
551
557
  const AppWorkspaceSidebarItemActions = props => _$ssr(_tmpl$15, _$ssrAttribute("data-visibility", props.visibility === "hover" ? "hover" : _$escape(undefined, true), false), _$escape(props.children));
552
558
  const AppWorkspaceSidebarItemStatus = props => {
553
559
  const workspaceMessages = useWorkspaceMessages();
554
- const count = () => typeof props.unread === "number" && props.unread >= 1 ? Math.floor(props.unread) : 0;
555
- const dot = () => props.unread === true;
560
+ const waiting = () => props.waiting && Number.isFinite(props.waiting.count) ? Math.max(0, Math.floor(props.waiting.count)) : null;
561
+ const count = () => waiting() ?? (typeof props.unread === "number" && props.unread >= 1 ? Math.floor(props.unread) : 0);
562
+ const dot = () => waiting() === null && props.unread === true;
556
563
  // The marks are decorative; the row's accessible name gets one phrase instead,
557
564
  // which says no more than the "99+" a capped count shows.
558
- const description = () => [count() > 99 ? workspaceMessages().sidebarUnreadMany : count() ? workspaceMessages().sidebarUnread({
565
+ const description = () => [waiting() !== null ? props.waiting?.label ?? "" : count() > 99 ? workspaceMessages().sidebarUnreadMany : count() ? workspaceMessages().sidebarUnread({
559
566
  count: count()
560
567
  }) : dot() ? workspaceMessages().sidebarNewActivity : "", props.mention ? workspaceMessages().sidebarMentioned : "", props.muted ? workspaceMessages().sidebarMuted : ""].filter(Boolean).join(", ");
561
568
  // The unread slot stays reserved at the end, and the marks that change most
@@ -565,7 +572,7 @@ const AppWorkspaceSidebarItemStatus = props => {
565
572
  return description();
566
573
  },
567
574
  get children() {
568
- return _$ssr(_tmpl$19, _$ssrAttribute("data-unread", count() ? "count" : dot() ? "dot" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
575
+ return _$ssr(_tmpl$19, _$ssrAttribute("data-unread", waiting() !== null ? _$escape(undefined, true) : count() ? "count" : dot() ? "dot" : _$escape(undefined, true), false) + _$ssrAttribute("data-waiting", waiting() === null ? _$escape(undefined, true) : waiting() ? "count" : "zero", false), _$escape(_$createComponent(Show, {
569
576
  get when() {
570
577
  return props.mention;
571
578
  },
@@ -581,7 +588,7 @@ const AppWorkspaceSidebarItemStatus = props => {
581
588
  }
582
589
  })), _$escape(_$createComponent(Show, {
583
590
  get when() {
584
- return count();
591
+ return count() || waiting() === 0;
585
592
  },
586
593
  get fallback() {
587
594
  return _$createComponent(Show, {
@@ -614,7 +621,7 @@ function AppWorkspaceSidebarRow(props) {
614
621
  },
615
622
  ...data()
616
623
  });
617
- const current = () => props.active ? "page" : undefined;
624
+ const current = () => props.active === "ancestor" ? "true" : props.active ? "page" : undefined;
618
625
  const Control = control => {
619
626
  const attrs = () => control.grouped ? {
620
627
  class: "k2b-app-workspace__sidebar-item-main"
@@ -1318,11 +1325,14 @@ const AppWorkspaceSidebarIconAction = props => {
1318
1325
  };
1319
1326
  /**
1320
1327
  * The row above the work area. From 48 to 64 rem it holds the button that
1321
- * opens the overlay sidebar. On a page with a page above it, at every width,
1322
- * it shows the host's page title with the way up (see `PageTitle`); the
1323
- * page's own `PageTitle` takes its place. It is one fixed height and draws no
1324
- * line; CSS decides when it shows, so the server-rendered page already has
1325
- * its final geometry.
1328
+ * opens the overlay sidebar. On a page with a page above it, it shows the
1329
+ * host's page title with the way up (see `PageTitle`); the page's own
1330
+ * `PageTitle` takes its place. The visible title is no heading, because many
1331
+ * pages render their own `h1`. From 64 rem a sidebar that marks the current
1332
+ * page names it already: the row hides, and only on a page without its own
1333
+ * `h1` its heading stays for assistive technology. It is one fixed height
1334
+ * and draws no line; CSS decides when it shows, so the server-rendered page
1335
+ * already has its final geometry.
1326
1336
  */
1327
1337
  function AppWorkspaceHead(props) {
1328
1338
  const messages = useWorkspaceMessages();
@@ -1345,13 +1355,13 @@ function AppWorkspaceHead(props) {
1345
1355
  get when() {
1346
1356
  return trail();
1347
1357
  },
1348
- children: current => _$createComponent(PageTitle, {
1358
+ children: current => [_$createComponent(PageTitle, {
1349
1359
  get trail() {
1350
1360
  return current();
1351
1361
  },
1352
1362
  as: "p",
1353
1363
  fallback: true
1354
- })
1364
+ }), _$ssr(_tmpl$41, _$ssrAttribute("data-k2b-server", isServer ? "" : _$escape(undefined, true), false), _$escape(current().title))]
1355
1365
  })));
1356
1366
  }
1357
1367
  const AppWorkspace = props => {
@@ -1386,7 +1396,7 @@ const AppWorkspace = props => {
1386
1396
  return _$createComponent(WorkspaceContext.Provider, {
1387
1397
  value: workspace,
1388
1398
  get children() {
1389
- return _$ssr(_tmpl$41, `k2b-app-workspace ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-mobile-surface", _$escape(props.mobileSurface, true), false) + _$ssrAttribute("data-sidebar-collapsed", initialLayoutState()?.sidebarCollapsed === undefined ? _$escape(undefined, true) : _$escape(String(initialLayoutState()?.sidebarCollapsed), true), false) + _$ssrAttribute("data-workspace-resizable", props.resizable === false ? "false" : "true", false), _$ssrStyle(appWorkspaceLayoutStyle(initialLayoutState())), _$escape(_$createComponent(AppWorkspaceHead, {
1399
+ return _$ssr(_tmpl$42, `k2b-app-workspace ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-mobile-surface", _$escape(props.mobileSurface, true), false) + _$ssrAttribute("data-flat", props.flat ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-sidebar-collapsed", initialLayoutState()?.sidebarCollapsed === undefined ? _$escape(undefined, true) : _$escape(String(initialLayoutState()?.sidebarCollapsed), true), false) + _$ssrAttribute("data-workspace-resizable", props.resizable === false ? "false" : "true", false), _$ssrStyle(appWorkspaceLayoutStyle(initialLayoutState())), _$escape(_$createComponent(AppWorkspaceHead, {
1390
1400
  sidebarId: sidebarId,
1391
1401
  nested: nested
1392
1402
  })), _$escape(props.children));
@@ -1403,13 +1413,14 @@ AppWorkspace.LayoutStateProvider = props => _$createComponent(LayoutStateContext
1403
1413
  return props.children;
1404
1414
  }
1405
1415
  });
1406
- AppWorkspace.Content = props => _$ssr(_tmpl$42, `k2b-app-workspace__content ${_$escape(props.class ?? "", true)}`, _$escape(props.children));
1416
+ AppWorkspace.Content = props => _$ssr(_tmpl$43, `k2b-app-workspace__content ${_$escape(props.class ?? "", true)}`, _$escape(props.children));
1407
1417
  AppWorkspace.Main = AppWorkspaceMain;
1408
1418
  AppWorkspace.MainPane = AppWorkspaceMainPane;
1409
1419
  AppWorkspace.Detail = AppWorkspaceDetail;
1410
1420
  AppWorkspace.BottomDrawer = AppWorkspaceBottomDrawer;
1411
1421
  AppWorkspace.Sidebar = AppWorkspaceSidebar;
1412
1422
  AppWorkspace.SidebarDesktop = AppWorkspaceSidebarDesktop;
1423
+ AppWorkspace.SidebarSwitcher = AppWorkspaceSidebarSwitcher;
1413
1424
  AppWorkspace.SidebarSection = AppWorkspaceSidebarSection;
1414
1425
  AppWorkspace.SidebarBody = AppWorkspaceSidebarBody;
1415
1426
  AppWorkspace.SidebarFooter = AppWorkspaceSidebarFooter;