@dxos/plugin-deck 0.6.13 → 0.6.14-main.7bd9c89

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 (45) hide show
  1. package/dist/lib/browser/{chunk-YVHGFQQR.mjs → chunk-GVOGPULO.mjs} +1 -1
  2. package/dist/lib/browser/chunk-GVOGPULO.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +245 -189
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/meta.mjs +1 -1
  7. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  8. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +2 -2
  9. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  10. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +3 -4
  11. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  12. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +4 -3
  13. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  14. package/dist/types/src/components/DeckLayout/Sidebar.d.ts +2 -3
  15. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  16. package/dist/types/src/components/DeckLayout/StatusBar.d.ts +3 -1
  17. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  18. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  19. package/dist/types/src/components/LayoutSettings.d.ts.map +1 -1
  20. package/dist/types/src/hooks/useNode.d.ts.map +1 -1
  21. package/dist/types/src/layout.d.ts.map +1 -1
  22. package/dist/types/src/meta.d.ts.map +1 -1
  23. package/dist/types/src/translations.d.ts +5 -3
  24. package/dist/types/src/translations.d.ts.map +1 -1
  25. package/dist/types/src/types.d.ts +1 -1
  26. package/dist/types/src/types.d.ts.map +1 -1
  27. package/dist/types/src/util/overscroll.d.ts +1 -1
  28. package/dist/types/src/util/overscroll.d.ts.map +1 -1
  29. package/package.json +30 -29
  30. package/src/DeckPlugin.tsx +93 -71
  31. package/src/components/DeckLayout/ComplementarySidebar.tsx +69 -27
  32. package/src/components/DeckLayout/DeckLayout.tsx +63 -84
  33. package/src/components/DeckLayout/NodePlankHeading.tsx +15 -15
  34. package/src/components/DeckLayout/Plank.tsx +3 -3
  35. package/src/components/DeckLayout/Sidebar.tsx +6 -5
  36. package/src/components/DeckLayout/StatusBar.tsx +12 -3
  37. package/src/components/DeckLayout/Toast.tsx +3 -3
  38. package/src/components/LayoutSettings.tsx +5 -8
  39. package/src/hooks/useNode.ts +5 -1
  40. package/src/layout.ts +1 -0
  41. package/src/meta.ts +3 -1
  42. package/src/translations.ts +7 -3
  43. package/src/types.ts +1 -1
  44. package/src/util/overscroll.ts +5 -5
  45. package/dist/lib/browser/chunk-YVHGFQQR.mjs.map +0 -7
@@ -3,21 +3,13 @@
3
3
  //
4
4
 
5
5
  import { Sidebar as MenuIcon } from '@phosphor-icons/react';
6
- import React, { useCallback, useEffect, useMemo, useRef, useState, useLayoutEffect, type UIEvent } from 'react';
6
+ import React, { useCallback, useEffect, useMemo, useRef, type UIEvent } from 'react';
7
7
 
8
- import {
9
- SLUG_PATH_SEPARATOR,
10
- type Attention,
11
- type LayoutEntry,
12
- type LayoutParts,
13
- Surface,
14
- type Toast as ToastSchema,
15
- firstIdInPart,
16
- usePlugin,
17
- } from '@dxos/app-framework';
18
- import { Button, Dialog, Main, Popover, useTranslation } from '@dxos/react-ui';
8
+ import { type LayoutParts, Surface, type Toast as ToastSchema, firstIdInPart, usePlugin } from '@dxos/app-framework';
9
+ import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
10
+ import { useAttended } from '@dxos/react-ui-attention';
19
11
  import { Deck } from '@dxos/react-ui-deck';
20
- import { getSize } from '@dxos/react-ui-theme';
12
+ import { getSize, mainPaddingTransitions } from '@dxos/react-ui-theme';
21
13
 
22
14
  import { ActiveNode } from './ActiveNode';
23
15
  import { ComplementarySidebar } from './ComplementarySidebar';
@@ -35,11 +27,10 @@ import { useLayout } from '../LayoutContext';
35
27
 
36
28
  export type DeckLayoutProps = {
37
29
  layoutParts: LayoutParts;
38
- attention: Attention;
39
30
  toasts: ToastSchema[];
40
31
  flatDeck?: boolean;
41
32
  overscroll: Overscroll;
42
- showHintsFooter: boolean;
33
+ showHints: boolean;
43
34
  slots?: {
44
35
  wallpaper?: { classNames?: string };
45
36
  };
@@ -48,11 +39,10 @@ export type DeckLayoutProps = {
48
39
 
49
40
  export const DeckLayout = ({
50
41
  layoutParts,
51
- attention,
52
42
  toasts,
53
43
  flatDeck,
54
44
  overscroll,
55
- showHintsFooter,
45
+ showHints,
56
46
  slots,
57
47
  onDismissToast,
58
48
  }: DeckLayoutProps) => {
@@ -70,36 +60,42 @@ export const DeckLayout = ({
70
60
  } = context;
71
61
  const { t } = useTranslation(DECK_PLUGIN);
72
62
  const { plankSizing } = useDeckContext();
63
+ const attended = useAttended();
73
64
  const searchPlugin = usePlugin('dxos.org/plugin/search');
74
65
  const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
75
66
 
76
- const [scrollLeft, setScrollLeft] = useState<number | null>(null);
77
- const deckRef = useRef<HTMLDivElement | null>(null);
78
- const restoreScrollRef = useRef<boolean>(false);
67
+ const scrollLeftRef = useRef<number | null>();
68
+ const deckRef = useRef<HTMLDivElement>(null);
69
+
70
+ // Ensure the first plank is attended when the deck is first rendered.
71
+ useEffect(() => {
72
+ const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
73
+ if (attended.length === 0 && firstId) {
74
+ // TODO(wittjosiah): Focusing the type button is a workaround.
75
+ // If the plank is directly focused on first load the focus ring appears.
76
+ document.querySelector<HTMLElement>(`article[data-attendable-id="${firstId}"] button`)?.focus();
77
+ }
78
+ }, []);
79
79
 
80
80
  /**
81
81
  * Clear scroll restoration state if the window is resized
82
82
  */
83
83
  const handleResize = useCallback(() => {
84
- setScrollLeft(null);
84
+ scrollLeftRef.current = null;
85
85
  }, []);
86
+
86
87
  useEffect(() => {
87
88
  window.addEventListener('resize', handleResize);
88
89
  return () => window.removeEventListener('resize', handleResize);
89
90
  }, [handleResize]);
90
91
 
91
- /**
92
- * Restore scroll when returning to deck mode
93
- */
94
- useLayoutEffect(() => {
95
- if (layoutMode !== 'deck') {
96
- restoreScrollRef.current = true;
97
- } else if (restoreScrollRef.current && deckRef.current && scrollLeft) {
98
- // console.log('[restoring scrollLeft]', scrollLeft);
99
- deckRef.current.scrollLeft = scrollLeft;
100
- restoreScrollRef.current = false;
92
+ const restoreScroll = useCallback(() => {
93
+ if (deckRef.current && scrollLeftRef.current != null) {
94
+ deckRef.current.scrollLeft = scrollLeftRef.current;
101
95
  }
102
- }, [layoutMode, deckRef.current, scrollLeft]);
96
+ }, []);
97
+
98
+ useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
103
99
 
104
100
  /**
105
101
  * Save scroll position as the user scrolls
@@ -107,22 +103,13 @@ export const DeckLayout = ({
107
103
  const handleScroll = useCallback(
108
104
  (event: UIEvent) => {
109
105
  if (layoutMode === 'deck' && event.currentTarget === event.target) {
110
- // console.log('[save scroll left]', (event.target as HTMLDivElement).scrollLeft);
111
- setScrollLeft((event.target as HTMLDivElement).scrollLeft);
106
+ scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
112
107
  }
113
108
  },
114
109
  [layoutMode],
115
110
  );
116
111
 
117
- const complementarySlug = useMemo(() => {
118
- const entry = layoutParts.complementary?.at(0);
119
- if (entry) {
120
- return entry.path ? `${entry.id}${SLUG_PATH_SEPARATOR}${entry.path}` : entry.id;
121
- }
122
- }, [layoutParts]);
123
-
124
- const firstAttendedId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
125
-
112
+ const firstAttendedId = attended[0];
126
113
  useEffect(() => {
127
114
  // TODO(burdon): Can we prevent the need to re-scroll since the planks are preserved?
128
115
  // E.g., hide the deck and just move the solo article?
@@ -134,21 +121,14 @@ export const DeckLayout = ({
134
121
  }
135
122
  }, [layoutMode, firstAttendedId]);
136
123
 
137
- // TODO(burdon): Needs cleaning up.
138
- const parts: LayoutEntry[] = useMemo(() => {
139
- const parts = [...(layoutParts.main ?? [])];
140
- for (const part of layoutParts.solo ?? []) {
141
- if (!parts.find((entry) => entry.id === part.id)) {
142
- parts.push(part);
143
- }
144
- }
145
- return parts;
146
- }, [layoutParts.main, layoutParts.solo]);
124
+ const isEmpty = layoutParts.main?.length === 0 && layoutParts.solo?.length === 0;
147
125
 
148
- const padding =
149
- layoutMode === 'deck' && overscroll === 'centering'
150
- ? calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen)
151
- : {};
126
+ const padding = useMemo(() => {
127
+ if (layoutMode === 'deck' && overscroll === 'centering') {
128
+ return calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen);
129
+ }
130
+ return {};
131
+ }, [layoutMode, overscroll, layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen]);
152
132
 
153
133
  if (layoutMode === 'fullscreen') {
154
134
  return <Fullscreen id={fullScreenSlug} />;
@@ -173,25 +153,17 @@ export const DeckLayout = ({
173
153
  <Main.Root
174
154
  navigationSidebarOpen={context.sidebarOpen}
175
155
  onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
176
- {...(complementarySidebarOpen !== null && {
177
- complementarySidebarOpen: /* complementaryAvailable && */ context.complementarySidebarOpen as boolean,
178
- onComplementarySidebarOpenChange: (next) => (context.complementarySidebarOpen = next),
179
- })}
156
+ complementarySidebarOpen={context.complementarySidebarOpen}
157
+ onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
180
158
  >
181
159
  {/* Notch */}
182
160
  <Main.Notch classNames='z-[21]'>
183
161
  <Surface role='notch-start' />
184
- <Button
185
- // disabled={!sidebarAvailable}
186
- onClick={() => (context.sidebarOpen = !context.sidebarOpen)}
187
- variant='ghost'
188
- classNames='p-1'
189
- >
162
+ <Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
190
163
  <span className='sr-only'>{t('open navigation sidebar label')}</span>
191
164
  <MenuIcon weight='light' className={getSize(5)} />
192
165
  </Button>
193
166
  <Button
194
- // disabled={!complementaryAvailable}
195
167
  onClick={() => (context.complementarySidebarOpen = !context.complementarySidebarOpen)}
196
168
  variant='ghost'
197
169
  classNames='p-1'
@@ -203,38 +175,52 @@ export const DeckLayout = ({
203
175
  </Main.Notch>
204
176
 
205
177
  {/* Left sidebar. */}
206
- <Sidebar attention={attention} layoutParts={layoutParts} />
178
+ <Sidebar layoutParts={layoutParts} />
207
179
 
208
180
  {/* Right sidebar. */}
209
- <ComplementarySidebar id={complementarySlug} layoutParts={layoutParts} flatDeck={flatDeck} />
181
+ {/* TODO(wittjosiah): Get context from layout parts. */}
182
+ <ComplementarySidebar context='comments' layoutParts={layoutParts} flatDeck={flatDeck} />
210
183
 
211
184
  {/* Dialog overlay to dismiss dialogs. */}
212
185
  <Main.Overlay />
213
186
 
214
187
  {/* No content. */}
215
- {parts.length === 0 && (
188
+ {isEmpty && (
216
189
  <Main.Content handlesFocus>
217
190
  <ContentEmpty />
218
191
  </Main.Content>
219
192
  )}
220
193
 
221
194
  {/* Solo/deck mode. */}
222
- {parts.length !== 0 && (
195
+ {!isEmpty && (
223
196
  <Main.Content bounce classNames='grid block-end-[--statusbar-size]' handlesFocus>
224
197
  <div role='none' className='relative'>
225
198
  <Deck.Root
226
199
  style={padding}
227
- classNames={[!flatDeck && 'bg-deck', 'absolute inset-0', slots?.wallpaper?.classNames]}
200
+ classNames={[
201
+ !flatDeck && 'bg-deck',
202
+ mainPaddingTransitions,
203
+ 'absolute inset-0',
204
+ slots?.wallpaper?.classNames,
205
+ ]}
228
206
  solo={layoutMode === 'solo'}
229
207
  onScroll={handleScroll}
230
208
  ref={deckRef}
231
209
  >
232
- {parts.map((layoutEntry) => (
210
+ <Plank
211
+ entry={layoutParts.solo?.[0] ?? { id: 'unknown-solo' }}
212
+ layoutParts={layoutParts}
213
+ part='solo'
214
+ layoutMode={layoutMode}
215
+ flatDeck={flatDeck}
216
+ searchEnabled={!!searchPlugin}
217
+ />
218
+ {layoutParts.main?.map((layoutEntry) => (
233
219
  <Plank
234
220
  key={layoutEntry.id}
235
221
  entry={layoutEntry}
236
222
  layoutParts={layoutParts}
237
- part={layoutMode === 'solo' && layoutEntry.id === layoutParts.solo?.[0]?.id ? 'solo' : 'main'}
223
+ part='main'
238
224
  layoutMode={layoutMode}
239
225
  flatDeck={flatDeck}
240
226
  searchEnabled={!!searchPlugin}
@@ -245,15 +231,8 @@ export const DeckLayout = ({
245
231
  </Main.Content>
246
232
  )}
247
233
 
248
- <StatusBar />
249
-
250
- {/* Help hints. */}
251
- {/* TODO(burdon): Need to make room for this in status bar. */}
252
- {showHintsFooter && (
253
- <div className='fixed bottom-0 left-0 right-0 h-[32px] z-[1] flex justify-center'>
254
- <Surface role='hints' limit={1} />
255
- </div>
256
- )}
234
+ {/* Footer status. */}
235
+ <StatusBar showHints={showHints} />
257
236
 
258
237
  {/* Global popovers. */}
259
238
  <Popover.Portal>
@@ -2,13 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Placeholder } from '@phosphor-icons/react';
6
5
  import React, { Fragment, useEffect } from 'react';
7
6
 
8
7
  import {
9
8
  LayoutAction,
10
9
  NavigationAction,
11
- SLUG_COLLECTION_INDICATOR,
12
10
  SLUG_PATH_SEPARATOR,
13
11
  Surface,
14
12
  useIntentDispatcher,
@@ -16,11 +14,10 @@ import {
16
14
  partLength,
17
15
  type LayoutParts,
18
16
  type LayoutPart,
19
- type LayoutEntry,
20
17
  } from '@dxos/app-framework';
21
18
  import { type Node, useGraph } from '@dxos/plugin-graph';
22
- import { Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
23
- import { PlankHeading, plankHeadingIconProps } from '@dxos/react-ui-deck';
19
+ import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
20
+ import { PlankHeading, type PlankHeadingAction } from '@dxos/react-ui-deck';
24
21
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
25
22
 
26
23
  import { DECK_PLUGIN } from '../../meta';
@@ -30,24 +27,23 @@ export const NodePlankHeading = ({
30
27
  id,
31
28
  layoutParts,
32
29
  layoutPart,
33
- // TODO(wittjosiah): Unused?
34
- layoutEntry,
35
30
  popoverAnchorId,
36
31
  pending,
37
32
  flatDeck,
33
+ actions = [],
38
34
  }: {
39
35
  node?: Node;
40
36
  id?: string;
41
37
  layoutParts?: LayoutParts;
42
38
  layoutPart?: LayoutPart;
43
- layoutEntry?: LayoutEntry;
44
39
  popoverAnchorId?: string;
45
40
  pending?: boolean;
46
41
  flatDeck?: boolean;
42
+ actions?: PlankHeadingAction[];
47
43
  }) => {
48
44
  const { t } = useTranslation(DECK_PLUGIN);
49
45
  const { graph } = useGraph();
50
- const Icon = node?.properties?.icon ?? Placeholder;
46
+ const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
51
47
  const label = pending
52
48
  ? t('pending heading')
53
49
  : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
@@ -80,10 +76,11 @@ export const NodePlankHeading = ({
80
76
  <ActionRoot>
81
77
  {node ? (
82
78
  <PlankHeading.ActionsMenu
83
- Icon={Icon}
79
+ icon={icon}
80
+ related={layoutPart === 'complementary'}
84
81
  attendableId={attendableId}
85
82
  triggerLabel={t('actions menu label')}
86
- actions={graph.actions(node)}
83
+ actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
87
84
  onAction={(action) =>
88
85
  typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
89
86
  }
@@ -93,12 +90,16 @@ export const NodePlankHeading = ({
93
90
  ) : (
94
91
  <PlankHeading.Button>
95
92
  <span className='sr-only'>{label}</span>
96
- <Icon {...plankHeadingIconProps} />
93
+ <Icon icon={icon} size={5} />
97
94
  </PlankHeading.Button>
98
95
  )}
99
96
  </ActionRoot>
100
97
  <TextTooltip text={label} onlyWhenTruncating>
101
- <PlankHeading.Label attendableId={node?.id} {...(pending && { classNames: 'text-description' })}>
98
+ <PlankHeading.Label
99
+ attendableId={attendableId}
100
+ related={layoutPart === 'complementary'}
101
+ {...(pending && { classNames: 'text-description' })}
102
+ >
102
103
  {label}
103
104
  </PlankHeading.Label>
104
105
  </TextTooltip>
@@ -143,7 +144,6 @@ export const NodePlankHeading = ({
143
144
  action: NavigationAction.CLOSE,
144
145
  data: {
145
146
  activeParts: {
146
- complementary: [`${id}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`],
147
147
  [layoutPart]: [id],
148
148
  },
149
149
  },
@@ -151,7 +151,7 @@ export const NodePlankHeading = ({
151
151
  : { action: NavigationAction.ADJUST, data: { type: eventType, layoutCoordinate } },
152
152
  );
153
153
  }}
154
- close={layoutCoordinate?.part === 'complementary' ? 'minify-end' : true}
154
+ close={layoutPart === 'complementary' ? 'minify-end' : true}
155
155
  />
156
156
  </PlankHeading.Root>
157
157
  );
@@ -19,7 +19,7 @@ import {
19
19
  import { debounce } from '@dxos/async';
20
20
  import { useGraph } from '@dxos/plugin-graph';
21
21
  import { Button, Tooltip, useTranslation } from '@dxos/react-ui';
22
- import { createAttendableAttributes } from '@dxos/react-ui-attention';
22
+ import { useAttendableAttributes } from '@dxos/react-ui-attention';
23
23
  import { Plank as NaturalPlank } from '@dxos/react-ui-deck';
24
24
  import { mainIntrinsicSize } from '@dxos/react-ui-theme';
25
25
 
@@ -52,7 +52,7 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
52
52
  const rootElement = useRef<HTMLDivElement | null>(null);
53
53
  const resizeable = layoutMode === 'deck';
54
54
 
55
- const attendableAttrs = createAttendableAttributes(entry.id);
55
+ const attendableAttrs = useAttendableAttributes(entry.id);
56
56
  const coordinate: LayoutCoordinate = { part, entryId: entry.id };
57
57
 
58
58
  const size = plankSizing?.[entry.id] as number | undefined;
@@ -78,7 +78,7 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
78
78
  }, [scrollIntoView, layoutMode]);
79
79
 
80
80
  const isSolo = layoutMode === 'solo' && part === 'solo';
81
- const isSuppressed = layoutMode === 'solo' && part !== 'solo';
81
+ const isSuppressed = (layoutMode === 'solo' && part !== 'solo') || (layoutMode === 'deck' && part === 'solo');
82
82
 
83
83
  const sizeAttrs = useMainSize();
84
84
 
@@ -4,18 +4,19 @@
4
4
 
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { type Attention, type LayoutParts, openIds, Surface } from '@dxos/app-framework';
7
+ import { type LayoutParts, openIds, Surface } from '@dxos/app-framework';
8
8
  import { Main } from '@dxos/react-ui';
9
+ import { useAttended } from '@dxos/react-ui-attention';
9
10
 
10
11
  import { useLayout } from '../LayoutContext';
11
12
 
12
13
  export type SidebarProps = {
13
- attention: Attention;
14
14
  layoutParts: LayoutParts;
15
15
  };
16
16
 
17
- export const Sidebar = ({ attention, layoutParts }: SidebarProps) => {
17
+ export const Sidebar = ({ layoutParts }: SidebarProps) => {
18
18
  const { layoutMode, popoverAnchorId } = useLayout();
19
+ const attended = useAttended();
19
20
 
20
21
  const activeIds = useMemo(() => {
21
22
  if (layoutMode === 'solo') {
@@ -31,9 +32,9 @@ export const Sidebar = ({ attention, layoutParts }: SidebarProps) => {
31
32
  () => ({
32
33
  popoverAnchorId,
33
34
  activeIds,
34
- attended: attention.attended,
35
+ attended,
35
36
  }),
36
- [popoverAnchorId, activeIds, attention.attended],
37
+ [popoverAnchorId, activeIds, attended],
37
38
  );
38
39
  return (
39
40
  <Main.NavigationSidebar>
@@ -5,14 +5,23 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
- import { mainPadding, mx } from '@dxos/react-ui-theme';
8
+ import { mainPadding, mainPaddingTransitions, mx } from '@dxos/react-ui-theme';
9
9
 
10
10
  import { useMainSize } from '../../hooks';
11
11
 
12
- export const StatusBar = () => {
12
+ export const StatusBar = ({ showHints }: { showHints?: boolean }) => {
13
13
  const sizeAttrs = useMainSize();
14
14
  return (
15
- <div role='none' {...sizeAttrs} className={mx('fixed block-end-0 inset-inline-0 z-[2]', mainPadding)}>
15
+ <div
16
+ role='none'
17
+ {...sizeAttrs}
18
+ className={mx(
19
+ 'fixed flex justify-between block-end-0 inset-inline-0 items-center border-bs border-separator z-[2]',
20
+ mainPadding,
21
+ mainPaddingTransitions,
22
+ )}
23
+ >
24
+ <div role='none'>{showHints && <Surface role='hints' limit={1} />}</div>
16
25
  <Surface role='status-bar' limit={1} />
17
26
  </div>
18
27
  );
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
 
7
7
  import type { Toast as ToastProps } from '@dxos/app-framework';
8
- import { Button, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
8
+ import { Button, Icon, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
9
9
 
10
10
  // TODO(wittjosiah): Render remaining duration as a progress bar within the toast.
11
11
  export const Toast = ({
@@ -23,8 +23,8 @@ export const Toast = ({
23
23
  return (
24
24
  <NaturalToast.Root data-testid={id} defaultOpen duration={duration} onOpenChange={onOpenChange}>
25
25
  <NaturalToast.Body>
26
- <NaturalToast.Title>
27
- {icon?.({ className: 'inline mr-1' })}
26
+ <NaturalToast.Title classNames='items-center'>
27
+ <Icon icon={icon} size={5} classNames='inline mr-1' />
28
28
  <span>{title}</span>
29
29
  </NaturalToast.Title>
30
30
  {description && <NaturalToast.Description>{description}</NaturalToast.Description>}
@@ -61,25 +61,22 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
61
61
  </Select.Portal>
62
62
  </Select.Root>
63
63
  </SettingsValue>
64
- <SettingsValue label={t('settings show footer label')}>
65
- <Input.Switch checked={settings.showFooter} onCheckedChange={(checked) => (settings.showFooter = !!checked)} />
64
+ <SettingsValue label={t('settings show hints label')}>
65
+ <Input.Switch checked={settings.showHints} onCheckedChange={(checked) => (settings.showHints = checked)} />
66
66
  </SettingsValue>
67
67
  {!isSocket && (
68
68
  <SettingsValue label={t('settings native redirect label')}>
69
69
  <Input.Switch
70
70
  checked={settings.enableNativeRedirect}
71
- onCheckedChange={(checked) => (settings.enableNativeRedirect = !!checked)}
71
+ onCheckedChange={(checked) => (settings.enableNativeRedirect = checked)}
72
72
  />
73
73
  </SettingsValue>
74
74
  )}
75
75
  <SettingsValue label={t('settings custom slots')}>
76
- <Input.Switch
77
- checked={settings.customSlots}
78
- onCheckedChange={(checked) => (settings.customSlots = !!checked)}
79
- />
76
+ <Input.Switch checked={settings.customSlots} onCheckedChange={(checked) => (settings.customSlots = checked)} />
80
77
  </SettingsValue>
81
78
  <SettingsValue label={t('settings flat deck')}>
82
- <Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = !!checked)} />
79
+ <Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = checked)} />
83
80
  </SettingsValue>
84
81
  </>
85
82
  );
@@ -16,9 +16,13 @@ import { type Graph, type Node } from '@dxos/plugin-graph';
16
16
  */
17
17
  // TODO(wittjosiah): Factor out.
18
18
  export const useNode = <T = any>(graph: Graph, id?: string, timeout?: number): Node<T> | undefined => {
19
- const [nodeState, setNodeState] = useState<Node<T> | undefined>(id ? graph.findNode(id) : undefined);
19
+ const [nodeState, setNodeState] = useState<Node<T> | undefined>(id ? graph.findNode(id, false) : undefined);
20
20
 
21
21
  useEffect(() => {
22
+ if (!id && nodeState) {
23
+ setNodeState(undefined);
24
+ }
25
+
22
26
  if (nodeState?.id === id || !id) {
23
27
  return;
24
28
  }
package/src/layout.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  //
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
+
4
5
  import { produce } from 'immer';
5
6
 
6
7
  import {
package/src/meta.ts CHANGED
@@ -2,9 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { type PluginMeta } from '@dxos/app-framework';
6
+
5
7
  export const DECK_PLUGIN = 'dxos.org/plugin/deck';
6
8
 
7
9
  export default {
8
10
  id: DECK_PLUGIN,
9
11
  name: 'Deck',
10
- };
12
+ } satisfies PluginMeta;
@@ -10,13 +10,12 @@ export default [
10
10
  'main header label': 'Main header',
11
11
  'open navigation sidebar label': 'Open navigation sidebar.',
12
12
  'open complementary sidebar label': 'Open complementary sidebar.',
13
- 'open settings label': 'Show settings',
14
13
  'plugin error message': 'Content failed to render.',
15
14
  'content fallback message': 'Unsupported',
16
15
  'content fallback description':
17
16
  'No plugin had a response for the address you navigated to. Double-check the URL, and ensure you’ve enabled a plugin that supports the object.',
18
17
  'toggle fullscreen label': 'Toggle fullscreen',
19
- 'settings show footer label': 'Show footer (experimental)',
18
+ 'settings show hints label': 'Show hints',
20
19
  'settings native redirect label': 'Enable native url redirect (experimental)',
21
20
  'settings custom slots': 'Theme option (experimental)',
22
21
  'settings new plank position start label': 'Start',
@@ -27,7 +26,6 @@ export default [
27
26
  'undo action label': 'Undo',
28
27
  'undo action alt': 'Undo previous action',
29
28
  'undo close label': 'Dismiss',
30
- 'open comments label': 'Open comments',
31
29
  'error fallback message': 'Unable to open this item',
32
30
  'plank heading fallback label': 'Untitled',
33
31
  'actions menu label': 'Options',
@@ -41,6 +39,12 @@ export default [
41
39
  'settings overscroll centering label': 'Centering',
42
40
  'settings overscroll none label': 'None',
43
41
  'settings flat deck': 'Flatten deck appearance',
42
+
43
+ // ComplementarySidebar
44
+ 'open settings label': 'Show settings',
45
+ 'open comments label': 'Show Comments',
46
+ 'open automation label': 'Show Automation',
47
+ 'open debug label': 'Show Debug',
44
48
  },
45
49
  },
46
50
  },
package/src/types.ts CHANGED
@@ -20,7 +20,7 @@ export const OverscrollOptions = ['none', 'centering'] as const;
20
20
  export type Overscroll = (typeof OverscrollOptions)[number];
21
21
 
22
22
  export type DeckSettingsProps = {
23
- showFooter: boolean;
23
+ showHints: boolean;
24
24
  customSlots: boolean;
25
25
  flatDeck: boolean;
26
26
  enableNativeRedirect: boolean;
@@ -56,9 +56,9 @@ export const calculateOverscroll = (
56
56
  plankSizing: Record<string, number>,
57
57
  sidebarOpen: boolean,
58
58
  complementarySidebarOpen: boolean,
59
- ): Pick<CSSProperties, 'paddingLeft' | 'paddingRight'> | undefined => {
59
+ ): Pick<CSSProperties, 'paddingInlineStart' | 'paddingInlineEnd'> | undefined => {
60
60
  if (!planks?.length) {
61
- return;
61
+ return { paddingInlineStart: 0, paddingInlineEnd: 0 };
62
62
  }
63
63
 
64
64
  // TODO(Zan): Move complementary sidebar size (360px), sidebar size (270px), plank resize handle size (20px) to CSS variables.
@@ -73,7 +73,7 @@ export const calculateOverscroll = (
73
73
  const plankSize = getPlankSize(plank.id);
74
74
  const overscrollPadding = `max(0px, calc(((100dvw - ${sidebarWidth} - ${complementarySidebarWidth} - (${plankSize} + 20px)) / 2)))`;
75
75
 
76
- return { paddingLeft: overscrollPadding, paddingRight: overscrollPadding };
76
+ return { paddingInlineStart: overscrollPadding, paddingInlineEnd: overscrollPadding };
77
77
  } else {
78
78
  // Center the plank on the screen.
79
79
  const first = planks[0];
@@ -83,8 +83,8 @@ export const calculateOverscroll = (
83
83
  const lastSize = getPlankSize(last.id);
84
84
 
85
85
  return {
86
- paddingLeft: `max(0px, calc(((100dvw - (${firstSize} + 20px)) / 2) - ${sidebarWidth}))`,
87
- paddingRight: `max(0px, calc(((100dvw - (${lastSize} + 20px)) / 2) - ${complementarySidebarWidth}))`,
86
+ paddingInlineStart: `max(0px, calc(((100dvw - (${firstSize} + 20px)) / 2) - ${sidebarWidth}))`,
87
+ paddingInlineEnd: `max(0px, calc(((100dvw - (${lastSize} + 20px)) / 2) - ${complementarySidebarWidth}))`,
88
88
  };
89
89
  }
90
90
  };
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport const DECK_PLUGIN = 'dxos.org/plugin/deck';\n\nexport default {\n id: DECK_PLUGIN,\n name: 'Deck',\n};\n"],
5
- "mappings": ";AAIO,IAAMA,cAAc;AAE3B,IAAA,eAAe;EACbC,IAAID;EACJE,MAAM;AACR;",
6
- "names": ["DECK_PLUGIN", "id", "name"]
7
- }