@dxos/plugin-deck 0.6.14-main.7bd9c89 → 0.6.14-staging.934c9de

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 (26) hide show
  1. package/dist/lib/browser/index.mjs +123 -127
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  5. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +1 -3
  6. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  7. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +2 -4
  8. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  9. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  10. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +7 -6
  11. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  12. package/dist/types/src/components/DeckLayout/Plank.d.ts +1 -1
  13. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  14. package/dist/types/src/components/DeckLayout/PlankError.d.ts +1 -2
  15. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  16. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  17. package/package.json +32 -31
  18. package/src/DeckPlugin.tsx +13 -25
  19. package/src/components/DeckLayout/ActiveNode.tsx +4 -1
  20. package/src/components/DeckLayout/ComplementarySidebar.tsx +12 -10
  21. package/src/components/DeckLayout/DeckLayout.tsx +7 -18
  22. package/src/components/DeckLayout/NodePlankHeading.tsx +127 -124
  23. package/src/components/DeckLayout/Plank.tsx +28 -19
  24. package/src/components/DeckLayout/PlankError.tsx +1 -9
  25. package/src/components/DeckLayout/Sidebar.tsx +5 -7
  26. package/src/components/LayoutSettings.tsx +13 -13
@@ -3,11 +3,12 @@
3
3
  //
4
4
 
5
5
  import { Sidebar as MenuIcon } from '@phosphor-icons/react';
6
+ import { untracked } from '@preact/signals-core';
6
7
  import React, { useCallback, useEffect, useMemo, useRef, type UIEvent } from 'react';
7
8
 
8
9
  import { type LayoutParts, Surface, type Toast as ToastSchema, firstIdInPart, usePlugin } from '@dxos/app-framework';
10
+ import { type AttentionPluginProvides } from '@dxos/plugin-attention';
9
11
  import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
10
- import { useAttended } from '@dxos/react-ui-attention';
11
12
  import { Deck } from '@dxos/react-ui-deck';
12
13
  import { getSize, mainPaddingTransitions } from '@dxos/react-ui-theme';
13
14
 
@@ -60,7 +61,8 @@ export const DeckLayout = ({
60
61
  } = context;
61
62
  const { t } = useTranslation(DECK_PLUGIN);
62
63
  const { plankSizing } = useDeckContext();
63
- const attended = useAttended();
64
+ // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
65
+ const attentionPlugin = usePlugin<AttentionPluginProvides>('dxos.org/plugin/attention');
64
66
  const searchPlugin = usePlugin('dxos.org/plugin/search');
65
67
  const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
66
68
 
@@ -69,6 +71,7 @@ export const DeckLayout = ({
69
71
 
70
72
  // Ensure the first plank is attended when the deck is first rendered.
71
73
  useEffect(() => {
74
+ const attended = untracked(() => attentionPlugin?.provides.attention.attended ?? []);
72
75
  const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
73
76
  if (attended.length === 0 && firstId) {
74
77
  // TODO(wittjosiah): Focusing the type button is a workaround.
@@ -109,18 +112,6 @@ export const DeckLayout = ({
109
112
  [layoutMode],
110
113
  );
111
114
 
112
- const firstAttendedId = attended[0];
113
- useEffect(() => {
114
- // TODO(burdon): Can we prevent the need to re-scroll since the planks are preserved?
115
- // E.g., hide the deck and just move the solo article?
116
- if (layoutMode === 'deck' && firstAttendedId) {
117
- // setTimeout(() => {
118
- // const el = deckRef.current?.querySelector(`article[data-attendable-id="${firstAttendedId}"]`);
119
- // el?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
120
- // }, 0);
121
- }
122
- }, [layoutMode, firstAttendedId]);
123
-
124
115
  const isEmpty = layoutParts.main?.length === 0 && layoutParts.solo?.length === 0;
125
116
 
126
117
  const padding = useMemo(() => {
@@ -147,8 +138,7 @@ export const DeckLayout = ({
147
138
  }
148
139
  }}
149
140
  >
150
- {/* TODO(burdon): Factor out hook to set document title. */}
151
- <ActiveNode id={firstAttendedId} />
141
+ <ActiveNode />
152
142
 
153
143
  <Main.Root
154
144
  navigationSidebarOpen={context.sidebarOpen}
@@ -178,8 +168,7 @@ export const DeckLayout = ({
178
168
  <Sidebar layoutParts={layoutParts} />
179
169
 
180
170
  {/* Right sidebar. */}
181
- {/* TODO(wittjosiah): Get context from layout parts. */}
182
- <ComplementarySidebar context='comments' layoutParts={layoutParts} flatDeck={flatDeck} />
171
+ <ComplementarySidebar panel={layoutParts.complementary?.[0].id} flatDeck={flatDeck} />
183
172
 
184
173
  {/* Dialog overlay to dismiss dialogs. */}
185
174
  <Main.Overlay />
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { Fragment, useEffect } from 'react';
5
+ import React, { Fragment, memo, useEffect, useMemo } from 'react';
6
6
 
7
7
  import {
8
8
  LayoutAction,
@@ -10,10 +10,7 @@ import {
10
10
  SLUG_PATH_SEPARATOR,
11
11
  Surface,
12
12
  useIntentDispatcher,
13
- indexInPart,
14
- partLength,
15
- type LayoutParts,
16
- type LayoutPart,
13
+ type LayoutCoordinate,
17
14
  } from '@dxos/app-framework';
18
15
  import { type Node, useGraph } from '@dxos/plugin-graph';
19
16
  import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
@@ -22,137 +19,143 @@ import { TextTooltip } from '@dxos/react-ui-text-tooltip';
22
19
 
23
20
  import { DECK_PLUGIN } from '../../meta';
24
21
 
25
- export const NodePlankHeading = ({
26
- node,
27
- id,
28
- layoutParts,
29
- layoutPart,
30
- popoverAnchorId,
31
- pending,
32
- flatDeck,
33
- actions = [],
34
- }: {
22
+ export type NodePlankHeadingProps = {
23
+ coordinate: LayoutCoordinate;
35
24
  node?: Node;
36
- id?: string;
37
- layoutParts?: LayoutParts;
38
- layoutPart?: LayoutPart;
25
+ canIncrementStart?: boolean;
26
+ canIncrementEnd?: boolean;
39
27
  popoverAnchorId?: string;
40
28
  pending?: boolean;
41
29
  flatDeck?: boolean;
42
30
  actions?: PlankHeadingAction[];
43
- }) => {
44
- const { t } = useTranslation(DECK_PLUGIN);
45
- const { graph } = useGraph();
46
- const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
47
- const label = pending
48
- ? t('pending heading')
49
- : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
50
- const dispatch = useIntentDispatcher();
51
- const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
52
- const [isNotMobile] = useMediaQuery('md');
53
-
54
- useEffect(() => {
55
- const frame = requestAnimationFrame(() => {
56
- // Load actions for the node.
57
- node && graph.actions(node);
58
- });
31
+ };
59
32
 
60
- return () => cancelAnimationFrame(frame);
61
- }, [node]);
33
+ export const NodePlankHeading = memo(
34
+ ({
35
+ coordinate,
36
+ node,
37
+ canIncrementStart,
38
+ canIncrementEnd,
39
+ popoverAnchorId,
40
+ pending,
41
+ flatDeck,
42
+ actions = [],
43
+ }: NodePlankHeadingProps) => {
44
+ const { t } = useTranslation(DECK_PLUGIN);
45
+ const { graph } = useGraph();
46
+ const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
47
+ const label = pending
48
+ ? t('pending heading')
49
+ : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
50
+ const dispatch = useIntentDispatcher();
51
+ const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
52
+ const [isNotMobile] = useMediaQuery('md');
62
53
 
63
- // NOTE(Zan): Node ids may now contain a path like `${space}:${id}~comments`
64
- const attendableId = id?.split(SLUG_PATH_SEPARATOR).at(0);
54
+ useEffect(() => {
55
+ const frame = requestAnimationFrame(() => {
56
+ // Load actions for the node.
57
+ node && graph.actions(node);
58
+ });
65
59
 
66
- const layoutCoordinate = layoutPart !== undefined && id !== undefined ? { part: layoutPart, entryId: id } : undefined;
67
- const index = indexInPart(layoutParts, layoutCoordinate);
68
- const length = partLength(layoutParts, layoutPart);
60
+ return () => cancelAnimationFrame(frame);
61
+ }, [node]);
69
62
 
70
- const canIncrementStart =
71
- layoutPart === 'main' && index !== undefined && index > 0 && length !== undefined && length > 1;
72
- const canIncrementEnd = layoutPart === 'main' && index !== undefined && index < length - 1 && length !== undefined;
63
+ const layoutPart = coordinate.part;
64
+ // NOTE(Zan): Node ids may now contain a path like `${space}:${id}~comments`
65
+ const attendableId = coordinate.entryId.split(SLUG_PATH_SEPARATOR).at(0);
66
+ const capabilities = useMemo(
67
+ () => ({
68
+ solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
69
+ incrementStart: canIncrementStart,
70
+ incrementEnd: canIncrementEnd,
71
+ }),
72
+ [isNotMobile, layoutPart, canIncrementStart, canIncrementEnd],
73
+ );
73
74
 
74
- return (
75
- <PlankHeading.Root {...((layoutPart !== 'main' || !flatDeck) && { classNames: 'pie-1 border-b border-separator' })}>
76
- <ActionRoot>
77
- {node ? (
78
- <PlankHeading.ActionsMenu
79
- icon={icon}
80
- related={layoutPart === 'complementary'}
75
+ return (
76
+ <PlankHeading.Root
77
+ {...((layoutPart !== 'main' || !flatDeck) && { classNames: 'pie-1 border-b border-separator' })}
78
+ >
79
+ <ActionRoot>
80
+ {node ? (
81
+ <PlankHeading.ActionsMenu
82
+ icon={icon}
83
+ related={layoutPart === 'complementary'}
84
+ attendableId={attendableId}
85
+ triggerLabel={t('actions menu label')}
86
+ actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
87
+ onAction={(action) =>
88
+ typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
89
+ }
90
+ >
91
+ <Surface role='menu-footer' data={{ object: node.data }} />
92
+ </PlankHeading.ActionsMenu>
93
+ ) : (
94
+ <PlankHeading.Button>
95
+ <span className='sr-only'>{label}</span>
96
+ <Icon icon={icon} size={5} />
97
+ </PlankHeading.Button>
98
+ )}
99
+ </ActionRoot>
100
+ <TextTooltip text={label} onlyWhenTruncating>
101
+ <PlankHeading.Label
81
102
  attendableId={attendableId}
82
- triggerLabel={t('actions menu label')}
83
- actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
84
- onAction={(action) =>
85
- typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
86
- }
103
+ related={layoutPart === 'complementary'}
104
+ {...(pending && { classNames: 'text-description' })}
87
105
  >
88
- <Surface role='menu-footer' data={{ object: node.data }} />
89
- </PlankHeading.ActionsMenu>
90
- ) : (
91
- <PlankHeading.Button>
92
- <span className='sr-only'>{label}</span>
93
- <Icon icon={icon} size={5} />
94
- </PlankHeading.Button>
106
+ {label}
107
+ </PlankHeading.Label>
108
+ </TextTooltip>
109
+ {node && layoutPart !== 'complementary' && (
110
+ // TODO(Zan): What are we doing with layout coordinate here?
111
+ <Surface role='navbar-end' direction='inline-reverse' data={{ object: node.data }} />
95
112
  )}
96
- </ActionRoot>
97
- <TextTooltip text={label} onlyWhenTruncating>
98
- <PlankHeading.Label
99
- attendableId={attendableId}
100
- related={layoutPart === 'complementary'}
101
- {...(pending && { classNames: 'text-description' })}
102
- >
103
- {label}
104
- </PlankHeading.Label>
105
- </TextTooltip>
106
- {node && layoutPart !== 'complementary' && (
107
- // TODO(Zan): What are we doing with layout coordinate here?
108
- <Surface role='navbar-end' direction='inline-reverse' data={{ object: node.data }} />
109
- )}
110
- {/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
111
- <PlankHeading.Controls
112
- capabilities={{
113
- solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
114
- incrementStart: canIncrementStart,
115
- incrementEnd: canIncrementEnd,
116
- }}
117
- isSolo={layoutPart === 'solo'}
118
- onClick={(eventType) => {
119
- if (!layoutPart) {
120
- return;
121
- }
122
-
123
- if (eventType === 'solo') {
124
- return dispatch([
125
- {
126
- action: NavigationAction.ADJUST,
127
- data: { type: eventType, layoutCoordinate: { part: 'main', entryId: id } },
128
- },
129
- ]);
130
- }
113
+ {/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
114
+ <PlankHeading.Controls
115
+ capabilities={capabilities}
116
+ isSolo={layoutPart === 'solo'}
117
+ onClick={(eventType) => {
118
+ if (!layoutPart) {
119
+ return;
120
+ }
131
121
 
132
- // TODO(Zan): Update this to use the new layout actions.
133
- return dispatch(
134
- eventType === 'close'
135
- ? layoutPart === 'complementary'
136
- ? {
137
- action: LayoutAction.SET_LAYOUT,
138
- data: {
139
- element: 'complementary',
140
- state: false,
141
- },
142
- }
143
- : {
144
- action: NavigationAction.CLOSE,
145
- data: {
146
- activeParts: {
147
- [layoutPart]: [id],
148
- },
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],
149
145
  },
150
- }
151
- : { action: NavigationAction.ADJUST, data: { type: eventType, layoutCoordinate } },
152
- );
153
- }}
154
- close={layoutPart === 'complementary' ? 'minify-end' : true}
155
- />
156
- </PlankHeading.Root>
157
- );
158
- };
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
+ </PlankHeading.Root>
159
+ );
160
+ },
161
+ );
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Plus } from '@phosphor-icons/react';
6
- import React, { type KeyboardEvent, useCallback, useLayoutEffect, useRef } from 'react';
6
+ import React, { type KeyboardEvent, memo, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
7
7
 
8
8
  import {
9
9
  LayoutAction,
@@ -15,6 +15,8 @@ import {
15
15
  Surface,
16
16
  useIntentDispatcher,
17
17
  type Layout,
18
+ indexInPart,
19
+ partLength,
18
20
  } from '@dxos/app-framework';
19
21
  import { debounce } from '@dxos/async';
20
22
  import { useGraph } from '@dxos/plugin-graph';
@@ -42,7 +44,7 @@ export type PlankProps = {
42
44
  searchEnabled?: boolean;
43
45
  };
44
46
 
45
- export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layoutMode }: PlankProps) => {
47
+ export const Plank = memo(({ entry, layoutParts, part, flatDeck, searchEnabled, layoutMode }: PlankProps) => {
46
48
  const { t } = useTranslation(DECK_PLUGIN);
47
49
  const dispatch = useIntentDispatcher();
48
50
  const { popoverAnchorId, scrollIntoView } = useLayout();
@@ -53,7 +55,11 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
53
55
  const resizeable = layoutMode === 'deck';
54
56
 
55
57
  const attendableAttrs = useAttendableAttributes(entry.id);
56
- const coordinate: LayoutCoordinate = { part, entryId: entry.id };
58
+ const coordinate: LayoutCoordinate = useMemo(() => ({ part, entryId: entry.id }), [entry.id, part]);
59
+ const index = indexInPart(layoutParts, coordinate);
60
+ const length = partLength(layoutParts, part);
61
+ const canIncrementStart = part === 'main' && index !== undefined && index > 0 && length !== undefined && length > 1;
62
+ const canIncrementEnd = part === 'main' && index !== undefined && index < length - 1 && length !== undefined;
57
63
 
58
64
  const size = plankSizing?.[entry.id] as number | undefined;
59
65
  const setSize = useCallback(
@@ -82,6 +88,19 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
82
88
 
83
89
  const sizeAttrs = useMainSize();
84
90
 
91
+ const data = useMemo(
92
+ () =>
93
+ node && {
94
+ ...(entry.path ? { subject: node.data, path: entry.path } : { object: node.data }),
95
+ coordinate,
96
+ popoverAnchorId,
97
+ },
98
+ [node],
99
+ );
100
+
101
+ // TODO(wittjosiah): Change prop to accept a component.
102
+ const placeholder = useMemo(() => <PlankLoading />, []);
103
+
85
104
  return (
86
105
  <NaturalPlank.Root
87
106
  size={size}
@@ -100,27 +119,17 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
100
119
  {node ? (
101
120
  <>
102
121
  <NodePlankHeading
103
- id={entry.id}
122
+ coordinate={coordinate}
104
123
  node={node}
105
- layoutPart={coordinate.part}
106
- layoutParts={layoutParts}
124
+ canIncrementStart={canIncrementStart}
125
+ canIncrementEnd={canIncrementEnd}
107
126
  popoverAnchorId={popoverAnchorId}
108
127
  flatDeck={flatDeck}
109
128
  />
110
- <Surface
111
- role='article'
112
- data={{
113
- ...(entry.path ? { subject: node.data, path: entry.path } : { object: node.data }),
114
- coordinate,
115
- popoverAnchorId,
116
- }}
117
- limit={1}
118
- fallback={PlankContentError}
119
- placeholder={<PlankLoading />}
120
- />
129
+ <Surface role='article' data={data} limit={1} fallback={PlankContentError} placeholder={placeholder} />
121
130
  </>
122
131
  ) : (
123
- <PlankError layoutCoordinate={coordinate} id={entry.id} flatDeck={flatDeck} />
132
+ <PlankError layoutCoordinate={coordinate} flatDeck={flatDeck} />
124
133
  )}
125
134
  </NaturalPlank.Content>
126
135
  {searchEnabled && resizeable ? (
@@ -166,4 +175,4 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
166
175
  ) : null}
167
176
  </NaturalPlank.Root>
168
177
  );
169
- };
178
+ });
@@ -34,13 +34,11 @@ export const PlankContentError = ({ error }: { error?: Error }) => {
34
34
 
35
35
  export const PlankError = ({
36
36
  layoutCoordinate,
37
- id,
38
37
  node,
39
38
  error,
40
39
  flatDeck,
41
40
  }: {
42
41
  layoutCoordinate: LayoutCoordinate;
43
- id: string;
44
42
  node?: Node;
45
43
  error?: Error;
46
44
  flatDeck?: boolean;
@@ -51,13 +49,7 @@ export const PlankError = ({
51
49
  }, []);
52
50
  return (
53
51
  <>
54
- <NodePlankHeading
55
- node={node}
56
- id={id}
57
- layoutPart={layoutCoordinate.part}
58
- pending={!timedOut}
59
- flatDeck={flatDeck}
60
- />
52
+ <NodePlankHeading coordinate={layoutCoordinate} node={node} pending={!timedOut} flatDeck={flatDeck} />
61
53
  {timedOut ? <PlankContentError error={error} /> : <PlankLoading />}
62
54
  </>
63
55
  );
@@ -6,7 +6,6 @@ import React, { useMemo } from 'react';
6
6
 
7
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';
10
9
 
11
10
  import { useLayout } from '../LayoutContext';
12
11
 
@@ -16,25 +15,24 @@ export type SidebarProps = {
16
15
 
17
16
  export const Sidebar = ({ layoutParts }: SidebarProps) => {
18
17
  const { layoutMode, popoverAnchorId } = useLayout();
19
- const attended = useAttended();
20
18
 
19
+ // TODO(wittjosiah): The activeIds should be path-based to avoid conflicts.
21
20
  const activeIds = useMemo(() => {
22
21
  if (layoutMode === 'solo') {
23
- return new Set<string>(layoutParts?.solo?.map((e) => e.id) ?? []);
22
+ return Array.from(new Set<string>(layoutParts?.solo?.map((e) => e.id) ?? []));
24
23
  } else if (layoutMode === 'deck') {
25
- return new Set<string>(layoutParts?.main?.map((e) => e.id) ?? []);
24
+ return Array.from(new Set<string>(layoutParts?.main?.map((e) => e.id) ?? []));
26
25
  }
27
26
 
28
- return new Set<string>(openIds(layoutParts));
27
+ return Array.from(new Set<string>(openIds(layoutParts)));
29
28
  }, [layoutParts, layoutMode]);
30
29
 
31
30
  const navigationData = useMemo(
32
31
  () => ({
33
32
  popoverAnchorId,
34
33
  activeIds,
35
- attended,
36
34
  }),
37
- [popoverAnchorId, activeIds, attended],
35
+ [popoverAnchorId, activeIds],
38
36
  );
39
37
  return (
40
38
  <Main.NavigationSidebar>
@@ -4,8 +4,8 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { SettingsValue } from '@dxos/plugin-settings';
8
7
  import { Input, Select, useTranslation } from '@dxos/react-ui';
8
+ import { FormInput } from '@dxos/react-ui-data';
9
9
 
10
10
  import { DECK_PLUGIN } from '../meta';
11
11
  import {
@@ -23,7 +23,7 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
23
23
 
24
24
  return (
25
25
  <>
26
- <SettingsValue label={t('select new plank positioning label')}>
26
+ <FormInput label={t('select new plank positioning label')}>
27
27
  <Select.Root
28
28
  value={settings.newPlankPositioning ?? 'start'}
29
29
  onValueChange={(value) => (settings.newPlankPositioning = value as NewPlankPositioning)}
@@ -41,8 +41,8 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
41
41
  </Select.Content>
42
42
  </Select.Portal>
43
43
  </Select.Root>
44
- </SettingsValue>
45
- <SettingsValue label={t('settings overscroll label')}>
44
+ </FormInput>
45
+ <FormInput label={t('settings overscroll label')}>
46
46
  <Select.Root
47
47
  value={settings.overscroll ?? 'none'}
48
48
  onValueChange={(value) => (settings.overscroll = value as Overscroll)}
@@ -60,24 +60,24 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
60
60
  </Select.Content>
61
61
  </Select.Portal>
62
62
  </Select.Root>
63
- </SettingsValue>
64
- <SettingsValue label={t('settings show hints label')}>
63
+ </FormInput>
64
+ <FormInput label={t('settings show hints label')}>
65
65
  <Input.Switch checked={settings.showHints} onCheckedChange={(checked) => (settings.showHints = checked)} />
66
- </SettingsValue>
66
+ </FormInput>
67
67
  {!isSocket && (
68
- <SettingsValue label={t('settings native redirect label')}>
68
+ <FormInput label={t('settings native redirect label')}>
69
69
  <Input.Switch
70
70
  checked={settings.enableNativeRedirect}
71
71
  onCheckedChange={(checked) => (settings.enableNativeRedirect = checked)}
72
72
  />
73
- </SettingsValue>
73
+ </FormInput>
74
74
  )}
75
- <SettingsValue label={t('settings custom slots')}>
75
+ <FormInput label={t('settings custom slots')}>
76
76
  <Input.Switch checked={settings.customSlots} onCheckedChange={(checked) => (settings.customSlots = checked)} />
77
- </SettingsValue>
78
- <SettingsValue label={t('settings flat deck')}>
77
+ </FormInput>
78
+ <FormInput label={t('settings flat deck')}>
79
79
  <Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = checked)} />
80
- </SettingsValue>
80
+ </FormInput>
81
81
  </>
82
82
  );
83
83
  };