@dxos/plugin-deck 0.6.12-main.5cc132e → 0.6.12-main.c974201

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.
@@ -3,10 +3,9 @@
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
8
  import {
9
- SLUG_PATH_SEPARATOR,
10
9
  type Attention,
11
10
  type LayoutEntry,
12
11
  type LayoutParts,
@@ -15,7 +14,7 @@ import {
15
14
  firstIdInPart,
16
15
  usePlugin,
17
16
  } from '@dxos/app-framework';
18
- import { Button, Dialog, Main, Popover, useTranslation } from '@dxos/react-ui';
17
+ import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
19
18
  import { Deck } from '@dxos/react-ui-deck';
20
19
  import { getSize } from '@dxos/react-ui-theme';
21
20
 
@@ -73,33 +72,38 @@ export const DeckLayout = ({
73
72
  const searchPlugin = usePlugin('dxos.org/plugin/search');
74
73
  const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
75
74
 
76
- const [scrollLeft, setScrollLeft] = useState<number | null>(null);
77
- const deckRef = useRef<HTMLDivElement | null>(null);
78
- const restoreScrollRef = useRef<boolean>(false);
75
+ const scrollLeftRef = useRef<number | null>();
76
+ const deckRef = useRef<HTMLDivElement>(null);
77
+
78
+ // Ensure the first plank is attended when the deck is first rendered.
79
+ useEffect(() => {
80
+ const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
81
+ if (attention.attended.size === 0 && firstId) {
82
+ // TODO(wittjosiah): Focusing the type button is a workaround.
83
+ // If the plank is directly focused on first load the focus ring appears.
84
+ document.querySelector<HTMLElement>(`article[data-attendable-id="${firstId}"] button`)?.focus();
85
+ }
86
+ }, []);
79
87
 
80
88
  /**
81
89
  * Clear scroll restoration state if the window is resized
82
90
  */
83
91
  const handleResize = useCallback(() => {
84
- setScrollLeft(null);
92
+ scrollLeftRef.current = null;
85
93
  }, []);
94
+
86
95
  useEffect(() => {
87
96
  window.addEventListener('resize', handleResize);
88
97
  return () => window.removeEventListener('resize', handleResize);
89
98
  }, [handleResize]);
90
99
 
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;
100
+ const restoreScroll = useCallback(() => {
101
+ if (deckRef.current && scrollLeftRef.current != null) {
102
+ deckRef.current.scrollLeft = scrollLeftRef.current;
101
103
  }
102
- }, [layoutMode, deckRef.current, scrollLeft]);
104
+ }, []);
105
+
106
+ useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
103
107
 
104
108
  /**
105
109
  * Save scroll position as the user scrolls
@@ -107,20 +111,12 @@ export const DeckLayout = ({
107
111
  const handleScroll = useCallback(
108
112
  (event: UIEvent) => {
109
113
  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);
114
+ scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
112
115
  }
113
116
  },
114
117
  [layoutMode],
115
118
  );
116
119
 
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
120
  const firstAttendedId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
125
121
 
126
122
  useEffect(() => {
@@ -145,10 +141,12 @@ export const DeckLayout = ({
145
141
  return parts;
146
142
  }, [layoutParts.main, layoutParts.solo]);
147
143
 
148
- const padding =
149
- layoutMode === 'deck' && overscroll === 'centering'
150
- ? calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen)
151
- : {};
144
+ const padding = useMemo(() => {
145
+ if (layoutMode === 'deck' && overscroll === 'centering') {
146
+ return calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen);
147
+ }
148
+ return {};
149
+ }, [layoutMode, overscroll, layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen]);
152
150
 
153
151
  if (layoutMode === 'fullscreen') {
154
152
  return <Fullscreen id={fullScreenSlug} />;
@@ -173,25 +171,17 @@ export const DeckLayout = ({
173
171
  <Main.Root
174
172
  navigationSidebarOpen={context.sidebarOpen}
175
173
  onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
176
- {...(complementarySidebarOpen !== null && {
177
- complementarySidebarOpen: /* complementaryAvailable && */ context.complementarySidebarOpen as boolean,
178
- onComplementarySidebarOpenChange: (next) => (context.complementarySidebarOpen = next),
179
- })}
174
+ complementarySidebarOpen={context.complementarySidebarOpen}
175
+ onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
180
176
  >
181
177
  {/* Notch */}
182
178
  <Main.Notch classNames='z-[21]'>
183
179
  <Surface role='notch-start' />
184
- <Button
185
- // disabled={!sidebarAvailable}
186
- onClick={() => (context.sidebarOpen = !context.sidebarOpen)}
187
- variant='ghost'
188
- classNames='p-1'
189
- >
180
+ <Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
190
181
  <span className='sr-only'>{t('open navigation sidebar label')}</span>
191
182
  <MenuIcon weight='light' className={getSize(5)} />
192
183
  </Button>
193
184
  <Button
194
- // disabled={!complementaryAvailable}
195
185
  onClick={() => (context.complementarySidebarOpen = !context.complementarySidebarOpen)}
196
186
  variant='ghost'
197
187
  classNames='p-1'
@@ -206,7 +196,8 @@ export const DeckLayout = ({
206
196
  <Sidebar attention={attention} layoutParts={layoutParts} />
207
197
 
208
198
  {/* Right sidebar. */}
209
- <ComplementarySidebar id={complementarySlug} layoutParts={layoutParts} flatDeck={flatDeck} />
199
+ {/* TODO(wittjosiah): Get context from layout parts. */}
200
+ <ComplementarySidebar context='comments' layoutParts={layoutParts} flatDeck={flatDeck} />
210
201
 
211
202
  {/* Dialog overlay to dismiss dialogs. */}
212
203
  <Main.Overlay />
@@ -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,
@@ -19,8 +17,8 @@ import {
19
17
  type LayoutEntry,
20
18
  } from '@dxos/app-framework';
21
19
  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';
20
+ import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
21
+ import { PlankHeading } from '@dxos/react-ui-deck';
24
22
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
25
23
 
26
24
  import { DECK_PLUGIN } from '../../meta';
@@ -47,7 +45,7 @@ export const NodePlankHeading = ({
47
45
  }) => {
48
46
  const { t } = useTranslation(DECK_PLUGIN);
49
47
  const { graph } = useGraph();
50
- const Icon = node?.properties?.icon ?? Placeholder;
48
+ const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
51
49
  const label = pending
52
50
  ? t('pending heading')
53
51
  : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
@@ -80,7 +78,7 @@ export const NodePlankHeading = ({
80
78
  <ActionRoot>
81
79
  {node ? (
82
80
  <PlankHeading.ActionsMenu
83
- Icon={Icon}
81
+ icon={icon}
84
82
  attendableId={attendableId}
85
83
  triggerLabel={t('actions menu label')}
86
84
  actions={graph.actions(node)}
@@ -93,7 +91,7 @@ export const NodePlankHeading = ({
93
91
  ) : (
94
92
  <PlankHeading.Button>
95
93
  <span className='sr-only'>{label}</span>
96
- <Icon {...plankHeadingIconProps} />
94
+ <Icon icon={icon} size={5} />
97
95
  </PlankHeading.Button>
98
96
  )}
99
97
  </ActionRoot>
@@ -143,7 +141,6 @@ export const NodePlankHeading = ({
143
141
  action: NavigationAction.CLOSE,
144
142
  data: {
145
143
  activeParts: {
146
- complementary: [`${id}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`],
147
144
  [layoutPart]: [id],
148
145
  },
149
146
  },
@@ -151,7 +148,7 @@ export const NodePlankHeading = ({
151
148
  : { action: NavigationAction.ADJUST, data: { type: eventType, layoutCoordinate } },
152
149
  );
153
150
  }}
154
- close={layoutCoordinate?.part === 'complementary' ? 'minify-end' : true}
151
+ close={layoutPart === 'complementary' ? 'minify-end' : true}
155
152
  />
156
153
  </PlankHeading.Root>
157
154
  );
@@ -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
  }