@dxos/plugin-deck 0.6.8-main.046e6cf
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/LICENSE +8 -0
- package/README.md +15 -0
- package/dist/lib/browser/chunk-YVHGFQQR.mjs +12 -0
- package/dist/lib/browser/chunk-YVHGFQQR.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1657 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/meta.mjs +9 -0
- package/dist/lib/browser/meta.mjs.map +7 -0
- package/dist/types/src/DeckPlugin.d.ts +15 -0
- package/dist/types/src/DeckPlugin.d.ts.map +1 -0
- package/dist/types/src/components/DeckContext.d.ts +8 -0
- package/dist/types/src/components/DeckContext.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +9 -0
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +25 -0
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Fallback.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/Fallback.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +14 -0
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Plank.d.ts +14 -0
- package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/PlankError.d.ts +14 -0
- package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/PlankLoading.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts +8 -0
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Toast.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/constants.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/constants.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/index.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/index.d.ts.map +1 -0
- package/dist/types/src/components/LayoutContext.d.ts +5 -0
- package/dist/types/src/components/LayoutContext.d.ts.map +1 -0
- package/dist/types/src/components/LayoutSettings.d.ts +6 -0
- package/dist/types/src/components/LayoutSettings.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useNode.d.ts +11 -0
- package/dist/types/src/hooks/useNode.d.ts.map +1 -0
- package/dist/types/src/hooks/useNodeActionExpander.d.ts +3 -0
- package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/layout.d.ts +25 -0
- package/dist/types/src/layout.d.ts.map +1 -0
- package/dist/types/src/layout.test.d.ts +2 -0
- package/dist/types/src/layout.test.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +7 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +41 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +16 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/util/check-app-scheme.d.ts +2 -0
- package/dist/types/src/util/check-app-scheme.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +4 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/layout-parts.d.ts +7 -0
- package/dist/types/src/util/layout-parts.d.ts.map +1 -0
- package/dist/types/src/util/overscroll.d.ts +7 -0
- package/dist/types/src/util/overscroll.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/DeckPlugin.tsx +629 -0
- package/src/components/DeckContext.ts +14 -0
- package/src/components/DeckLayout/ActiveNode.tsx +24 -0
- package/src/components/DeckLayout/ComplementarySidebar.tsx +58 -0
- package/src/components/DeckLayout/ContentEmpty.tsx +21 -0
- package/src/components/DeckLayout/DeckLayout.tsx +270 -0
- package/src/components/DeckLayout/Fallback.tsx +28 -0
- package/src/components/DeckLayout/Fullscreen.tsx +32 -0
- package/src/components/DeckLayout/NodePlankHeading.tsx +160 -0
- package/src/components/DeckLayout/Plank.tsx +142 -0
- package/src/components/DeckLayout/PlankError.tsx +64 -0
- package/src/components/DeckLayout/PlankLoading.tsx +15 -0
- package/src/components/DeckLayout/Sidebar.tsx +43 -0
- package/src/components/DeckLayout/Toast.tsx +48 -0
- package/src/components/DeckLayout/constants.ts +6 -0
- package/src/components/DeckLayout/index.ts +6 -0
- package/src/components/LayoutContext.ts +12 -0
- package/src/components/LayoutSettings.tsx +86 -0
- package/src/components/index.ts +8 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useNode.ts +40 -0
- package/src/hooks/useNodeActionExpander.ts +24 -0
- package/src/index.ts +9 -0
- package/src/layout.test.ts +380 -0
- package/src/layout.ts +245 -0
- package/src/meta.ts +10 -0
- package/src/translations.ts +47 -0
- package/src/types.ts +38 -0
- package/src/util/check-app-scheme.ts +21 -0
- package/src/util/index.ts +7 -0
- package/src/util/layout-parts.ts +12 -0
- package/src/util/overscroll.ts +97 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plus } from '@phosphor-icons/react';
|
|
6
|
+
import React, { useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
LayoutAction,
|
|
10
|
+
type LayoutCoordinate,
|
|
11
|
+
type LayoutEntry,
|
|
12
|
+
type LayoutPart,
|
|
13
|
+
type LayoutParts,
|
|
14
|
+
NavigationAction,
|
|
15
|
+
Surface,
|
|
16
|
+
useIntentDispatcher,
|
|
17
|
+
} from '@dxos/app-framework';
|
|
18
|
+
import { debounce } from '@dxos/async';
|
|
19
|
+
import { useGraph } from '@dxos/plugin-graph';
|
|
20
|
+
import { Button, Tooltip, useTranslation, type ClassNameValue } from '@dxos/react-ui';
|
|
21
|
+
import { createAttendableAttributes } from '@dxos/react-ui-attention';
|
|
22
|
+
import { Plank as NaturalPlank } from '@dxos/react-ui-deck';
|
|
23
|
+
|
|
24
|
+
import { NodePlankHeading } from './NodePlankHeading';
|
|
25
|
+
import { PlankContentError, PlankError } from './PlankError';
|
|
26
|
+
import { PlankLoading } from './PlankLoading';
|
|
27
|
+
import { NAV_ID } from './constants';
|
|
28
|
+
import { DeckAction } from '../../DeckPlugin';
|
|
29
|
+
import { useNode } from '../../hooks';
|
|
30
|
+
import { DECK_PLUGIN } from '../../meta';
|
|
31
|
+
import { useDeckContext } from '../DeckContext';
|
|
32
|
+
import { useLayout } from '../LayoutContext';
|
|
33
|
+
|
|
34
|
+
export type PlankProps = {
|
|
35
|
+
entry: LayoutEntry;
|
|
36
|
+
layoutParts: LayoutParts;
|
|
37
|
+
// TODO(wittjosiah): Remove.
|
|
38
|
+
part: LayoutPart;
|
|
39
|
+
resizeable?: boolean;
|
|
40
|
+
flatDeck?: boolean;
|
|
41
|
+
searchEnabled?: boolean;
|
|
42
|
+
classNames?: ClassNameValue;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Plank = ({ entry, layoutParts, part, resizeable, flatDeck, searchEnabled, classNames }: PlankProps) => {
|
|
46
|
+
const { t } = useTranslation(DECK_PLUGIN);
|
|
47
|
+
const dispatch = useIntentDispatcher();
|
|
48
|
+
const { popoverAnchorId, scrollIntoView } = useLayout();
|
|
49
|
+
const { plankSizing } = useDeckContext();
|
|
50
|
+
const { graph } = useGraph();
|
|
51
|
+
const node = useNode(graph, entry.id);
|
|
52
|
+
|
|
53
|
+
const attendableAttrs = createAttendableAttributes(entry.id);
|
|
54
|
+
|
|
55
|
+
const size = plankSizing?.[entry.id] as number | undefined;
|
|
56
|
+
const setSize = useCallback(
|
|
57
|
+
debounce((newSize: number) => {
|
|
58
|
+
void dispatch({ action: DeckAction.UPDATE_PLANK_SIZE, data: { id: entry.id, size: newSize } });
|
|
59
|
+
}, 200),
|
|
60
|
+
[dispatch, entry.id],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const coordinate: LayoutCoordinate = { part, entryId: entry.id };
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<NaturalPlank.Root size={size} setSize={setSize}>
|
|
67
|
+
<NaturalPlank.Content
|
|
68
|
+
{...attendableAttrs}
|
|
69
|
+
classNames={[!flatDeck && 'surface-base', classNames]}
|
|
70
|
+
scrollIntoViewOnMount={entry.id === scrollIntoView}
|
|
71
|
+
suppressAutofocus={entry.id === NAV_ID || !!node?.properties?.managesAutofocus}
|
|
72
|
+
>
|
|
73
|
+
{node ? (
|
|
74
|
+
<>
|
|
75
|
+
<NodePlankHeading
|
|
76
|
+
layoutPart={coordinate.part}
|
|
77
|
+
layoutParts={layoutParts}
|
|
78
|
+
node={node}
|
|
79
|
+
id={entry.id}
|
|
80
|
+
popoverAnchorId={popoverAnchorId}
|
|
81
|
+
flatDeck={flatDeck}
|
|
82
|
+
/>
|
|
83
|
+
<Surface
|
|
84
|
+
role='article'
|
|
85
|
+
data={{
|
|
86
|
+
...(entry.path ? { subject: node.data, path: entry.path } : { object: node.data }),
|
|
87
|
+
coordinate,
|
|
88
|
+
popoverAnchorId,
|
|
89
|
+
}}
|
|
90
|
+
limit={1}
|
|
91
|
+
fallback={PlankContentError}
|
|
92
|
+
placeholder={<PlankLoading />}
|
|
93
|
+
/>
|
|
94
|
+
</>
|
|
95
|
+
) : (
|
|
96
|
+
<PlankError layoutCoordinate={coordinate} id={entry.id} flatDeck={flatDeck} />
|
|
97
|
+
)}
|
|
98
|
+
</NaturalPlank.Content>
|
|
99
|
+
{searchEnabled && resizeable ? (
|
|
100
|
+
<div role='none' className='grid grid-rows-subgrid row-span-3'>
|
|
101
|
+
<Tooltip.Root>
|
|
102
|
+
<Tooltip.Trigger asChild>
|
|
103
|
+
<Button
|
|
104
|
+
data-testid='plankHeading.open'
|
|
105
|
+
variant='ghost'
|
|
106
|
+
classNames='p-1 w-fit'
|
|
107
|
+
onClick={() =>
|
|
108
|
+
dispatch([
|
|
109
|
+
{
|
|
110
|
+
action: LayoutAction.SET_LAYOUT,
|
|
111
|
+
data: {
|
|
112
|
+
element: 'dialog',
|
|
113
|
+
component: 'dxos.org/plugin/search/Dialog',
|
|
114
|
+
dialogBlockAlign: 'start',
|
|
115
|
+
subject: {
|
|
116
|
+
action: NavigationAction.SET,
|
|
117
|
+
position: 'add-after',
|
|
118
|
+
coordinate,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
])
|
|
123
|
+
}
|
|
124
|
+
>
|
|
125
|
+
<span className='sr-only'>{t('insert plank label')}</span>
|
|
126
|
+
<Plus />
|
|
127
|
+
</Button>
|
|
128
|
+
</Tooltip.Trigger>
|
|
129
|
+
<Tooltip.Portal>
|
|
130
|
+
<Tooltip.Content side='bottom' classNames='z-[70]'>
|
|
131
|
+
{t('insert plank label')}
|
|
132
|
+
</Tooltip.Content>
|
|
133
|
+
</Tooltip.Portal>
|
|
134
|
+
</Tooltip.Root>
|
|
135
|
+
<NaturalPlank.ResizeHandle classNames='row-start-[toolbar-start] row-end-[content-end]' />
|
|
136
|
+
</div>
|
|
137
|
+
) : resizeable ? (
|
|
138
|
+
<NaturalPlank.ResizeHandle classNames='row-span-3' />
|
|
139
|
+
) : null}
|
|
140
|
+
</NaturalPlank.Root>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type LayoutCoordinate } from '@dxos/app-framework';
|
|
8
|
+
import { type Node } from '@dxos/plugin-graph';
|
|
9
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
10
|
+
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
import { NodePlankHeading } from './NodePlankHeading';
|
|
13
|
+
import { PlankLoading } from './PlankLoading';
|
|
14
|
+
import { DECK_PLUGIN } from '../../meta';
|
|
15
|
+
|
|
16
|
+
export const PlankContentError = ({ error }: { error?: Error }) => {
|
|
17
|
+
const { t } = useTranslation(DECK_PLUGIN);
|
|
18
|
+
return (
|
|
19
|
+
<div role='none' className='grid place-items-center row-span-2'>
|
|
20
|
+
<p
|
|
21
|
+
role='alert'
|
|
22
|
+
className={mx(
|
|
23
|
+
descriptionText,
|
|
24
|
+
// TODO(burdon): Factor out common styles for all dialogs.
|
|
25
|
+
'overflow-hidden break-words',
|
|
26
|
+
'place-self-center border border-dashed border-neutral-400/50 rounded-lg text-center p-8 font-normal text-lg',
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
{error ? error.toString() : t('error fallback message')}
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const PlankError = ({
|
|
36
|
+
layoutCoordinate,
|
|
37
|
+
id,
|
|
38
|
+
node,
|
|
39
|
+
error,
|
|
40
|
+
flatDeck,
|
|
41
|
+
}: {
|
|
42
|
+
layoutCoordinate: LayoutCoordinate;
|
|
43
|
+
id: string;
|
|
44
|
+
node?: Node;
|
|
45
|
+
error?: Error;
|
|
46
|
+
flatDeck?: boolean;
|
|
47
|
+
}) => {
|
|
48
|
+
const [timedOut, setTimedOut] = useState(false);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setTimeout(() => setTimedOut(true), 5e3);
|
|
51
|
+
}, []);
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<NodePlankHeading
|
|
55
|
+
node={node}
|
|
56
|
+
id={id}
|
|
57
|
+
layoutPart={layoutCoordinate.part}
|
|
58
|
+
pending={!timedOut}
|
|
59
|
+
flatDeck={flatDeck}
|
|
60
|
+
/>
|
|
61
|
+
{timedOut ? <PlankContentError error={error} /> : <PlankLoading />}
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Status } from '@dxos/react-ui';
|
|
8
|
+
|
|
9
|
+
export const PlankLoading = () => {
|
|
10
|
+
return (
|
|
11
|
+
<div role='none' className='grid bs-[100dvh] place-items-center row-span-2'>
|
|
12
|
+
<Status indeterminate aria-label='Initializing' />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Attention, type LayoutParts, openIds, Surface } from '@dxos/app-framework';
|
|
8
|
+
import { Main } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { useLayout } from '../LayoutContext';
|
|
11
|
+
|
|
12
|
+
export type SidebarProps = {
|
|
13
|
+
attention: Attention;
|
|
14
|
+
layoutParts: LayoutParts;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Sidebar = ({ attention, layoutParts }: SidebarProps) => {
|
|
18
|
+
const { layoutMode, popoverAnchorId } = useLayout();
|
|
19
|
+
|
|
20
|
+
const activeIds = useMemo(() => {
|
|
21
|
+
if (layoutMode === 'solo') {
|
|
22
|
+
return new Set<string>(layoutParts?.solo?.map((e) => e.id) ?? []);
|
|
23
|
+
} else if (layoutMode === 'deck') {
|
|
24
|
+
return new Set<string>(layoutParts?.main?.map((e) => e.id) ?? []);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return new Set<string>(openIds(layoutParts));
|
|
28
|
+
}, [layoutParts, layoutMode]);
|
|
29
|
+
|
|
30
|
+
const navigationData = useMemo(
|
|
31
|
+
() => ({
|
|
32
|
+
popoverAnchorId,
|
|
33
|
+
activeIds,
|
|
34
|
+
attended: attention.attended,
|
|
35
|
+
}),
|
|
36
|
+
[popoverAnchorId, activeIds, attention.attended],
|
|
37
|
+
);
|
|
38
|
+
return (
|
|
39
|
+
<Main.NavigationSidebar>
|
|
40
|
+
<Surface role='navigation' data={{ ...navigationData }} limit={1} />
|
|
41
|
+
</Main.NavigationSidebar>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import type { Toast as ToastProps } from '@dxos/app-framework';
|
|
8
|
+
import { Button, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
// TODO(wittjosiah): Render remaining duration as a progress bar within the toast.
|
|
11
|
+
export const Toast = ({
|
|
12
|
+
id,
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
icon,
|
|
16
|
+
duration,
|
|
17
|
+
actionLabel,
|
|
18
|
+
actionAlt,
|
|
19
|
+
closeLabel,
|
|
20
|
+
onAction,
|
|
21
|
+
onOpenChange,
|
|
22
|
+
}: ToastProps & Pick<ToastRootProps, 'onOpenChange'>) => {
|
|
23
|
+
return (
|
|
24
|
+
<NaturalToast.Root data-testid={id} defaultOpen duration={duration} onOpenChange={onOpenChange}>
|
|
25
|
+
<NaturalToast.Body>
|
|
26
|
+
<NaturalToast.Title>
|
|
27
|
+
{icon?.({ className: 'inline mr-1' })}
|
|
28
|
+
<span>{title}</span>
|
|
29
|
+
</NaturalToast.Title>
|
|
30
|
+
{description && <NaturalToast.Description>{description}</NaturalToast.Description>}
|
|
31
|
+
</NaturalToast.Body>
|
|
32
|
+
<NaturalToast.Actions>
|
|
33
|
+
{onAction && actionAlt && actionLabel && (
|
|
34
|
+
<NaturalToast.Action altText={actionAlt} asChild>
|
|
35
|
+
<Button data-testid='toast.action' variant='primary' onClick={() => onAction?.()}>
|
|
36
|
+
{actionLabel}
|
|
37
|
+
</Button>
|
|
38
|
+
</NaturalToast.Action>
|
|
39
|
+
)}
|
|
40
|
+
{closeLabel && (
|
|
41
|
+
<NaturalToast.Close asChild>
|
|
42
|
+
<Button data-testid='toast.close'>{closeLabel}</Button>
|
|
43
|
+
</NaturalToast.Close>
|
|
44
|
+
)}
|
|
45
|
+
</NaturalToast.Actions>
|
|
46
|
+
</NaturalToast.Root>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Context, createContext, useContext } from 'react';
|
|
6
|
+
|
|
7
|
+
import type { Layout } from '@dxos/app-framework';
|
|
8
|
+
import { raise } from '@dxos/debug';
|
|
9
|
+
|
|
10
|
+
export const LayoutContext: Context<Layout | null> = createContext<Layout | null>(null);
|
|
11
|
+
|
|
12
|
+
export const useLayout = (): Layout => useContext(LayoutContext) ?? raise(new Error('Missing LayoutContext'));
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { SettingsValue } from '@dxos/plugin-settings';
|
|
8
|
+
import { Input, Select, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { DECK_PLUGIN } from '../meta';
|
|
11
|
+
import {
|
|
12
|
+
type NewPlankPositioning,
|
|
13
|
+
NewPlankPositions,
|
|
14
|
+
type DeckSettingsProps,
|
|
15
|
+
type Overscroll,
|
|
16
|
+
OverscrollOptions,
|
|
17
|
+
} from '../types';
|
|
18
|
+
|
|
19
|
+
const isSocket = !!(globalThis as any).__args;
|
|
20
|
+
|
|
21
|
+
export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) => {
|
|
22
|
+
const { t } = useTranslation(DECK_PLUGIN);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<SettingsValue label={t('select new plank positioning label')}>
|
|
27
|
+
<Select.Root
|
|
28
|
+
value={settings.newPlankPositioning ?? 'start'}
|
|
29
|
+
onValueChange={(value) => (settings.newPlankPositioning = value as NewPlankPositioning)}
|
|
30
|
+
>
|
|
31
|
+
<Select.TriggerButton placeholder={t('select new plank positioning placeholder')} />
|
|
32
|
+
<Select.Portal>
|
|
33
|
+
<Select.Content>
|
|
34
|
+
<Select.Viewport>
|
|
35
|
+
{NewPlankPositions.map((position) => (
|
|
36
|
+
<Select.Option key={position} value={position}>
|
|
37
|
+
{t(`settings new plank position ${position} label`)}
|
|
38
|
+
</Select.Option>
|
|
39
|
+
))}
|
|
40
|
+
</Select.Viewport>
|
|
41
|
+
</Select.Content>
|
|
42
|
+
</Select.Portal>
|
|
43
|
+
</Select.Root>
|
|
44
|
+
</SettingsValue>
|
|
45
|
+
<SettingsValue label={t('settings overscroll label')}>
|
|
46
|
+
<Select.Root
|
|
47
|
+
value={settings.overscroll ?? 'none'}
|
|
48
|
+
onValueChange={(value) => (settings.overscroll = value as Overscroll)}
|
|
49
|
+
>
|
|
50
|
+
<Select.TriggerButton placeholder={t('select overscroll placeholder')} />
|
|
51
|
+
<Select.Portal>
|
|
52
|
+
<Select.Content>
|
|
53
|
+
<Select.Viewport>
|
|
54
|
+
{OverscrollOptions.map((option) => (
|
|
55
|
+
<Select.Option key={option} value={option}>
|
|
56
|
+
{t(`settings overscroll ${option} label`)}
|
|
57
|
+
</Select.Option>
|
|
58
|
+
))}
|
|
59
|
+
</Select.Viewport>
|
|
60
|
+
</Select.Content>
|
|
61
|
+
</Select.Portal>
|
|
62
|
+
</Select.Root>
|
|
63
|
+
</SettingsValue>
|
|
64
|
+
<SettingsValue label={t('settings show footer label')}>
|
|
65
|
+
<Input.Switch checked={settings.showFooter} onCheckedChange={(checked) => (settings.showFooter = !!checked)} />
|
|
66
|
+
</SettingsValue>
|
|
67
|
+
{!isSocket && (
|
|
68
|
+
<SettingsValue label={t('settings native redirect label')}>
|
|
69
|
+
<Input.Switch
|
|
70
|
+
checked={settings.enableNativeRedirect}
|
|
71
|
+
onCheckedChange={(checked) => (settings.enableNativeRedirect = !!checked)}
|
|
72
|
+
/>
|
|
73
|
+
</SettingsValue>
|
|
74
|
+
)}
|
|
75
|
+
<SettingsValue label={t('settings custom slots')}>
|
|
76
|
+
<Input.Switch
|
|
77
|
+
checked={settings.customSlots}
|
|
78
|
+
onCheckedChange={(checked) => (settings.customSlots = !!checked)}
|
|
79
|
+
/>
|
|
80
|
+
</SettingsValue>
|
|
81
|
+
<SettingsValue label={t('settings flat deck')}>
|
|
82
|
+
<Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = !!checked)} />
|
|
83
|
+
</SettingsValue>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Graph, type Node } from '@dxos/plugin-graph';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* React hook to get a node from the graph.
|
|
11
|
+
*
|
|
12
|
+
* @param graph Graph to find the node in.
|
|
13
|
+
* @param id Id of the node to find.
|
|
14
|
+
* @param timeout Optional timeout in milliseconds to wait for the node to be found.
|
|
15
|
+
* @returns Node if found, undefined otherwise.
|
|
16
|
+
*/
|
|
17
|
+
// TODO(wittjosiah): Factor out.
|
|
18
|
+
export const useNode = <T = any>(graph: Graph, id?: string, timeout?: number): Node<T> | undefined => {
|
|
19
|
+
const [nodeState, setNodeState] = useState<Node<T> | undefined>(id ? graph.findNode(id) : undefined);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (nodeState?.id === id || !id) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Set timeout did not seem to effectively not block the UI thread.
|
|
27
|
+
const frame = requestAnimationFrame(async () => {
|
|
28
|
+
try {
|
|
29
|
+
const node = await graph.waitForNode(id, timeout);
|
|
30
|
+
if (node) {
|
|
31
|
+
setNodeState(node);
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return () => cancelAnimationFrame(frame);
|
|
37
|
+
}, [graph, id, timeout, nodeState?.id]);
|
|
38
|
+
|
|
39
|
+
return nodeState;
|
|
40
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import { ACTION_GROUP_TYPE, ACTION_TYPE, getGraph, type Node } from '@dxos/plugin-graph';
|
|
8
|
+
|
|
9
|
+
const expandNodeActions = async (node: Node) => {
|
|
10
|
+
const graph = getGraph(node);
|
|
11
|
+
await graph.expand(node, 'outbound', ACTION_GROUP_TYPE);
|
|
12
|
+
await graph.expand(node, 'outbound', ACTION_TYPE);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const useNodeActionExpander = (node?: Node) => {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (node) {
|
|
18
|
+
const frame = requestAnimationFrame(() => {
|
|
19
|
+
void expandNodeActions(node);
|
|
20
|
+
});
|
|
21
|
+
return () => cancelAnimationFrame(frame);
|
|
22
|
+
}
|
|
23
|
+
}, [node]);
|
|
24
|
+
};
|