@dxos/plugin-deck 0.7.5-labs.e27f9b9 → 0.7.5-main.2567c87

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 (209) hide show
  1. package/dist/lib/browser/{app-graph-builder-CI6ZFMNL.mjs → app-graph-builder-67VRUD5K.mjs} +31 -57
  2. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs → check-app-scheme-GEX6W2R5.mjs} +3 -3
  4. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs.map → check-app-scheme-GEX6W2R5.mjs.map} +2 -2
  5. package/dist/lib/browser/chunk-2M4PXYNB.mjs +1052 -0
  6. package/dist/lib/browser/chunk-2M4PXYNB.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-2PJNBVCY.mjs +39 -0
  8. package/dist/lib/browser/chunk-2PJNBVCY.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-4C2AFTET.mjs +186 -0
  10. package/dist/lib/browser/chunk-4C2AFTET.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-YQ2GWTDU.mjs → chunk-5VFDMW5M.mjs} +2 -2
  12. package/dist/lib/browser/chunk-5VFDMW5M.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-N7TEPFVR.mjs → chunk-JQJ5UWVB.mjs} +2 -3
  14. package/dist/lib/browser/{chunk-N7TEPFVR.mjs.map → chunk-JQJ5UWVB.mjs.map} +3 -3
  15. package/dist/lib/browser/chunk-KY5WXIXY.mjs +44 -0
  16. package/dist/lib/browser/chunk-KY5WXIXY.mjs.map +7 -0
  17. package/dist/lib/browser/deck-PLCSKPGL.mjs +26 -0
  18. package/dist/lib/browser/deck-PLCSKPGL.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +76 -36
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs +152 -0
  22. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs.map +7 -0
  23. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs +249 -0
  24. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs.map +7 -0
  25. package/dist/lib/browser/location-QHRBQBQN.mjs +35 -0
  26. package/dist/lib/browser/location-QHRBQBQN.mjs.map +7 -0
  27. package/dist/lib/browser/meta.json +1 -1
  28. package/dist/lib/browser/react-context-3BDW7W2N.mjs +32 -0
  29. package/dist/lib/browser/react-context-3BDW7W2N.mjs.map +7 -0
  30. package/dist/lib/browser/{react-root-HIHLRMCW.mjs → react-root-UL7ZDRVZ.mjs} +14 -10
  31. package/dist/lib/browser/react-root-UL7ZDRVZ.mjs.map +7 -0
  32. package/dist/lib/browser/react-surface-VPNOGGNN.mjs +28 -0
  33. package/dist/lib/browser/react-surface-VPNOGGNN.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-WACNLCPB.mjs → settings-FNWW6WIJ.mjs} +7 -6
  35. package/dist/lib/browser/settings-FNWW6WIJ.mjs.map +7 -0
  36. package/dist/lib/browser/state-7I5BD7SE.mjs +34 -0
  37. package/dist/lib/browser/state-7I5BD7SE.mjs.map +7 -0
  38. package/dist/lib/browser/types.mjs +4 -16
  39. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs +76 -0
  40. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs.map +7 -0
  41. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  42. package/dist/types/src/capabilities/capabilities.d.ts +3 -132
  43. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/index.d.ts +3 -188
  45. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  46. package/dist/types/src/capabilities/{app-graph-builder.d.ts → layout/app-graph-builder.d.ts} +22 -22
  47. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/layout/deck.d.ts +4 -0
  49. package/dist/types/src/capabilities/layout/deck.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/layout/index.d.ts +229 -0
  51. package/dist/types/src/capabilities/layout/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/layout/intent-resolver.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/layout/react-context.d.ts +8 -0
  54. package/dist/types/src/capabilities/layout/react-context.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/layout/react-root.d.ts.map +1 -0
  56. package/dist/types/src/capabilities/layout/state.d.ts +42 -0
  57. package/dist/types/src/capabilities/layout/state.d.ts.map +1 -0
  58. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts.map +1 -0
  59. package/dist/types/src/capabilities/navigation/index.d.ts +5 -0
  60. package/dist/types/src/capabilities/navigation/index.d.ts.map +1 -0
  61. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts +4 -0
  62. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts.map +1 -0
  63. package/dist/types/src/capabilities/navigation/location.d.ts +4 -0
  64. package/dist/types/src/capabilities/navigation/location.d.ts.map +1 -0
  65. package/dist/types/src/capabilities/navigation/set-location.d.ts +10 -0
  66. package/dist/types/src/capabilities/navigation/set-location.d.ts.map +1 -0
  67. package/dist/types/src/capabilities/navigation/url-handler.d.ts.map +1 -0
  68. package/dist/types/src/capabilities/settings/index.d.ts +3 -0
  69. package/dist/types/src/capabilities/settings/index.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/settings/react-surface.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/settings/settings.d.ts.map +1 -0
  72. package/dist/types/src/components/DeckContext.d.ts +11 -0
  73. package/dist/types/src/components/DeckContext.d.ts.map +1 -0
  74. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  75. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  76. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  77. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +4 -1
  78. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  79. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  80. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  81. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  82. package/dist/types/src/components/DeckLayout/Plank.d.ts +6 -8
  83. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  84. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  85. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  86. package/dist/types/src/components/DeckLayout/PlankError.d.ts +3 -4
  87. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  88. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  89. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +3 -5
  90. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
  91. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  92. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
  93. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  94. package/dist/types/src/components/LayoutContext.d.ts +5 -0
  95. package/dist/types/src/components/LayoutContext.d.ts.map +1 -0
  96. package/dist/types/src/components/fragments.d.ts +0 -2
  97. package/dist/types/src/components/fragments.d.ts.map +1 -1
  98. package/dist/types/src/components/index.d.ts +2 -0
  99. package/dist/types/src/components/index.d.ts.map +1 -1
  100. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  101. package/dist/types/src/layout.d.ts +19 -5
  102. package/dist/types/src/layout.d.ts.map +1 -1
  103. package/dist/types/src/layout.test.d.ts +2 -0
  104. package/dist/types/src/layout.test.d.ts.map +1 -0
  105. package/dist/types/src/meta.d.ts +0 -1
  106. package/dist/types/src/meta.d.ts.map +1 -1
  107. package/dist/types/src/translations.d.ts +3 -3
  108. package/dist/types/src/types.d.ts +2 -107
  109. package/dist/types/src/types.d.ts.map +1 -1
  110. package/dist/types/src/util/index.d.ts +1 -2
  111. package/dist/types/src/util/index.d.ts.map +1 -1
  112. package/dist/types/src/util/layout-parts.d.ts +7 -0
  113. package/dist/types/src/util/layout-parts.d.ts.map +1 -0
  114. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  115. package/package.json +31 -32
  116. package/src/DeckPlugin.ts +57 -17
  117. package/src/capabilities/capabilities.ts +3 -4
  118. package/src/capabilities/index.ts +3 -12
  119. package/src/capabilities/{app-graph-builder.ts → layout/app-graph-builder.ts} +28 -36
  120. package/src/capabilities/layout/deck.ts +25 -0
  121. package/src/capabilities/layout/index.ts +12 -0
  122. package/src/capabilities/layout/intent-resolver.ts +128 -0
  123. package/src/capabilities/layout/react-context.tsx +26 -0
  124. package/src/capabilities/{react-root.tsx → layout/react-root.tsx} +11 -7
  125. package/src/capabilities/layout/state.ts +32 -0
  126. package/src/capabilities/{check-app-scheme.ts → navigation/check-app-scheme.ts} +2 -2
  127. package/src/capabilities/navigation/index.ts +10 -0
  128. package/src/capabilities/navigation/intent-resolver.ts +216 -0
  129. package/src/capabilities/navigation/location.ts +28 -0
  130. package/src/capabilities/navigation/set-location.ts +38 -0
  131. package/src/capabilities/navigation/url-handler.ts +67 -0
  132. package/src/capabilities/settings/index.ts +8 -0
  133. package/src/capabilities/settings/react-surface.tsx +23 -0
  134. package/src/capabilities/{settings.ts → settings/settings.ts} +5 -4
  135. package/src/components/DeckContext.ts +19 -0
  136. package/src/components/DeckLayout/ActiveNode.tsx +3 -2
  137. package/src/components/DeckLayout/ComplementarySidebar.tsx +69 -120
  138. package/src/components/DeckLayout/ContentEmpty.tsx +10 -7
  139. package/src/components/DeckLayout/DeckLayout.tsx +61 -103
  140. package/src/components/DeckLayout/Fullscreen.tsx +3 -2
  141. package/src/components/DeckLayout/NodePlankHeading.tsx +65 -57
  142. package/src/components/DeckLayout/Plank.tsx +41 -32
  143. package/src/components/DeckLayout/PlankControls.tsx +10 -11
  144. package/src/components/DeckLayout/PlankError.tsx +5 -6
  145. package/src/components/DeckLayout/Sidebar.tsx +20 -17
  146. package/src/components/DeckLayout/SidebarButton.tsx +31 -25
  147. package/src/components/DeckLayout/StatusBar.tsx +11 -5
  148. package/src/components/DeckLayout/Toast.tsx +2 -2
  149. package/src/components/LayoutContext.ts +12 -0
  150. package/src/components/LayoutSettings.tsx +8 -8
  151. package/src/components/fragments.ts +0 -8
  152. package/src/components/index.ts +2 -0
  153. package/src/hooks/useMainSize.ts +3 -3
  154. package/src/layout.test.ts +380 -0
  155. package/src/layout.ts +212 -43
  156. package/src/meta.ts +0 -1
  157. package/src/translations.ts +8 -8
  158. package/src/types.ts +2 -88
  159. package/src/util/index.ts +1 -2
  160. package/src/util/layout-parts.ts +12 -0
  161. package/src/util/useHoistStatusbar.ts +8 -17
  162. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +0 -7
  163. package/dist/lib/browser/chunk-BTDY6SES.mjs +0 -1119
  164. package/dist/lib/browser/chunk-BTDY6SES.mjs.map +0 -7
  165. package/dist/lib/browser/chunk-FZOBKOA2.mjs +0 -24
  166. package/dist/lib/browser/chunk-FZOBKOA2.mjs.map +0 -7
  167. package/dist/lib/browser/chunk-M2L53AIH.mjs +0 -126
  168. package/dist/lib/browser/chunk-M2L53AIH.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +0 -7
  170. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs +0 -494
  171. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs.map +0 -7
  172. package/dist/lib/browser/react-root-HIHLRMCW.mjs.map +0 -7
  173. package/dist/lib/browser/react-surface-4QVWKQYY.mjs +0 -38
  174. package/dist/lib/browser/react-surface-4QVWKQYY.mjs.map +0 -7
  175. package/dist/lib/browser/settings-WACNLCPB.mjs.map +0 -7
  176. package/dist/lib/browser/state-VPOYUKK6.mjs +0 -117
  177. package/dist/lib/browser/state-VPOYUKK6.mjs.map +0 -7
  178. package/dist/lib/browser/tools-5LDJNU56.mjs +0 -51
  179. package/dist/lib/browser/tools-5LDJNU56.mjs.map +0 -7
  180. package/dist/lib/browser/url-handler-HLF42IHP.mjs +0 -70
  181. package/dist/lib/browser/url-handler-HLF42IHP.mjs.map +0 -7
  182. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  183. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +0 -1
  184. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  185. package/dist/types/src/capabilities/react-root.d.ts.map +0 -1
  186. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  187. package/dist/types/src/capabilities/set-active.d.ts +0 -9
  188. package/dist/types/src/capabilities/set-active.d.ts.map +0 -1
  189. package/dist/types/src/capabilities/settings.d.ts.map +0 -1
  190. package/dist/types/src/capabilities/state.d.ts +0 -76
  191. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  192. package/dist/types/src/capabilities/tools.d.ts +0 -10
  193. package/dist/types/src/capabilities/tools.d.ts.map +0 -1
  194. package/dist/types/src/capabilities/url-handler.d.ts.map +0 -1
  195. package/dist/types/src/util/layoutAppliesTopbar.d.ts +0 -2
  196. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +0 -1
  197. package/src/capabilities/intent-resolver.ts +0 -350
  198. package/src/capabilities/react-surface.tsx +0 -31
  199. package/src/capabilities/set-active.ts +0 -43
  200. package/src/capabilities/state.ts +0 -102
  201. package/src/capabilities/tools.ts +0 -61
  202. package/src/capabilities/url-handler.ts +0 -63
  203. package/src/util/layoutAppliesTopbar.ts +0 -7
  204. /package/dist/types/src/capabilities/{intent-resolver.d.ts → layout/intent-resolver.d.ts} +0 -0
  205. /package/dist/types/src/capabilities/{react-root.d.ts → layout/react-root.d.ts} +0 -0
  206. /package/dist/types/src/capabilities/{check-app-scheme.d.ts → navigation/check-app-scheme.d.ts} +0 -0
  207. /package/dist/types/src/capabilities/{url-handler.d.ts → navigation/url-handler.d.ts} +0 -0
  208. /package/dist/types/src/capabilities/{react-surface.d.ts → settings/react-surface.d.ts} +0 -0
  209. /package/dist/types/src/capabilities/{settings.d.ts → settings/settings.d.ts} +0 -0
@@ -2,28 +2,23 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useMemo, useState, type MouseEvent } from 'react';
5
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
- import {
8
- createIntent,
9
- LayoutAction,
10
- Surface,
11
- useAppGraph,
12
- useCapability,
13
- useIntentDispatcher,
14
- } from '@dxos/app-framework';
15
- import { Main, useTranslation, toLocalizedString, IconButton, ScrollArea } from '@dxos/react-ui';
7
+ import { createIntent, NavigationAction, SLUG_PATH_SEPARATOR, Surface, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { useGraph } from '@dxos/plugin-graph';
9
+ import { Main, ScrollArea, useTranslation, toLocalizedString } from '@dxos/react-ui';
16
10
  import { useAttended } from '@dxos/react-ui-attention';
11
+ import { railGridHorizontal, StackContext, StackItem } from '@dxos/react-ui-stack';
17
12
  import { Tabs } from '@dxos/react-ui-tabs';
13
+ import { mx } from '@dxos/react-ui-theme';
18
14
 
19
15
  import { PlankContentError } from './PlankError';
20
16
  import { PlankLoading } from './PlankLoading';
21
- import { ToggleComplementarySidebarButton } from './SidebarButton';
22
- import { DeckCapabilities } from '../../capabilities';
17
+ import { CloseComplementarySidebarButton } from './SidebarButton';
23
18
  import { useNode, useNodeActionExpander } from '../../hooks';
24
19
  import { DECK_PLUGIN } from '../../meta';
25
- import { SLUG_PATH_SEPARATOR, type Panel } from '../../types';
26
- import { layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
20
+ import { type Panel } from '../../types';
21
+ import { useLayout } from '../LayoutContext';
27
22
 
28
23
  export type ComplementarySidebarProps = {
29
24
  panels: Panel[];
@@ -31,19 +26,16 @@ export type ComplementarySidebarProps = {
31
26
  };
32
27
 
33
28
  export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarProps) => {
34
- const layout = useCapability(DeckCapabilities.MutableDeckState);
29
+ const { popoverAnchorId } = useLayout();
35
30
  const attended = useAttended();
36
- const panelIds = useMemo(() => panels.map((panel) => panel.id), [panels]);
37
- const activePanelId = panelIds.find((panelId) => panelId === current) ?? panels[0].id;
31
+ const panelIds = useMemo(() => panels.map((p) => p.id), [panels]);
32
+ const activePanelId = panelIds.find((p) => p === current) ?? panels[0].id;
38
33
  const activeEntryId = attended[0] ? `${attended[0]}${SLUG_PATH_SEPARATOR}${activePanelId}` : undefined;
39
- const { graph } = useAppGraph();
34
+ const { graph } = useGraph();
40
35
  const node = useNode(graph, activeEntryId);
41
36
  const { t } = useTranslation(DECK_PLUGIN);
42
37
  const { dispatchPromise: dispatch } = useIntentDispatcher();
43
38
  useNodeActionExpander(node);
44
- const breakpoint = useBreakpoints();
45
- const topbar = layoutAppliesTopbar(breakpoint);
46
- const hoistStatusbar = useHoistStatusbar(breakpoint);
47
39
 
48
40
  const [internalValue, setInternalValue] = useState(activePanelId);
49
41
 
@@ -51,113 +43,70 @@ export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarPr
51
43
  setInternalValue(activePanelId);
52
44
  }, [activePanelId]);
53
45
 
54
- const handleTabClick = useCallback(
55
- (event: MouseEvent) => {
56
- const nextValue = event.currentTarget.getAttribute('data-value') as string;
57
- if (nextValue === activePanelId) {
58
- layout.complementarySidebarState = layout.complementarySidebarState === 'expanded' ? 'collapsed' : 'expanded';
59
- } else {
60
- setInternalValue(nextValue);
61
- layout.complementarySidebarState = 'expanded';
62
- void dispatch(createIntent(LayoutAction.UpdateComplementary, { part: 'complementary', subject: nextValue }));
63
- }
46
+ const handleValueChange = useCallback(
47
+ (nextValue: string) => {
48
+ setInternalValue(nextValue);
49
+ void dispatch(createIntent(NavigationAction.Open, { activeParts: { complementary: nextValue } }));
64
50
  },
65
- [layout, activePanelId, dispatch],
51
+ [dispatch],
66
52
  );
67
53
 
68
54
  // TODO(burdon): Scroll area should be controlled by surface.
69
55
  return (
70
- <Main.ComplementarySidebar
71
- classNames={[
72
- topbar && 'block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
73
- hoistStatusbar && 'block-end-[--statusbar-size]',
74
- ]}
75
- >
76
- <Tabs.Root
77
- orientation='vertical'
78
- verticalVariant='stateless'
79
- value={internalValue}
80
- attendableId={attended[0]}
81
- classNames='contents'
82
- >
83
- <div
84
- role='none'
85
- className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] border-is border-separator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
86
- >
87
- <Tabs.Tablist classNames='grid grid-cols-1 auto-rows-[--rail-action] p-1 gap-1 !overflow-y-auto'>
88
- {panels.map((panel) => (
89
- <Tabs.Tab key={panel.id} value={panel.id} asChild>
90
- <IconButton
91
- label={toLocalizedString(panel.label, t)}
92
- icon={panel.icon}
93
- size={5}
94
- iconOnly
95
- tooltipSide='left'
96
- data-value={panel.id}
97
- variant={
98
- activePanelId === panel.id
99
- ? layout.complementarySidebarState === 'expanded'
100
- ? 'primary'
101
- : 'default'
102
- : 'ghost'
103
- }
104
- onClick={handleTabClick}
105
- />
106
- </Tabs.Tab>
107
- ))}
108
- </Tabs.Tablist>
109
- {!hoistStatusbar && (
110
- <div role='none' className='grid grid-cols-1 auto-rows-[--rail-item] p-1 overflow-y-auto'>
111
- <Surface role='status-bar--r0-footer' limit={1} />
112
- </div>
113
- )}
114
- <div role='none' className='hidden lg:grid grid-cols-1 auto-rows-[--rail-action] p-1'>
115
- <ToggleComplementarySidebarButton />
116
- </div>
117
- </div>
118
- {panels.map((panel) => (
119
- <Tabs.Tabpanel
120
- key={panel.id}
121
- value={panel.id}
122
- classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content]'
123
- {...(layout.complementarySidebarState !== 'expanded' && { inert: 'true' })}
56
+ <Main.ComplementarySidebar classNames='lg:block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]'>
57
+ <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
58
+ <div role='none' className={mx(railGridHorizontal, 'grid grid-cols-[100%] bs-full')}>
59
+ <Tabs.Root
60
+ orientation='horizontal'
61
+ value={internalValue}
62
+ onValueChange={handleValueChange}
63
+ attendableId={attended[0]}
64
+ classNames='contents'
124
65
  >
125
- {panel.id === activePanelId && node && (
126
- <>
127
- <h2 className='flex items-center pli-2 border-separator border-be'>
128
- {toLocalizedString(panel.label, t)}
129
- </h2>
130
- <ScrollArea.Root>
131
- <ScrollArea.Viewport>
132
- <Surface
133
- key={activeEntryId}
134
- role={`complementary--${activePanelId}`}
135
- data={{
136
- id: activeEntryId,
137
- subject: node.properties.object ?? node.properties.space,
138
- popoverAnchorId: layout.popoverAnchorId,
139
- }}
140
- fallback={PlankContentError}
141
- placeholder={<PlankLoading />}
142
- />
143
- </ScrollArea.Viewport>
144
- <ScrollArea.Scrollbar orientation='vertical'>
66
+ <StackItem.Heading classNames='border-be border-separator grid grid-cols-[1fr_min-content] items-stretch'>
67
+ <ScrollArea.Root classNames='flex-1 min-is-0'>
68
+ <ScrollArea.Viewport>
69
+ <Tabs.Tablist classNames='bs-[--rail-content] is-min items-stretch pis-[max(.5rem,env(safe-area-inset-left))] sm:pis-2'>
70
+ {panels.map((panel) => (
71
+ <Tabs.Tab key={panel.id} value={panel.id} classNames='!min-bs-0'>
72
+ {toLocalizedString(panel.label, t)}
73
+ </Tabs.Tab>
74
+ ))}
75
+ </Tabs.Tablist>
76
+ <ScrollArea.Scrollbar orientation='horizontal'>
145
77
  <ScrollArea.Thumb />
146
78
  </ScrollArea.Scrollbar>
147
- </ScrollArea.Root>
148
- {!hoistStatusbar && (
149
- <div
150
- role='contentinfo'
151
- className='flex flex-wrap justify-center items-center border-bs border-separator plb-1'
152
- >
153
- <Surface role='status-bar--r1-footer' limit={1} />
154
- </div>
155
- )}
156
- </>
157
- )}
158
- </Tabs.Tabpanel>
159
- ))}
160
- </Tabs.Root>
79
+ </ScrollArea.Viewport>
80
+ </ScrollArea.Root>
81
+ <CloseComplementarySidebarButton />
82
+ </StackItem.Heading>
83
+ <ScrollArea.Root>
84
+ <ScrollArea.Viewport>
85
+ {panels.map((panel) => (
86
+ <Tabs.Tabpanel key={panel.id} value={panel.id} classNames='pbe-[env(safe-area-inset-bottom)]'>
87
+ {panel.id === activePanelId && node && (
88
+ <Surface
89
+ key={activeEntryId}
90
+ role={`complementary--${activePanelId}`}
91
+ data={{
92
+ id: activeEntryId,
93
+ subject: node.properties.object ?? node.properties.space,
94
+ popoverAnchorId,
95
+ }}
96
+ fallback={PlankContentError}
97
+ placeholder={<PlankLoading />}
98
+ />
99
+ )}
100
+ </Tabs.Tabpanel>
101
+ ))}
102
+ <ScrollArea.Scrollbar orientation='vertical'>
103
+ <ScrollArea.Thumb />
104
+ </ScrollArea.Scrollbar>
105
+ </ScrollArea.Viewport>
106
+ </ScrollArea.Root>
107
+ </Tabs.Root>
108
+ </div>
109
+ </StackContext.Provider>
161
110
  </Main.ComplementarySidebar>
162
111
  );
163
112
  };
@@ -5,22 +5,25 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
+ import { mx } from '@dxos/react-ui-theme';
8
9
 
9
10
  import { ToggleSidebarButton } from './SidebarButton';
10
- import { layoutAppliesTopbar, useBreakpoints } from '../../util';
11
- import { fixedSidebarToggleStyles } from '../fragments';
11
+ import { soloInlinePadding } from '../fragments';
12
12
 
13
13
  export const ContentEmpty = () => {
14
- const breakpoint = useBreakpoints();
15
- const topbar = layoutAppliesTopbar(breakpoint);
16
14
  return (
17
15
  <div
18
16
  role='none'
19
- className='grid place-items-center p-8 relative bg-deck'
17
+ className='min-bs-screen is-dvw sm:is-full p-8 grid grid-rows-[var(--rail-size)_1fr]'
20
18
  data-testid='layoutPlugin.firstRunMessage'
21
19
  >
22
- <Surface role='keyshortcuts' />
23
- {!topbar && <ToggleSidebarButton variant='default' classNames={fixedSidebarToggleStyles} />}
20
+ <div role='toolbar' className={mx(soloInlinePadding, 'flex items-stretch')}>
21
+ <ToggleSidebarButton />
22
+ <span role='none' className='grow' />
23
+ </div>
24
+ <div role='none' className='grid place-items-center'>
25
+ <Surface role='keyshortcuts' />
26
+ </div>
24
27
  </div>
25
28
  );
26
29
  };
@@ -6,23 +6,14 @@ import { untracked } from '@preact/signals-core';
6
6
  import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment } from 'react';
7
7
 
8
8
  import {
9
- LayoutAction,
10
- createIntent,
9
+ firstIdInPart,
11
10
  Surface,
12
- useCapability,
13
- useIntentDispatcher,
14
11
  usePluginManager,
12
+ type LayoutParts,
13
+ type Toast as ToastSchema,
15
14
  } from '@dxos/app-framework';
16
15
  import { AttentionCapabilities } from '@dxos/plugin-attention';
17
- import {
18
- AlertDialog,
19
- Dialog as NaturalDialog,
20
- Main,
21
- Popover,
22
- useOnTransition,
23
- type MainProps,
24
- useMediaQuery,
25
- } from '@dxos/react-ui';
16
+ import { AlertDialog, Dialog as NaturalDialog, Main, Popover, useOnTransition, type MainProps } from '@dxos/react-ui';
26
17
  import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
27
18
  import { mainPaddingTransitions } from '@dxos/react-ui-theme';
28
19
 
@@ -32,16 +23,18 @@ import { ContentEmpty } from './ContentEmpty';
32
23
  import { Fullscreen } from './Fullscreen';
33
24
  import { Plank } from './Plank';
34
25
  import { Sidebar } from './Sidebar';
35
- import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
36
26
  import { StatusBar } from './StatusBar';
37
27
  import { Toast } from './Toast';
38
28
  import { Topbar } from './Topbar';
39
- import { DeckCapabilities } from '../../capabilities';
40
- import { getMode, type Overscroll } from '../../types';
41
- import { calculateOverscroll, layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
42
- import { fixedComplementarySidebarToggleStyles, fixedSidebarToggleStyles } from '../fragments';
29
+ import { type Overscroll } from '../../types';
30
+ import { calculateOverscroll, useBreakpoints } from '../../util';
31
+ import { useHoistStatusbar } from '../../util/useHoistStatusbar';
32
+ import { useDeckContext } from '../DeckContext';
33
+ import { useLayout } from '../LayoutContext';
43
34
 
44
35
  export type DeckLayoutProps = {
36
+ layoutParts: LayoutParts;
37
+ toasts: ToastSchema[];
45
38
  overscroll: Overscroll;
46
39
  showHints: boolean;
47
40
  onDismissToast: (id: string) => void;
@@ -50,13 +43,12 @@ export type DeckLayoutProps = {
50
43
  const PlankSeparator = ({ index }: { index: number }) =>
51
44
  index > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: index * 2 }} /> : null;
52
45
 
53
- export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: DeckLayoutProps) => {
54
- const { dispatchPromise: dispatch } = useIntentDispatcher();
55
- const context = useCapability(DeckCapabilities.MutableDeckState);
46
+ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels, onDismissToast }: DeckLayoutProps) => {
47
+ const context = useLayout();
56
48
  const {
57
- sidebarState,
58
- complementarySidebarState,
59
- complementarySidebarPanel,
49
+ layoutMode,
50
+ sidebarOpen,
51
+ complementarySidebarOpen,
60
52
  dialogOpen,
61
53
  dialogContent,
62
54
  dialogBlockAlign,
@@ -64,18 +56,18 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
64
56
  popoverOpen,
65
57
  popoverContent,
66
58
  popoverAnchorId,
67
- deck,
68
- toasts,
69
59
  } = context;
70
- const { active, fullscreen, solo, plankSizing } = deck;
71
60
  const breakpoint = useBreakpoints();
72
- const topbar = layoutAppliesTopbar(breakpoint);
73
61
  const hoistStatusbar = useHoistStatusbar(breakpoint);
62
+ const { plankSizing } = useDeckContext();
74
63
  const pluginManager = usePluginManager();
64
+ const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
75
65
 
76
66
  const scrollLeftRef = useRef<number | null>();
77
67
  const deckRef = useRef<HTMLDivElement>(null);
78
68
 
69
+ const isSoloModeLoaded = layoutMode === 'solo' && Boolean(layoutParts.solo?.[0]);
70
+
79
71
  // Ensure the first plank is attended when the deck is first rendered.
80
72
  useEffect(() => {
81
73
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
@@ -83,7 +75,7 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
83
75
  const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
84
76
  return attention.current;
85
77
  });
86
- const firstId = solo ?? active[0];
78
+ const firstId = isSoloModeLoaded ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
87
79
  if (attended.length === 0 && firstId) {
88
80
  // TODO(wittjosiah): Focusing the type button is a workaround.
89
81
  // If the plank is directly focused on first load the focus ring appears.
@@ -91,27 +83,6 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
91
83
  }
92
84
  }, []);
93
85
 
94
- // Not using `breakpoint` to avoid firing when breakpoint changes between tablet and desktop.
95
- // `ssr: false` to avoid using fallback values and flashing into solo mode on startup.
96
- const [isNotMobile] = useMediaQuery('md', { ssr: false });
97
- const shouldRevert = useRef(false);
98
- useEffect(() => {
99
- if (!isNotMobile && getMode(deck) === 'deck') {
100
- // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
101
- const attended = untracked(() => {
102
- const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
103
- return attention.current;
104
- });
105
-
106
- shouldRevert.current = true;
107
- void dispatch(
108
- createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: attended[0], options: { mode: 'solo' } }),
109
- );
110
- } else if (isNotMobile && getMode(deck) === 'solo' && shouldRevert.current) {
111
- void dispatch(createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { revert: true } }));
112
- }
113
- }, [isNotMobile, deck, dispatch]);
114
-
115
86
  /**
116
87
  * Clear scroll restoration state if the window is resized
117
88
  */
@@ -130,7 +101,6 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
130
101
  }
131
102
  }, []);
132
103
 
133
- const layoutMode = getMode(deck);
134
104
  useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
135
105
 
136
106
  /**
@@ -138,30 +108,21 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
138
108
  */
139
109
  const handleScroll = useCallback(
140
110
  (event: UIEvent) => {
141
- if (!solo && event.currentTarget === event.target) {
111
+ if (layoutMode === 'deck' && event.currentTarget === event.target) {
142
112
  scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
143
113
  }
144
114
  },
145
- [solo],
115
+ [layoutMode],
146
116
  );
147
117
 
148
- const isEmpty = !solo && active.length === 0;
118
+ const isEmpty = (layoutParts.main?.length ?? 0) === 0 && (layoutParts.solo?.length ?? 0) === 0;
149
119
 
150
120
  const padding = useMemo(() => {
151
- if (!solo && overscroll === 'centering') {
152
- return calculateOverscroll(active.length);
121
+ if (layoutMode === 'deck' && overscroll === 'centering') {
122
+ return calculateOverscroll(layoutParts.main?.length ?? 0);
153
123
  }
154
124
  return {};
155
- }, [solo, overscroll, deck]);
156
-
157
- const mainPosition = useMemo(
158
- () => [
159
- 'grid !block-start-[env(safe-area-inset-top)]',
160
- topbar && '!block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
161
- hoistStatusbar && 'lg:block-end-[--statusbar-size]',
162
- ],
163
- [topbar, hoistStatusbar],
164
- );
125
+ }, [layoutMode, overscroll, layoutParts.main]);
165
126
 
166
127
  const Dialog = dialogType === 'alert' ? AlertDialog : NaturalDialog;
167
128
 
@@ -180,27 +141,27 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
180
141
  >
181
142
  <ActiveNode />
182
143
 
183
- {fullscreen && <Fullscreen id={solo} />}
144
+ {layoutMode === 'fullscreen' && <Fullscreen id={fullScreenSlug} />}
184
145
 
185
- {!fullscreen && (
146
+ {layoutMode !== 'fullscreen' && (
186
147
  <Main.Root
187
- navigationSidebarState={context.sidebarState}
188
- onNavigationSidebarStateChange={(next) => (context.sidebarState = next)}
189
- complementarySidebarState={context.complementarySidebarState}
190
- onComplementarySidebarStateChange={(next) => (context.complementarySidebarState = next)}
148
+ navigationSidebarOpen={context.sidebarOpen}
149
+ onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
150
+ complementarySidebarOpen={context.complementarySidebarOpen}
151
+ onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
191
152
  >
192
153
  {/* Left sidebar. */}
193
154
  <Sidebar />
194
155
 
195
156
  {/* Right sidebar. */}
196
- <ComplementarySidebar panels={panels} current={complementarySidebarPanel} />
157
+ <ComplementarySidebar panels={panels} current={layoutParts.complementary?.[0].id} />
197
158
 
198
159
  {/* Dialog overlay to dismiss dialogs. */}
199
160
  <Main.Overlay />
200
161
 
201
162
  {/* No content. */}
202
163
  {isEmpty && (
203
- <Main.Content bounce handlesFocus classNames={mainPosition}>
164
+ <Main.Content handlesFocus>
204
165
  <ContentEmpty />
205
166
  </Main.Content>
206
167
  )}
@@ -209,67 +170,64 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
209
170
  {!isEmpty && (
210
171
  <Main.Content
211
172
  bounce
212
- classNames={mainPosition}
173
+ classNames={[
174
+ 'grid !block-start-[env(safe-area-inset-top)] lg:!block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
175
+ hoistStatusbar && 'lg:block-end-[--statusbar-size]',
176
+ ]}
213
177
  handlesFocus
214
178
  style={
215
179
  {
216
- '--dx-main-sidebarWidth':
217
- sidebarState === 'expanded'
218
- ? 'var(--nav-sidebar-size)'
219
- : sidebarState === 'collapsed'
220
- ? 'var(--l0-size)'
221
- : '0',
222
- '--dx-main-complementaryWidth':
223
- complementarySidebarState === 'expanded'
224
- ? 'var(--complementary-sidebar-size)'
225
- : complementarySidebarState === 'collapsed'
226
- ? 'var(--rail-size)'
227
- : '0',
228
- '--dx-main-contentFirstWidth': `${plankSizing[active[0] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
229
- '--dx-main-contentLastWidth': `${plankSizing[active[(active.length ?? 1) - 1] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
180
+ '--dx-main-sidebarWidth': sidebarOpen ? 'var(--nav-sidebar-size)' : '0px',
181
+ '--dx-main-complementaryWidth': complementarySidebarOpen
182
+ ? 'var(--complementary-sidebar-size)'
183
+ : '0px',
184
+ '--dx-main-contentFirstWidth': `${plankSizing[layoutParts.main?.[0]?.id ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
185
+ '--dx-main-contentLastWidth': `${plankSizing[layoutParts.main?.[(layoutParts.main?.length ?? 1) - 1]?.id ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
230
186
  } as MainProps['style']
231
187
  }
232
188
  >
233
189
  <div
234
190
  role='none'
235
- className={!solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
236
- {...(solo && { inert: '' })}
191
+ className={!isSoloModeLoaded ? 'relative bg-deck overflow-hidden' : 'sr-only'}
192
+ {...(isSoloModeLoaded && { inert: '' })}
237
193
  >
238
- {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
239
- {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
240
194
  <Stack
241
195
  orientation='horizontal'
242
196
  size='contain'
243
197
  classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
244
198
  onScroll={handleScroll}
245
- itemsCount={2 * (active.length ?? 0) - 1}
199
+ itemsCount={2 * (layoutParts.main?.length ?? 0) - 1}
246
200
  style={padding}
247
201
  ref={deckRef}
248
202
  >
249
- {active.map((entryId, index) => (
250
- <Fragment key={entryId}>
203
+ {layoutParts.main?.map((layoutEntry, index) => (
204
+ <Fragment key={layoutEntry.id}>
251
205
  <PlankSeparator index={index} />
252
- <Plank id={entryId} part='deck' order={index * 2 + 1} active={active} layoutMode={layoutMode} />
206
+ <Plank
207
+ entry={layoutEntry}
208
+ layoutParts={layoutParts}
209
+ part='main'
210
+ layoutMode={layoutMode}
211
+ order={index * 2 + 1}
212
+ />
253
213
  </Fragment>
254
214
  ))}
255
215
  </Stack>
256
216
  </div>
257
217
  <div
258
218
  role='none'
259
- className={solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
260
- {...(!solo && { inert: '' })}
219
+ className={isSoloModeLoaded ? 'relative bg-deck overflow-hidden' : 'sr-only'}
220
+ {...(!isSoloModeLoaded && { inert: '' })}
261
221
  >
262
- {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
263
- {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
264
222
  <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
265
- <Plank id={solo} part='solo' layoutMode={layoutMode} />
223
+ <Plank entry={layoutParts.solo?.[0]} layoutParts={layoutParts} part='solo' layoutMode={layoutMode} />
266
224
  </StackContext.Provider>
267
225
  </div>
268
226
  </Main.Content>
269
227
  )}
270
228
 
271
229
  {/* Status bar. */}
272
- {topbar && <Topbar />}
230
+ {breakpoint === 'desktop' && <Topbar />}
273
231
  {hoistStatusbar && <StatusBar showHints={showHints} />}
274
232
  </Main.Root>
275
233
  )}
@@ -4,7 +4,8 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { Surface, useAppGraph } from '@dxos/app-framework';
7
+ import { Surface } from '@dxos/app-framework';
8
+ import { useGraph } from '@dxos/plugin-graph';
8
9
  import { fixedInsetFlexLayout } from '@dxos/react-ui-theme';
9
10
 
10
11
  import { Fallback } from './Fallback';
@@ -12,7 +13,7 @@ import { SURFACE_PREFIX } from './constants';
12
13
  import { useNode } from '../../hooks';
13
14
 
14
15
  export const Fullscreen = ({ id }: { id?: string }) => {
15
- const { graph } = useAppGraph();
16
+ const { graph } = useGraph();
16
17
  const fullScreenNode = useNode(graph, id);
17
18
 
18
19
  return (