@dxos/plugin-simple-layout 0.8.4-main.52d7546f51 → 0.8.4-main.6fa680abb7
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/{chunk-O3BQBYMW.mjs → chunk-MDPEKLKR.mjs} +181 -183
- package/dist/lib/browser/chunk-MDPEKLKR.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7VLT3S46.mjs → chunk-MRR7PXSM.mjs} +3 -3
- package/dist/lib/browser/{chunk-7VLT3S46.mjs.map → chunk-MRR7PXSM.mjs.map} +1 -1
- package/dist/lib/browser/index.mjs +6 -6
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{operation-resolver-BYRIQOQT.mjs → operation-resolver-VTZ6HZ4B.mjs} +24 -35
- package/dist/lib/browser/operation-resolver-VTZ6HZ4B.mjs.map +7 -0
- package/dist/lib/browser/{react-root-GPTKI5H2.mjs → react-root-WVQYY2JA.mjs} +3 -3
- package/dist/lib/browser/{react-surface-LT5JJTPR.mjs → react-surface-VLBR37ED.mjs} +11 -8
- package/dist/lib/browser/{react-surface-LT5JJTPR.mjs.map → react-surface-VLBR37ED.mjs.map} +3 -3
- package/dist/lib/browser/{state-A3PGDWWZ.mjs → state-TXSMUWYI.mjs} +2 -2
- package/dist/lib/browser/{url-handler-HTIUY6WL.mjs → url-handler-RBRONH7S.mjs} +18 -19
- package/dist/lib/browser/url-handler-RBRONH7S.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-UAWM4B2S.mjs → chunk-DCKASLMP.mjs} +181 -183
- package/dist/lib/node-esm/chunk-DCKASLMP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-VIDE5UMB.mjs → chunk-WMNTJ2MK.mjs} +3 -3
- package/dist/lib/node-esm/{chunk-VIDE5UMB.mjs.map → chunk-WMNTJ2MK.mjs.map} +1 -1
- package/dist/lib/node-esm/index.mjs +6 -6
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{operation-resolver-BDTFNCS2.mjs → operation-resolver-R7CQ6ERU.mjs} +24 -35
- package/dist/lib/node-esm/operation-resolver-R7CQ6ERU.mjs.map +7 -0
- package/dist/lib/node-esm/{react-root-GRG2OAI2.mjs → react-root-XBNDM7BE.mjs} +3 -3
- package/dist/lib/node-esm/{react-surface-TCUSDIN2.mjs → react-surface-U5NHA367.mjs} +11 -8
- package/dist/lib/node-esm/{react-surface-TCUSDIN2.mjs.map → react-surface-U5NHA367.mjs.map} +3 -3
- package/dist/lib/node-esm/{state-ZCFZTTPL.mjs → state-JMX6FAG4.mjs} +2 -2
- package/dist/lib/node-esm/{url-handler-WBVVKVPC.mjs → url-handler-QSMCH3JB.mjs} +18 -19
- package/dist/lib/node-esm/url-handler-QSMCH3JB.mjs.map +7 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-root/react-root.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/index.d.ts +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/spotlight-dismiss.d.ts +1 -1
- package/dist/types/src/capabilities/state/index.d.ts +1 -1
- package/dist/types/src/capabilities/state/state.d.ts +1 -1
- package/dist/types/src/capabilities/url-handler/url-handler.d.ts +3 -3
- package/dist/types/src/capabilities/url-handler/url-handler.d.ts.map +1 -1
- package/dist/types/src/components/ContentError.stories.d.ts +1 -3
- package/dist/types/src/components/ContentError.stories.d.ts.map +1 -1
- package/dist/types/src/components/ContentLoading/ContentLoading.d.ts.map +1 -0
- package/dist/types/src/components/ContentLoading/ContentLoading.stories.d.ts.map +1 -0
- package/dist/types/src/components/ContentLoading/index.d.ts +2 -0
- package/dist/types/src/components/ContentLoading/index.d.ts.map +1 -0
- package/dist/types/src/components/Home/Home.d.ts.map +1 -1
- package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavBranch/NavBranch.d.ts +11 -0
- package/dist/types/src/components/NavBranch/NavBranch.d.ts.map +1 -0
- package/dist/types/src/components/NavBranch/index.d.ts +2 -0
- package/dist/types/src/components/NavBranch/index.d.ts.map +1 -0
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/AppBar.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/Drawer.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/Main.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/NavBar.d.ts.map +1 -1
- package/dist/types/src/components/hooks.d.ts +4 -2
- package/dist/types/src/components/hooks.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/hooks/actions.d.ts +3 -4
- package/dist/types/src/hooks/actions.d.ts.map +1 -1
- package/dist/types/src/hooks/useAppBarProps.d.ts.map +1 -1
- package/dist/types/src/hooks/useDrawerActions.d.ts.map +1 -1
- package/dist/types/src/hooks/useNavbarActions.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -28
- package/src/capabilities/operation-resolver/operation-resolver.ts +19 -34
- package/src/capabilities/react-surface/react-surface.tsx +8 -6
- package/src/capabilities/url-handler/url-handler.ts +11 -35
- package/src/components/ContentError.stories.tsx +7 -6
- package/src/components/{ContentLoading.stories.tsx → ContentLoading/ContentLoading.stories.tsx} +1 -1
- package/src/components/{ContentLoading.tsx → ContentLoading/ContentLoading.tsx} +1 -1
- package/src/components/ContentLoading/index.ts +5 -0
- package/src/components/Dialog/Dialog.tsx +3 -3
- package/src/components/Home/Home.tsx +30 -24
- package/src/components/MobileLayout/MobileLayout.stories.tsx +23 -19
- package/src/components/MobileLayout/MobileLayout.tsx +2 -2
- package/src/components/{Workspace/Workspace.tsx → NavBranch/NavBranch.tsx} +43 -32
- package/src/components/{Workspace → NavBranch}/index.ts +1 -1
- package/src/components/Popover/Popover.tsx +14 -4
- package/src/components/SimpleLayout/AppBar.stories.tsx +2 -2
- package/src/components/SimpleLayout/AppBar.tsx +14 -21
- package/src/components/SimpleLayout/Drawer.tsx +15 -21
- package/src/components/SimpleLayout/Main.tsx +11 -10
- package/src/components/SimpleLayout/NavBar.stories.tsx +8 -8
- package/src/components/SimpleLayout/NavBar.tsx +4 -10
- package/src/components/SimpleLayout/SimpleLayout.stories.tsx +2 -2
- package/src/components/SimpleLayout/SimpleLayout.tsx +1 -1
- package/src/components/hooks.ts +8 -8
- package/src/components/index.ts +1 -1
- package/src/hooks/actions.ts +15 -17
- package/src/hooks/useAppBarProps.ts +8 -5
- package/src/hooks/useCompanions.ts +1 -1
- package/src/hooks/useDrawerActions.ts +9 -7
- package/src/hooks/useNavbarActions.ts +10 -9
- package/src/meta.ts +1 -1
- package/src/types/capabilities.ts +1 -1
- package/dist/lib/browser/chunk-O3BQBYMW.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-BYRIQOQT.mjs.map +0 -7
- package/dist/lib/browser/url-handler-HTIUY6WL.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-UAWM4B2S.mjs.map +0 -7
- package/dist/lib/node-esm/operation-resolver-BDTFNCS2.mjs.map +0 -7
- package/dist/lib/node-esm/url-handler-WBVVKVPC.mjs.map +0 -7
- package/dist/types/src/components/ContentError.d.ts +0 -5
- package/dist/types/src/components/ContentError.d.ts.map +0 -1
- package/dist/types/src/components/ContentLoading.d.ts.map +0 -1
- package/dist/types/src/components/ContentLoading.stories.d.ts.map +0 -1
- package/dist/types/src/components/Workspace/Workspace.d.ts +0 -11
- package/dist/types/src/components/Workspace/Workspace.d.ts.map +0 -1
- package/dist/types/src/components/Workspace/index.d.ts +0 -2
- package/dist/types/src/components/Workspace/index.d.ts.map +0 -1
- package/src/components/ContentError.tsx +0 -23
- /package/dist/lib/browser/{react-root-GPTKI5H2.mjs.map → react-root-WVQYY2JA.mjs.map} +0 -0
- /package/dist/lib/browser/{state-A3PGDWWZ.mjs.map → state-TXSMUWYI.mjs.map} +0 -0
- /package/dist/lib/node-esm/{react-root-GRG2OAI2.mjs.map → react-root-XBNDM7BE.mjs.map} +0 -0
- /package/dist/lib/node-esm/{state-ZCFZTTPL.mjs.map → state-JMX6FAG4.mjs.map} +0 -0
- /package/dist/types/src/components/{ContentLoading.d.ts → ContentLoading/ContentLoading.d.ts} +0 -0
- /package/dist/types/src/components/{ContentLoading.stories.d.ts → ContentLoading/ContentLoading.stories.d.ts} +0 -0
|
@@ -2,65 +2,76 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { useCallback, useEffect, useRef } from 'react';
|
|
5
|
+
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
8
|
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
9
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
10
10
|
import { type Node, useConnections } from '@dxos/plugin-graph';
|
|
11
|
-
import { Avatar, Icon,
|
|
12
|
-
import { Card
|
|
11
|
+
import { Avatar, Icon, Panel, ScrollArea, Toolbar, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { Card } from '@dxos/react-ui';
|
|
13
|
+
import { Mosaic, type MosaicStackTileComponent } from '@dxos/react-ui-mosaic';
|
|
13
14
|
import { SearchList, useSearchListItem, useSearchListResults } from '@dxos/react-ui-searchlist';
|
|
14
15
|
import { mx } from '@dxos/ui-theme';
|
|
15
16
|
|
|
16
17
|
import { meta } from '../../meta';
|
|
17
|
-
import {
|
|
18
|
+
import { useExpandPath } from '../hooks';
|
|
18
19
|
|
|
19
|
-
export type
|
|
20
|
+
export type NavBranchProps = {
|
|
20
21
|
id: string;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Renders the children of a graph branch node as a searchable mosaic list.
|
|
26
|
+
* Used for any node with `role: 'branch'` or a workspace disposition, including
|
|
27
|
+
* spaces, collection sections, type sections, and schema nodes.
|
|
27
28
|
*/
|
|
28
|
-
export const
|
|
29
|
+
export const NavBranch = ({ id }: NavBranchProps) => {
|
|
29
30
|
const { t } = useTranslation(meta.id);
|
|
30
31
|
const { graph } = useAppGraph();
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
useLoadDescendents(id);
|
|
33
|
+
useExpandPath(id);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const children = useConnections(graph, id, 'child');
|
|
36
|
+
|
|
37
|
+
// TODO(wittjosiah): Move alternate-tree nodes to a non-child relation so they don't need filtering.
|
|
38
|
+
const visibleChildren = useMemo(
|
|
39
|
+
() => children.filter((node) => node.properties.disposition !== 'alternate-tree'),
|
|
40
|
+
[children],
|
|
41
|
+
);
|
|
37
42
|
|
|
38
43
|
const { results, handleSearch } = useSearchListResults({
|
|
39
|
-
items:
|
|
44
|
+
items: visibleChildren,
|
|
40
45
|
extract: (child) => toLocalizedString(child.properties.label, t),
|
|
41
46
|
});
|
|
42
47
|
|
|
43
48
|
return (
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
<Toolbar
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
<SearchList.Root onSearch={handleSearch}>
|
|
50
|
+
<Panel.Root>
|
|
51
|
+
<Panel.Toolbar asChild>
|
|
52
|
+
<Toolbar.Root>
|
|
53
|
+
{/* TODO(wittjosiah): Search should be pluggable. Must support searching via ECHO query inside a space. */}
|
|
54
|
+
<SearchList.Input placeholder={t('search placeholder')} autoFocus />
|
|
55
|
+
</Toolbar.Root>
|
|
56
|
+
</Panel.Toolbar>
|
|
57
|
+
<Panel.Content asChild>
|
|
58
|
+
<SearchList.Content>
|
|
59
|
+
<Mosaic.Container asChild>
|
|
60
|
+
<ScrollArea.Root orientation='vertical'>
|
|
61
|
+
<ScrollArea.Viewport classNames='p-2'>
|
|
62
|
+
<Mosaic.Stack items={results} getId={(child) => child.id} Tile={NavBranchTile} />
|
|
63
|
+
</ScrollArea.Viewport>
|
|
64
|
+
</ScrollArea.Root>
|
|
65
|
+
</Mosaic.Container>
|
|
66
|
+
</SearchList.Content>
|
|
67
|
+
</Panel.Content>
|
|
68
|
+
</Panel.Root>
|
|
69
|
+
</SearchList.Root>
|
|
60
70
|
);
|
|
61
71
|
};
|
|
62
72
|
|
|
63
|
-
const
|
|
73
|
+
const NavBranchTile: MosaicStackTileComponent<Node.Node> = (props) => {
|
|
74
|
+
const data = props.data;
|
|
64
75
|
const { t } = useTranslation(meta.id);
|
|
65
76
|
const { invokeSync } = useOperationInvoker();
|
|
66
77
|
const ref = useRef<HTMLDivElement>(null);
|
|
@@ -97,7 +108,7 @@ const WorkspaceChildTile: StackTileComponent<Node.Node> = ({ data }) => {
|
|
|
97
108
|
fullWidth
|
|
98
109
|
tabIndex={-1} // TODO(burdon): Use Mosaic.Focus.
|
|
99
110
|
data-selected={isSelected}
|
|
100
|
-
classNames={mx('dx-focus-ring', isSelected && 'bg-
|
|
111
|
+
classNames={mx('dx-focus-ring', isSelected && 'bg-hover-overlay')}
|
|
101
112
|
onClick={handleSelect}
|
|
102
113
|
>
|
|
103
114
|
<Card.Toolbar density='coarse'>
|
|
@@ -6,8 +6,9 @@ import { createContext } from '@radix-ui/react-context';
|
|
|
6
6
|
import React, { type PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { Surface } from '@dxos/app-framework/ui';
|
|
9
|
+
import { useObjectNavigate } from '@dxos/app-toolkit/ui';
|
|
9
10
|
import { Popover, type PopoverContentInteractOutsideEvent, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
10
|
-
import { Card } from '@dxos/react-ui
|
|
11
|
+
import { Card } from '@dxos/react-ui';
|
|
11
12
|
|
|
12
13
|
import { useSimpleLayoutState } from '../../hooks';
|
|
13
14
|
import { meta } from '../../meta';
|
|
@@ -53,10 +54,15 @@ export const PopoverRoot = ({ children }: PropsWithChildren) => {
|
|
|
53
54
|
);
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
// Extracts the subject from popover content if it has one, otherwise returns the content as-is.
|
|
58
|
+
const getPopoverSubject = (content: unknown): unknown =>
|
|
59
|
+
content && typeof content === 'object' && 'subject' in content ? (content as { subject: unknown }).subject : content;
|
|
60
|
+
|
|
56
61
|
export const PopoverContent = () => {
|
|
57
62
|
const { t } = useTranslation(meta.id);
|
|
58
63
|
const { state, updateState } = useSimpleLayoutState();
|
|
59
64
|
const { setOpen } = useLayoutPopoverContext('PopoverContent');
|
|
65
|
+
const handleNavigate = useObjectNavigate(getPopoverSubject(state.popoverContent));
|
|
60
66
|
|
|
61
67
|
const handleClose = useCallback(() => {
|
|
62
68
|
setOpen(false);
|
|
@@ -102,12 +108,16 @@ export const PopoverContent = () => {
|
|
|
102
108
|
<Popover.Viewport>
|
|
103
109
|
{state.popoverKind === 'base' && <Surface.Surface role='popover' data={state.popoverContent} limit={1} />}
|
|
104
110
|
{state.popoverKind === 'card' && (
|
|
105
|
-
<Card.Root border={false} classNames='
|
|
111
|
+
<Card.Root border={false} classNames='dx-card-popover'>
|
|
106
112
|
<Card.Toolbar>
|
|
107
113
|
{/* TODO(wittjosiah): Cleaner way to handle no drag handle in toolbar? */}
|
|
108
114
|
<span />
|
|
109
|
-
{state.popoverTitle ?
|
|
110
|
-
|
|
115
|
+
{state.popoverTitle ? (
|
|
116
|
+
<Card.Title onClick={handleNavigate}>{toLocalizedString(state.popoverTitle, t)}</Card.Title>
|
|
117
|
+
) : (
|
|
118
|
+
<span />
|
|
119
|
+
)}
|
|
120
|
+
<Card.CloseIconButton onClick={handleClose} />
|
|
111
121
|
</Card.Toolbar>
|
|
112
122
|
<Surface.Surface role='card--content' data={state.popoverContent} limit={1} />
|
|
113
123
|
</Card.Root>
|
|
@@ -35,7 +35,7 @@ const buildDefaultActions = (): ActionGraphProps => {
|
|
|
35
35
|
}),
|
|
36
36
|
];
|
|
37
37
|
result.nodes.push(...actions);
|
|
38
|
-
result.edges.push(...actions.map((a) => ({ source: 'root', target: a.id })));
|
|
38
|
+
result.edges.push(...actions.map((a) => ({ source: 'root', target: a.id, relation: 'child' })));
|
|
39
39
|
return result;
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -53,7 +53,7 @@ const DefaultStory = ({ actions: actionsProp, ...props }: StoryProps) => {
|
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
const meta = {
|
|
56
|
-
title: 'plugins/plugin-simple-layout/AppBar',
|
|
56
|
+
title: 'plugins/plugin-simple-layout/components/AppBar',
|
|
57
57
|
render: DefaultStory,
|
|
58
58
|
decorators: [
|
|
59
59
|
withTheme(),
|
|
@@ -6,13 +6,7 @@ import { type Atom, useAtomValue } from '@effect-atom/atom-react';
|
|
|
6
6
|
import React, { Fragment } from 'react';
|
|
7
7
|
|
|
8
8
|
import { IconButton, Popover, type ThemedClassName, Toolbar, useTranslation } from '@dxos/react-ui';
|
|
9
|
-
import {
|
|
10
|
-
type ActionExecutor,
|
|
11
|
-
type ActionGraphProps,
|
|
12
|
-
DropdownMenu,
|
|
13
|
-
MenuProvider,
|
|
14
|
-
useMenuActions,
|
|
15
|
-
} from '@dxos/react-ui-menu';
|
|
9
|
+
import { type ActionExecutor, type ActionGraphProps, Menu, useMenuActions } from '@dxos/react-ui-menu';
|
|
16
10
|
import { mx, osTranslations } from '@dxos/ui-theme';
|
|
17
11
|
|
|
18
12
|
import { meta } from '../../meta';
|
|
@@ -63,8 +57,8 @@ export const AppBar = ({
|
|
|
63
57
|
<Toolbar.Root
|
|
64
58
|
role='banner'
|
|
65
59
|
classNames={mx(
|
|
66
|
-
'grid grid-cols-[var(--rail-size)_1fr_var(--rail-size)]
|
|
67
|
-
'density-fine',
|
|
60
|
+
'grid grid-cols-[var(--dx-rail-size)_1fr_var(--dx-rail-size)] items-center',
|
|
61
|
+
'dx-density-fine',
|
|
68
62
|
classNames,
|
|
69
63
|
)}
|
|
70
64
|
>
|
|
@@ -78,18 +72,17 @@ export const AppBar = ({
|
|
|
78
72
|
<h1 className='text-center truncate font-thin uppercase'>{displayTitle}</h1>
|
|
79
73
|
{hasActions ? (
|
|
80
74
|
<AnchorRoot>
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</MenuProvider>
|
|
75
|
+
<Menu.Root {...menu} caller={meta.id} onAction={onAction}>
|
|
76
|
+
<Menu.Trigger asChild>
|
|
77
|
+
<IconButton
|
|
78
|
+
variant='ghost'
|
|
79
|
+
icon='ph--dots-three-vertical--regular'
|
|
80
|
+
iconOnly
|
|
81
|
+
label={t('actions menu label')}
|
|
82
|
+
/>
|
|
83
|
+
</Menu.Trigger>
|
|
84
|
+
<Menu.Content />
|
|
85
|
+
</Menu.Root>
|
|
93
86
|
</AnchorRoot>
|
|
94
87
|
) : (
|
|
95
88
|
<span />
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
import React, { useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Surface } from '@dxos/app-framework/ui';
|
|
8
|
+
import { getCompanionVariant } from '@dxos/app-toolkit';
|
|
8
9
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
9
10
|
import { type Node, useNode } from '@dxos/plugin-graph';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { MenuProvider, ToolbarMenu, useMenuActions } from '@dxos/react-ui-menu';
|
|
11
|
+
import { ErrorFallback, Panel } from '@dxos/react-ui';
|
|
12
|
+
import { Menu, useMenuActions } from '@dxos/react-ui-menu';
|
|
13
13
|
|
|
14
14
|
import { useCompanions, useDrawerActions, useSimpleLayoutState } from '../../hooks';
|
|
15
|
-
import { ContentError } from '../ContentError';
|
|
16
15
|
import { ContentLoading } from '../ContentLoading';
|
|
17
16
|
|
|
18
17
|
const DRAWER_NAME = 'SimpleLayout.Drawer';
|
|
@@ -53,23 +52,21 @@ export const Drawer = () => {
|
|
|
53
52
|
const menu = useMenuActions(actions);
|
|
54
53
|
|
|
55
54
|
return (
|
|
56
|
-
<
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
<Panel.Root>
|
|
56
|
+
<Panel.Toolbar>
|
|
57
|
+
<Menu.Root {...menu} alwaysActive onAction={onAction}>
|
|
58
|
+
<Menu.Toolbar density='coarse' />
|
|
59
|
+
</Menu.Root>
|
|
60
|
+
</Panel.Toolbar>
|
|
61
|
+
<Panel.Content asChild>
|
|
62
|
+
<Surface.Surface role='article' data={data} limit={1} fallback={ErrorFallback} placeholder={placeholder} />
|
|
63
|
+
</Panel.Content>
|
|
64
|
+
</Panel.Root>
|
|
62
65
|
);
|
|
63
66
|
};
|
|
64
67
|
|
|
65
68
|
Drawer.displayName = DRAWER_NAME;
|
|
66
69
|
|
|
67
|
-
/** Parse entry ID to extract primary ID and variant. */
|
|
68
|
-
const parseEntryId = (entryId: string) => {
|
|
69
|
-
const [id, variant] = entryId.split(ATTENDABLE_PATH_SEPARATOR);
|
|
70
|
-
return { id, variant };
|
|
71
|
-
};
|
|
72
|
-
|
|
73
70
|
/**
|
|
74
71
|
* Resolves which companion to show based on variant preference.
|
|
75
72
|
* Falls back to first available if preferred variant not available.
|
|
@@ -82,10 +79,7 @@ const useSelectedCompanion = (companions: Node.Node[], preferredVariant?: string
|
|
|
82
79
|
|
|
83
80
|
// Try to find companion matching the preferred variant.
|
|
84
81
|
if (preferredVariant) {
|
|
85
|
-
const preferred = companions.find((c) =>
|
|
86
|
-
const { variant } = parseEntryId(c.id);
|
|
87
|
-
return variant === preferredVariant;
|
|
88
|
-
});
|
|
82
|
+
const preferred = companions.find((c) => getCompanionVariant(c.id) === preferredVariant);
|
|
89
83
|
if (preferred) {
|
|
90
84
|
return preferred;
|
|
91
85
|
}
|
|
@@ -96,7 +90,7 @@ const useSelectedCompanion = (companions: Node.Node[], preferredVariant?: string
|
|
|
96
90
|
}, [companions, preferredVariant]);
|
|
97
91
|
|
|
98
92
|
const companionId = selectedCompanion?.id;
|
|
99
|
-
const
|
|
93
|
+
const variant = companionId ? getCompanionVariant(companionId) : undefined;
|
|
100
94
|
|
|
101
95
|
return { selectedCompanion, companionId, variant };
|
|
102
96
|
};
|
|
@@ -7,14 +7,14 @@ import React, { useMemo } from 'react';
|
|
|
7
7
|
import { Surface } from '@dxos/app-framework/ui';
|
|
8
8
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
9
9
|
import { useNode } from '@dxos/plugin-graph';
|
|
10
|
+
import { ErrorFallback } from '@dxos/react-ui';
|
|
10
11
|
import { useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
11
12
|
import { mx } from '@dxos/ui-theme';
|
|
12
13
|
|
|
13
14
|
import { useAppBarProps, useNavbarActions, useSimpleLayoutState } from '../../hooks';
|
|
14
|
-
import { ContentError } from '../ContentError';
|
|
15
15
|
import { ContentLoading } from '../ContentLoading';
|
|
16
|
-
import {
|
|
17
|
-
import { useMobileLayout } from '../MobileLayout
|
|
16
|
+
import { useExpandPath } from '../hooks';
|
|
17
|
+
import { useMobileLayout } from '../MobileLayout';
|
|
18
18
|
|
|
19
19
|
import { AppBar } from './AppBar';
|
|
20
20
|
import { NavBar } from './NavBar';
|
|
@@ -47,8 +47,7 @@ export const Main = () => {
|
|
|
47
47
|
);
|
|
48
48
|
}, [id, node, node?.data, node?.properties, state.popoverAnchorId]);
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
useLoadDescendents(id);
|
|
50
|
+
useExpandPath(id);
|
|
52
51
|
|
|
53
52
|
// TODO(burdon): BUG: When showing ANY statusbar the size progressively shrinks when the keyboard opens/closes.
|
|
54
53
|
const showNavBar = !keyboardOpen && !state.isPopover && state.drawerState === 'closed';
|
|
@@ -57,23 +56,25 @@ export const Main = () => {
|
|
|
57
56
|
<div
|
|
58
57
|
role='none'
|
|
59
58
|
className={mx(
|
|
60
|
-
'
|
|
61
|
-
showNavBar
|
|
59
|
+
'h-full grid overflow-hidden bg-toolbar-surface',
|
|
60
|
+
showNavBar
|
|
61
|
+
? 'grid-rows-[var(--dx-rail-action)_1fr_var(--dx-toolbar-size)]'
|
|
62
|
+
: 'grid-rows-[var(--dx-rail-action)_1fr]',
|
|
62
63
|
)}
|
|
63
64
|
{...attentionAttrs}
|
|
64
65
|
>
|
|
65
66
|
<AppBar {...appBarProps} />
|
|
66
|
-
<article className='
|
|
67
|
+
<article className='h-full overflow-hidden bg-base-surface'>
|
|
67
68
|
<Surface.Surface
|
|
68
69
|
key={id}
|
|
69
70
|
role='article'
|
|
70
71
|
data={data}
|
|
71
72
|
limit={1}
|
|
72
|
-
fallback={
|
|
73
|
+
fallback={ErrorFallback}
|
|
73
74
|
placeholder={placeholder}
|
|
74
75
|
/>
|
|
75
76
|
</article>
|
|
76
|
-
{showNavBar && <NavBar classNames='border-
|
|
77
|
+
{showNavBar && <NavBar classNames='border-y border-subdued-separator' actions={actions} onAction={onAction} />}
|
|
77
78
|
</div>
|
|
78
79
|
);
|
|
79
80
|
};
|
|
@@ -7,7 +7,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
7
7
|
import React, { useMemo } from 'react';
|
|
8
8
|
import { type Mock, expect, fn, screen, userEvent, within } from 'storybook/test';
|
|
9
9
|
|
|
10
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
import { type ActionGraphProps, createGapSeparator, createMenuAction, createMenuItemGroup } from '@dxos/react-ui-menu';
|
|
12
12
|
import { withRegistry } from '@dxos/storybook-utils';
|
|
13
13
|
|
|
@@ -39,7 +39,7 @@ const buildCompanionOnlyActions = (): ActionGraphProps => {
|
|
|
39
39
|
}),
|
|
40
40
|
];
|
|
41
41
|
result.nodes.push(...companions);
|
|
42
|
-
result.edges.push(...companions.map((c) => ({ source: 'root', target: c.id })));
|
|
42
|
+
result.edges.push(...companions.map((c) => ({ source: 'root', target: c.id, relation: 'child' })));
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -81,18 +81,18 @@ const buildDefaultActions = (): ActionGraphProps => {
|
|
|
81
81
|
];
|
|
82
82
|
result.nodes.push(...companions, ...gapSeparator.nodes, mainMenuGroup, ...menuActions);
|
|
83
83
|
result.edges.push(
|
|
84
|
-
...companions.map((c) => ({ source: 'root', target: c.id })),
|
|
84
|
+
...companions.map((c) => ({ source: 'root', target: c.id, relation: 'child' })),
|
|
85
85
|
...gapSeparator.edges,
|
|
86
|
-
{ source: 'root', target: mainMenuGroup.id },
|
|
87
|
-
...menuActions.map((action) => ({ source: MAIN_MENU_GROUP_ID, target: action.id })),
|
|
86
|
+
{ source: 'root', target: mainMenuGroup.id, relation: 'child' },
|
|
87
|
+
...menuActions.map((action) => ({ source: MAIN_MENU_GROUP_ID, target: action.id, relation: 'child' })),
|
|
88
88
|
);
|
|
89
89
|
return result;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
const meta = {
|
|
93
|
-
title: 'plugins/plugin-simple-layout/NavBar',
|
|
93
|
+
title: 'plugins/plugin-simple-layout/components/NavBar',
|
|
94
94
|
component: NavBar,
|
|
95
|
-
decorators: [withTheme(), withRegistry],
|
|
95
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withRegistry],
|
|
96
96
|
parameters: {
|
|
97
97
|
layout: 'fullscreen',
|
|
98
98
|
translations,
|
|
@@ -106,7 +106,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
106
106
|
const DefaultStory = ({ onAction }: { onAction: (action: { id: string }) => void }) => {
|
|
107
107
|
const actions = useMemo(() => Atom.make(buildDefaultActions()).pipe(Atom.keepAlive), []);
|
|
108
108
|
|
|
109
|
-
return <NavBar classNames='border-
|
|
109
|
+
return <NavBar classNames='border-y border-separator' actions={actions} onAction={onAction} />;
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
export const Default: Story = {
|
|
@@ -6,13 +6,7 @@ import { type Atom } from '@effect-atom/atom-react';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
|
-
import {
|
|
10
|
-
type ActionExecutor,
|
|
11
|
-
type ActionGraphProps,
|
|
12
|
-
MenuProvider,
|
|
13
|
-
ToolbarMenu,
|
|
14
|
-
useMenuActions,
|
|
15
|
-
} from '@dxos/react-ui-menu';
|
|
9
|
+
import { type ActionExecutor, type ActionGraphProps, Menu, useMenuActions } from '@dxos/react-ui-menu';
|
|
16
10
|
import { mx } from '@dxos/ui-theme';
|
|
17
11
|
|
|
18
12
|
const NAVBAR_NAME = 'SimpleLayout.NavBar';
|
|
@@ -31,9 +25,9 @@ export const NavBar = ({ classNames, actions, onAction }: NavBarProps) => {
|
|
|
31
25
|
const menu = useMenuActions(actions);
|
|
32
26
|
|
|
33
27
|
return (
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
</
|
|
28
|
+
<Menu.Root {...menu} alwaysActive onAction={onAction}>
|
|
29
|
+
<Menu.Toolbar density='coarse' classNames={mx(classNames)} />
|
|
30
|
+
</Menu.Root>
|
|
37
31
|
);
|
|
38
32
|
};
|
|
39
33
|
|
|
@@ -8,6 +8,7 @@ import * as Effect from 'effect/Effect';
|
|
|
8
8
|
import { ActivationEvents, Capabilities, Capability, Plugin } from '@dxos/app-framework';
|
|
9
9
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
10
10
|
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
11
|
+
import { Collection } from '@dxos/echo';
|
|
11
12
|
import { ClientOperation, ClientPlugin } from '@dxos/plugin-client';
|
|
12
13
|
import { SearchPlugin } from '@dxos/plugin-search';
|
|
13
14
|
import { SpacePlugin } from '@dxos/plugin-space';
|
|
@@ -15,7 +16,6 @@ import { SpaceOperation } from '@dxos/plugin-space/types';
|
|
|
15
16
|
import { corePlugins } from '@dxos/plugin-testing';
|
|
16
17
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
17
18
|
import { translations as searchTranslation } from '@dxos/react-ui-searchlist';
|
|
18
|
-
import { Collection } from '@dxos/schema';
|
|
19
19
|
|
|
20
20
|
import { OperationResolver, type SimpleLayoutStateOptions, State } from '../../capabilities';
|
|
21
21
|
import { meta as pluginMeta } from '../../meta';
|
|
@@ -80,7 +80,7 @@ const createPluginManager = ({ isPopover }: { isPopover: boolean }) => {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
const meta = {
|
|
83
|
-
title: 'plugins/plugin-simple-layout/SimpleLayout',
|
|
83
|
+
title: 'plugins/plugin-simple-layout/components/SimpleLayout',
|
|
84
84
|
component: SimpleLayout,
|
|
85
85
|
parameters: {
|
|
86
86
|
layout: 'fullscreen',
|
|
@@ -36,7 +36,7 @@ export const SimpleLayout = () => {
|
|
|
36
36
|
return (
|
|
37
37
|
<Mosaic.Root classNames='contents'>
|
|
38
38
|
<MobileLayout.Root
|
|
39
|
-
classNames='bg-
|
|
39
|
+
classNames='bg-toolbar-surface'
|
|
40
40
|
onKeyboardOpenChange={(keyboardOpen: boolean) => setKeyboardOpen(keyboardOpen)}
|
|
41
41
|
>
|
|
42
42
|
<MobileLayout.Panel safe={{ top: true, bottom: splitterMode === 'upper' }}>
|
package/src/components/hooks.ts
CHANGED
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
import { useEffect } from 'react';
|
|
6
6
|
|
|
7
|
+
import { expandAttendableId } from '@dxos/react-ui-attention';
|
|
7
8
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
8
9
|
import { Graph } from '@dxos/plugin-graph';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Expand graph nodes along the full path from root to the given node ID.
|
|
13
|
+
* Walks each progressive prefix, ensuring ancestor nodes are materialized
|
|
14
|
+
* before attempting to access their children.
|
|
12
15
|
*/
|
|
13
|
-
export const
|
|
16
|
+
export const useExpandPath = (nodeId?: string) => {
|
|
14
17
|
const { graph } = useAppGraph();
|
|
15
18
|
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
if (nodeId) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Graph.getConnections(graph, nodeId, 'outbound').forEach((child) => {
|
|
22
|
-
Graph.expand(graph, child.id, 'outbound');
|
|
23
|
-
});
|
|
21
|
+
for (const prefix of expandAttendableId(nodeId)) {
|
|
22
|
+
Graph.expand(graph, prefix, 'child');
|
|
23
|
+
}
|
|
24
24
|
}
|
|
25
25
|
}, [nodeId, graph]);
|
|
26
26
|
};
|
package/src/components/index.ts
CHANGED
package/src/hooks/actions.ts
CHANGED
|
@@ -5,25 +5,23 @@
|
|
|
5
5
|
import { type Atom } from '@effect-atom/atom-react';
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
7
|
|
|
8
|
-
import { type
|
|
9
|
-
import { type AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { type AppCapabilities, getCompanionVariant } from '@dxos/app-toolkit';
|
|
10
9
|
import { Node } from '@dxos/plugin-graph';
|
|
11
|
-
import { ATTENDABLE_PATH_SEPARATOR } from '@dxos/react-ui-attention';
|
|
12
10
|
import { type ActionGraphProps } from '@dxos/react-ui-menu';
|
|
13
11
|
import { byPosition } from '@dxos/util';
|
|
14
12
|
|
|
15
13
|
import { type SimpleLayoutState } from '../types';
|
|
16
14
|
|
|
17
15
|
// TODO(wittjosiah): Factor out to shared location with plugin-deck.
|
|
18
|
-
export const PLANK_COMPANION_TYPE = 'dxos.
|
|
16
|
+
export const PLANK_COMPANION_TYPE = 'org.dxos.plugin.deck.plank-companion';
|
|
19
17
|
|
|
20
18
|
export type CompanionActionsConfig = {
|
|
21
19
|
/** Prefix for companion action IDs (e.g. 'navbar' or 'drawer') */
|
|
22
20
|
idPrefix: string;
|
|
23
21
|
/** Optional: highlight companion with this variant */
|
|
24
22
|
selectedVariant?: string;
|
|
25
|
-
/**
|
|
26
|
-
|
|
23
|
+
/** State updater for toggling the drawer. */
|
|
24
|
+
updateState: (fn: (state: SimpleLayoutState) => SimpleLayoutState) => void;
|
|
27
25
|
};
|
|
28
26
|
|
|
29
27
|
/**
|
|
@@ -37,14 +35,14 @@ export const createCompanionActions = (
|
|
|
37
35
|
get: (atom: Atom.Atom<any>) => any,
|
|
38
36
|
config: CompanionActionsConfig,
|
|
39
37
|
): Pick<ActionGraphProps, 'nodes' | 'edges'> => {
|
|
40
|
-
const { idPrefix, selectedVariant,
|
|
38
|
+
const { idPrefix, selectedVariant, updateState } = config;
|
|
41
39
|
|
|
42
40
|
// Derive activeId from state atom.
|
|
43
41
|
const state = get(stateAtom);
|
|
44
42
|
const activeId = state.active ?? state.workspace;
|
|
45
43
|
|
|
46
44
|
// Get companions from graph connections for activeId.
|
|
47
|
-
const activeConnections = activeId ? get(graph.connections(activeId)) : [];
|
|
45
|
+
const activeConnections = activeId ? get(graph.connections(activeId, 'child')) : [];
|
|
48
46
|
const companions = activeConnections
|
|
49
47
|
.filter((node: Node.Node) => node.type === PLANK_COMPANION_TYPE)
|
|
50
48
|
.toSorted((a: Node.Node, b: Node.Node) => byPosition(a.properties, b.properties));
|
|
@@ -52,12 +50,8 @@ export const createCompanionActions = (
|
|
|
52
50
|
const nodes: ActionGraphProps['nodes'] = [];
|
|
53
51
|
const edges: ActionGraphProps['edges'] = [];
|
|
54
52
|
|
|
55
|
-
// Add companion actions.
|
|
56
|
-
// TODO(burdon): Cap at 6 items.
|
|
57
53
|
companions.forEach((companion: Node.Node) => {
|
|
58
|
-
|
|
59
|
-
const [, companionVariant] = companion.id.split(ATTENDABLE_PATH_SEPARATOR);
|
|
60
|
-
|
|
54
|
+
const companionVariant = getCompanionVariant(companion.id);
|
|
61
55
|
const companionAction = {
|
|
62
56
|
id: `${idPrefix}-companion-${companion.id}`,
|
|
63
57
|
type: Node.ActionType,
|
|
@@ -65,20 +59,24 @@ export const createCompanionActions = (
|
|
|
65
59
|
icon: companion.properties.icon ?? 'ph--placeholder--regular',
|
|
66
60
|
label: companion.properties.label,
|
|
67
61
|
iconOnly: true,
|
|
68
|
-
// Conditionally add variant highlighting.
|
|
69
62
|
...(selectedVariant !== undefined && {
|
|
70
63
|
variant: selectedVariant === companionVariant ? 'primary' : 'ghost',
|
|
71
64
|
}),
|
|
72
65
|
},
|
|
73
66
|
data: () =>
|
|
74
67
|
Effect.sync(() =>
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
updateState((current) => {
|
|
69
|
+
const closing = current.companionVariant === companionVariant && current.drawerState !== 'closed';
|
|
70
|
+
return {
|
|
71
|
+
...current,
|
|
72
|
+
companionVariant: closing ? undefined : companionVariant,
|
|
73
|
+
drawerState: closing ? 'closed' : 'open',
|
|
74
|
+
};
|
|
77
75
|
}),
|
|
78
76
|
),
|
|
79
77
|
};
|
|
80
78
|
nodes.push(companionAction);
|
|
81
|
-
edges.push({ source: 'root', target: companionAction.id });
|
|
79
|
+
edges.push({ source: 'root', target: companionAction.id, relation: 'child' });
|
|
82
80
|
});
|
|
83
81
|
|
|
84
82
|
return { nodes, edges };
|