@dxos/plugin-deck 0.7.4 → 0.7.5-labs.071a3e2

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