@dxos/plugin-deck 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446

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 (151) hide show
  1. package/dist/lib/browser/{app-graph-builder-R7COZ4A6.mjs → app-graph-builder-VYZ4IWI3.mjs} +15 -16
  2. package/dist/lib/browser/app-graph-builder-VYZ4IWI3.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-7AXGR6UT.mjs → check-app-scheme-SEYECDHI.mjs} +3 -2
  4. package/dist/lib/browser/check-app-scheme-SEYECDHI.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-6ZSOFCPP.mjs +117 -0
  6. package/dist/lib/browser/chunk-6ZSOFCPP.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-B4LOJUWW.mjs +24 -0
  8. package/dist/lib/browser/chunk-B4LOJUWW.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-KIGMELV2.mjs → chunk-FJBMNSUC.mjs} +336 -366
  10. package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-FLOVGNYB.mjs +81 -0
  12. package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-RJNCG4ND.mjs +154 -0
  14. package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +5 -6
  16. package/dist/lib/browser/index.mjs.map +2 -2
  17. package/dist/lib/browser/{intent-resolver-MAKOS57L.mjs → intent-resolver-UDYKO2QW.mjs} +89 -130
  18. package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-root-DGQVIHXP.mjs → react-root-XLXN2VEW.mjs} +6 -6
  21. package/dist/lib/browser/{react-surface-PXBXIOPU.mjs → react-surface-WNGMZL7I.mjs} +6 -6
  22. package/dist/lib/browser/{settings-UBWJF7J7.mjs → settings-HMDGSBGO.mjs} +6 -6
  23. package/dist/lib/browser/settings-HMDGSBGO.mjs.map +7 -0
  24. package/dist/lib/browser/{chunk-OF5RIATN.mjs → state-7TN26M42.mjs} +7 -10
  25. package/dist/lib/browser/state-7TN26M42.mjs.map +7 -0
  26. package/dist/lib/browser/{tools-IVPIPTVA.mjs → tools-SC6QEN7R.mjs} +7 -7
  27. package/dist/lib/browser/tools-SC6QEN7R.mjs.map +7 -0
  28. package/dist/lib/browser/types.mjs +1 -1
  29. package/dist/lib/browser/{url-handler-JSYGSVSB.mjs → url-handler-ODG4B6NX.mjs} +4 -4
  30. package/dist/lib/browser/url-handler-ODG4B6NX.mjs.map +7 -0
  31. package/dist/types/src/capabilities/app-graph-builder.d.ts +179 -2
  32. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  33. package/dist/types/src/capabilities/capabilities.d.ts +6 -8
  34. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/check-app-scheme.d.ts +2 -2
  36. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/index.d.ts +182 -7
  38. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  40. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/state.d.ts +6 -7
  42. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/url-handler.d.ts +2 -2
  45. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  46. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
  47. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  48. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  49. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +5 -0
  50. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -0
  51. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  52. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
  54. package/dist/types/src/components/Plank/Plank.d.ts +5 -18
  55. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
  56. package/dist/types/src/components/Plank/Plank.stories.d.ts +3 -3
  57. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/Plank/PlankControls.d.ts +2 -3
  59. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
  60. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
  61. package/dist/types/src/components/Plank/PlankHeading.d.ts +2 -3
  62. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
  63. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  64. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -1
  65. package/dist/types/src/components/Sidebar/SidebarButton.d.ts +1 -2
  66. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
  67. package/dist/types/src/hooks/index.d.ts +1 -5
  68. package/dist/types/src/hooks/index.d.ts.map +1 -1
  69. package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
  70. package/dist/types/src/index.d.ts +1 -1
  71. package/dist/types/src/index.d.ts.map +1 -1
  72. package/dist/types/src/layout.d.ts.map +1 -1
  73. package/dist/types/src/translations.d.ts +0 -2
  74. package/dist/types/src/translations.d.ts.map +1 -1
  75. package/dist/types/src/types.d.ts +104 -106
  76. package/dist/types/src/types.d.ts.map +1 -1
  77. package/dist/types/src/util/index.d.ts +4 -1
  78. package/dist/types/src/util/index.d.ts.map +1 -1
  79. package/dist/types/src/util/layoutAppliesTopbar.d.ts +1 -2
  80. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
  81. package/dist/types/src/util/overscroll.d.ts.map +1 -1
  82. package/dist/types/src/util/set-active.d.ts.map +1 -1
  83. package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
  84. package/dist/types/src/util/useCompanions.d.ts.map +1 -0
  85. package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
  86. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
  87. package/dist/types/tsconfig.tsbuildinfo +1 -1
  88. package/package.json +29 -36
  89. package/src/capabilities/app-graph-builder.ts +92 -120
  90. package/src/capabilities/check-app-scheme.ts +7 -3
  91. package/src/capabilities/index.ts +2 -3
  92. package/src/capabilities/intent-resolver.ts +133 -175
  93. package/src/capabilities/settings.ts +4 -4
  94. package/src/capabilities/state.ts +4 -7
  95. package/src/capabilities/tools.ts +3 -4
  96. package/src/capabilities/url-handler.ts +4 -4
  97. package/src/components/DeckLayout/ContentEmpty.tsx +4 -9
  98. package/src/components/DeckLayout/DeckLayout.tsx +188 -123
  99. package/src/components/DeckLayout/Fullscreen.tsx +31 -0
  100. package/src/components/Plank/Plank.stories.tsx +8 -20
  101. package/src/components/Plank/Plank.tsx +68 -104
  102. package/src/components/Plank/PlankControls.tsx +57 -53
  103. package/src/components/Plank/PlankError.tsx +6 -2
  104. package/src/components/Plank/PlankHeading.tsx +12 -29
  105. package/src/components/Sidebar/ComplementarySidebar.tsx +54 -33
  106. package/src/components/Sidebar/Sidebar.tsx +4 -7
  107. package/src/components/Sidebar/SidebarButton.tsx +7 -30
  108. package/src/components/fragments.ts +1 -1
  109. package/src/hooks/index.ts +1 -5
  110. package/src/hooks/useNodeActionExpander.ts +8 -3
  111. package/src/index.ts +1 -1
  112. package/src/translations.ts +0 -2
  113. package/src/types.ts +71 -75
  114. package/src/util/index.ts +4 -1
  115. package/src/util/layoutAppliesTopbar.ts +2 -8
  116. package/src/{hooks → util}/useCompanions.ts +3 -3
  117. package/src/{hooks → util}/useHoistStatusbar.ts +4 -9
  118. package/dist/lib/browser/app-graph-builder-R7COZ4A6.mjs.map +0 -7
  119. package/dist/lib/browser/check-app-scheme-7AXGR6UT.mjs.map +0 -7
  120. package/dist/lib/browser/chunk-3O2UZVBA.mjs +0 -121
  121. package/dist/lib/browser/chunk-3O2UZVBA.mjs.map +0 -7
  122. package/dist/lib/browser/chunk-JAYQ5BTF.mjs +0 -157
  123. package/dist/lib/browser/chunk-JAYQ5BTF.mjs.map +0 -7
  124. package/dist/lib/browser/chunk-KIGMELV2.mjs.map +0 -7
  125. package/dist/lib/browser/chunk-OF5RIATN.mjs.map +0 -7
  126. package/dist/lib/browser/chunk-TRFYUEBA.mjs +0 -145
  127. package/dist/lib/browser/chunk-TRFYUEBA.mjs.map +0 -7
  128. package/dist/lib/browser/intent-resolver-MAKOS57L.mjs.map +0 -7
  129. package/dist/lib/browser/settings-UBWJF7J7.mjs.map +0 -7
  130. package/dist/lib/browser/state-4WFB4SDO.mjs +0 -10
  131. package/dist/lib/browser/state-4WFB4SDO.mjs.map +0 -7
  132. package/dist/lib/browser/tools-IVPIPTVA.mjs.map +0 -7
  133. package/dist/lib/browser/url-handler-JSYGSVSB.mjs.map +0 -7
  134. package/dist/types/src/components/DeckLayout/Dialog.d.ts +0 -3
  135. package/dist/types/src/components/DeckLayout/Dialog.d.ts.map +0 -1
  136. package/dist/types/src/components/DeckLayout/Popover.d.ts +0 -5
  137. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +0 -1
  138. package/dist/types/src/hooks/useBreakpoints.d.ts.map +0 -1
  139. package/dist/types/src/hooks/useCompanions.d.ts.map +0 -1
  140. package/dist/types/src/hooks/useDeckCompanions.d.ts +0 -13
  141. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +0 -1
  142. package/dist/types/src/hooks/useHoistStatusbar.d.ts +0 -3
  143. package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +0 -1
  144. package/src/components/DeckLayout/Dialog.tsx +0 -36
  145. package/src/components/DeckLayout/Popover.tsx +0 -104
  146. package/src/hooks/useDeckCompanions.ts +0 -33
  147. /package/dist/lib/browser/{react-root-DGQVIHXP.mjs.map → react-root-XLXN2VEW.mjs.map} +0 -0
  148. /package/dist/lib/browser/{react-surface-PXBXIOPU.mjs.map → react-surface-WNGMZL7I.mjs.map} +0 -0
  149. /package/dist/types/src/{hooks → util}/useBreakpoints.d.ts +0 -0
  150. /package/dist/types/src/{hooks → util}/useCompanions.d.ts +0 -0
  151. /package/src/{hooks → util}/useBreakpoints.ts +0 -0
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  import React, {
6
+ Fragment,
6
7
  type KeyboardEvent,
7
8
  type PropsWithChildren,
8
9
  memo,
@@ -30,117 +31,40 @@ import { PlankContentError, PlankError } from './PlankError';
30
31
  import { PlankHeading } from './PlankHeading';
31
32
  import { PlankLoading } from './PlankLoading';
32
33
  import { DeckCapabilities } from '../../capabilities';
33
- import { useMainSize, useCompanions } from '../../hooks';
34
+ import { useMainSize } from '../../hooks';
34
35
  import { parseEntryId } from '../../layout';
35
- import { DeckAction, type LayoutMode, type ResolvedPart, type DeckSettingsProps } from '../../types';
36
+ import { DeckAction, type LayoutMode, type Part, type ResolvedPart, type DeckSettingsProps } from '../../types';
37
+ import { useCompanions } from '../../util';
36
38
 
37
39
  const UNKNOWN_ID = 'unknown_id';
38
40
 
39
- export type PlankProps = Pick<PlankComponentProps, 'layoutMode' | 'part' | 'path' | 'order' | 'active' | 'settings'> & {
41
+ export type PlankProps = {
40
42
  id?: string;
41
43
  companionId?: string;
44
+ part: Part;
45
+ path?: string[];
46
+ order?: number;
47
+ active?: string[];
48
+ layoutMode: LayoutMode;
49
+ settings?: DeckSettingsProps;
42
50
  };
43
51
 
44
- // TODO(burdon): Factor out conditional rendering.
45
- // Remove this wrapper component and render the entire set of planks in the deck with conditional visibility
46
- // to obviate mounting and unmounting when switching between solo and companion mode?
47
- // NOTE(thure, in reply): Whether any surface should be rendered and hidden is a performance matter — remember that
48
- // article surfaces contain full experiences, so being able to unmount them will yield relatively large performance
49
- // benefits. I think where we anticipate users will definitely want to quickly switch between showing and hiding entire
50
- // articles, over the (again probably large) performance benefit that unmounting them would confer, we can mount and
51
- // hide them, but I think that scenario in its most unambiguous form is probably rare. You could extrapolate
52
- // the scenario to include all “potential” planks such as companions, which we could keep mounted and hidden, but I
53
- // don’t think the resulting performance would be acceptable. I think the real issue is “perceived performance” which
54
- // has mitigations that are in between mounting and un-mounting since both of those have tradeoffs; we may need one or more
55
- // “partially-mounted” experiences, like loading skeletons at the simple end, or screenshots of “sleeping” planks at
56
- // the advanced end.
57
-
58
- /**
59
- * A Plank is the main container for surfaces within a Deck.
60
- * It may be paired with a companion plank that enables the user to select one of multiple companion surfaces.
61
- */
62
- export const Plank = memo(({ id = UNKNOWN_ID, companionId, ...props }: PlankProps) => {
63
- const { graph } = useAppGraph();
64
- const node = useNode(graph, id);
65
- const companions = useCompanions(id);
66
- const currentCompanion = companions.find(({ id }) => id === companionId);
67
- const hasCompanion = !!(companionId && currentCompanion);
68
-
69
- return (
70
- <PlankContainer solo={props.part === 'solo'} companion={hasCompanion}>
71
- <PlankComponent
72
- id={id}
73
- node={node}
74
- companioned={hasCompanion ? 'primary' : undefined}
75
- companions={hasCompanion ? [] : companions}
76
- {...props}
77
- {...(props.part === 'solo' ? { part: 'solo-primary' } : {})}
78
- />
79
- {hasCompanion && (
80
- <PlankComponent
81
- id={companionId}
82
- node={currentCompanion}
83
- primary={node}
84
- companions={companions}
85
- companioned='companion'
86
- {...props}
87
- {...(props.part === 'solo' ? { part: 'solo-companion' } : { order: (props.order ?? 0) + 1 })}
88
- />
89
- )}
90
- </PlankContainer>
91
- );
92
- });
93
-
94
- const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo: boolean; companion: boolean }>) => {
95
- const sizeAttrs = useMainSize();
96
- if (!solo) {
97
- return children;
98
- }
99
-
100
- // TODO(burdon): Make resizable.
101
- return (
102
- <div
103
- role='none'
104
- className={mx('absolute inset-0 grid', companion && 'grid-cols-[1fr_1fr]', railGridHorizontal, mainIntrinsicSize)}
105
- {...sizeAttrs}
106
- >
107
- {children}
108
- </div>
109
- );
110
- };
111
-
112
- type PlankComponentProps = {
113
- layoutMode: LayoutMode;
52
+ type PlankImplProps = Omit<PlankProps, 'id' | 'companionId' | 'part'> & {
114
53
  id: string;
115
54
  part: ResolvedPart;
116
- path?: string[];
117
- order?: number;
118
- active?: string[];
119
- // TODO(burdon): Change to role?
120
- companioned?: 'primary' | 'companion';
121
55
  node?: Node;
56
+ companioned?: 'primary' | 'companion';
122
57
  primary?: Node;
123
58
  companions?: Node[];
124
- settings?: DeckSettingsProps;
125
59
  };
126
60
 
127
- const PlankComponent = memo(
128
- ({
129
- layoutMode,
130
- id,
131
- part,
132
- path,
133
- order,
134
- active,
135
- companioned,
136
- node,
137
- primary,
138
- companions,
139
- settings,
140
- }: PlankComponentProps) => {
61
+ const PlankImpl = memo(
62
+ ({ id, node, part, path, order, active, layoutMode, companioned, primary, companions, settings }: PlankImplProps) => {
141
63
  const { dispatchPromise: dispatch } = useIntentDispatcher();
142
64
  const { deck, popoverAnchorId, scrollIntoView } = useCapability(DeckCapabilities.DeckState);
65
+ const rootElement = useRef<HTMLDivElement | null>(null);
143
66
  const canResize = layoutMode === 'deck';
67
+ const Root = part.startsWith('solo') ? 'article' : StackItem.Root;
144
68
 
145
69
  const attendableAttrs = useAttendableAttributes(primary?.id ?? id);
146
70
  const index = active ? active.findIndex((entryId) => entryId === id) : 0;
@@ -148,13 +72,10 @@ const PlankComponent = memo(
148
72
  const canIncrementStart = active && index !== undefined && index > 0 && length !== undefined && length > 1;
149
73
  const canIncrementEnd = active && index !== undefined && index < length - 1 && length !== undefined;
150
74
 
151
- const rootElement = useRef<HTMLDivElement | null>(null);
152
-
153
75
  const { variant } = parseEntryId(id);
154
76
  const sizeKey = `${id.split('+')[0]}${variant ? `${ATTENDABLE_PATH_SEPARATOR}${variant}` : ''}`;
155
77
  const size = deck.plankSizing[sizeKey] as number | undefined;
156
-
157
- const handleSizeChange = useCallback(
78
+ const setSize = useCallback(
158
79
  debounce((nextSize: number) => {
159
80
  return dispatch(createIntent(DeckAction.UpdatePlankSize, { id: sizeKey, size: nextSize }));
160
81
  }, 200),
@@ -180,9 +101,9 @@ const PlankComponent = memo(
180
101
  }
181
102
  }, [id, scrollIntoView, layoutMode]);
182
103
 
183
- const isSolo = layoutMode.startsWith('solo') && part === 'solo';
104
+ const isSolo = layoutMode === 'solo' && part === 'solo';
184
105
  const isAttendable =
185
- (layoutMode.startsWith('solo') && part.startsWith('solo')) || (layoutMode === 'deck' && part === 'deck');
106
+ (layoutMode === 'solo' && part.startsWith('solo')) || (layoutMode === 'deck' && part === 'deck');
186
107
 
187
108
  const sizeAttrs = useMainSize();
188
109
 
@@ -195,13 +116,12 @@ const PlankComponent = memo(
195
116
  path,
196
117
  popoverAnchorId,
197
118
  },
198
- [node, node?.data, path, popoverAnchorId, primary?.data, variant],
119
+ [node, node?.data, path, popoverAnchorId, primary?.data],
199
120
  );
200
121
 
201
122
  // TODO(wittjosiah): Change prop to accept a component.
202
123
  const placeholder = useMemo(() => <PlankLoading />, []);
203
124
 
204
- const Root = part.startsWith('solo') ? 'article' : StackItem.Root;
205
125
  const className = mx(
206
126
  'attention-surface relative',
207
127
  isSolo && mainIntrinsicSize,
@@ -223,7 +143,7 @@ const PlankComponent = memo(
223
143
  : {
224
144
  item: { id },
225
145
  size,
226
- onSizeChange: handleSizeChange,
146
+ onSizeChange: setSize,
227
147
  classNames: className,
228
148
  order,
229
149
  role: 'article',
@@ -237,7 +157,6 @@ const PlankComponent = memo(
237
157
  id={id}
238
158
  part={part.startsWith('solo-') ? 'solo' : part}
239
159
  node={node}
240
- layoutMode={layoutMode}
241
160
  deckEnabled={settings?.enableDeck}
242
161
  canIncrementStart={canIncrementStart}
243
162
  canIncrementEnd={canIncrementEnd}
@@ -258,9 +177,54 @@ const PlankComponent = memo(
258
177
  ) : (
259
178
  <PlankError id={id} part={part} />
260
179
  )}
261
-
262
180
  {canResize && <StackItem.ResizeHandle />}
263
181
  </Root>
264
182
  );
265
183
  },
266
184
  );
185
+
186
+ const SplitFrame = ({ children }: PropsWithChildren<{}>) => {
187
+ const sizeAttrs = useMainSize();
188
+ return (
189
+ <div
190
+ role='none'
191
+ className={mx('grid grid-cols-[1fr_1fr] absolute inset-0', railGridHorizontal, mainIntrinsicSize)}
192
+ {...sizeAttrs}
193
+ >
194
+ {children}
195
+ </div>
196
+ );
197
+ };
198
+
199
+ export const Plank = ({ id = UNKNOWN_ID, ...props }: PlankProps) => {
200
+ const { graph } = useAppGraph();
201
+ const node = useNode(graph, id);
202
+ const companions = useCompanions(id);
203
+ const currentCompanion = companions.find(({ id }) => id === props.companionId);
204
+
205
+ if (props.companionId) {
206
+ const Root = props.part === 'solo' ? SplitFrame : Fragment;
207
+ return (
208
+ <Root>
209
+ <PlankImpl
210
+ id={id}
211
+ node={node}
212
+ companioned='primary'
213
+ {...props}
214
+ {...(props.part === 'solo' ? { part: 'solo-primary' } : {})}
215
+ />
216
+ <PlankImpl
217
+ id={props.companionId}
218
+ node={currentCompanion}
219
+ companioned='companion'
220
+ primary={node}
221
+ companions={companions}
222
+ {...props}
223
+ {...(props.part === 'solo' ? { part: 'solo-companion' } : { order: props.order! + 1 })}
224
+ />
225
+ </Root>
226
+ );
227
+ } else {
228
+ return <PlankImpl id={id} node={node} companions={companions} {...props} />;
229
+ }
230
+ };
@@ -6,10 +6,18 @@ import React, { forwardRef, useCallback } from 'react';
6
6
 
7
7
  import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { invariant } from '@dxos/invariant';
9
- import { ButtonGroup, type ButtonGroupProps, type ButtonProps, IconButton, useTranslation } from '@dxos/react-ui';
9
+ import {
10
+ Button,
11
+ ButtonGroup,
12
+ type ButtonGroupProps,
13
+ type ButtonProps,
14
+ Icon,
15
+ Tooltip,
16
+ useTranslation,
17
+ } from '@dxos/react-ui';
10
18
 
11
19
  import { DECK_PLUGIN } from '../../meta';
12
- import { DeckAction, type LayoutMode } from '../../types';
20
+ import { DeckAction } from '../../types';
13
21
 
14
22
  export type PlankControlHandler = (event: DeckAction.PartAdjustment) => void;
15
23
 
@@ -18,7 +26,6 @@ export type PlankCapabilities = {
18
26
  incrementEnd?: boolean;
19
27
  deck?: boolean;
20
28
  solo?: boolean;
21
- fullscreen?: boolean;
22
29
  companion?: boolean;
23
30
  };
24
31
 
@@ -27,15 +34,27 @@ export type PlankControlsProps = Omit<ButtonGroupProps, 'onClick'> & {
27
34
  variant?: 'hide-disabled' | 'default';
28
35
  close?: boolean | 'minify-start' | 'minify-end';
29
36
  capabilities: PlankCapabilities;
30
- layoutMode?: LayoutMode;
37
+ isSolo?: boolean;
31
38
  pin?: 'start' | 'end' | 'both';
32
39
  };
33
40
 
34
41
  const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> & { label: string; icon: string }) => {
35
- return <IconButton iconOnly label={label} icon={icon} size={5} variant='ghost' tooltipSide='bottom' {...props} />;
42
+ return (
43
+ <Tooltip.Root>
44
+ <Tooltip.Trigger asChild>
45
+ <Button variant='ghost' {...props}>
46
+ <span className='sr-only'>{label}</span>
47
+ <Icon icon={icon} size={5} />
48
+ </Button>
49
+ </Tooltip.Trigger>
50
+ <Tooltip.Portal>
51
+ <Tooltip.Content side='bottom'>{label}</Tooltip.Content>
52
+ </Tooltip.Portal>
53
+ </Tooltip.Root>
54
+ );
36
55
  };
37
56
 
38
- const plankControlSpacing = 'pli-2';
57
+ const plankControlSpacing = 'pli-2 plb-3';
39
58
 
40
59
  type PlankComplimentControlsProps = {
41
60
  primary?: string;
@@ -54,7 +73,8 @@ export const PlankCompanionControls = forwardRef<HTMLDivElement, PlankCompliment
54
73
  <PlankControl
55
74
  label={t('close companion label')}
56
75
  variant='ghost'
57
- icon='ph--x--regular'
76
+ // icon='ph--minus--regular'
77
+ icon='ph--caret-left--regular'
58
78
  onClick={handleCloseCompanion}
59
79
  classNames={plankControlSpacing}
60
80
  />
@@ -68,51 +88,35 @@ export const PlankCompanionControls = forwardRef<HTMLDivElement, PlankCompliment
68
88
  // NOTE(thure): Pinning & unpinning are disabled indefinitely.
69
89
  export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
70
90
  (
71
- { children, classNames, variant = 'default', capabilities, layoutMode, pin, close = false, onClick, ...props },
91
+ { children, classNames, variant = 'default', capabilities, isSolo, pin, close = false, onClick, ...props },
72
92
  forwardedRef,
73
93
  ) => {
74
94
  const { t } = useTranslation(DECK_PLUGIN);
75
95
  const buttonClassNames =
76
96
  variant === 'hide-disabled' ? `disabled:hidden ${plankControlSpacing}` : plankControlSpacing;
77
97
 
78
- const layoutIsAnySolo = !!layoutMode?.startsWith('solo');
79
-
80
98
  return (
81
- <ButtonGroup {...props} classNames={['app-no-drag !opacity-100', classNames]} ref={forwardedRef}>
82
- {capabilities.deck ? (
99
+ <ButtonGroup {...props} classNames={['app-no-drag', classNames]} ref={forwardedRef}>
100
+ {/* {pin && !isSolo && ['both', 'start'].includes(pin) && (
101
+ <PlankControl
102
+ label={t('pin start label')}
103
+ variant='ghost'
104
+ classNames={buttonClassNames}
105
+ onClick={() => onClick?.('pin-start')}
106
+ icon='ph--caret-line-left--regular'
107
+ />
108
+ )} */}
109
+
110
+ {capabilities.deck && capabilities.solo && (
83
111
  <>
84
- {capabilities.solo && (
85
- <>
86
- {layoutMode === 'solo' && (
87
- <PlankControl
88
- label={t('show fullscreen plank label')}
89
- classNames={buttonClassNames}
90
- icon='ph--corners-out--regular'
91
- onClick={() => onClick?.('solo--fullscreen')}
92
- />
93
- )}
94
- <PlankControl
95
- label={t(
96
- layoutMode === 'solo--fullscreen'
97
- ? 'exit fullscreen label'
98
- : layoutIsAnySolo
99
- ? 'show deck plank label'
100
- : 'show solo plank label',
101
- )}
102
- classNames={buttonClassNames}
103
- icon={
104
- layoutMode === 'solo--fullscreen'
105
- ? 'ph--corners-in--regular'
106
- : layoutIsAnySolo
107
- ? 'ph--arrows-in-line-horizontal--regular'
108
- : 'ph--arrows-out-line-horizontal--regular'
109
- }
110
- onClick={() => onClick?.(layoutMode === 'solo--fullscreen' ? 'solo--fullscreen' : 'solo')}
111
- />
112
- </>
113
- )}
112
+ <PlankControl
113
+ label={isSolo ? t('show deck plank label') : t('show solo plank label')}
114
+ classNames={buttonClassNames}
115
+ icon={isSolo ? 'ph--corners-in--regular' : 'ph--corners-out--regular'}
116
+ onClick={() => onClick?.('solo')}
117
+ />
114
118
 
115
- {!layoutIsAnySolo && (
119
+ {!isSolo && (
116
120
  <>
117
121
  <PlankControl
118
122
  label={t('increment start label')}
@@ -131,18 +135,18 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
131
135
  </>
132
136
  )}
133
137
  </>
134
- ) : (
135
- capabilities.fullscreen && (
136
- <PlankControl
137
- label={t(layoutMode === 'solo--fullscreen' ? 'exit fullscreen label' : 'show fullscreen plank label')}
138
- classNames={buttonClassNames}
139
- icon={layoutMode === 'solo--fullscreen' ? 'ph--corners-in--regular' : 'ph--corners-out--regular'}
140
- onClick={() => onClick?.('solo--fullscreen')}
141
- />
142
- )
143
138
  )}
144
139
 
145
- {close && !layoutIsAnySolo && (
140
+ {/* {pin && !isSolo && ['both', 'end'].includes(pin) && (
141
+ <PlankControl
142
+ label={t('pin end label')}
143
+ classNames={buttonClassNames}
144
+ icon='ph--caret-line-right--regular'
145
+ onClick={() => onClick?.('pin-end')}
146
+ />
147
+ )} */}
148
+
149
+ {close && !isSolo && (
146
150
  <PlankControl
147
151
  label={t(`${typeof close === 'string' ? 'minify' : 'close'} label`)}
148
152
  classNames={buttonClassNames}
@@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react';
6
6
 
7
7
  import { type Node } from '@dxos/plugin-graph';
8
8
  import { useTranslation } from '@dxos/react-ui';
9
- import { descriptionMessage, mx } from '@dxos/react-ui-theme';
9
+ import { descriptionText, mx } from '@dxos/react-ui-theme';
10
10
 
11
11
  import { PlankHeading, type PlankHeadingProps } from './PlankHeading';
12
12
  import { PlankLoading } from './PlankLoading';
@@ -19,7 +19,11 @@ export const PlankContentError = ({ error }: { error?: Error }) => {
19
19
  <div role='none' className='overflow-auto p-8 attention-surface grid place-items-center'>
20
20
  <p
21
21
  role='alert'
22
- className={mx(descriptionMessage, 'break-words rounded-lg p-8', errorString.length < 256 && 'text-lg')}
22
+ className={mx(
23
+ descriptionText,
24
+ 'break-words border border-dashed border-separator rounded-lg p-8',
25
+ errorString.length < 256 && 'text-lg',
26
+ )}
23
27
  >
24
28
  {error ? errorString : t('error fallback message')}
25
29
  </p>
@@ -9,21 +9,17 @@ import { type Node } from '@dxos/plugin-graph';
9
9
  import { Icon, IconButton, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
10
10
  import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
11
11
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
12
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
13
12
 
14
13
  import { PlankCompanionControls, PlankControls } from './PlankControls';
15
- import { useBreakpoints } from '../../hooks';
16
14
  import { parseEntryId } from '../../layout';
17
15
  import { DECK_PLUGIN } from '../../meta';
18
- import { PLANK_COMPANION_TYPE, DeckAction, type ResolvedPart, type LayoutMode } from '../../types';
16
+ import { PLANK_COMPANION_TYPE, DeckAction, type ResolvedPart } from '../../types';
17
+ import { useBreakpoints } from '../../util';
19
18
  import { soloInlinePadding } from '../fragments';
20
19
 
21
- const MAX_COMPANIONS = 5;
22
-
23
20
  export type PlankHeadingProps = {
24
21
  id: string;
25
22
  part: ResolvedPart;
26
- layoutMode?: LayoutMode;
27
23
  node?: Node;
28
24
  deckEnabled?: boolean;
29
25
  canIncrementStart?: boolean;
@@ -49,7 +45,6 @@ export const PlankHeading = memo(
49
45
  pending,
50
46
  companioned,
51
47
  companions,
52
- layoutMode,
53
48
  actions = [],
54
49
  }: PlankHeadingProps) => {
55
50
  const { t } = useTranslation(DECK_PLUGIN);
@@ -66,7 +61,7 @@ export const PlankHeading = memo(
66
61
  useEffect(() => {
67
62
  const frame = requestAnimationFrame(() => {
68
63
  // Load actions for the node.
69
- node && graph.expand(node.id);
64
+ node && graph.actions(node);
70
65
  });
71
66
 
72
67
  return () => cancelAnimationFrame(frame);
@@ -79,7 +74,6 @@ export const PlankHeading = memo(
79
74
  solo: breakpoint !== 'mobile' && (part === 'solo' || part === 'deck'),
80
75
  incrementStart: canIncrementStart,
81
76
  incrementEnd: canIncrementEnd,
82
- fullscreen: !isCompanionNode,
83
77
  companion: !isCompanionNode && companions && companions.length > 0,
84
78
  }),
85
79
  [breakpoint, part, companions, canIncrementStart, canIncrementEnd, isCompanionNode, deckEnabled],
@@ -92,20 +86,17 @@ export const PlankHeading = memo(
92
86
  } else if (variant) {
93
87
  return [];
94
88
  } else {
95
- return [actions, graph.getActions(node.id)].filter((a) => a.length > 0);
89
+ return [actions, graph.actions(node)].filter((a) => a.length > 0);
96
90
  }
97
91
  }, [actions, node, variant, graph]);
98
92
 
99
- const handleAction = useCallback(
100
- (action: StackItemSigilAction) => {
101
- typeof action.data === 'function' && action.data?.({ parent: node, caller: DECK_PLUGIN });
102
- },
103
- [node],
104
- );
93
+ const handleAction = useCallback((action: StackItemSigilAction) => {
94
+ typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN });
95
+ }, []);
105
96
 
106
97
  const handlePlankAction = useCallback(
107
98
  (eventType: DeckAction.PartAdjustment) => {
108
- if (eventType.startsWith('solo')) {
99
+ if (eventType === 'solo') {
109
100
  return dispatch(createIntent(DeckAction.Adjust, { type: eventType, id }));
110
101
  } else if (eventType === 'close') {
111
102
  if (part === 'complementary') {
@@ -148,26 +139,18 @@ export const PlankHeading = memo(
148
139
  return (
149
140
  <StackItem.Heading
150
141
  classNames={[
151
- 'plb-1 border-be border-separator items-stretch gap-1 sticky inline-start-12 app-drag min-is-0 contain-layout',
142
+ 'plb-1 border-be border-separator items-stretch gap-1 sticky inline-start-12 app-drag min-is-0 layout-contain',
152
143
  part === 'solo' ? soloInlinePadding : 'pli-1',
153
- ...(layoutMode === 'solo--fullscreen'
154
- ? [
155
- hoverableControls,
156
- hoverableFocusedWithinControls,
157
- '[&>*]:transition-opacity [&>*]:opacity-[--controls-opacity] bg-transparent border-transparent transition-[background-color,border-color] hover-hover:hover:bg-headerSurface focus-within:bg-headerSurface hover-hover:hover:border-separator focus-within:border-separator',
158
- ]
159
- : []),
160
144
  ]}
161
- data-plank-heading
162
145
  >
163
- {companions && isCompanionNode /* TODO(thure): This is a tablist, it should be implemented as such. */ ? (
146
+ {companions && isCompanionNode ? (
164
147
  <div role='none' className='flex-1 min-is-0 overflow-x-auto scrollbar-thin flex gap-1'>
165
148
  {companions.map(({ id, properties: { icon, label } }) => (
166
149
  <IconButton
167
150
  key={id}
168
151
  data-id={id}
169
152
  icon={icon}
170
- iconOnly={companions.length > MAX_COMPANIONS && node?.id !== id}
153
+ iconOnly={node?.id !== id}
171
154
  label={toLocalizedString(label, t)}
172
155
  size={5}
173
156
  variant={node?.id === id ? 'primary' : 'default'}
@@ -213,7 +196,7 @@ export const PlankHeading = memo(
213
196
  ) : (
214
197
  <PlankControls
215
198
  capabilities={capabilities}
216
- layoutMode={layoutMode}
199
+ isSolo={part === 'solo'}
217
200
  close={part === 'complementary' ? 'minify-end' : true}
218
201
  onClick={handlePlankAction}
219
202
  />