@dxos/react-ui-stack 0.6.14-staging.e15392e → 0.7.1-staging.599df14
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/index.mjs +493 -335
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +3 -6
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +477 -326
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +3 -6
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/index.mjs +493 -335
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +3 -6
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/components/LayoutControls.d.ts +19 -0
- package/dist/types/src/components/LayoutControls.d.ts.map +1 -0
- package/dist/types/src/components/MenuSignifier.d.ts +4 -0
- package/dist/types/src/components/MenuSignifier.d.ts.map +1 -0
- package/dist/types/src/components/Stack.d.ts +12 -12
- package/dist/types/src/components/Stack.d.ts.map +1 -1
- package/dist/types/src/components/Stack.stories.d.ts +6 -83
- package/dist/types/src/components/Stack.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackContext.d.ts +19 -0
- package/dist/types/src/components/StackContext.d.ts.map +1 -0
- package/dist/types/src/components/StackItem.d.ts +41 -0
- package/dist/types/src/components/StackItem.d.ts.map +1 -0
- package/dist/types/src/components/StackItemContent.d.ts +8 -0
- package/dist/types/src/components/StackItemContent.d.ts.map +1 -0
- package/dist/types/src/components/StackItemHeading.d.ts +8 -0
- package/dist/types/src/components/StackItemHeading.d.ts.map +1 -0
- package/dist/types/src/components/StackItemResizeHandle.d.ts +3 -0
- package/dist/types/src/components/StackItemResizeHandle.d.ts.map +1 -0
- package/dist/types/src/components/StackItemSigil.d.ts +31 -0
- package/dist/types/src/components/StackItemSigil.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +2 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/testing/EditorContent.d.ts +2 -2
- package/dist/types/src/testing/EditorContent.d.ts.map +1 -1
- package/dist/types/src/testing/stack-manager.d.ts +0 -1
- package/dist/types/src/testing/stack-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +7 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +20 -21
- package/src/components/LayoutControls.tsx +131 -0
- package/src/components/MenuSignifier.tsx +33 -0
- package/src/components/Stack.stories.tsx +109 -182
- package/src/components/Stack.tsx +61 -156
- package/src/components/StackContext.tsx +38 -0
- package/src/components/StackItem.tsx +173 -0
- package/src/components/StackItemContent.tsx +47 -0
- package/src/components/StackItemHeading.tsx +55 -0
- package/src/components/StackItemResizeHandle.tsx +115 -0
- package/src/components/StackItemSigil.tsx +170 -0
- package/src/components/index.ts +3 -2
- package/src/playwright/smoke.spec.ts +3 -3
- package/src/testing/EditorContent.tsx +4 -4
- package/src/testing/stack-manager.ts +3 -7
- package/src/translations.ts +7 -8
- package/dist/types/src/components/CaretDownUp.d.ts +0 -4
- package/dist/types/src/components/CaretDownUp.d.ts.map +0 -1
- package/dist/types/src/components/ContentTypes.stories.d.ts +0 -96
- package/dist/types/src/components/ContentTypes.stories.d.ts.map +0 -1
- package/dist/types/src/components/Deck.stories.d.ts +0 -19
- package/dist/types/src/components/Deck.stories.d.ts.map +0 -1
- package/dist/types/src/components/Section.d.ts +0 -53
- package/dist/types/src/components/Section.d.ts.map +0 -1
- package/dist/types/src/components/Section.stories.d.ts +0 -36
- package/dist/types/src/components/Section.stories.d.ts.map +0 -1
- package/dist/types/src/components/style-fragments.d.ts +0 -2
- package/dist/types/src/components/style-fragments.d.ts.map +0 -1
- package/dist/types/src/next/Stack.d.ts +0 -9
- package/dist/types/src/next/Stack.d.ts.map +0 -1
- package/dist/types/src/next/Stack.stories.d.ts +0 -8
- package/dist/types/src/next/Stack.stories.d.ts.map +0 -1
- package/dist/types/src/next/StackItem.d.ts +0 -14
- package/dist/types/src/next/StackItem.d.ts.map +0 -1
- package/dist/types/src/next/index.d.ts +0 -2
- package/dist/types/src/next/index.d.ts.map +0 -1
- package/dist/types/src/testing/TableContent.d.ts +0 -20
- package/dist/types/src/testing/TableContent.d.ts.map +0 -1
- package/src/components/CaretDownUp.tsx +0 -31
- package/src/components/ContentTypes.stories.tsx +0 -104
- package/src/components/Deck.stories.tsx +0 -362
- package/src/components/Section.stories.tsx +0 -50
- package/src/components/Section.tsx +0 -378
- package/src/components/style-fragments.ts +0 -5
- package/src/next/Stack.stories.tsx +0 -148
- package/src/next/Stack.tsx +0 -30
- package/src/next/StackItem.tsx +0 -78
- package/src/next/index.ts +0 -5
- package/src/testing/TableContent.tsx +0 -119
|
@@ -1,207 +1,134 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// eslint-disable-next-line unused-imports/no-unused-imports
|
|
9
|
-
import { type IconProps } from '@phosphor-icons/react';
|
|
10
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
6
|
+
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import React, { useState, useCallback } from 'react';
|
|
11
8
|
|
|
12
9
|
import { faker } from '@dxos/random';
|
|
13
|
-
import {
|
|
14
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
15
|
-
|
|
16
|
-
import { type StackSectionContent, type StackSectionItem } from './Section';
|
|
17
|
-
import { Stack, type StackProps } from './Stack';
|
|
18
|
-
import { TestObjectGenerator } from '../testing/generator';
|
|
10
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
19
11
|
|
|
20
|
-
|
|
12
|
+
import { Stack } from './Stack';
|
|
13
|
+
import { StackItem, type StackItemData } from './StackItem';
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
type StoryStackItem = {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
items?: StoryStackItem[];
|
|
19
|
+
};
|
|
25
20
|
|
|
26
|
-
const
|
|
27
|
-
data,
|
|
28
|
-
}: StackSectionItem & { data: StackSectionContent & { title?: string; body?: string; image?: string } }) => {
|
|
29
|
-
useEffect(() => () => console.log('[ComplexContent]', 'unmount'), []);
|
|
21
|
+
const KanbanBlock = ({ item }: { item: StoryStackItem }) => {
|
|
30
22
|
return (
|
|
31
|
-
<div className='
|
|
32
|
-
<
|
|
33
|
-
<h1>{data.title ?? data.id}</h1>
|
|
34
|
-
{data.body && <p>{data.body}</p>}
|
|
35
|
-
</div>
|
|
36
|
-
{data.image && <img src={data.image} />}
|
|
23
|
+
<div className='overflow-hidden'>
|
|
24
|
+
<p className='place-content-center p-4'>{item.title}</p>
|
|
37
25
|
</div>
|
|
38
26
|
);
|
|
39
27
|
};
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
29
|
+
const StorybookStack = () => {
|
|
30
|
+
const [columns, setColumns] = useState<StoryStackItem[]>(
|
|
31
|
+
faker.helpers.multiple(
|
|
32
|
+
() =>
|
|
33
|
+
({
|
|
34
|
+
id: faker.string.uuid(),
|
|
35
|
+
title: faker.lorem.paragraph(),
|
|
36
|
+
items: faker.helpers.multiple(
|
|
37
|
+
() =>
|
|
38
|
+
({
|
|
39
|
+
id: faker.string.uuid(),
|
|
40
|
+
title: faker.lorem.paragraph(),
|
|
41
|
+
}) satisfies StoryStackItem,
|
|
42
|
+
{ count: { min: 32, max: 64 } },
|
|
43
|
+
),
|
|
44
|
+
}) satisfies StoryStackItem,
|
|
45
|
+
{ count: 8 },
|
|
46
|
+
),
|
|
47
|
+
);
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
const reorderItem = useCallback((source: StackItemData, target: StackItemData, closestEdge: Edge | null) => {
|
|
50
|
+
setColumns((prevColumns) => {
|
|
51
|
+
const newColumns = [...prevColumns];
|
|
52
|
+
const sourceColumn = newColumns.find(
|
|
53
|
+
(col) => col.id === source.id || col.items?.some((card) => card.id === source.id),
|
|
54
|
+
);
|
|
55
|
+
const targetColumn = newColumns.find(
|
|
56
|
+
(col) => col.id === target.id || col.items?.some((card) => card.id === target.id),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (sourceColumn && targetColumn) {
|
|
60
|
+
if (source.type === 'column' && target.type === 'column') {
|
|
61
|
+
// Reordering columns
|
|
62
|
+
const sourceIndex = newColumns.findIndex((col) => col.id === source.id);
|
|
63
|
+
const targetIndex = newColumns.findIndex((col) => col.id === target.id);
|
|
64
|
+
const [movedColumn] = newColumns.splice(sourceIndex, 1);
|
|
65
|
+
const insertIndex = closestEdge === 'right' ? targetIndex + 1 : targetIndex;
|
|
66
|
+
newColumns.splice(insertIndex, 0, movedColumn);
|
|
67
|
+
} else {
|
|
68
|
+
// Reordering cards within a column
|
|
69
|
+
const sourceCardIndex = sourceColumn.items?.findIndex((card) => card.id === source.id);
|
|
70
|
+
const targetCardIndex = targetColumn.items?.findIndex((card) => card.id === target.id);
|
|
71
|
+
if (
|
|
72
|
+
typeof sourceCardIndex === 'number' &&
|
|
73
|
+
typeof targetCardIndex === 'number' &&
|
|
74
|
+
sourceColumn.items &&
|
|
75
|
+
targetColumn.items
|
|
76
|
+
) {
|
|
77
|
+
const [movedCard] = sourceColumn.items.splice(sourceCardIndex, 1);
|
|
78
|
+
|
|
79
|
+
let insertIndex;
|
|
80
|
+
if (sourceColumn === targetColumn && sourceCardIndex < targetCardIndex) {
|
|
81
|
+
insertIndex = closestEdge === 'bottom' ? targetCardIndex : targetCardIndex - 1;
|
|
82
|
+
} else {
|
|
83
|
+
insertIndex = closestEdge === 'bottom' ? targetCardIndex + 1 : targetCardIndex;
|
|
84
|
+
}
|
|
85
|
+
targetColumn.items.splice(insertIndex, 0, movedCard);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
61
89
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
types: ['document'],
|
|
66
|
-
debug: true,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
90
|
+
return newColumns;
|
|
91
|
+
});
|
|
92
|
+
}, []);
|
|
69
93
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
94
|
+
return (
|
|
95
|
+
<main className='fixed inset-0'>
|
|
96
|
+
<Stack orientation='horizontal' size='contain'>
|
|
97
|
+
{columns.map((column) => (
|
|
98
|
+
<StackItem.Root key={column.id} item={column} onRearrange={reorderItem}>
|
|
99
|
+
<StackItem.Heading>
|
|
100
|
+
<StackItem.ResizeHandle />
|
|
101
|
+
</StackItem.Heading>
|
|
102
|
+
<Stack orientation='vertical' size='contain'>
|
|
103
|
+
{column.items?.map((card) => (
|
|
104
|
+
<StackItem.Root key={card.id} item={card} onRearrange={reorderItem}>
|
|
105
|
+
<StackItem.Heading>
|
|
106
|
+
<StackItem.ResizeHandle />
|
|
107
|
+
</StackItem.Heading>
|
|
108
|
+
<KanbanBlock item={card} />
|
|
109
|
+
</StackItem.Root>
|
|
110
|
+
))}
|
|
111
|
+
</Stack>
|
|
112
|
+
</StackItem.Root>
|
|
113
|
+
))}
|
|
114
|
+
</Stack>
|
|
115
|
+
</main>
|
|
116
|
+
);
|
|
76
117
|
};
|
|
77
118
|
|
|
78
|
-
|
|
79
|
-
args: {
|
|
80
|
-
SectionContent: SimpleContent,
|
|
81
|
-
types: ['document'],
|
|
82
|
-
count: 8,
|
|
83
|
-
className: 'w-[400px]',
|
|
84
|
-
},
|
|
85
|
-
render: ({ debug, ...args }: DemoStackProps & { debug: boolean }) => {
|
|
86
|
-
return (
|
|
87
|
-
<Mosaic.Root debug={debug}>
|
|
88
|
-
<Mosaic.DragOverlay />
|
|
89
|
-
<div className='flex grow justify-center p-4' data-testid='stack-transfer'>
|
|
90
|
-
<div className='grid grid-cols-2 gap-4'>
|
|
91
|
-
<DemoStack {...args} id='stack-1' />
|
|
92
|
-
<DemoStack {...args} id='stack-2' />
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
</Mosaic.Root>
|
|
96
|
-
);
|
|
97
|
-
},
|
|
98
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
99
|
-
};
|
|
119
|
+
type Story = StoryObj<typeof StorybookStack>;
|
|
100
120
|
|
|
101
|
-
export const
|
|
121
|
+
export const Default: Story = {
|
|
102
122
|
args: {
|
|
103
|
-
|
|
104
|
-
types: ['document'],
|
|
105
|
-
className: 'w-[400px]',
|
|
106
|
-
},
|
|
107
|
-
render: ({ debug, ...args }: DemoStackProps & { debug: boolean }) => {
|
|
108
|
-
return (
|
|
109
|
-
<Mosaic.Root debug={debug}>
|
|
110
|
-
<Mosaic.DragOverlay debug={debug} />
|
|
111
|
-
<div className='flex grow justify-center p-4' data-testid='stack-copy'>
|
|
112
|
-
<div className='grid grid-cols-2 gap-4'>
|
|
113
|
-
<DemoStack {...args} id='stack-1' />
|
|
114
|
-
<DemoStack {...args} id='stack-2' operation='copy' count={0} />
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
</Mosaic.Root>
|
|
118
|
-
);
|
|
123
|
+
orientation: 'horizontal',
|
|
119
124
|
},
|
|
120
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
121
125
|
};
|
|
122
126
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const meta: Meta<typeof StorybookStack> = {
|
|
128
|
+
title: 'ui/react-ui-stack-next/Stack',
|
|
129
|
+
component: StorybookStack,
|
|
130
|
+
decorators: [withTheme],
|
|
131
|
+
argTypes: { orientation: { control: 'radio', options: ['horizontal', 'vertical'] } },
|
|
127
132
|
};
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
id = 'stack',
|
|
131
|
-
SectionContent,
|
|
132
|
-
types,
|
|
133
|
-
count = 8,
|
|
134
|
-
operation = 'transfer',
|
|
135
|
-
classNames,
|
|
136
|
-
}: DemoStackProps) => {
|
|
137
|
-
const [items, setItems] = useState<StackSectionItem[]>(() => {
|
|
138
|
-
const generator = new TestObjectGenerator({ types });
|
|
139
|
-
return generator.createObjects({ length: count }).map((object) => ({
|
|
140
|
-
id: faker.string.uuid(),
|
|
141
|
-
object,
|
|
142
|
-
}));
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const itemsRef = useRef(items);
|
|
146
|
-
|
|
147
|
-
const handleOver = ({ active, over }: MosaicMoveEvent<number>) => {
|
|
148
|
-
if (operation === 'reject') {
|
|
149
|
-
return 'reject';
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (Path.parent(active.path) === Path.parent(over.path)) {
|
|
153
|
-
return 'rearrange';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// TODO(wittjosiah): Items is stale here for some inexplicable reason, so ref helps.
|
|
157
|
-
const exists = itemsRef.current.findIndex((item) => item.id === active.item.id) >= 0;
|
|
158
|
-
|
|
159
|
-
if (!exists) {
|
|
160
|
-
return operation;
|
|
161
|
-
} else {
|
|
162
|
-
return 'reject';
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const handleDrop = ({ operation, active, over }: MosaicDropEvent<number>) => {
|
|
167
|
-
setItems((items) => {
|
|
168
|
-
if (
|
|
169
|
-
(active.path === Path.create(id, active.item.id) || active.path === id) &&
|
|
170
|
-
(operation !== 'copy' || over.path === Path.create(id, over.item.id) || over.path === id)
|
|
171
|
-
) {
|
|
172
|
-
items.splice(active.position!, 1);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (over.path === Path.create(id, over.item.id)) {
|
|
176
|
-
items.splice(over.position!, 0, active.item as StackSectionItem);
|
|
177
|
-
} else if (over.path === id) {
|
|
178
|
-
items.push(active.item as StackSectionItem);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const i = [...items];
|
|
182
|
-
itemsRef.current = i;
|
|
183
|
-
return i;
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const handleRemove = (path: string) => {
|
|
188
|
-
setItems((items) => {
|
|
189
|
-
const newItems = items.filter((item) => item.id !== Path.last(path));
|
|
190
|
-
itemsRef.current = newItems;
|
|
191
|
-
return newItems;
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<Stack
|
|
197
|
-
id={id}
|
|
198
|
-
classNames={classNames}
|
|
199
|
-
data-testid={id}
|
|
200
|
-
SectionContent={SectionContent}
|
|
201
|
-
items={items}
|
|
202
|
-
onOver={handleOver}
|
|
203
|
-
onDrop={handleDrop}
|
|
204
|
-
onDeleteSection={handleRemove}
|
|
205
|
-
/>
|
|
206
|
-
);
|
|
207
|
-
};
|
|
134
|
+
export default meta;
|
package/src/components/Stack.tsx
CHANGED
|
@@ -1,173 +1,78 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useArrowNavigationGroup
|
|
6
|
-
import React, { type
|
|
7
|
-
import { useResizeDetector } from 'react-resize-detector';
|
|
5
|
+
import { useArrowNavigationGroup } from '@fluentui/react-tabster';
|
|
6
|
+
import React, { Children, type CSSProperties, type ComponentPropsWithRef, forwardRef } from 'react';
|
|
8
7
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
type MosaicContainerProps,
|
|
12
|
-
type MosaicTileComponent,
|
|
13
|
-
Mosaic,
|
|
14
|
-
Path,
|
|
15
|
-
useContainer,
|
|
16
|
-
useItemsWithPreview,
|
|
17
|
-
useMosaic,
|
|
18
|
-
} from '@dxos/react-ui-mosaic';
|
|
19
|
-
import { dropRingInner } from '@dxos/react-ui-theme';
|
|
8
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
20
10
|
|
|
21
|
-
import {
|
|
22
|
-
type CollapsedSections,
|
|
23
|
-
type AddSectionPosition,
|
|
24
|
-
SectionTile,
|
|
25
|
-
type StackContextValue,
|
|
26
|
-
type StackItem,
|
|
27
|
-
type StackSectionContent,
|
|
28
|
-
type StackSectionItem,
|
|
29
|
-
} from './Section';
|
|
30
|
-
import { stackColumns } from './style-fragments';
|
|
31
|
-
import { translationKey } from '../translations';
|
|
11
|
+
import { type StackContextValue, StackContext } from './StackContext';
|
|
32
12
|
|
|
33
|
-
export type
|
|
13
|
+
export type Orientation = 'horizontal' | 'vertical';
|
|
14
|
+
export type Size = 'intrinsic' | 'contain';
|
|
34
15
|
|
|
35
|
-
export type
|
|
16
|
+
export type StackProps = Omit<ThemedClassName<ComponentPropsWithRef<'div'>>, 'aria-orientation'> &
|
|
17
|
+
Partial<StackContextValue> & { itemsCount?: number };
|
|
36
18
|
|
|
37
|
-
export const
|
|
19
|
+
export const railGridHorizontal = 'grid-rows-[[rail-start]_var(--rail-size)_[content-start]_1fr_[content-end]]';
|
|
38
20
|
|
|
39
|
-
export
|
|
40
|
-
MosaicContainerProps<TData, number>,
|
|
41
|
-
'debug' | 'Component'
|
|
42
|
-
> &
|
|
43
|
-
Omit<StackContextValue<TData>, 'setCollapsedSections'> & {
|
|
44
|
-
items?: StackSectionItem[];
|
|
45
|
-
separation?: boolean; // TODO(burdon): Style.
|
|
46
|
-
onCollapseSection?: (id: string, collapsed: boolean) => void;
|
|
47
|
-
emptyComponent?: ReactNode;
|
|
48
|
-
};
|
|
21
|
+
export const railGridVertical = 'grid-cols-[[rail-start]_var(--rail-size)_[content-start]_1fr_[content-end]]';
|
|
49
22
|
|
|
50
|
-
export const Stack = (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
onCollapseSection,
|
|
63
|
-
...props
|
|
64
|
-
}: StackProps) => {
|
|
65
|
-
const { ref: containerRef, width = 0 } = useResizeDetector<HTMLDivElement>({ refreshRate: 200 });
|
|
66
|
-
const { operation, overItem } = useMosaic();
|
|
67
|
-
const itemsWithPreview = useItemsWithPreview({ path: id, items });
|
|
68
|
-
|
|
69
|
-
const getOverlayStyle = useCallback(() => ({ width }), [width]);
|
|
70
|
-
const getOverlayProps = useCallback(
|
|
71
|
-
() => ({ itemContext: { transform, SectionContent } }),
|
|
72
|
-
[transform, SectionContent],
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
// TODO(thure): The root cause of the discrepancy between `activeNodeRect.top` and `overlayNodeRect.top` in Composer
|
|
76
|
-
// in particular is not yet known, so this solution may may backfire in unforeseeable cases.
|
|
77
|
-
const stackModifier = useCallback<Exclude<MosaicContainerProps['modifier'], undefined>>(
|
|
78
|
-
(_activeItem, { transform, activeNodeRect, overlayNodeRect }) => {
|
|
79
|
-
if (activeNodeRect && overlayNodeRect) {
|
|
80
|
-
transform.y += activeNodeRect?.top - overlayNodeRect?.top;
|
|
81
|
-
}
|
|
82
|
-
return transform;
|
|
23
|
+
export const Stack = forwardRef<HTMLDivElement, StackProps>(
|
|
24
|
+
(
|
|
25
|
+
{
|
|
26
|
+
children,
|
|
27
|
+
classNames,
|
|
28
|
+
style,
|
|
29
|
+
orientation = 'vertical',
|
|
30
|
+
rail = true,
|
|
31
|
+
separators = true,
|
|
32
|
+
size = 'intrinsic',
|
|
33
|
+
itemsCount = Children.count(children),
|
|
34
|
+
...props
|
|
83
35
|
},
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<Mosaic.Container
|
|
89
|
-
{...{
|
|
90
|
-
id,
|
|
91
|
-
type,
|
|
92
|
-
Component: SectionTile as MosaicTileComponent<StackSectionItem, HTMLDivElement>,
|
|
93
|
-
getOverlayStyle,
|
|
94
|
-
getOverlayProps,
|
|
95
|
-
onOver,
|
|
96
|
-
onDrop,
|
|
97
|
-
modifier: stackModifier,
|
|
98
|
-
}}
|
|
99
|
-
>
|
|
100
|
-
<Mosaic.DroppableTile
|
|
101
|
-
path={id}
|
|
102
|
-
type={type}
|
|
103
|
-
item={{ id, items: itemsWithPreview }}
|
|
104
|
-
// TODO(wittjosiah): Should this actually be a context?
|
|
105
|
-
itemContext={{
|
|
106
|
-
separation,
|
|
107
|
-
transform,
|
|
108
|
-
onDeleteSection,
|
|
109
|
-
onNavigateToSection,
|
|
110
|
-
onAddSection,
|
|
111
|
-
onCollapseSection,
|
|
112
|
-
SectionContent,
|
|
113
|
-
}}
|
|
114
|
-
isOver={
|
|
115
|
-
overItem &&
|
|
116
|
-
!!overItem.path &&
|
|
117
|
-
Path.hasRoot(overItem.path, id) &&
|
|
118
|
-
(operation === 'copy' || operation === 'transfer')
|
|
119
|
-
}
|
|
120
|
-
Component={StackTile}
|
|
121
|
-
{...props}
|
|
122
|
-
ref={containerRef}
|
|
123
|
-
/>
|
|
124
|
-
</Mosaic.Container>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
36
|
+
forwardedRef,
|
|
37
|
+
) => {
|
|
38
|
+
const arrowNavigationGroup = useArrowNavigationGroup({ axis: orientation });
|
|
127
39
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const domAttributes = useArrowNavigationGroup({ axis: 'grid' });
|
|
133
|
-
const { activeItem } = useMosaic();
|
|
40
|
+
const styles: CSSProperties = {
|
|
41
|
+
[orientation === 'horizontal' ? 'gridTemplateColumns' : 'gridTemplateRows']: `repeat(${itemsCount}, min-content)`,
|
|
42
|
+
...style,
|
|
43
|
+
};
|
|
134
44
|
|
|
135
|
-
// NOTE(thure): Ensure “groupper” is available, but no need to use it here.
|
|
136
|
-
const _group = useFocusableGroup();
|
|
137
|
-
|
|
138
|
-
// NOTE: Keep outer padding the same as MarkdownMain.
|
|
139
45
|
return (
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
{t('empty stack message')}
|
|
168
|
-
</p>
|
|
169
|
-
)}
|
|
170
|
-
</List>
|
|
46
|
+
<StackContext.Provider value={{ orientation, rail, size, separators }}>
|
|
47
|
+
<div
|
|
48
|
+
{...props}
|
|
49
|
+
{...arrowNavigationGroup}
|
|
50
|
+
className={mx(
|
|
51
|
+
'grid relative',
|
|
52
|
+
rail
|
|
53
|
+
? orientation === 'horizontal'
|
|
54
|
+
? railGridHorizontal
|
|
55
|
+
: railGridVertical
|
|
56
|
+
: orientation === 'horizontal'
|
|
57
|
+
? 'grid-rows-1'
|
|
58
|
+
: 'grid-cols-1',
|
|
59
|
+
size === 'contain' &&
|
|
60
|
+
(orientation === 'horizontal'
|
|
61
|
+
? 'overflow-x-auto min-bs-0 bs-full max-bs-full'
|
|
62
|
+
: 'overflow-y-auto min-is-0 is-full max-is-full'),
|
|
63
|
+
separators && (orientation === 'horizontal' ? 'divide-separator divide-x' : 'divide-separator divide-y'),
|
|
64
|
+
classNames,
|
|
65
|
+
)}
|
|
66
|
+
aria-orientation={orientation}
|
|
67
|
+
style={styles}
|
|
68
|
+
ref={forwardedRef}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
</StackContext.Provider>
|
|
171
73
|
);
|
|
172
74
|
},
|
|
173
75
|
);
|
|
76
|
+
|
|
77
|
+
export { StackContext };
|
|
78
|
+
export type { StackContextValue };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext, useContext } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Orientation, type Size } from './Stack';
|
|
8
|
+
import { type StackItemSize } from './StackItem';
|
|
9
|
+
|
|
10
|
+
export type StackContextValue = {
|
|
11
|
+
orientation: Orientation;
|
|
12
|
+
separators: boolean;
|
|
13
|
+
rail: boolean;
|
|
14
|
+
size: Size;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const StackContext = createContext<StackContextValue>({
|
|
18
|
+
orientation: 'vertical',
|
|
19
|
+
rail: true,
|
|
20
|
+
size: 'intrinsic',
|
|
21
|
+
separators: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const useStack = () => useContext(StackContext);
|
|
25
|
+
|
|
26
|
+
type StackItemContextValue = {
|
|
27
|
+
selfDragHandleRef: (element: HTMLDivElement | null) => void;
|
|
28
|
+
size: StackItemSize;
|
|
29
|
+
setSize: (nextSize: StackItemSize, commit?: boolean) => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const StackItemContext = createContext<StackItemContextValue>({
|
|
33
|
+
selfDragHandleRef: () => {},
|
|
34
|
+
size: 'min-content',
|
|
35
|
+
setSize: () => {},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const useStackItem = () => useContext(StackItemContext);
|