@dxos/plugin-deck 0.7.5-labs.a279d8c → 0.7.5-labs.d453967

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 (214) hide show
  1. package/dist/lib/browser/{app-graph-builder-67VRUD5K.mjs → app-graph-builder-CI6ZFMNL.mjs} +57 -31
  2. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-GEX6W2R5.mjs → check-app-scheme-S3EYUPMF.mjs} +3 -3
  4. package/dist/lib/browser/{check-app-scheme-GEX6W2R5.mjs.map → check-app-scheme-S3EYUPMF.mjs.map} +2 -2
  5. package/dist/lib/browser/chunk-23KS5L3I.mjs +1124 -0
  6. package/dist/lib/browser/chunk-23KS5L3I.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-4URQJVGI.mjs +24 -0
  8. package/dist/lib/browser/chunk-4URQJVGI.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-JQJ5UWVB.mjs → chunk-N7TEPFVR.mjs} +3 -2
  10. package/dist/lib/browser/{chunk-JQJ5UWVB.mjs.map → chunk-N7TEPFVR.mjs.map} +3 -3
  11. package/dist/lib/browser/chunk-PTLNGUND.mjs +126 -0
  12. package/dist/lib/browser/chunk-PTLNGUND.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-5VFDMW5M.mjs → chunk-YQ2GWTDU.mjs} +2 -2
  14. package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +36 -82
  16. package/dist/lib/browser/index.mjs.map +4 -4
  17. package/dist/lib/browser/intent-resolver-WAYXVAFN.mjs +497 -0
  18. package/dist/lib/browser/intent-resolver-WAYXVAFN.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-root-AWYSGU4Q.mjs → react-root-YATKEIAZ.mjs} +10 -14
  21. package/dist/lib/browser/react-root-YATKEIAZ.mjs.map +7 -0
  22. package/dist/lib/browser/react-surface-SS2BX6FS.mjs +38 -0
  23. package/dist/lib/browser/react-surface-SS2BX6FS.mjs.map +7 -0
  24. package/dist/lib/browser/{settings-FNWW6WIJ.mjs → settings-CRQTVMN3.mjs} +6 -7
  25. package/dist/lib/browser/settings-CRQTVMN3.mjs.map +7 -0
  26. package/dist/lib/browser/state-YEQA3IIB.mjs +117 -0
  27. package/dist/lib/browser/state-YEQA3IIB.mjs.map +7 -0
  28. package/dist/lib/browser/{tools-4XY7KFQF.mjs → tools-5LDJNU56.mjs} +14 -9
  29. package/dist/lib/browser/tools-5LDJNU56.mjs.map +7 -0
  30. package/dist/lib/browser/types.mjs +16 -4
  31. package/dist/lib/browser/url-handler-HLF42IHP.mjs +70 -0
  32. package/dist/lib/browser/url-handler-HLF42IHP.mjs.map +7 -0
  33. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/{layout/app-graph-builder.d.ts → app-graph-builder.d.ts} +22 -22
  35. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/capabilities.d.ts +132 -3
  37. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -0
  39. package/dist/types/src/capabilities/index.d.ts +188 -3
  40. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  43. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/set-active.d.ts +9 -0
  45. package/dist/types/src/capabilities/set-active.d.ts.map +1 -0
  46. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/state.d.ts +76 -0
  48. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/{navigation/tools.d.ts → tools.d.ts} +1 -0
  50. package/dist/types/src/capabilities/tools.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
  52. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  54. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  55. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
  56. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  57. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  58. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  59. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  60. package/dist/types/src/components/DeckLayout/Plank.d.ts +8 -6
  61. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  62. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  63. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  64. package/dist/types/src/components/DeckLayout/PlankError.d.ts +4 -3
  65. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  66. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  67. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +5 -3
  68. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
  69. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  70. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
  71. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  72. package/dist/types/src/components/fragments.d.ts +2 -0
  73. package/dist/types/src/components/fragments.d.ts.map +1 -1
  74. package/dist/types/src/components/index.d.ts +0 -2
  75. package/dist/types/src/components/index.d.ts.map +1 -1
  76. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  77. package/dist/types/src/hooks/useNode.d.ts.map +1 -1
  78. package/dist/types/src/layout.d.ts +5 -19
  79. package/dist/types/src/layout.d.ts.map +1 -1
  80. package/dist/types/src/meta.d.ts +1 -0
  81. package/dist/types/src/meta.d.ts.map +1 -1
  82. package/dist/types/src/translations.d.ts +3 -3
  83. package/dist/types/src/types.d.ts +107 -2
  84. package/dist/types/src/types.d.ts.map +1 -1
  85. package/dist/types/src/util/index.d.ts +2 -1
  86. package/dist/types/src/util/index.d.ts.map +1 -1
  87. package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -0
  88. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -0
  89. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +30 -31
  92. package/src/DeckPlugin.ts +17 -63
  93. package/src/capabilities/{layout/app-graph-builder.ts → app-graph-builder.ts} +36 -28
  94. package/src/capabilities/capabilities.ts +4 -3
  95. package/src/capabilities/{navigation/check-app-scheme.ts → check-app-scheme.ts} +2 -2
  96. package/src/capabilities/index.ts +12 -3
  97. package/src/capabilities/intent-resolver.ts +350 -0
  98. package/src/capabilities/{layout/react-root.tsx → react-root.tsx} +7 -11
  99. package/src/capabilities/react-surface.tsx +31 -0
  100. package/src/capabilities/set-active.ts +47 -0
  101. package/src/capabilities/{settings/settings.ts → settings.ts} +4 -5
  102. package/src/capabilities/state.ts +102 -0
  103. package/src/capabilities/{navigation/tools.ts → tools.ts} +10 -6
  104. package/src/capabilities/url-handler.ts +63 -0
  105. package/src/components/DeckLayout/ActiveNode.tsx +2 -3
  106. package/src/components/DeckLayout/ComplementarySidebar.tsx +120 -69
  107. package/src/components/DeckLayout/ContentEmpty.tsx +7 -10
  108. package/src/components/DeckLayout/DeckLayout.tsx +116 -64
  109. package/src/components/DeckLayout/Fullscreen.tsx +2 -3
  110. package/src/components/DeckLayout/NodePlankHeading.tsx +57 -65
  111. package/src/components/DeckLayout/Plank.tsx +32 -41
  112. package/src/components/DeckLayout/PlankControls.tsx +11 -10
  113. package/src/components/DeckLayout/PlankError.tsx +6 -5
  114. package/src/components/DeckLayout/Sidebar.tsx +17 -20
  115. package/src/components/DeckLayout/SidebarButton.tsx +25 -31
  116. package/src/components/DeckLayout/StatusBar.tsx +5 -11
  117. package/src/components/DeckLayout/Toast.tsx +2 -2
  118. package/src/components/LayoutSettings.tsx +8 -8
  119. package/src/components/fragments.ts +8 -0
  120. package/src/components/index.ts +0 -2
  121. package/src/hooks/useMainSize.ts +3 -3
  122. package/src/hooks/useNode.ts +3 -1
  123. package/src/layout.ts +43 -212
  124. package/src/meta.ts +1 -0
  125. package/src/translations.ts +8 -8
  126. package/src/types.ts +88 -2
  127. package/src/util/index.ts +2 -1
  128. package/src/util/layoutAppliesTopbar.ts +7 -0
  129. package/src/util/useHoistStatusbar.ts +17 -8
  130. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs.map +0 -7
  131. package/dist/lib/browser/chunk-2PJNBVCY.mjs +0 -39
  132. package/dist/lib/browser/chunk-2PJNBVCY.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-4C2AFTET.mjs +0 -186
  134. package/dist/lib/browser/chunk-4C2AFTET.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-5VFDMW5M.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-KY5WXIXY.mjs +0 -44
  137. package/dist/lib/browser/chunk-KY5WXIXY.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-WUMAJGVA.mjs +0 -1052
  139. package/dist/lib/browser/chunk-WUMAJGVA.mjs.map +0 -7
  140. package/dist/lib/browser/deck-PLCSKPGL.mjs +0 -26
  141. package/dist/lib/browser/deck-PLCSKPGL.mjs.map +0 -7
  142. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs +0 -152
  143. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs.map +0 -7
  144. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs +0 -249
  145. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs.map +0 -7
  146. package/dist/lib/browser/location-AIO6V3MK.mjs +0 -35
  147. package/dist/lib/browser/location-AIO6V3MK.mjs.map +0 -7
  148. package/dist/lib/browser/react-context-G6PDXUI5.mjs +0 -32
  149. package/dist/lib/browser/react-context-G6PDXUI5.mjs.map +0 -7
  150. package/dist/lib/browser/react-root-AWYSGU4Q.mjs.map +0 -7
  151. package/dist/lib/browser/react-surface-CLUABFNX.mjs +0 -28
  152. package/dist/lib/browser/react-surface-CLUABFNX.mjs.map +0 -7
  153. package/dist/lib/browser/settings-FNWW6WIJ.mjs.map +0 -7
  154. package/dist/lib/browser/state-7I5BD7SE.mjs +0 -34
  155. package/dist/lib/browser/state-7I5BD7SE.mjs.map +0 -7
  156. package/dist/lib/browser/tools-4XY7KFQF.mjs.map +0 -7
  157. package/dist/lib/browser/url-handler-JRAQRY73.mjs +0 -76
  158. package/dist/lib/browser/url-handler-JRAQRY73.mjs.map +0 -7
  159. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts.map +0 -1
  160. package/dist/types/src/capabilities/layout/deck.d.ts +0 -4
  161. package/dist/types/src/capabilities/layout/deck.d.ts.map +0 -1
  162. package/dist/types/src/capabilities/layout/index.d.ts +0 -229
  163. package/dist/types/src/capabilities/layout/index.d.ts.map +0 -1
  164. package/dist/types/src/capabilities/layout/intent-resolver.d.ts.map +0 -1
  165. package/dist/types/src/capabilities/layout/react-context.d.ts +0 -8
  166. package/dist/types/src/capabilities/layout/react-context.d.ts.map +0 -1
  167. package/dist/types/src/capabilities/layout/react-root.d.ts.map +0 -1
  168. package/dist/types/src/capabilities/layout/state.d.ts +0 -42
  169. package/dist/types/src/capabilities/layout/state.d.ts.map +0 -1
  170. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts.map +0 -1
  171. package/dist/types/src/capabilities/navigation/index.d.ts +0 -6
  172. package/dist/types/src/capabilities/navigation/index.d.ts.map +0 -1
  173. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts +0 -4
  174. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts.map +0 -1
  175. package/dist/types/src/capabilities/navigation/location.d.ts +0 -4
  176. package/dist/types/src/capabilities/navigation/location.d.ts.map +0 -1
  177. package/dist/types/src/capabilities/navigation/set-location.d.ts +0 -10
  178. package/dist/types/src/capabilities/navigation/set-location.d.ts.map +0 -1
  179. package/dist/types/src/capabilities/navigation/tools.d.ts.map +0 -1
  180. package/dist/types/src/capabilities/navigation/url-handler.d.ts.map +0 -1
  181. package/dist/types/src/capabilities/settings/index.d.ts +0 -3
  182. package/dist/types/src/capabilities/settings/index.d.ts.map +0 -1
  183. package/dist/types/src/capabilities/settings/react-surface.d.ts.map +0 -1
  184. package/dist/types/src/capabilities/settings/settings.d.ts.map +0 -1
  185. package/dist/types/src/components/DeckContext.d.ts +0 -11
  186. package/dist/types/src/components/DeckContext.d.ts.map +0 -1
  187. package/dist/types/src/components/LayoutContext.d.ts +0 -5
  188. package/dist/types/src/components/LayoutContext.d.ts.map +0 -1
  189. package/dist/types/src/layout.test.d.ts +0 -2
  190. package/dist/types/src/layout.test.d.ts.map +0 -1
  191. package/dist/types/src/util/layout-parts.d.ts +0 -7
  192. package/dist/types/src/util/layout-parts.d.ts.map +0 -1
  193. package/src/capabilities/layout/deck.ts +0 -25
  194. package/src/capabilities/layout/index.ts +0 -12
  195. package/src/capabilities/layout/intent-resolver.ts +0 -128
  196. package/src/capabilities/layout/react-context.tsx +0 -26
  197. package/src/capabilities/layout/state.ts +0 -32
  198. package/src/capabilities/navigation/index.ts +0 -11
  199. package/src/capabilities/navigation/intent-resolver.ts +0 -216
  200. package/src/capabilities/navigation/location.ts +0 -28
  201. package/src/capabilities/navigation/set-location.ts +0 -38
  202. package/src/capabilities/navigation/url-handler.ts +0 -67
  203. package/src/capabilities/settings/index.ts +0 -8
  204. package/src/capabilities/settings/react-surface.tsx +0 -23
  205. package/src/components/DeckContext.ts +0 -19
  206. package/src/components/LayoutContext.ts +0 -12
  207. package/src/layout.test.ts +0 -380
  208. package/src/util/layout-parts.ts +0 -12
  209. /package/dist/types/src/capabilities/{navigation/check-app-scheme.d.ts → check-app-scheme.d.ts} +0 -0
  210. /package/dist/types/src/capabilities/{layout/intent-resolver.d.ts → intent-resolver.d.ts} +0 -0
  211. /package/dist/types/src/capabilities/{layout/react-root.d.ts → react-root.d.ts} +0 -0
  212. /package/dist/types/src/capabilities/{settings/react-surface.d.ts → react-surface.d.ts} +0 -0
  213. /package/dist/types/src/capabilities/{settings/settings.d.ts → settings.d.ts} +0 -0
  214. /package/dist/types/src/capabilities/{navigation/url-handler.d.ts → url-handler.d.ts} +0 -0
@@ -6,14 +6,23 @@ 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
- firstIdInPart,
9
+ LayoutAction,
10
+ createIntent,
10
11
  Surface,
12
+ useCapability,
13
+ useIntentDispatcher,
11
14
  usePluginManager,
12
- type LayoutParts,
13
- type Toast as ToastSchema,
14
15
  } from '@dxos/app-framework';
15
16
  import { AttentionCapabilities } from '@dxos/plugin-attention';
16
- import { AlertDialog, Dialog as NaturalDialog, Main, Popover, useOnTransition, type MainProps } from '@dxos/react-ui';
17
+ import {
18
+ AlertDialog,
19
+ Dialog as NaturalDialog,
20
+ Main,
21
+ Popover,
22
+ useOnTransition,
23
+ type MainProps,
24
+ useMediaQuery,
25
+ } from '@dxos/react-ui';
17
26
  import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
18
27
  import { mainPaddingTransitions } from '@dxos/react-ui-theme';
19
28
 
@@ -23,18 +32,16 @@ import { ContentEmpty } from './ContentEmpty';
23
32
  import { Fullscreen } from './Fullscreen';
24
33
  import { Plank } from './Plank';
25
34
  import { Sidebar } from './Sidebar';
35
+ import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
26
36
  import { StatusBar } from './StatusBar';
27
37
  import { Toast } from './Toast';
28
38
  import { Topbar } from './Topbar';
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';
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';
34
43
 
35
44
  export type DeckLayoutProps = {
36
- layoutParts: LayoutParts;
37
- toasts: ToastSchema[];
38
45
  overscroll: Overscroll;
39
46
  showHints: boolean;
40
47
  onDismissToast: (id: string) => void;
@@ -43,12 +50,13 @@ export type DeckLayoutProps = {
43
50
  const PlankSeparator = ({ index }: { index: number }) =>
44
51
  index > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: index * 2 }} /> : null;
45
52
 
46
- export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels, onDismissToast }: DeckLayoutProps) => {
47
- const context = useLayout();
53
+ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: DeckLayoutProps) => {
54
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
55
+ const context = useCapability(DeckCapabilities.MutableDeckState);
48
56
  const {
49
- layoutMode,
50
- sidebarOpen,
51
- complementarySidebarOpen,
57
+ sidebarState,
58
+ complementarySidebarState,
59
+ complementarySidebarPanel,
52
60
  dialogOpen,
53
61
  dialogContent,
54
62
  dialogBlockAlign,
@@ -56,18 +64,18 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
56
64
  popoverOpen,
57
65
  popoverContent,
58
66
  popoverAnchorId,
67
+ deck,
68
+ toasts,
59
69
  } = context;
70
+ const { active, fullscreen, solo, plankSizing } = deck;
60
71
  const breakpoint = useBreakpoints();
72
+ const topbar = layoutAppliesTopbar(breakpoint);
61
73
  const hoistStatusbar = useHoistStatusbar(breakpoint);
62
- const { plankSizing } = useDeckContext();
63
74
  const pluginManager = usePluginManager();
64
- const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
65
75
 
66
76
  const scrollLeftRef = useRef<number | null>();
67
77
  const deckRef = useRef<HTMLDivElement>(null);
68
78
 
69
- const isSoloModeLoaded = layoutMode === 'solo' && Boolean(layoutParts.solo?.[0]);
70
-
71
79
  // Ensure the first plank is attended when the deck is first rendered.
72
80
  useEffect(() => {
73
81
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
@@ -75,7 +83,7 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
75
83
  const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
76
84
  return attention.current;
77
85
  });
78
- const firstId = isSoloModeLoaded ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
86
+ const firstId = solo ?? active[0];
79
87
  if (attended.length === 0 && firstId) {
80
88
  // TODO(wittjosiah): Focusing the type button is a workaround.
81
89
  // If the plank is directly focused on first load the focus ring appears.
@@ -83,6 +91,27 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
83
91
  }
84
92
  }, []);
85
93
 
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
+
86
115
  /**
87
116
  * Clear scroll restoration state if the window is resized
88
117
  */
@@ -101,6 +130,7 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
101
130
  }
102
131
  }, []);
103
132
 
133
+ const layoutMode = getMode(deck);
104
134
  useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
105
135
 
106
136
  /**
@@ -108,21 +138,30 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
108
138
  */
109
139
  const handleScroll = useCallback(
110
140
  (event: UIEvent) => {
111
- if (layoutMode === 'deck' && event.currentTarget === event.target) {
141
+ if (!solo && event.currentTarget === event.target) {
112
142
  scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
113
143
  }
114
144
  },
115
- [layoutMode],
145
+ [solo],
116
146
  );
117
147
 
118
- const isEmpty = (layoutParts.main?.length ?? 0) === 0 && (layoutParts.solo?.length ?? 0) === 0;
148
+ const isEmpty = !solo && active.length === 0;
119
149
 
120
150
  const padding = useMemo(() => {
121
- if (layoutMode === 'deck' && overscroll === 'centering') {
122
- return calculateOverscroll(layoutParts.main?.length ?? 0);
151
+ if (!solo && overscroll === 'centering') {
152
+ return calculateOverscroll(active.length);
123
153
  }
124
154
  return {};
125
- }, [layoutMode, overscroll, layoutParts.main]);
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
+ );
126
165
 
127
166
  const Dialog = dialogType === 'alert' ? AlertDialog : NaturalDialog;
128
167
 
@@ -141,27 +180,27 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
141
180
  >
142
181
  <ActiveNode />
143
182
 
144
- {layoutMode === 'fullscreen' && <Fullscreen id={fullScreenSlug} />}
183
+ {fullscreen && <Fullscreen id={solo} />}
145
184
 
146
- {layoutMode !== 'fullscreen' && (
185
+ {!fullscreen && (
147
186
  <Main.Root
148
- navigationSidebarOpen={context.sidebarOpen}
149
- onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
150
- complementarySidebarOpen={context.complementarySidebarOpen}
151
- onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
187
+ navigationSidebarState={context.sidebarState}
188
+ onNavigationSidebarStateChange={(next) => (context.sidebarState = next)}
189
+ complementarySidebarState={context.complementarySidebarState}
190
+ onComplementarySidebarStateChange={(next) => (context.complementarySidebarState = next)}
152
191
  >
153
192
  {/* Left sidebar. */}
154
193
  <Sidebar />
155
194
 
156
195
  {/* Right sidebar. */}
157
- <ComplementarySidebar panels={panels} current={layoutParts.complementary?.[0].id} />
196
+ <ComplementarySidebar panels={panels} current={complementarySidebarPanel} />
158
197
 
159
198
  {/* Dialog overlay to dismiss dialogs. */}
160
199
  <Main.Overlay />
161
200
 
162
201
  {/* No content. */}
163
202
  {isEmpty && (
164
- <Main.Content handlesFocus>
203
+ <Main.Content bounce handlesFocus classNames={mainPosition}>
165
204
  <ContentEmpty />
166
205
  </Main.Content>
167
206
  )}
@@ -170,64 +209,67 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
170
209
  {!isEmpty && (
171
210
  <Main.Content
172
211
  bounce
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
- ]}
212
+ classNames={mainPosition}
177
213
  handlesFocus
178
214
  style={
179
215
  {
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`,
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`,
186
230
  } as MainProps['style']
187
231
  }
188
232
  >
189
233
  <div
190
234
  role='none'
191
- className={!isSoloModeLoaded ? 'relative bg-deck overflow-hidden' : 'sr-only'}
192
- {...(isSoloModeLoaded && { inert: '' })}
235
+ className={!solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
236
+ {...(solo && { inert: '' })}
193
237
  >
238
+ {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
239
+ {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
194
240
  <Stack
195
241
  orientation='horizontal'
196
242
  size='contain'
197
243
  classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
198
244
  onScroll={handleScroll}
199
- itemsCount={2 * (layoutParts.main?.length ?? 0) - 1}
245
+ itemsCount={2 * (active.length ?? 0) - 1}
200
246
  style={padding}
201
247
  ref={deckRef}
202
248
  >
203
- {layoutParts.main?.map((layoutEntry, index) => (
204
- <Fragment key={layoutEntry.id}>
249
+ {active.map((entryId, index) => (
250
+ <Fragment key={entryId}>
205
251
  <PlankSeparator index={index} />
206
- <Plank
207
- entry={layoutEntry}
208
- layoutParts={layoutParts}
209
- part='main'
210
- layoutMode={layoutMode}
211
- order={index * 2 + 1}
212
- />
252
+ <Plank id={entryId} part='deck' order={index * 2 + 1} active={active} layoutMode={layoutMode} />
213
253
  </Fragment>
214
254
  ))}
215
255
  </Stack>
216
256
  </div>
217
257
  <div
218
258
  role='none'
219
- className={isSoloModeLoaded ? 'relative bg-deck overflow-hidden' : 'sr-only'}
220
- {...(!isSoloModeLoaded && { inert: '' })}
259
+ className={solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
260
+ {...(!solo && { inert: '' })}
221
261
  >
262
+ {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
263
+ {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
222
264
  <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
223
- <Plank entry={layoutParts.solo?.[0]} layoutParts={layoutParts} part='solo' layoutMode={layoutMode} />
265
+ <Plank id={solo} part='solo' layoutMode={layoutMode} />
224
266
  </StackContext.Provider>
225
267
  </div>
226
268
  </Main.Content>
227
269
  )}
228
270
 
229
271
  {/* Status bar. */}
230
- {breakpoint === 'desktop' && <Topbar />}
272
+ {topbar && <Topbar />}
231
273
  {hoistStatusbar && <StatusBar showHints={showHints} />}
232
274
  </Main.Root>
233
275
  )}
@@ -248,10 +290,20 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
248
290
  </Popover.Portal>
249
291
 
250
292
  {/* Global dialog. */}
251
- <Dialog.Root open={dialogOpen} onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}>
252
- <Dialog.Overlay blockAlign={dialogBlockAlign}>
293
+ {/* TODO(thure): End block alignment affecting `modal` and whether the surface renders in an overlay is tailored
294
+ to the needs of the ambient chat dialog. As the feature matures, consider separating concerns. */}
295
+ <Dialog.Root
296
+ modal={dialogBlockAlign !== 'end'}
297
+ open={dialogOpen}
298
+ onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}
299
+ >
300
+ {dialogBlockAlign === 'end' ? (
253
301
  <Surface role='dialog' data={dialogContent} limit={1} />
254
- </Dialog.Overlay>
302
+ ) : (
303
+ <Dialog.Overlay blockAlign={dialogBlockAlign}>
304
+ <Surface role='dialog' data={dialogContent} limit={1} />
305
+ </Dialog.Overlay>
306
+ )}
255
307
  </Dialog.Root>
256
308
 
257
309
  {/* Global toasts. */}
@@ -4,8 +4,7 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { Surface } from '@dxos/app-framework';
8
- import { useGraph } from '@dxos/plugin-graph';
7
+ import { Surface, useAppGraph } from '@dxos/app-framework';
9
8
  import { fixedInsetFlexLayout } from '@dxos/react-ui-theme';
10
9
 
11
10
  import { Fallback } from './Fallback';
@@ -13,7 +12,7 @@ import { SURFACE_PREFIX } from './constants';
13
12
  import { useNode } from '../../hooks';
14
13
 
15
14
  export const Fullscreen = ({ id }: { id?: string }) => {
16
- const { graph } = useGraph();
15
+ const { graph } = useAppGraph();
17
16
  const fullScreenNode = useNode(graph, id);
18
17
 
19
18
  return (
@@ -2,30 +2,23 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { Fragment, memo, useEffect, useMemo } from 'react';
5
+ import React, { Fragment, memo, useCallback, useEffect, useMemo } from 'react';
6
6
 
7
- import {
8
- createIntent,
9
- LayoutAction,
10
- NavigationAction,
11
- SLUG_PATH_SEPARATOR,
12
- Surface,
13
- useIntentDispatcher,
14
- type LayoutCoordinate,
15
- } from '@dxos/app-framework';
16
- import { type Node, useGraph } from '@dxos/plugin-graph';
7
+ import { createIntent, LayoutAction, Surface, useAppGraph, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { type Node } from '@dxos/plugin-graph';
17
9
  import { Icon, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
18
10
  import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
19
11
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
20
12
 
21
13
  import { PlankControls } from './PlankControls';
22
- import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
23
14
  import { DECK_PLUGIN } from '../../meta';
15
+ import { DeckAction, SLUG_PATH_SEPARATOR } from '../../types';
24
16
  import { useBreakpoints } from '../../util';
25
17
  import { soloInlinePadding } from '../fragments';
26
18
 
27
19
  export type NodePlankHeadingProps = {
28
- coordinate: LayoutCoordinate;
20
+ id: string;
21
+ part: 'solo' | 'deck' | 'complementary';
29
22
  node?: Node;
30
23
  canIncrementStart?: boolean;
31
24
  canIncrementEnd?: boolean;
@@ -36,7 +29,8 @@ export type NodePlankHeadingProps = {
36
29
 
37
30
  export const NodePlankHeading = memo(
38
31
  ({
39
- coordinate,
32
+ id,
33
+ part,
40
34
  node,
41
35
  canIncrementStart,
42
36
  canIncrementEnd,
@@ -45,14 +39,14 @@ export const NodePlankHeading = memo(
45
39
  actions = [],
46
40
  }: NodePlankHeadingProps) => {
47
41
  const { t } = useTranslation(DECK_PLUGIN);
48
- const { graph } = useGraph();
42
+ const { graph } = useAppGraph();
43
+ const breakpoint = useBreakpoints();
49
44
  const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
50
45
  const label = pending
51
46
  ? t('pending heading')
52
47
  : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
53
48
  const { dispatchPromise: dispatch } = useIntentDispatcher();
54
49
  const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
55
- const breakpoint = useBreakpoints();
56
50
 
57
51
  useEffect(() => {
58
52
  const frame = requestAnimationFrame(() => {
@@ -63,36 +57,65 @@ export const NodePlankHeading = memo(
63
57
  return () => cancelAnimationFrame(frame);
64
58
  }, [node]);
65
59
 
66
- const layoutPart = coordinate.part;
67
60
  // NOTE(Zan): Node ids may now contain a path like `${space}:${id}~comments`
68
- const attendableId = coordinate.entryId.split(SLUG_PATH_SEPARATOR).at(0);
61
+ const attendableId = id.split(SLUG_PATH_SEPARATOR).at(0);
69
62
  const capabilities = useMemo(
70
63
  () => ({
71
- solo: layoutPart === 'solo' || layoutPart === 'main',
64
+ solo: breakpoint !== 'mobile' && (part === 'solo' || part === 'deck'),
72
65
  incrementStart: canIncrementStart,
73
66
  incrementEnd: canIncrementEnd,
74
67
  }),
75
- [breakpoint, layoutPart, canIncrementStart, canIncrementEnd],
68
+ [breakpoint, part, canIncrementStart, canIncrementEnd],
69
+ );
70
+
71
+ const sigilActions = useMemo(
72
+ () => node && [actions, graph.actions(node)].filter((a) => a.length > 0),
73
+ [actions, node, graph],
74
+ );
75
+ const handleAction = useCallback((action: StackItemSigilAction) => {
76
+ typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN });
77
+ }, []);
78
+
79
+ const handlePlankAction = useCallback(
80
+ (eventType: DeckAction.PartAdjustment) => {
81
+ if (eventType === 'solo') {
82
+ return dispatch(createIntent(DeckAction.Adjust, { type: eventType, id }));
83
+ } else if (eventType === 'close') {
84
+ if (part === 'complementary') {
85
+ return dispatch(
86
+ createIntent(LayoutAction.UpdateComplementary, {
87
+ part: 'complementary',
88
+ options: { state: 'collapsed' },
89
+ }),
90
+ );
91
+ } else {
92
+ return dispatch(
93
+ createIntent(LayoutAction.Close, { part: 'main', subject: [id], options: { state: false } }),
94
+ );
95
+ }
96
+ } else {
97
+ return dispatch(createIntent(DeckAction.Adjust, { type: eventType, id }));
98
+ }
99
+ },
100
+ [dispatch, id, part],
76
101
  );
77
102
 
78
103
  return (
79
104
  <StackItem.Heading
80
105
  classNames={[
81
- 'plb-1 border-be border-separator items-stretch gap-1',
82
- layoutPart === 'solo' ? soloInlinePadding : 'pli-1',
106
+ 'plb-1 border-be border-separator items-stretch gap-1 sticky inline-start-12 app-drag',
107
+ part === 'solo' ? soloInlinePadding : 'pli-1',
83
108
  ]}
84
109
  >
85
110
  <ActionRoot>
86
- {node ? (
111
+ {node && sigilActions ? (
87
112
  <StackItem.Sigil
88
113
  icon={icon}
89
- related={layoutPart === 'complementary'}
114
+ related={part === 'complementary'}
90
115
  attendableId={attendableId}
91
116
  triggerLabel={t('actions menu label')}
92
- actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
93
- onAction={(action) =>
94
- typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
95
- }
117
+ actions={sigilActions}
118
+ onAction={handleAction}
96
119
  >
97
120
  <Surface role='menu-footer' data={{ subject: node.data }} />
98
121
  </StackItem.Sigil>
@@ -103,53 +126,22 @@ export const NodePlankHeading = memo(
103
126
  </StackItem.SigilButton>
104
127
  )}
105
128
  </ActionRoot>
106
- {breakpoint !== 'desktop' && <ToggleSidebarButton />}
107
129
  <TextTooltip text={label} onlyWhenTruncating>
108
130
  <StackItem.HeadingLabel
109
131
  attendableId={attendableId}
110
- related={layoutPart === 'complementary'}
132
+ related={part === 'complementary'}
111
133
  {...(pending && { classNames: 'text-description' })}
112
134
  >
113
135
  {label}
114
136
  </StackItem.HeadingLabel>
115
137
  </TextTooltip>
116
- {node && layoutPart !== 'complementary' && (
117
- // TODO(Zan): What are we doing with layout coordinate here?
118
- <Surface role='navbar-end' data={{ subject: node.data }} />
119
- )}
120
- {/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
138
+ {node && part !== 'complementary' && <Surface role='navbar-end' data={{ subject: node.data }} />}
121
139
  <PlankControls
122
140
  capabilities={capabilities}
123
- isSolo={layoutPart === 'solo'}
124
- onClick={(eventType) => {
125
- if (!layoutPart) {
126
- return;
127
- }
128
-
129
- // TODO(Zan): Update this to use the new layout actions.
130
- if (eventType === 'solo') {
131
- return dispatch(
132
- createIntent(NavigationAction.Adjust, {
133
- type: eventType,
134
- layoutCoordinate: { part: 'main', entryId: coordinate.entryId },
135
- }),
136
- );
137
- } else if (eventType === 'close') {
138
- if (layoutPart === 'complementary') {
139
- return dispatch(createIntent(LayoutAction.SetLayout, { element: 'complementary', state: false }));
140
- } else {
141
- return dispatch(
142
- createIntent(NavigationAction.Close, { activeParts: { [layoutPart]: [coordinate.entryId] } }),
143
- );
144
- }
145
- } else {
146
- return dispatch(createIntent(NavigationAction.Adjust, { type: eventType, layoutCoordinate: coordinate }));
147
- }
148
- }}
149
- close={layoutPart === 'complementary' ? 'minify-end' : true}
150
- >
151
- <ToggleComplementarySidebarButton />
152
- </PlankControls>
141
+ isSolo={part === 'solo'}
142
+ onClick={handlePlankAction}
143
+ close={part === 'complementary' ? 'minify-end' : true}
144
+ />
153
145
  </StackItem.Heading>
154
146
  );
155
147
  },