@dxos/plugin-deck 0.8.4-main.3f58842 → 0.8.4-main.548089c
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.
- package/dist/lib/browser/{app-graph-builder-JTFSDT2I.mjs → app-graph-builder-2DE73K5P.mjs} +17 -41
- package/dist/lib/browser/app-graph-builder-2DE73K5P.mjs.map +7 -0
- package/dist/lib/browser/{check-app-scheme-57U62A3A.mjs → check-app-scheme-HIEVFAAX.mjs} +6 -6
- package/dist/lib/browser/check-app-scheme-HIEVFAAX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-JFTXENFN.mjs → chunk-27I7DJUG.mjs} +9 -9
- package/dist/lib/browser/chunk-27I7DJUG.mjs.map +7 -0
- package/dist/lib/browser/{chunk-D7KTFCUV.mjs → chunk-2YHPKFFA.mjs} +551 -509
- package/dist/lib/browser/chunk-2YHPKFFA.mjs.map +7 -0
- package/dist/lib/browser/chunk-5KMJPIQC.mjs +16 -0
- package/dist/lib/browser/chunk-5KMJPIQC.mjs.map +7 -0
- package/dist/lib/browser/{chunk-QDZO4AJ4.mjs → chunk-ABGD5GHY.mjs} +19 -18
- package/dist/lib/browser/chunk-ABGD5GHY.mjs.map +7 -0
- package/dist/lib/browser/{chunk-F5BQOOEG.mjs → chunk-F3VCCHVL.mjs} +8 -6
- package/dist/lib/browser/chunk-F3VCCHVL.mjs.map +7 -0
- package/dist/lib/browser/chunk-UXLU6CMW.mjs +16 -0
- package/dist/lib/browser/chunk-UXLU6CMW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +20 -14
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-P2OBK5HK.mjs → intent-resolver-Q3KETDSS.mjs} +21 -18
- package/dist/lib/browser/intent-resolver-Q3KETDSS.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-root-PO64J7ML.mjs → react-root-EXJMQNOO.mjs} +13 -12
- package/dist/lib/browser/react-root-EXJMQNOO.mjs.map +7 -0
- package/dist/lib/browser/{react-surface-E45YOVF5.mjs → react-surface-D5R4UUKV.mjs} +13 -13
- package/dist/lib/browser/react-surface-D5R4UUKV.mjs.map +7 -0
- package/dist/lib/browser/{settings-6AJZPZPM.mjs → settings-SDPTOCCM.mjs} +7 -6
- package/dist/lib/browser/{settings-6AJZPZPM.mjs.map → settings-SDPTOCCM.mjs.map} +3 -3
- package/dist/lib/browser/state-QTVNH7N6.mjs +12 -0
- package/dist/lib/browser/toolkit-L5CFXJCF.mjs +52 -0
- package/dist/lib/browser/toolkit-L5CFXJCF.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/browser/{url-handler-7L7M6IKH.mjs → url-handler-QEYGYE2H.mjs} +7 -7
- package/dist/lib/browser/url-handler-QEYGYE2H.mjs.map +7 -0
- package/dist/types/src/DeckPlugin.d.ts +1 -1
- package/dist/types/src/DeckPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/check-app-scheme.d.ts +1 -1
- package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +9 -9
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-root.d.ts +2 -2
- package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/capabilities/settings.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +5 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/capabilities/toolkit.d.ts +25 -0
- package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
- package/dist/types/src/capabilities/tools.d.ts +1 -1
- package/dist/types/src/capabilities/tools.d.ts.map +1 -1
- package/dist/types/src/capabilities/url-handler.d.ts +1 -1
- package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -3
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts +74 -0
- package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/DeckMain.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/DeckMain.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
- package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.d.ts +1 -1
- package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.stories.d.ts +86 -5
- package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankHeading.d.ts +1 -1
- package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -1
- package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -1
- package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +4 -3
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +46 -42
- package/src/DeckPlugin.ts +64 -59
- package/src/capabilities/app-graph-builder.ts +31 -29
- package/src/capabilities/capabilities.ts +3 -3
- package/src/capabilities/check-app-scheme.ts +3 -3
- package/src/capabilities/index.ts +2 -1
- package/src/capabilities/intent-resolver.ts +21 -17
- package/src/capabilities/react-root.tsx +6 -4
- package/src/capabilities/react-surface.tsx +5 -5
- package/src/capabilities/settings.ts +2 -1
- package/src/capabilities/state.ts +9 -5
- package/src/capabilities/toolkit.ts +66 -0
- package/src/capabilities/tools.ts +10 -7
- package/src/capabilities/url-handler.ts +3 -2
- package/src/components/DeckLayout/ActiveNode.tsx +1 -1
- package/src/components/DeckLayout/Banner.tsx +4 -4
- package/src/components/DeckLayout/ContentEmpty.tsx +2 -2
- package/src/components/DeckLayout/DeckLayout.stories.tsx +63 -0
- package/src/components/DeckLayout/DeckLayout.tsx +10 -279
- package/src/components/DeckLayout/DeckMain.tsx +281 -0
- package/src/components/DeckLayout/Dialog.tsx +1 -1
- package/src/components/DeckLayout/Fallback.tsx +2 -2
- package/src/components/DeckLayout/Popover.tsx +2 -11
- package/src/components/DeckLayout/StatusBar.tsx +1 -1
- package/src/components/DeckLayout/Toast.tsx +28 -3
- package/src/components/DeckSettings/DeckSettings.tsx +80 -65
- package/src/components/Plank/Plank.stories.tsx +9 -10
- package/src/components/Plank/Plank.tsx +75 -42
- package/src/components/Plank/PlankControls.tsx +6 -5
- package/src/components/Plank/PlankError.tsx +3 -2
- package/src/components/Plank/PlankHeading.tsx +14 -12
- package/src/components/Sidebar/ComplementarySidebar.tsx +11 -10
- package/src/components/Sidebar/Sidebar.tsx +3 -3
- package/src/components/Sidebar/SidebarButton.tsx +12 -11
- package/src/events.ts +2 -2
- package/src/hooks/useCompanions.ts +1 -1
- package/src/hooks/useDeckCompanions.ts +4 -3
- package/src/hooks/useHoistStatusbar.ts +6 -4
- package/src/hooks/useNodeActionExpander.ts +1 -1
- package/src/meta.ts +6 -3
- package/src/translations.ts +2 -0
- package/src/types/schema.ts +5 -3
- package/dist/lib/browser/app-graph-builder-JTFSDT2I.mjs.map +0 -7
- package/dist/lib/browser/check-app-scheme-57U62A3A.mjs.map +0 -7
- package/dist/lib/browser/chunk-D7KTFCUV.mjs.map +0 -7
- package/dist/lib/browser/chunk-F5BQOOEG.mjs.map +0 -7
- package/dist/lib/browser/chunk-JFTXENFN.mjs.map +0 -7
- package/dist/lib/browser/chunk-M57WD3V6.mjs +0 -16
- package/dist/lib/browser/chunk-M57WD3V6.mjs.map +0 -7
- package/dist/lib/browser/chunk-QDZO4AJ4.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z5KITAZW.mjs +0 -13
- package/dist/lib/browser/chunk-Z5KITAZW.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-P2OBK5HK.mjs.map +0 -7
- package/dist/lib/browser/react-root-PO64J7ML.mjs.map +0 -7
- package/dist/lib/browser/react-surface-E45YOVF5.mjs.map +0 -7
- package/dist/lib/browser/state-MVDYX77Y.mjs +0 -12
- package/dist/lib/browser/tools-TKQDPCHJ.mjs +0 -88
- package/dist/lib/browser/tools-TKQDPCHJ.mjs.map +0 -7
- package/dist/lib/browser/url-handler-7L7M6IKH.mjs.map +0 -7
- /package/dist/lib/browser/{state-MVDYX77Y.mjs.map → state-QTVNH7N6.mjs.map} +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useFocusFinders } from '@fluentui/react-tabster';
|
|
5
6
|
import React, {
|
|
6
7
|
type KeyboardEvent,
|
|
7
8
|
type PropsWithChildren,
|
|
@@ -12,30 +13,29 @@ import React, {
|
|
|
12
13
|
useRef,
|
|
13
14
|
} from 'react';
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
Surface,
|
|
18
|
-
createIntent,
|
|
19
|
-
useCapability,
|
|
20
|
-
useAppGraph,
|
|
21
|
-
useIntentDispatcher,
|
|
22
|
-
} from '@dxos/app-framework';
|
|
16
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
17
|
+
import { Surface, useAppGraph, useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
23
18
|
import { debounce } from '@dxos/async';
|
|
24
|
-
import {
|
|
19
|
+
import { type Node, useNode } from '@dxos/plugin-graph';
|
|
25
20
|
import { ATTENDABLE_PATH_SEPARATOR, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
26
21
|
import { StackItem, railGridHorizontal } from '@dxos/react-ui-stack';
|
|
27
22
|
import { mainIntrinsicSize, mx } from '@dxos/react-ui-theme';
|
|
28
23
|
|
|
24
|
+
import { DeckCapabilities } from '../../capabilities';
|
|
25
|
+
import { useCompanions, useMainSize } from '../../hooks';
|
|
26
|
+
import { parseEntryId } from '../../layout';
|
|
27
|
+
import { DeckAction, type DeckSettingsProps, type LayoutMode, type ResolvedPart } from '../../types';
|
|
28
|
+
|
|
29
29
|
import { PlankContentError, PlankError } from './PlankError';
|
|
30
30
|
import { PlankHeading } from './PlankHeading';
|
|
31
31
|
import { PlankLoading } from './PlankLoading';
|
|
32
|
-
import { DeckCapabilities } from '../../capabilities';
|
|
33
|
-
import { useMainSize, useCompanions } from '../../hooks';
|
|
34
|
-
import { parseEntryId } from '../../layout';
|
|
35
|
-
import { DeckAction, type LayoutMode, type ResolvedPart, type DeckSettingsProps } from '../../types';
|
|
36
32
|
|
|
37
33
|
const UNKNOWN_ID = 'unknown_id';
|
|
38
34
|
|
|
35
|
+
//
|
|
36
|
+
// Plank
|
|
37
|
+
//
|
|
38
|
+
|
|
39
39
|
export type PlankProps = Pick<PlankComponentProps, 'layoutMode' | 'part' | 'path' | 'order' | 'active' | 'settings'> & {
|
|
40
40
|
id?: string;
|
|
41
41
|
companionId?: string;
|
|
@@ -67,7 +67,11 @@ export const Plank = memo(({ id = UNKNOWN_ID, companionId, ...props }: PlankProp
|
|
|
67
67
|
const hasCompanion = !!(companionId && currentCompanion);
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
|
-
<PlankContainer
|
|
70
|
+
<PlankContainer
|
|
71
|
+
solo={props.part === 'solo'}
|
|
72
|
+
companion={hasCompanion}
|
|
73
|
+
encapsulate={!!props.settings?.encapsulatedPlanks}
|
|
74
|
+
>
|
|
71
75
|
<PlankComponent
|
|
72
76
|
id={id}
|
|
73
77
|
node={node}
|
|
@@ -91,7 +95,13 @@ export const Plank = memo(({ id = UNKNOWN_ID, companionId, ...props }: PlankProp
|
|
|
91
95
|
);
|
|
92
96
|
});
|
|
93
97
|
|
|
94
|
-
|
|
98
|
+
//
|
|
99
|
+
// PlankContainer
|
|
100
|
+
//
|
|
101
|
+
|
|
102
|
+
type PlankContainerProps = PropsWithChildren<{ solo: boolean; companion: boolean; encapsulate: boolean }>;
|
|
103
|
+
|
|
104
|
+
const PlankContainer = ({ children, solo, companion, encapsulate }: PlankContainerProps) => {
|
|
95
105
|
const sizeAttrs = useMainSize();
|
|
96
106
|
if (!solo) {
|
|
97
107
|
return children;
|
|
@@ -101,7 +111,14 @@ const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo:
|
|
|
101
111
|
return (
|
|
102
112
|
<div
|
|
103
113
|
role='none'
|
|
104
|
-
|
|
114
|
+
data-popover-collision-boundary={true}
|
|
115
|
+
className={mx(
|
|
116
|
+
'absolute inset-[--main-spacing] grid',
|
|
117
|
+
encapsulate && 'border border-separator rounded overflow-hidden',
|
|
118
|
+
companion && 'grid-cols-[6fr_4fr]', // TODO(burdon): Resize.
|
|
119
|
+
railGridHorizontal,
|
|
120
|
+
mainIntrinsicSize,
|
|
121
|
+
)}
|
|
105
122
|
{...sizeAttrs}
|
|
106
123
|
>
|
|
107
124
|
{children}
|
|
@@ -109,6 +126,10 @@ const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo:
|
|
|
109
126
|
);
|
|
110
127
|
};
|
|
111
128
|
|
|
129
|
+
//
|
|
130
|
+
// PlankComponent
|
|
131
|
+
//
|
|
132
|
+
|
|
112
133
|
type PlankComponentProps = {
|
|
113
134
|
layoutMode: LayoutMode;
|
|
114
135
|
id: string;
|
|
@@ -116,7 +137,6 @@ type PlankComponentProps = {
|
|
|
116
137
|
path?: string[];
|
|
117
138
|
order?: number;
|
|
118
139
|
active?: string[];
|
|
119
|
-
// TODO(burdon): Change to role?
|
|
120
140
|
companioned?: 'primary' | 'companion';
|
|
121
141
|
node?: Node;
|
|
122
142
|
primary?: Node;
|
|
@@ -140,6 +160,7 @@ const PlankComponent = memo(
|
|
|
140
160
|
}: PlankComponentProps) => {
|
|
141
161
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
142
162
|
const { deck, popoverAnchorId, scrollIntoView } = useCapability(DeckCapabilities.DeckState);
|
|
163
|
+
const { findFirstFocusable } = useFocusFinders();
|
|
143
164
|
const canResize = layoutMode === 'deck';
|
|
144
165
|
|
|
145
166
|
const attentionAttrs = useAttentionAttributes(primary?.id ?? id);
|
|
@@ -163,18 +184,21 @@ const PlankComponent = memo(
|
|
|
163
184
|
|
|
164
185
|
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
165
186
|
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
166
|
-
if (event.target === event.currentTarget
|
|
167
|
-
|
|
187
|
+
if (event.target === event.currentTarget) {
|
|
188
|
+
switch (event.key) {
|
|
189
|
+
case 'Escape':
|
|
190
|
+
rootElement.current?.closest('main')?.focus();
|
|
191
|
+
break;
|
|
192
|
+
case 'Enter':
|
|
193
|
+
rootElement.current && findFirstFocusable(rootElement.current)?.focus();
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
168
196
|
}
|
|
169
197
|
}, []);
|
|
170
198
|
|
|
171
199
|
useLayoutEffect(() => {
|
|
172
200
|
if (scrollIntoView === id) {
|
|
173
|
-
|
|
174
|
-
// Forcing focus to something smaller than the plank prevents large focus ring in the interim.
|
|
175
|
-
const focusable = rootElement.current?.querySelector('button') || rootElement.current;
|
|
176
|
-
focusable?.focus({ preventScroll: true });
|
|
177
|
-
layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
201
|
+
layoutMode === 'deck' && rootElement.current?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
178
202
|
// Clear the scroll into view state once it has been actioned.
|
|
179
203
|
void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: undefined }));
|
|
180
204
|
}
|
|
@@ -189,34 +213,42 @@ const PlankComponent = memo(
|
|
|
189
213
|
const data = useMemo(
|
|
190
214
|
() =>
|
|
191
215
|
node && {
|
|
216
|
+
attendableId: id,
|
|
192
217
|
subject: node.data,
|
|
193
218
|
companionTo: primary?.data,
|
|
219
|
+
properties: node.properties,
|
|
194
220
|
variant,
|
|
195
221
|
path,
|
|
196
222
|
popoverAnchorId,
|
|
197
223
|
},
|
|
198
|
-
[node, node?.data, path, popoverAnchorId, primary?.data, variant],
|
|
224
|
+
[node, node?.data, node?.properties, path, popoverAnchorId, primary?.data, variant],
|
|
199
225
|
);
|
|
200
226
|
|
|
201
227
|
// TODO(wittjosiah): Change prop to accept a component.
|
|
202
228
|
const placeholder = useMemo(() => <PlankLoading />, []);
|
|
203
229
|
|
|
204
230
|
const Root = part.startsWith('solo') ? 'article' : StackItem.Root;
|
|
231
|
+
const fullscreen = layoutMode === 'solo--fullscreen';
|
|
205
232
|
const className = mx(
|
|
206
233
|
'attention-surface relative dx-focus-ring-inset-over-all density-coarse',
|
|
207
|
-
isSolo && mainIntrinsicSize,
|
|
208
|
-
isSolo && railGridHorizontal,
|
|
209
234
|
isSolo && 'absolute inset-0',
|
|
235
|
+
isSolo && mainIntrinsicSize,
|
|
236
|
+
railGridHorizontal,
|
|
210
237
|
part.startsWith('solo') && 'grid',
|
|
238
|
+
part.startsWith('solo-') && 'grid-rows-subgrid row-span-2 min-is-0',
|
|
239
|
+
fullscreen && 'grid-rows-1',
|
|
211
240
|
part === 'deck' && (companioned === 'companion' ? '!border-separator border-ie' : '!border-separator border-li'),
|
|
212
|
-
part.startsWith('solo-') && 'row-span-2 grid-rows-subgrid min-is-0',
|
|
213
241
|
part === 'solo-companion' && '!border-separator border-is',
|
|
242
|
+
settings?.encapsulatedPlanks &&
|
|
243
|
+
!part.startsWith('solo') &&
|
|
244
|
+
'mli-[--main-spacing] !border-separator border rounded overflow-hidden',
|
|
214
245
|
);
|
|
215
246
|
|
|
216
247
|
return (
|
|
217
248
|
<Root
|
|
218
249
|
ref={rootElement}
|
|
219
250
|
data-testid='deck.plank'
|
|
251
|
+
data-popover-collision-boundary={true}
|
|
220
252
|
tabIndex={0}
|
|
221
253
|
{...(part.startsWith('solo')
|
|
222
254
|
? ({ ...sizeAttrs, className } as any)
|
|
@@ -233,19 +265,21 @@ const PlankComponent = memo(
|
|
|
233
265
|
>
|
|
234
266
|
{node ? (
|
|
235
267
|
<>
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
268
|
+
{!fullscreen && (
|
|
269
|
+
<PlankHeading
|
|
270
|
+
id={id}
|
|
271
|
+
part={part.startsWith('solo-') ? 'solo' : part}
|
|
272
|
+
node={node}
|
|
273
|
+
layoutMode={layoutMode}
|
|
274
|
+
deckEnabled={settings?.enableDeck}
|
|
275
|
+
canIncrementStart={canIncrementStart}
|
|
276
|
+
canIncrementEnd={canIncrementEnd}
|
|
277
|
+
popoverAnchorId={popoverAnchorId}
|
|
278
|
+
primaryId={primary?.id}
|
|
279
|
+
companioned={companioned}
|
|
280
|
+
companions={companions}
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
249
283
|
<Surface
|
|
250
284
|
key={node.id}
|
|
251
285
|
role='article'
|
|
@@ -258,7 +292,6 @@ const PlankComponent = memo(
|
|
|
258
292
|
) : (
|
|
259
293
|
<PlankError id={id} part={part} />
|
|
260
294
|
)}
|
|
261
|
-
|
|
262
295
|
{canResize && <StackItem.ResizeHandle />}
|
|
263
296
|
</Root>
|
|
264
297
|
);
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { forwardRef, useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import { createIntent
|
|
7
|
+
import { createIntent } from '@dxos/app-framework';
|
|
8
|
+
import { useIntentDispatcher } from '@dxos/app-framework/react';
|
|
8
9
|
import { invariant } from '@dxos/invariant';
|
|
9
10
|
import { ButtonGroup, type ButtonGroupProps, type ButtonProps, IconButton, useTranslation } from '@dxos/react-ui';
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import { meta } from '../../meta';
|
|
12
13
|
import { DeckAction, type LayoutMode } from '../../types';
|
|
13
14
|
|
|
14
15
|
export type PlankControlHandler = (event: DeckAction.PartAdjustment) => void;
|
|
@@ -32,7 +33,7 @@ export type PlankControlsProps = Omit<ButtonGroupProps, 'onClick'> & {
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> & { label: string; icon: string }) => {
|
|
35
|
-
return <IconButton
|
|
36
|
+
return <IconButton label={label} icon={icon} iconOnly variant='ghost' tooltipSide='bottom' {...props} />;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
const plankControlSpacing = 'pli-2';
|
|
@@ -43,7 +44,7 @@ type PlankComplimentControlsProps = {
|
|
|
43
44
|
|
|
44
45
|
export const PlankCompanionControls = forwardRef<HTMLDivElement, PlankComplimentControlsProps>(
|
|
45
46
|
({ primary }, forwardedRef) => {
|
|
46
|
-
const { t } = useTranslation(
|
|
47
|
+
const { t } = useTranslation(meta.id);
|
|
47
48
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
48
49
|
const handleCloseCompanion = useCallback(() => {
|
|
49
50
|
invariant(primary);
|
|
@@ -71,7 +72,7 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
|
|
|
71
72
|
{ children, classNames, variant = 'default', capabilities, layoutMode, pin, close = false, onClick, ...props },
|
|
72
73
|
forwardedRef,
|
|
73
74
|
) => {
|
|
74
|
-
const { t } = useTranslation(
|
|
75
|
+
const { t } = useTranslation(meta.id);
|
|
75
76
|
const buttonClassNames =
|
|
76
77
|
variant === 'hide-disabled' ? `disabled:hidden ${plankControlSpacing}` : plankControlSpacing;
|
|
77
78
|
|
|
@@ -8,12 +8,13 @@ import { type Node } from '@dxos/plugin-graph';
|
|
|
8
8
|
import { useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { descriptionMessage, mx } from '@dxos/react-ui-theme';
|
|
10
10
|
|
|
11
|
+
import { meta } from '../../meta';
|
|
12
|
+
|
|
11
13
|
import { PlankHeading, type PlankHeadingProps } from './PlankHeading';
|
|
12
14
|
import { PlankLoading } from './PlankLoading';
|
|
13
|
-
import { DECK_PLUGIN } from '../../meta';
|
|
14
15
|
|
|
15
16
|
export const PlankContentError = ({ error }: { error?: Error }) => {
|
|
16
|
-
const { t } = useTranslation(
|
|
17
|
+
const { t } = useTranslation(meta.id);
|
|
17
18
|
const errorString = error?.toString() ?? '';
|
|
18
19
|
return (
|
|
19
20
|
<div role='none' className='overflow-y-auto p-8 attention-surface grid place-items-center'>
|
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { Fragment, memo, useCallback, useEffect, useMemo
|
|
5
|
+
import React, { Fragment, type MouseEvent, memo, useCallback, useEffect, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
8
|
+
import { Surface, useAppGraph, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
8
9
|
import { type Node } from '@dxos/plugin-graph';
|
|
9
10
|
import { Icon, IconButton, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
10
11
|
import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
|
|
11
12
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
12
13
|
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
|
|
13
14
|
|
|
14
|
-
import { PlankCompanionControls, PlankControls } from './PlankControls';
|
|
15
15
|
import { useBreakpoints } from '../../hooks';
|
|
16
16
|
import { parseEntryId } from '../../layout';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { meta } from '../../meta';
|
|
18
|
+
import { DeckAction, type LayoutMode, PLANK_COMPANION_TYPE, type ResolvedPart } from '../../types';
|
|
19
19
|
import { soloInlinePadding } from '../fragments';
|
|
20
20
|
|
|
21
|
+
import { PlankCompanionControls, PlankControls } from './PlankControls';
|
|
22
|
+
|
|
21
23
|
const MAX_COMPANIONS = 5;
|
|
22
24
|
|
|
23
25
|
export type PlankHeadingProps = {
|
|
@@ -52,14 +54,14 @@ export const PlankHeading = memo(
|
|
|
52
54
|
layoutMode,
|
|
53
55
|
actions = [],
|
|
54
56
|
}: PlankHeadingProps) => {
|
|
55
|
-
const { t } = useTranslation(
|
|
57
|
+
const { t } = useTranslation(meta.id);
|
|
56
58
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
57
59
|
const { graph } = useAppGraph();
|
|
58
60
|
const breakpoint = useBreakpoints();
|
|
59
61
|
const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
|
|
60
62
|
const label = pending
|
|
61
63
|
? t('pending heading')
|
|
62
|
-
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns:
|
|
64
|
+
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: meta.id }], t);
|
|
63
65
|
|
|
64
66
|
const isCompanionNode = node?.type === PLANK_COMPANION_TYPE;
|
|
65
67
|
|
|
@@ -105,7 +107,7 @@ export const PlankHeading = memo(
|
|
|
105
107
|
|
|
106
108
|
const handleAction = useCallback(
|
|
107
109
|
(action: StackItemSigilAction) => {
|
|
108
|
-
typeof action.data === 'function' && void action.data?.({ parent: node, caller:
|
|
110
|
+
typeof action.data === 'function' && void action.data?.({ parent: node, caller: meta.id });
|
|
109
111
|
},
|
|
110
112
|
[node],
|
|
111
113
|
);
|
|
@@ -134,7 +136,7 @@ export const PlankHeading = memo(
|
|
|
134
136
|
[dispatch, id, part],
|
|
135
137
|
);
|
|
136
138
|
|
|
137
|
-
const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${
|
|
139
|
+
const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${meta.id}/${node.id}` ? Popover.Anchor : Fragment;
|
|
138
140
|
|
|
139
141
|
const handleTabClick = useCallback(
|
|
140
142
|
(event: MouseEvent) => {
|
|
@@ -161,7 +163,8 @@ export const PlankHeading = memo(
|
|
|
161
163
|
? [
|
|
162
164
|
hoverableControls,
|
|
163
165
|
hoverableFocusedWithinControls,
|
|
164
|
-
'*:transition-opacity *:opacity-[--controls-opacity] bg-transparent border-transparent transition-[background-color,border-color]
|
|
166
|
+
'*:transition-opacity *:opacity-[--controls-opacity] bg-transparent border-transparent transition-[background-color,border-color]',
|
|
167
|
+
'hover-hover:hover:bg-headerSurface focus-within:bg-headerSurface hover-hover:hover:border-subduedSeparator focus-within:border-subduedSeparator',
|
|
165
168
|
]
|
|
166
169
|
: []),
|
|
167
170
|
]}
|
|
@@ -177,7 +180,6 @@ export const PlankHeading = memo(
|
|
|
177
180
|
icon={icon}
|
|
178
181
|
iconOnly={companions.length > MAX_COMPANIONS && node?.id !== id}
|
|
179
182
|
label={toLocalizedString(label, t)}
|
|
180
|
-
size={5}
|
|
181
183
|
variant={node?.id === id ? 'primary' : 'ghost'}
|
|
182
184
|
onClick={handleTabClick}
|
|
183
185
|
/>
|
|
@@ -200,7 +202,7 @@ export const PlankHeading = memo(
|
|
|
200
202
|
) : (
|
|
201
203
|
<StackItem.SigilButton>
|
|
202
204
|
<span className='sr-only'>{label}</span>
|
|
203
|
-
<Icon icon={icon}
|
|
205
|
+
<Icon icon={icon} />
|
|
204
206
|
</StackItem.SigilButton>
|
|
205
207
|
)}
|
|
206
208
|
</ActionRoot>
|
|
@@ -12,26 +12,28 @@ import React, {
|
|
|
12
12
|
useState,
|
|
13
13
|
} from 'react';
|
|
14
14
|
|
|
15
|
-
import { LayoutAction,
|
|
16
|
-
import {
|
|
15
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
16
|
+
import { Surface, useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
17
|
+
import { IconButton, type Label, Main, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
17
18
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
18
19
|
|
|
19
|
-
import { ToggleComplementarySidebarButton } from './SidebarButton';
|
|
20
20
|
import { DeckCapabilities } from '../../capabilities';
|
|
21
|
-
import { type DeckCompanion, getCompanionId,
|
|
22
|
-
import {
|
|
21
|
+
import { type DeckCompanion, getCompanionId, useBreakpoints, useDeckCompanions, useHoistStatusbar } from '../../hooks';
|
|
22
|
+
import { meta } from '../../meta';
|
|
23
23
|
import { getMode } from '../../types';
|
|
24
24
|
import { layoutAppliesTopbar } from '../../util';
|
|
25
25
|
import { PlankContentError, PlankLoading } from '../Plank';
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
import { ToggleComplementarySidebarButton } from './SidebarButton';
|
|
28
|
+
|
|
29
|
+
const label = ['complementary sidebar title', { ns: meta.id }] satisfies Label;
|
|
28
30
|
|
|
29
31
|
export type ComplementarySidebarProps = {
|
|
30
32
|
current?: string;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) => {
|
|
34
|
-
const { t } = useTranslation(
|
|
36
|
+
const { t } = useTranslation(meta.id);
|
|
35
37
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
36
38
|
const layout = useCapability(DeckCapabilities.MutableDeckState);
|
|
37
39
|
const layoutMode = getMode(layout.deck);
|
|
@@ -98,7 +100,6 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
|
|
|
98
100
|
<IconButton
|
|
99
101
|
label={toLocalizedString(companion.properties.label, t)}
|
|
100
102
|
icon={companion.properties.icon}
|
|
101
|
-
size={5}
|
|
102
103
|
iconOnly
|
|
103
104
|
tooltipSide='left'
|
|
104
105
|
data-value={getCompanionId(companion.id)}
|
|
@@ -129,7 +130,7 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
|
|
|
129
130
|
key={getCompanionId(companion.id)}
|
|
130
131
|
value={getCompanionId(companion.id)}
|
|
131
132
|
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)]'
|
|
132
|
-
{...(layout.complementarySidebarState !== 'expanded' && { inert:
|
|
133
|
+
{...(layout.complementarySidebarState !== 'expanded' && { inert: true })}
|
|
133
134
|
>
|
|
134
135
|
<ComplementarySidebarPanel
|
|
135
136
|
companion={companion}
|
|
@@ -159,7 +160,7 @@ const ScrollArea = ({ children }: PropsWithChildren) => {
|
|
|
159
160
|
};
|
|
160
161
|
|
|
161
162
|
const ComplementarySidebarPanel = ({ companion, activeId, data, hoistStatusbar }: ComplementarySidebarPanelProps) => {
|
|
162
|
-
const { t } = useTranslation(
|
|
163
|
+
const { t } = useTranslation(meta.id);
|
|
163
164
|
|
|
164
165
|
if (getCompanionId(companion.id) !== activeId && !data) {
|
|
165
166
|
return null;
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Surface, useCapability } from '@dxos/app-framework';
|
|
7
|
+
import { Surface, useCapability } from '@dxos/app-framework/react';
|
|
8
8
|
import { type Label, Main } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
10
|
import { DeckCapabilities } from '../../capabilities';
|
|
11
11
|
import { useBreakpoints, useHoistStatusbar } from '../../hooks';
|
|
12
|
-
import {
|
|
12
|
+
import { meta } from '../../meta';
|
|
13
13
|
import { getMode } from '../../types';
|
|
14
14
|
import { layoutAppliesTopbar } from '../../util';
|
|
15
15
|
|
|
16
|
-
const label = ['sidebar title', { ns:
|
|
16
|
+
const label = ['sidebar title', { ns: meta.id }] satisfies Label;
|
|
17
17
|
|
|
18
18
|
export const Sidebar = () => {
|
|
19
19
|
const { popoverAnchorId, activeDeck: current, deck } = useCapability(DeckCapabilities.DeckState);
|
|
@@ -4,24 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
8
|
+
import { useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
8
9
|
import { IconButton, type IconButtonProps, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
9
10
|
|
|
10
11
|
import { DeckCapabilities } from '../../capabilities';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { getCompanionId, useDeckCompanions } from '../../hooks';
|
|
13
|
+
import { meta } from '../../meta';
|
|
13
14
|
|
|
14
15
|
export const ToggleSidebarButton = ({
|
|
15
16
|
classNames,
|
|
16
17
|
variant = 'ghost',
|
|
17
18
|
}: ThemedClassName<Pick<IconButtonProps, 'variant'>>) => {
|
|
18
19
|
const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
|
|
19
|
-
const { t } = useTranslation(
|
|
20
|
+
const { t } = useTranslation(meta.id);
|
|
20
21
|
return (
|
|
21
22
|
<IconButton
|
|
22
23
|
variant={variant}
|
|
23
|
-
iconOnly
|
|
24
24
|
icon='ph--sidebar--regular'
|
|
25
|
+
iconOnly
|
|
25
26
|
size={4}
|
|
26
27
|
label={t('open navigation sidebar label')}
|
|
27
28
|
onClick={() =>
|
|
@@ -34,12 +35,12 @@ export const ToggleSidebarButton = ({
|
|
|
34
35
|
|
|
35
36
|
export const CloseSidebarButton = () => {
|
|
36
37
|
const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
|
|
37
|
-
const { t } = useTranslation(
|
|
38
|
+
const { t } = useTranslation(meta.id);
|
|
38
39
|
return (
|
|
39
40
|
<IconButton
|
|
40
41
|
variant='ghost'
|
|
41
|
-
iconOnly
|
|
42
42
|
icon='ph--caret-line-left--regular'
|
|
43
|
+
iconOnly
|
|
43
44
|
size={4}
|
|
44
45
|
label={t('close navigation sidebar label')}
|
|
45
46
|
onClick={() => (layoutContext.sidebarState = 'collapsed')}
|
|
@@ -55,7 +56,7 @@ export const ToggleComplementarySidebarButton = ({
|
|
|
55
56
|
}: ThemedClassName<{ inR0?: boolean; current?: string }>) => {
|
|
56
57
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
57
58
|
const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
|
|
58
|
-
const { t } = useTranslation(
|
|
59
|
+
const { t } = useTranslation(meta.id);
|
|
59
60
|
|
|
60
61
|
const companions = useDeckCompanions();
|
|
61
62
|
const handleClick = useCallback(async () => {
|
|
@@ -74,14 +75,14 @@ export const ToggleComplementarySidebarButton = ({
|
|
|
74
75
|
|
|
75
76
|
return (
|
|
76
77
|
<IconButton
|
|
77
|
-
iconOnly
|
|
78
|
-
onClick={handleClick}
|
|
79
78
|
variant='ghost'
|
|
80
|
-
label={t('open complementary sidebar label')}
|
|
81
79
|
classNames={['[&>svg]:-scale-x-100', classNames]}
|
|
82
80
|
icon='ph--sidebar-simple--regular'
|
|
81
|
+
iconOnly
|
|
82
|
+
label={t('open complementary sidebar label')}
|
|
83
83
|
size={inR0 ? 5 : 4}
|
|
84
84
|
tooltipSide={inR0 ? 'left' : undefined}
|
|
85
|
+
onClick={handleClick}
|
|
85
86
|
/>
|
|
86
87
|
);
|
|
87
88
|
};
|
package/src/events.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { Events } from '@dxos/app-framework';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { meta } from './meta';
|
|
8
8
|
|
|
9
9
|
export namespace DeckEvents {
|
|
10
|
-
export const StateReady = Events.createStateEvent(`${
|
|
10
|
+
export const StateReady = Events.createStateEvent(`${meta.id}/state-ready`);
|
|
11
11
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Label
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { type Label } from '@dxos/app-framework';
|
|
6
|
+
import { useAppGraph } from '@dxos/app-framework/react';
|
|
7
|
+
import { type Node, ROOT_ID, useConnections } from '@dxos/plugin-graph';
|
|
8
|
+
import { type Position, byPosition } from '@dxos/util';
|
|
8
9
|
|
|
9
10
|
import { ATTENDABLE_PATH_SEPARATOR, DECK_COMPANION_TYPE } from '../types';
|
|
10
11
|
|
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Capabilities
|
|
7
|
+
import { Capabilities } from '@dxos/app-framework';
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/react';
|
|
8
9
|
import { useThemeContext } from '@dxos/react-ui';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import { meta } from '../meta';
|
|
11
12
|
import type { DeckSettingsProps, LayoutMode } from '../types';
|
|
12
13
|
|
|
13
14
|
export const useHoistStatusbar = (breakpoint: string, layoutMode?: LayoutMode): boolean => {
|
|
14
|
-
const enableStatusbar = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value
|
|
15
|
-
.enableStatusbar;
|
|
16
15
|
const { safeAreaPadding } = useThemeContext();
|
|
16
|
+
const enableStatusbar = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(meta.id)?.value
|
|
17
|
+
.enableStatusbar;
|
|
18
|
+
|
|
17
19
|
return useMemo(() => {
|
|
18
20
|
return (
|
|
19
21
|
breakpoint === 'desktop' &&
|
package/src/meta.ts
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type PluginMeta } from '@dxos/app-framework';
|
|
6
|
-
|
|
7
|
-
export const DECK_PLUGIN = 'dxos.org/plugin/deck' as const;
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
8
7
|
|
|
9
8
|
export const meta: PluginMeta = {
|
|
10
|
-
id:
|
|
9
|
+
id: 'dxos.org/plugin/deck',
|
|
11
10
|
name: 'Layout',
|
|
11
|
+
description: trim`
|
|
12
|
+
Flexible layout system for arranging workspace views in tabs, splits, and panels.
|
|
13
|
+
Customize your workspace organization with drag-and-drop layout management.
|
|
14
|
+
`,
|
|
12
15
|
icon: 'ph--layout--regular',
|
|
13
16
|
};
|
package/src/translations.ts
CHANGED
|
@@ -11,6 +11,7 @@ export const translations = [
|
|
|
11
11
|
'en-US': {
|
|
12
12
|
[meta.id]: {
|
|
13
13
|
'plugin name': 'Deck',
|
|
14
|
+
'settings title': 'Deck settings',
|
|
14
15
|
'main header label': 'Main header',
|
|
15
16
|
'open navigation sidebar label': 'Open sidebar',
|
|
16
17
|
'collapse navigation sidebar label': 'Minimize sidebar',
|
|
@@ -58,6 +59,7 @@ export const translations = [
|
|
|
58
59
|
'settings overscroll none label': 'None',
|
|
59
60
|
'settings enable statusbar label': 'Show status bar',
|
|
60
61
|
'settings enable deck label': 'Enable Deck',
|
|
62
|
+
'settings encapsulated planks label': 'Encapsulated planks',
|
|
61
63
|
'close current label': 'Close current plank',
|
|
62
64
|
'close others label': 'Close other planks',
|
|
63
65
|
'close all label': 'Close all planks',
|