@dxos/react-ui-stack 0.6.14-staging.934c9de → 0.6.14-staging.9b873ce
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 +494 -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 +478 -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 +494 -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 +9 -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 +8 -8
- package/package.json +19 -20
- 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 +49 -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 +8 -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,378 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useFocusableGroup, useTabsterAttributes } from '@fluentui/react-tabster';
|
|
6
|
-
import { ArrowLineDown, ArrowLineUp, ArrowSquareOut, CaretUpDown, Trash } from '@phosphor-icons/react';
|
|
7
|
-
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
8
|
-
import React, {
|
|
9
|
-
forwardRef,
|
|
10
|
-
useState,
|
|
11
|
-
type ForwardRefExoticComponent,
|
|
12
|
-
type RefAttributes,
|
|
13
|
-
type FC,
|
|
14
|
-
type PropsWithChildren,
|
|
15
|
-
type ComponentPropsWithRef,
|
|
16
|
-
} from 'react';
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
Button,
|
|
20
|
-
DropdownMenu,
|
|
21
|
-
Icon,
|
|
22
|
-
type Label,
|
|
23
|
-
List,
|
|
24
|
-
ListItem,
|
|
25
|
-
ScrollArea,
|
|
26
|
-
type ThemedClassName,
|
|
27
|
-
Toolbar,
|
|
28
|
-
toLocalizedString,
|
|
29
|
-
useTranslation,
|
|
30
|
-
} from '@dxos/react-ui';
|
|
31
|
-
import { useAttendableAttributes } from '@dxos/react-ui-attention';
|
|
32
|
-
import { DropDownMenuDragHandleTrigger, resizeHandle, resizeHandleHorizontal } from '@dxos/react-ui-deck';
|
|
33
|
-
import {
|
|
34
|
-
type MosaicActiveType,
|
|
35
|
-
type MosaicDataItem,
|
|
36
|
-
type MosaicTileComponent,
|
|
37
|
-
type MosaicTileProps,
|
|
38
|
-
useMosaic,
|
|
39
|
-
} from '@dxos/react-ui-mosaic';
|
|
40
|
-
import {
|
|
41
|
-
focusRing,
|
|
42
|
-
getSize,
|
|
43
|
-
hoverableControlItem,
|
|
44
|
-
hoverableControls,
|
|
45
|
-
hoverableFocusedWithinControls,
|
|
46
|
-
mx,
|
|
47
|
-
} from '@dxos/react-ui-theme';
|
|
48
|
-
|
|
49
|
-
import { CaretDownUp } from './CaretDownUp';
|
|
50
|
-
import { stackColumns } from './style-fragments';
|
|
51
|
-
import { translationKey } from '../translations';
|
|
52
|
-
|
|
53
|
-
const sectionActionDimensions = 'p-1 shrink-0 min-bs-0 is-[--rail-action] bs-min';
|
|
54
|
-
|
|
55
|
-
export type StackSectionContent = MosaicDataItem;
|
|
56
|
-
|
|
57
|
-
export type CollapsedSections = Record<string, boolean>;
|
|
58
|
-
|
|
59
|
-
export type AddSectionPosition = 'before' | 'after' | 'beforeAll' | 'afterAll';
|
|
60
|
-
|
|
61
|
-
export type StackContextValue<TData extends StackSectionContent = StackSectionContent> = {
|
|
62
|
-
SectionContent: FC<{ data: TData }>;
|
|
63
|
-
separation?: boolean;
|
|
64
|
-
isResizable?: boolean;
|
|
65
|
-
transform?: (item: MosaicDataItem, type?: string) => StackSectionItem;
|
|
66
|
-
onDeleteSection?: (path: string) => void;
|
|
67
|
-
onAddSection?: (path: string, position: AddSectionPosition) => void;
|
|
68
|
-
onNavigateToSection?: (object: MosaicDataItem) => void;
|
|
69
|
-
onCollapseSection?: (id: string, collapsed: boolean) => void;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type StackItem = MosaicDataItem & {
|
|
73
|
-
items: StackSectionItem[];
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export type StackSectionItem = MosaicDataItem & {
|
|
77
|
-
object: StackSectionContent;
|
|
78
|
-
// TODO(wittjosiah): Use effect schema? Share schema with echo.
|
|
79
|
-
view?: {
|
|
80
|
-
title?: string;
|
|
81
|
-
size?: SectionSize;
|
|
82
|
-
height?: number;
|
|
83
|
-
collapsed?: boolean;
|
|
84
|
-
custom?: Record<string, any>;
|
|
85
|
-
};
|
|
86
|
-
// TODO(wittjosiah): Common type? Factor out?
|
|
87
|
-
metadata?: {
|
|
88
|
-
icon?: string;
|
|
89
|
-
placeholder?: Label;
|
|
90
|
-
viewActions?: (item: StackSectionItem) => StackAction;
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export type StackAction = {
|
|
95
|
-
icon: string;
|
|
96
|
-
label: Label;
|
|
97
|
-
onClick: () => void;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export type SectionSize = 'intrinsic' | 'extrinsic';
|
|
101
|
-
|
|
102
|
-
export type SectionProps = PropsWithChildren<
|
|
103
|
-
{
|
|
104
|
-
// Data props.
|
|
105
|
-
id: string;
|
|
106
|
-
title: string;
|
|
107
|
-
|
|
108
|
-
// Tile props.
|
|
109
|
-
active?: MosaicActiveType;
|
|
110
|
-
} & Pick<
|
|
111
|
-
MosaicTileProps,
|
|
112
|
-
'draggableProps' | 'draggableStyle' | 'onDelete' | 'onNavigate' | 'onAddAfter' | 'onAddBefore'
|
|
113
|
-
> &
|
|
114
|
-
Pick<StackContextValue, 'separation' | 'isResizable' | 'onCollapseSection'> &
|
|
115
|
-
Pick<Required<StackSectionItem>['view'], 'collapsed' | 'size'> &
|
|
116
|
-
Pick<Required<StackSectionItem>['metadata'], 'icon'>
|
|
117
|
-
>;
|
|
118
|
-
|
|
119
|
-
const resizeHandleStyles = mx(resizeHandle, resizeHandleHorizontal, 'is-full bs-[--rail-action] col-start-2');
|
|
120
|
-
|
|
121
|
-
export const Section: ForwardRefExoticComponent<SectionProps & RefAttributes<HTMLLIElement>> = forwardRef<
|
|
122
|
-
HTMLLIElement,
|
|
123
|
-
SectionProps
|
|
124
|
-
>(
|
|
125
|
-
(
|
|
126
|
-
{
|
|
127
|
-
id,
|
|
128
|
-
title,
|
|
129
|
-
icon = 'ph--placeholder--regular',
|
|
130
|
-
size = 'intrinsic',
|
|
131
|
-
collapsed,
|
|
132
|
-
active,
|
|
133
|
-
isResizable,
|
|
134
|
-
draggableProps,
|
|
135
|
-
draggableStyle,
|
|
136
|
-
onDelete,
|
|
137
|
-
onNavigate,
|
|
138
|
-
onAddBefore,
|
|
139
|
-
onAddAfter,
|
|
140
|
-
onCollapseSection,
|
|
141
|
-
children,
|
|
142
|
-
},
|
|
143
|
-
forwardedRef,
|
|
144
|
-
) => {
|
|
145
|
-
const { t } = useTranslation(translationKey);
|
|
146
|
-
const [optionsMenuOpen, setOptionsMenuOpen] = useState(false);
|
|
147
|
-
const sectionActionsToolbar = useTabsterAttributes({
|
|
148
|
-
groupper: {},
|
|
149
|
-
focusable: {},
|
|
150
|
-
mover: { cyclic: true, direction: 1, memorizeCurrent: false },
|
|
151
|
-
});
|
|
152
|
-
const sectionContentGroup = useFocusableGroup({});
|
|
153
|
-
const attendableAttrs = useAttendableAttributes(id);
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<CollapsiblePrimitive.Root
|
|
157
|
-
asChild
|
|
158
|
-
open={!collapsed}
|
|
159
|
-
onOpenChange={(nextOpen) => onCollapseSection?.(id, !nextOpen)}
|
|
160
|
-
>
|
|
161
|
-
<ListItem.Root
|
|
162
|
-
ref={forwardedRef}
|
|
163
|
-
id={id}
|
|
164
|
-
{...attendableAttrs}
|
|
165
|
-
classNames={[
|
|
166
|
-
'grid col-span-2 group/section',
|
|
167
|
-
active === 'overlay' ? stackColumns : 'grid-cols-subgrid snap-start',
|
|
168
|
-
]}
|
|
169
|
-
style={draggableStyle}
|
|
170
|
-
>
|
|
171
|
-
<div
|
|
172
|
-
role='none'
|
|
173
|
-
className={mx(
|
|
174
|
-
'grid col-span-2 grid-cols-subgrid',
|
|
175
|
-
'bg-base attention-surface',
|
|
176
|
-
hoverableControls,
|
|
177
|
-
hoverableFocusedWithinControls,
|
|
178
|
-
(active === 'origin' || active === 'rearrange' || active === 'destination') && 'opacity-0',
|
|
179
|
-
)}
|
|
180
|
-
>
|
|
181
|
-
<div
|
|
182
|
-
role='toolbar'
|
|
183
|
-
aria-orientation='vertical'
|
|
184
|
-
aria-label={t('section controls label')}
|
|
185
|
-
{...(!active && { tabIndex: 0 })}
|
|
186
|
-
{...(!active && sectionActionsToolbar)}
|
|
187
|
-
className={mx(
|
|
188
|
-
'grid grid-cols-subgrid ch-focus-ring rounded-sm grid-rows-[min-content_min-content_1fr] m-1',
|
|
189
|
-
'group-has-[[role=toolbar][aria-orientation=horizontal]]/section:pbs-[--rail-action]',
|
|
190
|
-
)}
|
|
191
|
-
>
|
|
192
|
-
<div role='none' className='sticky -block-start-px bg-[--sticky-bg]'>
|
|
193
|
-
<DropdownMenu.Root
|
|
194
|
-
{...{
|
|
195
|
-
open: optionsMenuOpen,
|
|
196
|
-
onOpenChange: setOptionsMenuOpen,
|
|
197
|
-
}}
|
|
198
|
-
>
|
|
199
|
-
<DropDownMenuDragHandleTrigger active={!!active} variant='ghost' classNames='m-0' {...draggableProps}>
|
|
200
|
-
<Icon icon={icon} size={5} classNames='transition-opacity' />
|
|
201
|
-
</DropDownMenuDragHandleTrigger>
|
|
202
|
-
<DropdownMenu.Portal>
|
|
203
|
-
<DropdownMenu.Content>
|
|
204
|
-
<DropdownMenu.Viewport>
|
|
205
|
-
{collapsed ? (
|
|
206
|
-
<DropdownMenu.Item onClick={onNavigate} data-testid='section.navigate-to'>
|
|
207
|
-
<ArrowSquareOut className={mx(getSize(5), 'mr-2')} />
|
|
208
|
-
<span className='grow'>{t('navigate to section label')}</span>
|
|
209
|
-
</DropdownMenu.Item>
|
|
210
|
-
) : (
|
|
211
|
-
<CollapsiblePrimitive.Trigger asChild>
|
|
212
|
-
<DropdownMenu.Item>
|
|
213
|
-
<CaretDownUp className={mx(getSize(5), 'mr-2')} />
|
|
214
|
-
<span className='grow'>{t('collapse label')}</span>
|
|
215
|
-
</DropdownMenu.Item>
|
|
216
|
-
</CollapsiblePrimitive.Trigger>
|
|
217
|
-
)}
|
|
218
|
-
<DropdownMenu.Item onClick={onAddBefore} data-testid='section.add-before'>
|
|
219
|
-
<ArrowLineUp className={mx(getSize(5), 'mr-2')} />
|
|
220
|
-
<span className='grow'>{t('add section before label')}</span>
|
|
221
|
-
</DropdownMenu.Item>
|
|
222
|
-
<DropdownMenu.Item onClick={onAddAfter} data-testid='section.add-after'>
|
|
223
|
-
<ArrowLineDown className={mx(getSize(5), 'mr-2')} />
|
|
224
|
-
<span className='grow'>{t('add section after label')}</span>
|
|
225
|
-
</DropdownMenu.Item>
|
|
226
|
-
<DropdownMenu.Item onClick={() => onDelete?.()} data-testid='section.remove'>
|
|
227
|
-
<Trash className={mx(getSize(5), 'mr-2')} />
|
|
228
|
-
<span className='grow'>{t('remove section label')}</span>
|
|
229
|
-
</DropdownMenu.Item>
|
|
230
|
-
</DropdownMenu.Viewport>
|
|
231
|
-
<DropdownMenu.Arrow />
|
|
232
|
-
</DropdownMenu.Content>
|
|
233
|
-
</DropdownMenu.Portal>
|
|
234
|
-
</DropdownMenu.Root>
|
|
235
|
-
{collapsed ? (
|
|
236
|
-
<CollapsiblePrimitive.Trigger asChild>
|
|
237
|
-
<Button variant='ghost' classNames={sectionActionDimensions}>
|
|
238
|
-
<span className='sr-only'>{t('expand label')}</span>
|
|
239
|
-
<CaretUpDown className={getSize(4)} />
|
|
240
|
-
</Button>
|
|
241
|
-
</CollapsiblePrimitive.Trigger>
|
|
242
|
-
) : (
|
|
243
|
-
<Button
|
|
244
|
-
variant='ghost'
|
|
245
|
-
classNames={sectionActionDimensions}
|
|
246
|
-
onClick={onNavigate}
|
|
247
|
-
data-testid='section.navigate-to'
|
|
248
|
-
>
|
|
249
|
-
<ArrowSquareOut className={mx(getSize(4))} />
|
|
250
|
-
<span className='sr-only'>{t('navigate to section label')}</span>
|
|
251
|
-
</Button>
|
|
252
|
-
)}
|
|
253
|
-
</div>
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
{/* Main content */}
|
|
257
|
-
|
|
258
|
-
<ListItem.Heading
|
|
259
|
-
classNames={collapsed ? ['grid grid-rows-subgrid grid-cols-subgrid items-center', focusRing] : 'sr-only'}
|
|
260
|
-
{...(collapsed && { ...sectionContentGroup, tabIndex: 0 })}
|
|
261
|
-
>
|
|
262
|
-
{/*
|
|
263
|
-
TODO(thure): This needs to be made extensible; Markdown document titles especially are difficult.
|
|
264
|
-
Using `Surface` in a UI package like this would be unprecedented and needs motivation.
|
|
265
|
-
Refactoring to use subcomponents is complicated by sections being a sortable Mosaic Tile.
|
|
266
|
-
Reevaluate when work on collections (Folders, Stacks, etc) settles.
|
|
267
|
-
*/}
|
|
268
|
-
<span className='truncate'>{title}</span>
|
|
269
|
-
</ListItem.Heading>
|
|
270
|
-
{size === 'intrinsic' ? (
|
|
271
|
-
<CollapsiblePrimitive.Content
|
|
272
|
-
{...(!collapsed && {
|
|
273
|
-
...sectionContentGroup,
|
|
274
|
-
tabIndex: 0,
|
|
275
|
-
})}
|
|
276
|
-
// TODO(burdon): Add margin to fragment?
|
|
277
|
-
className={mx(focusRing, 'm-[2px]')}
|
|
278
|
-
>
|
|
279
|
-
{children}
|
|
280
|
-
</CollapsiblePrimitive.Content>
|
|
281
|
-
) : (
|
|
282
|
-
<CollapsiblePrimitive.Content asChild>
|
|
283
|
-
<ScrollArea.Root
|
|
284
|
-
type='always'
|
|
285
|
-
{...(!collapsed && { ...sectionContentGroup, tabIndex: 0 })}
|
|
286
|
-
classNames={mx(focusRing, 'is-full has-[[data-radix-scroll-area-viewport]]:pbe-4')}
|
|
287
|
-
>
|
|
288
|
-
<ScrollArea.Viewport>{children}</ScrollArea.Viewport>
|
|
289
|
-
<ScrollArea.Scrollbar
|
|
290
|
-
orientation='horizontal'
|
|
291
|
-
variant='coarse'
|
|
292
|
-
classNames='hidden has-[div]:flex !inline-end-[max(.25rem,var(--radix-scroll-area-corner-width))]'
|
|
293
|
-
>
|
|
294
|
-
<ScrollArea.Thumb />
|
|
295
|
-
</ScrollArea.Scrollbar>
|
|
296
|
-
<ScrollArea.Scrollbar orientation='vertical' variant='coarse' classNames='hidden has-[div]:flex'>
|
|
297
|
-
<ScrollArea.Thumb />
|
|
298
|
-
</ScrollArea.Scrollbar>
|
|
299
|
-
<ScrollArea.Corner />
|
|
300
|
-
</ScrollArea.Root>
|
|
301
|
-
</CollapsiblePrimitive.Content>
|
|
302
|
-
)}
|
|
303
|
-
</div>
|
|
304
|
-
{isResizable && !collapsed && (
|
|
305
|
-
<button className={resizeHandleStyles}>
|
|
306
|
-
<span className='sr-only'>{t('resize section label')}</span>
|
|
307
|
-
</button>
|
|
308
|
-
)}
|
|
309
|
-
</ListItem.Root>
|
|
310
|
-
</CollapsiblePrimitive.Root>
|
|
311
|
-
);
|
|
312
|
-
},
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
export type SectionToolbarProps = ThemedClassName<ComponentPropsWithRef<'div'>>;
|
|
316
|
-
|
|
317
|
-
// TODO(burdon): block-start
|
|
318
|
-
export const sectionToolbarLayout =
|
|
319
|
-
'bs-[--rail-action] bg-[--sticky-bg] sticky block-start-0 __-block-start-px transition-opacity';
|
|
320
|
-
|
|
321
|
-
export const SectionToolbar = ({ children, classNames }: SectionToolbarProps) => {
|
|
322
|
-
return (
|
|
323
|
-
<Toolbar.Root orientation='horizontal' classNames={[sectionToolbarLayout, hoverableControlItem, classNames]}>
|
|
324
|
-
{children}
|
|
325
|
-
</Toolbar.Root>
|
|
326
|
-
);
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
export const SectionTile: MosaicTileComponent<
|
|
330
|
-
StackSectionItem,
|
|
331
|
-
HTMLLIElement
|
|
332
|
-
// TODO(wittjosiah): If props is specified there is a type error with Mosaic.Container.
|
|
333
|
-
// { itemContext: StackContextValue }
|
|
334
|
-
> = forwardRef(({ path, type, active, draggableStyle, draggableProps, item, itemContext }, forwardedRef) => {
|
|
335
|
-
const { t } = useTranslation(translationKey);
|
|
336
|
-
const { activeItem } = useMosaic();
|
|
337
|
-
|
|
338
|
-
const separation = !!itemContext?.separation;
|
|
339
|
-
const isResizable = !!itemContext?.isResizable;
|
|
340
|
-
const { transform, onDeleteSection, onNavigateToSection, onAddSection, onCollapseSection, SectionContent } =
|
|
341
|
-
itemContext as StackContextValue;
|
|
342
|
-
|
|
343
|
-
const transformedItem = transform
|
|
344
|
-
? transform(
|
|
345
|
-
item,
|
|
346
|
-
// TODO(wittjosiah): `active` doesn't always seem to be accurate here.
|
|
347
|
-
activeItem?.item.id === item.id ? activeItem?.type : type,
|
|
348
|
-
)
|
|
349
|
-
: item;
|
|
350
|
-
|
|
351
|
-
const placeholder = transformedItem.metadata?.placeholder ?? ['untitled section title', { ns: translationKey }];
|
|
352
|
-
const title = transformedItem.view?.title ?? toLocalizedString(placeholder, t);
|
|
353
|
-
|
|
354
|
-
const section = (
|
|
355
|
-
<Section
|
|
356
|
-
ref={forwardedRef}
|
|
357
|
-
title={title}
|
|
358
|
-
id={transformedItem.id}
|
|
359
|
-
size={transformedItem.view?.size}
|
|
360
|
-
icon={transformedItem.metadata?.icon}
|
|
361
|
-
collapsed={transformedItem.view?.collapsed}
|
|
362
|
-
separation={separation}
|
|
363
|
-
active={active}
|
|
364
|
-
draggableProps={draggableProps}
|
|
365
|
-
draggableStyle={draggableStyle}
|
|
366
|
-
onCollapseSection={onCollapseSection}
|
|
367
|
-
isResizable={isResizable}
|
|
368
|
-
onDelete={() => onDeleteSection?.(path)}
|
|
369
|
-
onNavigate={() => onNavigateToSection?.(transformedItem)}
|
|
370
|
-
onAddAfter={() => onAddSection?.(path, 'after')}
|
|
371
|
-
onAddBefore={() => onAddSection?.(path, 'before')}
|
|
372
|
-
>
|
|
373
|
-
{SectionContent && <SectionContent data={transformedItem.object} />}
|
|
374
|
-
</Section>
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
return active === 'overlay' ? <List>{section}</List> : section;
|
|
378
|
-
});
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
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';
|
|
8
|
-
|
|
9
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
10
|
-
|
|
11
|
-
import { Stack } from './Stack';
|
|
12
|
-
import { StackItem } from './StackItem';
|
|
13
|
-
|
|
14
|
-
type CardItem = {
|
|
15
|
-
id: string;
|
|
16
|
-
type: 'card';
|
|
17
|
-
content: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
type ColumnItem = {
|
|
21
|
-
id: string;
|
|
22
|
-
type: 'column';
|
|
23
|
-
title: string;
|
|
24
|
-
cards: CardItem[];
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const KanbanBlock = ({ item }: { item: CardItem }) => {
|
|
28
|
-
return (
|
|
29
|
-
<div className='is-64 bs-24 bg-input rounded-lg border border-separator shadow-sm grid place-content-center'>
|
|
30
|
-
<span className='text-sm font-medium'>{item.content}</span>
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const StorybookStack = () => {
|
|
36
|
-
const [columns, setColumns] = useState<ColumnItem[]>([
|
|
37
|
-
{
|
|
38
|
-
id: 'col-0',
|
|
39
|
-
type: 'column',
|
|
40
|
-
title: 'To Do',
|
|
41
|
-
cards: [
|
|
42
|
-
{ id: 'banana', type: 'card', content: 'Banana' },
|
|
43
|
-
{ id: 'pickle', type: 'card', content: 'Pickle' },
|
|
44
|
-
{ id: 'wombat', type: 'card', content: 'Wombat' },
|
|
45
|
-
{ id: 'kazoo', type: 'card', content: 'Kazoo' },
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: 'col-1',
|
|
50
|
-
type: 'column',
|
|
51
|
-
title: 'In Progress',
|
|
52
|
-
cards: [
|
|
53
|
-
{ id: 'noodle', type: 'card', content: 'Noodle' },
|
|
54
|
-
{ id: 'squish', type: 'card', content: 'Squish' },
|
|
55
|
-
{ id: 'wobble', type: 'card', content: 'Wobble' },
|
|
56
|
-
{ id: 'floof', type: 'card', content: 'Floof' },
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: 'col-2',
|
|
61
|
-
type: 'column',
|
|
62
|
-
title: 'Done',
|
|
63
|
-
cards: [
|
|
64
|
-
{ id: 'snorkel', type: 'card', content: 'Snorkel' },
|
|
65
|
-
{ id: 'bloop', type: 'card', content: 'Bloop' },
|
|
66
|
-
{ id: 'wiggle', type: 'card', content: 'Wiggle' },
|
|
67
|
-
{ id: 'zoop', type: 'card', content: 'Zoop' },
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
const reorderItem = useCallback((sourceId: string, targetId: string, closestEdge: Edge | null) => {
|
|
73
|
-
setColumns((prevColumns) => {
|
|
74
|
-
const newColumns = [...prevColumns];
|
|
75
|
-
const sourceColumn = newColumns.find(
|
|
76
|
-
(col) => col.id === sourceId || col.cards.some((card) => card.id === sourceId),
|
|
77
|
-
);
|
|
78
|
-
const targetColumn = newColumns.find(
|
|
79
|
-
(col) => col.id === targetId || col.cards.some((card) => card.id === targetId),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
if (sourceColumn && targetColumn) {
|
|
83
|
-
if (sourceId.startsWith('col-') && targetId.startsWith('col-')) {
|
|
84
|
-
// Reordering columns
|
|
85
|
-
const sourceIndex = newColumns.findIndex((col) => col.id === sourceId);
|
|
86
|
-
const targetIndex = newColumns.findIndex((col) => col.id === targetId);
|
|
87
|
-
const [movedColumn] = newColumns.splice(sourceIndex, 1);
|
|
88
|
-
const insertIndex = closestEdge === 'right' ? targetIndex + 1 : targetIndex;
|
|
89
|
-
newColumns.splice(insertIndex, 0, movedColumn);
|
|
90
|
-
} else {
|
|
91
|
-
// Reordering cards within a column
|
|
92
|
-
const sourceCardIndex = sourceColumn.cards.findIndex((card) => card.id === sourceId);
|
|
93
|
-
const targetCardIndex = targetColumn.cards.findIndex((card) => card.id === targetId);
|
|
94
|
-
const [movedCard] = sourceColumn.cards.splice(sourceCardIndex, 1);
|
|
95
|
-
|
|
96
|
-
let insertIndex;
|
|
97
|
-
if (sourceColumn === targetColumn && sourceCardIndex < targetCardIndex) {
|
|
98
|
-
insertIndex = closestEdge === 'bottom' ? targetCardIndex : targetCardIndex - 1;
|
|
99
|
-
} else {
|
|
100
|
-
insertIndex = closestEdge === 'bottom' ? targetCardIndex + 1 : targetCardIndex;
|
|
101
|
-
}
|
|
102
|
-
targetColumn.cards.splice(insertIndex, 0, movedCard);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return newColumns;
|
|
107
|
-
});
|
|
108
|
-
}, []);
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<Stack orientation={'horizontal'} classNames='gap-1'>
|
|
112
|
-
{columns.map((column) => (
|
|
113
|
-
<StackItem
|
|
114
|
-
key={column.id}
|
|
115
|
-
item={column}
|
|
116
|
-
orientation={'horizontal'}
|
|
117
|
-
classNames='p-4 bg-deck rounded-md'
|
|
118
|
-
onReorder={reorderItem}
|
|
119
|
-
>
|
|
120
|
-
<Stack orientation={'vertical'} classNames='gap-1'>
|
|
121
|
-
{column.cards.map((card) => (
|
|
122
|
-
<StackItem key={card.id} item={card} orientation={'vertical'} onReorder={reorderItem}>
|
|
123
|
-
<KanbanBlock item={card} />
|
|
124
|
-
</StackItem>
|
|
125
|
-
))}
|
|
126
|
-
</Stack>
|
|
127
|
-
</StackItem>
|
|
128
|
-
))}
|
|
129
|
-
</Stack>
|
|
130
|
-
);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
type Story = StoryObj<typeof StorybookStack>;
|
|
134
|
-
|
|
135
|
-
export const Default: Story = {
|
|
136
|
-
args: {
|
|
137
|
-
orientation: 'horizontal',
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const meta: Meta<typeof StorybookStack> = {
|
|
142
|
-
title: 'ui/react-ui-stack-next/Stack',
|
|
143
|
-
component: StorybookStack,
|
|
144
|
-
decorators: [withTheme],
|
|
145
|
-
argTypes: { orientation: { control: 'radio', options: ['horizontal', 'vertical'] } },
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export default meta;
|
package/src/next/Stack.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { Children, type CSSProperties, type ComponentPropsWithoutRef } from 'react';
|
|
6
|
-
|
|
7
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
8
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
9
|
-
|
|
10
|
-
type Orientation = 'horizontal' | 'vertical';
|
|
11
|
-
|
|
12
|
-
export type StackProps = Omit<ThemedClassName<ComponentPropsWithoutRef<'div'>>, 'aria-orientation'> & {
|
|
13
|
-
orientation?: Orientation;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const Stack = ({ children, classNames, style, orientation, ...props }: StackProps) => {
|
|
17
|
-
const childrenCount = Children.count(children);
|
|
18
|
-
|
|
19
|
-
const styles: CSSProperties = {
|
|
20
|
-
[orientation === 'horizontal' ? 'gridTemplateColumns' : 'gridTemplateRows']:
|
|
21
|
-
`repeat(${childrenCount}, min-content)`,
|
|
22
|
-
...style,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className={mx('grid relative', classNames)} aria-orientation={orientation} style={styles} {...props}>
|
|
27
|
-
{children}
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
};
|
package/src/next/StackItem.tsx
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
6
|
-
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
7
|
-
import {
|
|
8
|
-
attachClosestEdge,
|
|
9
|
-
type Edge,
|
|
10
|
-
extractClosestEdge,
|
|
11
|
-
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
12
|
-
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
|
|
13
|
-
import React, { useEffect, useRef, useState, type ComponentPropsWithoutRef } from 'react';
|
|
14
|
-
|
|
15
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
16
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
17
|
-
|
|
18
|
-
import { type StackProps } from './Stack';
|
|
19
|
-
|
|
20
|
-
export type StackItemProps = Omit<ThemedClassName<ComponentPropsWithoutRef<'div'>>, 'aria-orientation'> & {
|
|
21
|
-
item: { id: string; type: 'column' | 'card' };
|
|
22
|
-
orientation?: StackProps['orientation'];
|
|
23
|
-
onReorder: (sourceId: string, targetId: string, closestEdge: Edge | null) => void;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const StackItem = ({ item, children, classNames, orientation, onReorder, ...props }: StackItemProps) => {
|
|
27
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
28
|
-
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (!ref.current) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const element = ref.current;
|
|
36
|
-
|
|
37
|
-
return combine(
|
|
38
|
-
draggable({ element, getInitialData: () => ({ id: item.id, type: item.type }) }),
|
|
39
|
-
dropTargetForElements({
|
|
40
|
-
element,
|
|
41
|
-
getData: ({ input, element }) => {
|
|
42
|
-
return attachClosestEdge(
|
|
43
|
-
{ id: item.id, type: item.type },
|
|
44
|
-
{ input, element, allowedEdges: orientation === 'vertical' ? ['top', 'bottom'] : ['left', 'right'] },
|
|
45
|
-
);
|
|
46
|
-
},
|
|
47
|
-
onDragEnter: ({ self, source }) => {
|
|
48
|
-
if (source.data.type === self.data.type) {
|
|
49
|
-
setEdge(extractClosestEdge(self.data));
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
onDrag: ({ self, source }) => {
|
|
53
|
-
if (source.data.type === self.data.type) {
|
|
54
|
-
setEdge(extractClosestEdge(self.data));
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
onDragLeave: () => setEdge(null),
|
|
58
|
-
onDrop: ({ self, source }) => {
|
|
59
|
-
setEdge(null);
|
|
60
|
-
if (source.data.type === self.data.type) {
|
|
61
|
-
onReorder(source.data.id as string, self.data.id as string, extractClosestEdge(self.data));
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
66
|
-
}, [orientation, item, onReorder]);
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<div
|
|
70
|
-
ref={ref}
|
|
71
|
-
className={mx('relative', orientation === 'horizontal' ? 'grid-cols-subgrid' : 'grid-rows-subgrid', classNames)}
|
|
72
|
-
{...props}
|
|
73
|
-
>
|
|
74
|
-
{children}
|
|
75
|
-
{closestEdge && <DropIndicator edge={closestEdge} />}
|
|
76
|
-
</div>
|
|
77
|
-
);
|
|
78
|
-
};
|