@dxos/plugin-deck 0.8.1-staging.391c573 → 0.8.1-staging.9eaf14f

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 (176) hide show
  1. package/dist/lib/browser/{app-graph-builder-IYHAGFA3.mjs → app-graph-builder-VYZ4IWI3.mjs} +3 -3
  2. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs → check-app-scheme-SEYECDHI.mjs} +2 -2
  3. package/dist/lib/browser/{chunk-YCKJNTKG.mjs → chunk-6ZSOFCPP.mjs} +26 -6
  4. package/dist/lib/browser/chunk-6ZSOFCPP.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-B4LOJUWW.mjs +24 -0
  6. package/dist/lib/browser/{chunk-Z23S33X6.mjs → chunk-FJBMNSUC.mjs} +638 -483
  7. package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-FLOVGNYB.mjs +81 -0
  9. package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-N7TEPFVR.mjs → chunk-NSATFAEE.mjs} +3 -3
  11. package/dist/lib/browser/{chunk-N7TEPFVR.mjs.map → chunk-NSATFAEE.mjs.map} +2 -2
  12. package/dist/lib/browser/{chunk-FYKBOM3C.mjs → chunk-RJNCG4ND.mjs} +66 -40
  13. package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-22AQ5IVX.mjs → chunk-XMCG42ID.mjs} +2 -3
  15. package/dist/lib/browser/chunk-XMCG42ID.mjs.map +7 -0
  16. package/dist/lib/browser/index.mjs +14 -9
  17. package/dist/lib/browser/index.mjs.map +3 -3
  18. package/dist/lib/browser/{intent-resolver-P5BVUQKU.mjs → intent-resolver-UDYKO2QW.mjs} +78 -88
  19. package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +7 -0
  20. package/dist/lib/browser/meta.json +1 -1
  21. package/dist/lib/browser/{react-root-EP4UF3KA.mjs → react-root-XLXN2VEW.mjs} +8 -10
  22. package/dist/lib/browser/react-root-XLXN2VEW.mjs.map +7 -0
  23. package/dist/lib/browser/{react-surface-5B3RLJCD.mjs → react-surface-WNGMZL7I.mjs} +11 -10
  24. package/dist/lib/browser/react-surface-WNGMZL7I.mjs.map +7 -0
  25. package/dist/lib/browser/{settings-X3P2HKQJ.mjs → settings-HMDGSBGO.mjs} +5 -4
  26. package/dist/lib/browser/settings-HMDGSBGO.mjs.map +7 -0
  27. package/dist/lib/browser/{state-2MOTLKVR.mjs → state-7TN26M42.mjs} +7 -11
  28. package/dist/lib/browser/state-7TN26M42.mjs.map +7 -0
  29. package/dist/lib/browser/tools-SC6QEN7R.mjs +78 -0
  30. package/dist/lib/browser/tools-SC6QEN7R.mjs.map +7 -0
  31. package/dist/lib/browser/types.mjs +12 -6
  32. package/dist/lib/browser/{url-handler-MVHTKUYA.mjs → url-handler-ODG4B6NX.mjs} +7 -9
  33. package/dist/lib/browser/url-handler-ODG4B6NX.mjs.map +7 -0
  34. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/capabilities.d.ts +36 -14
  36. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/state.d.ts +18 -6
  42. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/tools.d.ts +1 -0
  44. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  45. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  46. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  47. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
  48. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
  49. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  50. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  51. package/dist/types/src/components/DeckLayout/index.d.ts +1 -0
  52. package/dist/types/src/components/DeckLayout/index.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts +6 -0
  54. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -0
  55. package/dist/types/src/components/DeckSettings/index.d.ts +2 -0
  56. package/dist/types/src/components/DeckSettings/index.d.ts.map +1 -0
  57. package/dist/types/src/components/Plank/Plank.d.ts +14 -0
  58. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -0
  59. package/dist/types/src/components/Plank/Plank.stories.d.ts +8 -0
  60. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -0
  61. package/dist/types/src/components/{DeckLayout → Plank}/PlankControls.d.ts +8 -1
  62. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -0
  63. package/dist/types/src/components/{DeckLayout → Plank}/PlankError.d.ts +2 -2
  64. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -0
  65. package/dist/types/src/components/Plank/PlankHeading.d.ts +20 -0
  66. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -0
  67. package/dist/types/src/components/Plank/PlankLoading.d.ts.map +1 -0
  68. package/dist/types/src/components/Plank/index.d.ts +6 -0
  69. package/dist/types/src/components/Plank/index.d.ts.map +1 -0
  70. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -0
  71. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -0
  72. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -0
  73. package/dist/types/src/components/Sidebar/index.d.ts +4 -0
  74. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -0
  75. package/dist/types/src/components/index.d.ts +1 -1
  76. package/dist/types/src/components/index.d.ts.map +1 -1
  77. package/dist/types/src/events.d.ts +0 -1
  78. package/dist/types/src/events.d.ts.map +1 -1
  79. package/dist/types/src/hooks/index.d.ts +0 -1
  80. package/dist/types/src/hooks/index.d.ts.map +1 -1
  81. package/dist/types/src/index.d.ts +1 -0
  82. package/dist/types/src/index.d.ts.map +1 -1
  83. package/dist/types/src/layout.d.ts +7 -1
  84. package/dist/types/src/layout.d.ts.map +1 -1
  85. package/dist/types/src/meta.d.ts +2 -5
  86. package/dist/types/src/meta.d.ts.map +1 -1
  87. package/dist/types/src/translations.d.ts +4 -0
  88. package/dist/types/src/translations.d.ts.map +1 -1
  89. package/dist/types/src/types.d.ts +50 -48
  90. package/dist/types/src/types.d.ts.map +1 -1
  91. package/dist/types/src/util/index.d.ts +1 -0
  92. package/dist/types/src/util/index.d.ts.map +1 -1
  93. package/dist/types/src/util/set-active.d.ts +2 -2
  94. package/dist/types/src/util/set-active.d.ts.map +1 -1
  95. package/dist/types/src/util/useCompanions.d.ts +8 -0
  96. package/dist/types/src/util/useCompanions.d.ts.map +1 -0
  97. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  98. package/package.json +28 -29
  99. package/src/DeckPlugin.ts +0 -1
  100. package/src/capabilities/capabilities.ts +3 -4
  101. package/src/capabilities/intent-resolver.ts +63 -9
  102. package/src/capabilities/react-root.tsx +1 -9
  103. package/src/capabilities/react-surface.tsx +3 -4
  104. package/src/capabilities/settings.ts +7 -2
  105. package/src/capabilities/state.ts +4 -11
  106. package/src/capabilities/tools.ts +34 -22
  107. package/src/capabilities/url-handler.ts +2 -8
  108. package/src/components/DeckLayout/ActiveNode.tsx +2 -1
  109. package/src/components/DeckLayout/Banner.tsx +5 -3
  110. package/src/components/DeckLayout/ContentEmpty.tsx +1 -1
  111. package/src/components/DeckLayout/DeckLayout.tsx +58 -24
  112. package/src/components/DeckLayout/Fullscreen.tsx +1 -1
  113. package/src/components/DeckLayout/Toast.tsx +1 -1
  114. package/src/components/DeckLayout/index.ts +2 -0
  115. package/src/components/{LayoutSettings.tsx → DeckSettings/DeckSettings.tsx} +15 -10
  116. package/src/components/DeckSettings/index.ts +5 -0
  117. package/src/components/Plank/Plank.stories.tsx +43 -0
  118. package/src/components/Plank/Plank.tsx +230 -0
  119. package/src/components/{DeckLayout → Plank}/PlankControls.tsx +73 -27
  120. package/src/components/{DeckLayout → Plank}/PlankError.tsx +3 -3
  121. package/src/components/Plank/PlankHeading.tsx +207 -0
  122. package/src/components/Plank/index.ts +9 -0
  123. package/src/components/{DeckLayout → Sidebar}/ComplementarySidebar.tsx +65 -81
  124. package/src/components/Sidebar/index.ts +7 -0
  125. package/src/components/index.ts +1 -1
  126. package/src/events.ts +0 -1
  127. package/src/hooks/index.ts +0 -1
  128. package/src/index.ts +1 -0
  129. package/src/layout.ts +19 -2
  130. package/src/meta.ts +4 -4
  131. package/src/translations.ts +4 -0
  132. package/src/types.ts +81 -79
  133. package/src/util/index.ts +1 -0
  134. package/src/util/set-active.ts +2 -2
  135. package/src/util/useCompanions.ts +18 -0
  136. package/src/util/useHoistStatusbar.ts +2 -2
  137. package/dist/lib/browser/chunk-22AQ5IVX.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-FYKBOM3C.mjs.map +0 -7
  139. package/dist/lib/browser/chunk-IZ5RPJ6T.mjs +0 -24
  140. package/dist/lib/browser/chunk-YCKJNTKG.mjs.map +0 -7
  141. package/dist/lib/browser/chunk-Z23S33X6.mjs.map +0 -7
  142. package/dist/lib/browser/intent-resolver-P5BVUQKU.mjs.map +0 -7
  143. package/dist/lib/browser/react-root-EP4UF3KA.mjs.map +0 -7
  144. package/dist/lib/browser/react-surface-5B3RLJCD.mjs.map +0 -7
  145. package/dist/lib/browser/settings-X3P2HKQJ.mjs.map +0 -7
  146. package/dist/lib/browser/state-2MOTLKVR.mjs.map +0 -7
  147. package/dist/lib/browser/tools-64LXGLYR.mjs +0 -59
  148. package/dist/lib/browser/tools-64LXGLYR.mjs.map +0 -7
  149. package/dist/lib/browser/url-handler-MVHTKUYA.mjs.map +0 -7
  150. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +0 -1
  151. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +0 -15
  152. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +0 -1
  153. package/dist/types/src/components/DeckLayout/Plank.d.ts +0 -13
  154. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +0 -1
  155. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +0 -1
  156. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +0 -1
  157. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +0 -1
  158. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +0 -1
  159. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +0 -1
  160. package/dist/types/src/components/LayoutSettings.d.ts +0 -6
  161. package/dist/types/src/components/LayoutSettings.d.ts.map +0 -1
  162. package/dist/types/src/hooks/useNode.d.ts +0 -11
  163. package/dist/types/src/hooks/useNode.d.ts.map +0 -1
  164. package/src/components/DeckLayout/NodePlankHeading.tsx +0 -148
  165. package/src/components/DeckLayout/Plank.tsx +0 -149
  166. package/src/hooks/useNode.ts +0 -46
  167. /package/dist/lib/browser/{app-graph-builder-IYHAGFA3.mjs.map → app-graph-builder-VYZ4IWI3.mjs.map} +0 -0
  168. /package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs.map → check-app-scheme-SEYECDHI.mjs.map} +0 -0
  169. /package/dist/lib/browser/{chunk-IZ5RPJ6T.mjs.map → chunk-B4LOJUWW.mjs.map} +0 -0
  170. /package/dist/types/src/components/{DeckLayout → Plank}/PlankLoading.d.ts +0 -0
  171. /package/dist/types/src/components/{DeckLayout → Sidebar}/ComplementarySidebar.d.ts +0 -0
  172. /package/dist/types/src/components/{DeckLayout → Sidebar}/Sidebar.d.ts +0 -0
  173. /package/dist/types/src/components/{DeckLayout → Sidebar}/SidebarButton.d.ts +0 -0
  174. /package/src/components/{DeckLayout → Plank}/PlankLoading.tsx +0 -0
  175. /package/src/components/{DeckLayout → Sidebar}/Sidebar.tsx +0 -0
  176. /package/src/components/{DeckLayout → Sidebar}/SidebarButton.tsx +0 -0
@@ -0,0 +1,207 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { Fragment, memo, useCallback, useEffect, useMemo, type MouseEvent } from 'react';
6
+
7
+ import { createIntent, LayoutAction, Surface, useAppGraph, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { type Node } from '@dxos/plugin-graph';
9
+ import { Icon, IconButton, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
10
+ import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
11
+ import { TextTooltip } from '@dxos/react-ui-text-tooltip';
12
+
13
+ import { PlankCompanionControls, PlankControls } from './PlankControls';
14
+ import { parseEntryId } from '../../layout';
15
+ import { DECK_PLUGIN } from '../../meta';
16
+ import { PLANK_COMPANION_TYPE, DeckAction, type ResolvedPart } from '../../types';
17
+ import { useBreakpoints } from '../../util';
18
+ import { soloInlinePadding } from '../fragments';
19
+
20
+ export type PlankHeadingProps = {
21
+ id: string;
22
+ part: ResolvedPart;
23
+ node?: Node;
24
+ deckEnabled?: boolean;
25
+ canIncrementStart?: boolean;
26
+ canIncrementEnd?: boolean;
27
+ popoverAnchorId?: string;
28
+ primaryId?: string;
29
+ pending?: boolean;
30
+ companioned?: 'primary' | 'companion';
31
+ companions?: Node[];
32
+ actions?: StackItemSigilAction[];
33
+ };
34
+
35
+ export const PlankHeading = memo(
36
+ ({
37
+ id,
38
+ part,
39
+ node,
40
+ deckEnabled,
41
+ canIncrementStart,
42
+ canIncrementEnd,
43
+ popoverAnchorId,
44
+ primaryId,
45
+ pending,
46
+ companioned,
47
+ companions,
48
+ actions = [],
49
+ }: PlankHeadingProps) => {
50
+ const { t } = useTranslation(DECK_PLUGIN);
51
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
52
+ const { graph } = useAppGraph();
53
+ const breakpoint = useBreakpoints();
54
+ const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
55
+ const label = pending
56
+ ? t('pending heading')
57
+ : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
58
+
59
+ const isCompanionNode = node?.type === PLANK_COMPANION_TYPE;
60
+
61
+ useEffect(() => {
62
+ const frame = requestAnimationFrame(() => {
63
+ // Load actions for the node.
64
+ node && graph.actions(node);
65
+ });
66
+
67
+ return () => cancelAnimationFrame(frame);
68
+ }, [node]);
69
+
70
+ const attendableId = primaryId ?? id;
71
+ const capabilities = useMemo(
72
+ () => ({
73
+ deck: deckEnabled ?? true,
74
+ solo: breakpoint !== 'mobile' && (part === 'solo' || part === 'deck'),
75
+ incrementStart: canIncrementStart,
76
+ incrementEnd: canIncrementEnd,
77
+ companion: !isCompanionNode && companions && companions.length > 0,
78
+ }),
79
+ [breakpoint, part, companions, canIncrementStart, canIncrementEnd, isCompanionNode, deckEnabled],
80
+ );
81
+
82
+ const { variant } = parseEntryId(id);
83
+ const sigilActions = useMemo(() => {
84
+ if (!node) {
85
+ return undefined;
86
+ } else if (variant) {
87
+ return [];
88
+ } else {
89
+ return [actions, graph.actions(node)].filter((a) => a.length > 0);
90
+ }
91
+ }, [actions, node, variant, graph]);
92
+
93
+ const handleAction = useCallback((action: StackItemSigilAction) => {
94
+ typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN });
95
+ }, []);
96
+
97
+ const handlePlankAction = useCallback(
98
+ (eventType: DeckAction.PartAdjustment) => {
99
+ if (eventType === 'solo') {
100
+ return dispatch(createIntent(DeckAction.Adjust, { type: eventType, id }));
101
+ } else if (eventType === 'close') {
102
+ if (part === 'complementary') {
103
+ return dispatch(
104
+ createIntent(LayoutAction.UpdateComplementary, {
105
+ part: 'complementary',
106
+ options: { state: 'collapsed' },
107
+ }),
108
+ );
109
+ } else {
110
+ return dispatch(
111
+ createIntent(LayoutAction.Close, { part: 'main', subject: [id], options: { state: false } }),
112
+ );
113
+ }
114
+ } else {
115
+ return dispatch(createIntent(DeckAction.Adjust, { type: eventType, id }));
116
+ }
117
+ },
118
+ [dispatch, id, part],
119
+ );
120
+
121
+ const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
122
+
123
+ const handleTabClick = useCallback(
124
+ (event: MouseEvent) => {
125
+ const target = (event.target as HTMLElement).closest('[data-id]') as HTMLElement | null;
126
+ const tabId = target?.dataset?.id;
127
+ if (primaryId && tabId) {
128
+ void dispatch(
129
+ createIntent(DeckAction.ChangeCompanion, {
130
+ primary: primaryId,
131
+ companion: tabId,
132
+ }),
133
+ );
134
+ }
135
+ },
136
+ [primaryId],
137
+ );
138
+
139
+ return (
140
+ <StackItem.Heading
141
+ classNames={[
142
+ 'plb-1 border-be border-separator items-stretch gap-1 sticky inline-start-12 app-drag min-is-0 layout-contain',
143
+ part === 'solo' ? soloInlinePadding : 'pli-1',
144
+ ]}
145
+ >
146
+ {companions && isCompanionNode ? (
147
+ <div role='none' className='flex-1 min-is-0 overflow-x-auto scrollbar-thin flex gap-1'>
148
+ {companions.map(({ id, properties: { icon, label } }) => (
149
+ <IconButton
150
+ key={id}
151
+ data-id={id}
152
+ icon={icon}
153
+ iconOnly={node?.id !== id}
154
+ label={toLocalizedString(label, t)}
155
+ size={5}
156
+ variant={node?.id === id ? 'primary' : 'default'}
157
+ onClick={handleTabClick}
158
+ />
159
+ ))}
160
+ </div>
161
+ ) : (
162
+ <>
163
+ <ActionRoot>
164
+ {node && sigilActions ? (
165
+ <StackItem.Sigil
166
+ icon={icon}
167
+ related={part === 'complementary'}
168
+ attendableId={attendableId}
169
+ triggerLabel={t('actions menu label')}
170
+ actions={sigilActions}
171
+ onAction={handleAction}
172
+ >
173
+ <Surface role='menu-footer' data={{ subject: node.data }} />
174
+ </StackItem.Sigil>
175
+ ) : (
176
+ <StackItem.SigilButton>
177
+ <span className='sr-only'>{label}</span>
178
+ <Icon icon={icon} size={5} />
179
+ </StackItem.SigilButton>
180
+ )}
181
+ </ActionRoot>
182
+ <TextTooltip text={label} onlyWhenTruncating>
183
+ <StackItem.HeadingLabel
184
+ attendableId={attendableId}
185
+ related={part === 'complementary'}
186
+ {...(pending && { classNames: 'text-description' })}
187
+ >
188
+ {label}
189
+ </StackItem.HeadingLabel>
190
+ </TextTooltip>
191
+ </>
192
+ )}
193
+ {node && part !== 'complementary' && <Surface role='navbar-end' data={{ subject: node.data }} />}
194
+ {companioned === 'companion' ? (
195
+ <PlankCompanionControls primary={primaryId} />
196
+ ) : (
197
+ <PlankControls
198
+ capabilities={capabilities}
199
+ isSolo={part === 'solo'}
200
+ close={part === 'complementary' ? 'minify-end' : true}
201
+ onClick={handlePlankAction}
202
+ />
203
+ )}
204
+ </StackItem.Heading>
205
+ );
206
+ },
207
+ );
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './Plank';
6
+ export * from './PlankControls';
7
+ export * from './PlankError';
8
+ export * from './PlankHeading';
9
+ export * from './PlankLoading';
@@ -3,83 +3,85 @@
3
3
  //
4
4
 
5
5
  import React, {
6
+ Fragment,
7
+ type MouseEvent,
6
8
  type PropsWithChildren,
7
9
  useCallback,
8
10
  useEffect,
9
11
  useMemo,
10
12
  useState,
11
- type MouseEvent,
12
- Fragment,
13
13
  } from 'react';
14
14
 
15
15
  import {
16
- createIntent,
17
16
  LayoutAction,
18
17
  Surface,
18
+ createIntent,
19
19
  useAppGraph,
20
- useCapabilities,
21
20
  useCapability,
22
21
  useIntentDispatcher,
23
22
  } from '@dxos/app-framework';
24
- import {
25
- Main,
26
- useTranslation,
27
- toLocalizedString,
28
- IconButton,
29
- ScrollArea as NaturalScrollArea,
30
- type Label,
31
- } from '@dxos/react-ui';
32
- import { useAttended } from '@dxos/react-ui-attention';
23
+ import { type Node } from '@dxos/plugin-graph';
24
+ import { Main, useTranslation, toLocalizedString, IconButton, type Label } from '@dxos/react-ui';
33
25
  import { Tabs } from '@dxos/react-ui-tabs';
34
- import { byPosition } from '@dxos/util';
26
+ import { byPosition, type Position } from '@dxos/util';
35
27
 
36
- import { PlankContentError } from './PlankError';
37
- import { PlankLoading } from './PlankLoading';
38
28
  import { ToggleComplementarySidebarButton } from './SidebarButton';
39
29
  import { DeckCapabilities } from '../../capabilities';
40
- import { useNode } from '../../hooks';
41
30
  import { DECK_PLUGIN } from '../../meta';
42
- import { type Panel } from '../../types';
31
+ import { ATTENDABLE_PATH_SEPARATOR, DECK_COMPANION_TYPE } from '../../types';
43
32
  import { layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
33
+ import { PlankContentError, PlankLoading } from '../Plank';
34
+
35
+ const label = ['complementary sidebar title', { ns: DECK_PLUGIN }] satisfies Label;
36
+
37
+ const getCompanionId = (id: string) => {
38
+ const [_, companionId] = id.split(ATTENDABLE_PATH_SEPARATOR);
39
+ return companionId ?? 'never';
40
+ };
41
+
42
+ type DeckCompanion = Node<
43
+ any,
44
+ {
45
+ label: Label;
46
+ icon: string;
47
+ // TODO(burdon): Scroll area should be controlled by surface.
48
+ /** If true, the panel will not be wrapped in a scroll area. */
49
+ fixed?: boolean;
50
+ position?: Position;
51
+ }
52
+ >;
53
+
54
+ const useDeckCompanions = (): DeckCompanion[] => {
55
+ const { graph } = useAppGraph();
56
+ const companions = graph.nodes(graph.root, { type: DECK_COMPANION_TYPE }) as DeckCompanion[];
57
+ return companions.toSorted((a, b) => byPosition(a.properties, b.properties));
58
+ };
44
59
 
45
60
  export type ComplementarySidebarProps = {
46
61
  current?: string;
47
62
  };
48
63
 
49
- const label = ['complementary sidebar title', { ns: DECK_PLUGIN }] satisfies Label;
50
-
51
64
  export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) => {
52
65
  const { t } = useTranslation(DECK_PLUGIN);
53
66
  const { dispatchPromise: dispatch } = useIntentDispatcher();
54
67
  const layout = useCapability(DeckCapabilities.MutableDeckState);
55
- const attended = useAttended();
56
- const { graph } = useAppGraph();
57
- const node = useNode(graph, attended[0]);
58
68
  const breakpoint = useBreakpoints();
59
69
  const topbar = layoutAppliesTopbar(breakpoint);
60
70
  const hoistStatusbar = useHoistStatusbar(breakpoint);
61
71
 
62
- const panels = useCapabilities(DeckCapabilities.ComplementaryPanel);
63
- const availablePanels = panels
64
- .filter((panel) => {
65
- if (!node || !panel.filter) {
66
- return true;
67
- }
68
-
69
- return panel.filter(node);
70
- })
71
- .toSorted(byPosition);
72
- const activePanelId = availablePanels.find((panel) => panel.id === current)?.id ?? availablePanels[0]?.id;
73
- const [internalValue, setInternalValue] = useState(activePanelId);
72
+ const companions = useDeckCompanions();
73
+ const activeCompanion = companions.find((companion) => getCompanionId(companion.id) === current) ?? companions.at(0);
74
+ const activeId = getCompanionId(activeCompanion?.id ?? 'never');
75
+ const [internalValue, setInternalValue] = useState(activeId);
74
76
 
75
77
  useEffect(() => {
76
- setInternalValue(activePanelId);
77
- }, [activePanelId]);
78
+ setInternalValue(activeId);
79
+ }, [activeId]);
78
80
 
79
81
  const handleTabClick = useCallback(
80
82
  (event: MouseEvent) => {
81
83
  const nextValue = event.currentTarget.getAttribute('data-value') as string;
82
- if (nextValue === activePanelId) {
84
+ if (nextValue === activeId) {
83
85
  layout.complementarySidebarState = layout.complementarySidebarState === 'expanded' ? 'collapsed' : 'expanded';
84
86
  } else {
85
87
  setInternalValue(nextValue);
@@ -87,21 +89,18 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
87
89
  void dispatch(createIntent(LayoutAction.UpdateComplementary, { part: 'complementary', subject: nextValue }));
88
90
  }
89
91
  },
90
- [layout, activePanelId, dispatch],
92
+ [layout, activeId, dispatch],
91
93
  );
92
94
 
93
95
  const data = useMemo(
94
96
  () =>
95
- node && {
96
- id: node.id,
97
- subject: node.data,
98
- workspace: layout.activeDeck,
99
- popoverAnchorId: layout.popoverAnchorId,
97
+ activeCompanion && {
98
+ id: activeCompanion.id,
99
+ subject: activeCompanion.data,
100
100
  },
101
- [node, layout.popoverAnchorId],
101
+ [activeCompanion?.id, activeCompanion?.data],
102
102
  );
103
103
 
104
- // TODO(burdon): Scroll area should be controlled by surface.
105
104
  return (
106
105
  <Main.ComplementarySidebar
107
106
  label={label}
@@ -110,29 +109,23 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
110
109
  hoistStatusbar && 'block-end-[--statusbar-size]',
111
110
  ]}
112
111
  >
113
- <Tabs.Root
114
- orientation='vertical'
115
- verticalVariant='stateless'
116
- value={internalValue}
117
- attendableId={attended[0]}
118
- classNames='contents'
119
- >
112
+ <Tabs.Root orientation='vertical' verticalVariant='stateless' value={internalValue} classNames='contents'>
120
113
  <div
121
114
  role='none'
122
115
  className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] pbs-[env(safe-area-inset-top)] pbe-[env(safe-area-inset-bottom)] border-is border-separator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
123
116
  >
124
117
  <Tabs.Tablist classNames='grid grid-cols-1 auto-rows-[--rail-action] p-1 gap-1 !overflow-y-auto'>
125
- {availablePanels.map((panel) => (
126
- <Tabs.Tab key={panel.id} value={panel.id} asChild>
118
+ {companions.map((companion) => (
119
+ <Tabs.Tab key={getCompanionId(companion.id)} value={getCompanionId(companion.id)} asChild>
127
120
  <IconButton
128
- label={toLocalizedString(panel.label, t)}
129
- icon={panel.icon}
121
+ label={toLocalizedString(companion.properties.label, t)}
122
+ icon={companion.properties.icon}
130
123
  size={5}
131
124
  iconOnly
132
125
  tooltipSide='left'
133
- data-value={panel.id}
126
+ data-value={getCompanionId(companion.id)}
134
127
  variant={
135
- activePanelId === panel.id
128
+ activeId === getCompanionId(companion.id)
136
129
  ? layout.complementarySidebarState === 'expanded'
137
130
  ? 'primary'
138
131
  : 'default'
@@ -152,16 +145,16 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
152
145
  <ToggleComplementarySidebarButton />
153
146
  </div>
154
147
  </div>
155
- {availablePanels.map((panel) => (
148
+ {companions.map((companion) => (
156
149
  <Tabs.Tabpanel
157
- key={panel.id}
158
- value={panel.id}
150
+ key={getCompanionId(companion.id)}
151
+ value={getCompanionId(companion.id)}
159
152
  classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content] pbs-[env(safe-area-inset-top)]'
160
153
  {...(layout.complementarySidebarState !== 'expanded' && { inert: 'true' })}
161
154
  >
162
155
  <ComplementarySidebarPanel
163
- panel={panel}
164
- activePanelId={activePanelId}
156
+ companion={companion}
157
+ activeId={activeId}
165
158
  data={data}
166
159
  hoistStatusbar={hoistStatusbar}
167
160
  />
@@ -173,45 +166,36 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
173
166
  };
174
167
 
175
168
  type ComplementarySidebarPanelProps = {
176
- panel: Panel;
177
- activePanelId: string;
169
+ companion: DeckCompanion;
170
+ activeId: string;
178
171
  data?: {
179
172
  id: string;
180
173
  subject: any;
181
- workspace: string;
182
- popoverAnchorId?: string;
183
174
  };
184
175
  hoistStatusbar: boolean;
185
176
  };
186
177
 
187
178
  const ScrollArea = ({ children }: PropsWithChildren) => {
188
- return (
189
- <NaturalScrollArea.Root>
190
- <NaturalScrollArea.Viewport>{children}</NaturalScrollArea.Viewport>
191
- <NaturalScrollArea.Scrollbar orientation='vertical'>
192
- <NaturalScrollArea.Thumb />
193
- </NaturalScrollArea.Scrollbar>
194
- </NaturalScrollArea.Root>
195
- );
179
+ return <div className='flex flex-col grow overflow-x-hidden overflow-y-auto scrollbar-thin'>{children}</div>;
196
180
  };
197
181
 
198
- const ComplementarySidebarPanel = ({ panel, activePanelId, data, hoistStatusbar }: ComplementarySidebarPanelProps) => {
182
+ const ComplementarySidebarPanel = ({ companion, activeId, data, hoistStatusbar }: ComplementarySidebarPanelProps) => {
199
183
  const { t } = useTranslation(DECK_PLUGIN);
200
184
 
201
- if (panel.id !== activePanelId || !data) {
185
+ if (getCompanionId(companion.id) !== activeId && !data) {
202
186
  return null;
203
187
  }
204
188
 
205
- const Wrapper = panel.fixed ? Fragment : ScrollArea;
189
+ const Wrapper = companion.properties.fixed ? Fragment : ScrollArea;
206
190
 
207
191
  return (
208
192
  <>
209
193
  <h2 className='flex items-center pli-2 border-separator border-be font-medium'>
210
- {toLocalizedString(panel.label, t)}
194
+ {toLocalizedString(companion.properties.label, t)}
211
195
  </h2>
212
196
  <Wrapper>
213
197
  <Surface
214
- role={`complementary--${activePanelId}`}
198
+ role={`deck-companion--${getCompanionId(companion.id)}`}
215
199
  data={data}
216
200
  fallback={PlankContentError}
217
201
  placeholder={<PlankLoading />}
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './ComplementarySidebar';
6
+ export * from './Sidebar';
7
+ export * from './SidebarButton';
@@ -3,4 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './DeckLayout';
6
- export * from './LayoutSettings';
6
+ export * from './DeckSettings';
package/src/events.ts CHANGED
@@ -7,6 +7,5 @@ import { Events } from '@dxos/app-framework';
7
7
  import { DECK_PLUGIN } from './meta';
8
8
 
9
9
  export namespace DeckEvents {
10
- export const SetupComplementaryPanels = Events.createStateEvent(`${DECK_PLUGIN}/setup-complementary-panels`);
11
10
  export const StateReady = Events.createStateEvent(`${DECK_PLUGIN}/state-ready`);
12
11
  }
@@ -2,6 +2,5 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './useNode';
6
5
  export * from './useNodeActionExpander';
7
6
  export * from './useMainSize';
package/src/index.ts CHANGED
@@ -6,3 +6,4 @@ export { DeckCapabilities } from './capabilities';
6
6
  export { DeckEvents } from './events';
7
7
  export * from './DeckPlugin';
8
8
  export * from './meta';
9
+ export { useCompanions } from './util';
package/src/layout.ts CHANGED
@@ -4,12 +4,29 @@
4
4
 
5
5
  import { produce } from 'immer';
6
6
 
7
+ import { ATTENDABLE_PATH_SEPARATOR } from '@dxos/react-ui-attention';
8
+
7
9
  import { type DeckAction, type NewPlankPositioning } from './types';
8
10
 
9
- type OpenLayoutEntryOptions = { key?: string; positioning?: NewPlankPositioning; pivotId?: string };
11
+ export const createEntryId = (entryId: string, variant?: string) =>
12
+ variant ? `${entryId}${ATTENDABLE_PATH_SEPARATOR}${variant}` : entryId;
13
+
14
+ export const parseEntryId = (entryId: string) => {
15
+ const [id, variant] = entryId.split(ATTENDABLE_PATH_SEPARATOR);
16
+ return { id, variant };
17
+ };
18
+
19
+ type OpenLayoutEntryOptions = {
20
+ key?: string;
21
+ positioning?: NewPlankPositioning;
22
+ pivotId?: string;
23
+ variant?: string;
24
+ };
10
25
 
11
- export const openEntry = (deck: string[], entryId: string, options?: OpenLayoutEntryOptions): string[] => {
26
+ export const openEntry = (deck: string[], _entryId: string, options?: OpenLayoutEntryOptions): string[] => {
12
27
  return produce(deck, (draft) => {
28
+ const entryId = createEntryId(_entryId, options?.variant);
29
+
13
30
  // Check that the entry is not already in the part
14
31
  if (draft.find((id) => id === entryId)) {
15
32
  return;
package/src/meta.ts CHANGED
@@ -6,8 +6,8 @@ import { type PluginMeta } from '@dxos/app-framework';
6
6
 
7
7
  export const DECK_PLUGIN = 'dxos.org/plugin/deck' as const;
8
8
 
9
- export const meta = {
9
+ export const meta: PluginMeta = {
10
10
  id: DECK_PLUGIN,
11
- name: 'Deck',
12
- icon: 'ph--columns--regular',
13
- } satisfies PluginMeta;
11
+ name: 'Layout',
12
+ icon: 'ph--layout--regular',
13
+ };
@@ -47,14 +47,18 @@ export default [
47
47
  'show solo plank label': 'Maximize',
48
48
  'close label': 'Close',
49
49
  'minify label': 'Minify',
50
+ 'open companion label': 'Open companion',
51
+ 'close companion label': 'Close companion',
50
52
  'settings overscroll label': 'Plank scrolling',
51
53
  'select overscroll placeholder': 'Select plank scrolling behavior',
52
54
  'settings overscroll centering label': 'Centering',
53
55
  'settings overscroll none label': 'None',
54
56
  'settings enable statusbar label': 'Show status bar',
57
+ 'settings enable deck label': 'Enable Deck',
55
58
  'close current label': 'Close current plank',
56
59
  'close others label': 'Close other planks',
57
60
  'close all label': 'Close all planks',
61
+ 'companion plank heading fallback label': 'Related',
58
62
  },
59
63
  },
60
64
  },