@dxos/react-ui-mosaic 0.3.3-main.3ce60d4
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 +45 -0
- package/dist/lib/browser/index.mjs +596 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/mosaic/Container.d.ts +65 -0
- package/dist/types/src/mosaic/Container.d.ts.map +1 -0
- package/dist/types/src/mosaic/Debug.d.ts +9 -0
- package/dist/types/src/mosaic/Debug.d.ts.map +1 -0
- package/dist/types/src/mosaic/DefaultComponent.d.ts +4 -0
- package/dist/types/src/mosaic/DefaultComponent.d.ts.map +1 -0
- package/dist/types/src/mosaic/DragOverlay.d.ts +11 -0
- package/dist/types/src/mosaic/DragOverlay.d.ts.map +1 -0
- package/dist/types/src/mosaic/Mosaic.d.ts +32 -0
- package/dist/types/src/mosaic/Mosaic.d.ts.map +1 -0
- package/dist/types/src/mosaic/Root.d.ts +24 -0
- package/dist/types/src/mosaic/Root.d.ts.map +1 -0
- package/dist/types/src/mosaic/SortableContext.d.ts +14 -0
- package/dist/types/src/mosaic/SortableContext.d.ts.map +1 -0
- package/dist/types/src/mosaic/Tile.d.ts +41 -0
- package/dist/types/src/mosaic/Tile.d.ts.map +1 -0
- package/dist/types/src/mosaic/hooks/index.d.ts +5 -0
- package/dist/types/src/mosaic/hooks/index.d.ts.map +1 -0
- package/dist/types/src/mosaic/hooks/useContainer.d.ts +4 -0
- package/dist/types/src/mosaic/hooks/useContainer.d.ts.map +1 -0
- package/dist/types/src/mosaic/hooks/useItemsWithPreview.d.ts +10 -0
- package/dist/types/src/mosaic/hooks/useItemsWithPreview.d.ts.map +1 -0
- package/dist/types/src/mosaic/hooks/useMosaic.d.ts +2 -0
- package/dist/types/src/mosaic/hooks/useMosaic.d.ts.map +1 -0
- package/dist/types/src/mosaic/hooks/useSortedItems.d.ts +6 -0
- package/dist/types/src/mosaic/hooks/useSortedItems.d.ts.map +1 -0
- package/dist/types/src/mosaic/index.d.ts +5 -0
- package/dist/types/src/mosaic/index.d.ts.map +1 -0
- package/dist/types/src/mosaic/types.d.ts +9 -0
- package/dist/types/src/mosaic/types.d.ts.map +1 -0
- package/dist/types/src/mosaic/util/data.d.ts +3 -0
- package/dist/types/src/mosaic/util/data.d.ts.map +1 -0
- package/dist/types/src/mosaic/util/index.d.ts +4 -0
- package/dist/types/src/mosaic/util/index.d.ts.map +1 -0
- package/dist/types/src/mosaic/util/path.d.ts +14 -0
- package/dist/types/src/mosaic/util/path.d.ts.map +1 -0
- package/dist/types/src/mosaic/util/path.test.d.ts +2 -0
- package/dist/types/src/mosaic/util/path.test.d.ts.map +1 -0
- package/dist/types/src/mosaic/util/styles.d.ts +3 -0
- package/dist/types/src/mosaic/util/styles.d.ts.map +1 -0
- package/dist/types/src/testing/Cards.d.ts +10 -0
- package/dist/types/src/testing/Cards.d.ts.map +1 -0
- package/dist/types/src/testing/decorators.d.ts +4 -0
- package/dist/types/src/testing/decorators.d.ts.map +1 -0
- package/dist/types/src/testing/generator.d.ts +43 -0
- package/dist/types/src/testing/generator.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +5 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/next-fractional-index.d.ts +5 -0
- package/dist/types/src/testing/next-fractional-index.d.ts.map +1 -0
- package/dist/types/src/views/Demo/Demo.stories.d.ts +13 -0
- package/dist/types/src/views/Demo/Demo.stories.d.ts.map +1 -0
- package/dist/types/src/views/Demo/Echo.stories.d.ts +12 -0
- package/dist/types/src/views/Demo/Echo.stories.d.ts.map +1 -0
- package/dist/types/src/views/Grid/Grid.d.ts +30 -0
- package/dist/types/src/views/Grid/Grid.d.ts.map +1 -0
- package/dist/types/src/views/Grid/Grid.stories.d.ts +13 -0
- package/dist/types/src/views/Grid/Grid.stories.d.ts.map +1 -0
- package/dist/types/src/views/Grid/index.d.ts +3 -0
- package/dist/types/src/views/Grid/index.d.ts.map +1 -0
- package/dist/types/src/views/Grid/layout.d.ts +27 -0
- package/dist/types/src/views/Grid/layout.d.ts.map +1 -0
- package/dist/types/src/views/Grid/testing.d.ts +10 -0
- package/dist/types/src/views/Grid/testing.d.ts.map +1 -0
- package/dist/types/src/views/Kanban/Echo.stories.d.ts +18 -0
- package/dist/types/src/views/Kanban/Echo.stories.d.ts.map +1 -0
- package/dist/types/src/views/Kanban/Kanban.d.ts +11 -0
- package/dist/types/src/views/Kanban/Kanban.d.ts.map +1 -0
- package/dist/types/src/views/Kanban/Kanban.stories.d.ts +28 -0
- package/dist/types/src/views/Kanban/Kanban.stories.d.ts.map +1 -0
- package/dist/types/src/views/Kanban/index.d.ts +2 -0
- package/dist/types/src/views/Kanban/index.d.ts.map +1 -0
- package/dist/types/src/views/Kanban/testing.d.ts +20 -0
- package/dist/types/src/views/Kanban/testing.d.ts.map +1 -0
- package/dist/types/src/views/Stack/Columns.stories.d.ts +34 -0
- package/dist/types/src/views/Stack/Columns.stories.d.ts.map +1 -0
- package/dist/types/src/views/Stack/Stack.d.ts +9 -0
- package/dist/types/src/views/Stack/Stack.d.ts.map +1 -0
- package/dist/types/src/views/Stack/Stack.stories.d.ts +39 -0
- package/dist/types/src/views/Stack/Stack.stories.d.ts.map +1 -0
- package/dist/types/src/views/Stack/index.d.ts +2 -0
- package/dist/types/src/views/Stack/index.d.ts.map +1 -0
- package/dist/types/src/views/Stack/testing.d.ts +9 -0
- package/dist/types/src/views/Stack/testing.d.ts.map +1 -0
- package/dist/types/src/views/Tree/Graph.stories.d.ts +21 -0
- package/dist/types/src/views/Tree/Graph.stories.d.ts.map +1 -0
- package/dist/types/src/views/Tree/Tree.d.ts +13 -0
- package/dist/types/src/views/Tree/Tree.d.ts.map +1 -0
- package/dist/types/src/views/Tree/Tree.stories.d.ts +41 -0
- package/dist/types/src/views/Tree/Tree.stories.d.ts.map +1 -0
- package/dist/types/src/views/Tree/index.d.ts +2 -0
- package/dist/types/src/views/Tree/index.d.ts.map +1 -0
- package/dist/types/src/views/Tree/testing.d.ts +15 -0
- package/dist/types/src/views/Tree/testing.d.ts.map +1 -0
- package/dist/types/src/views/index.d.ts +5 -0
- package/dist/types/src/views/index.d.ts.map +1 -0
- package/package.json +67 -0
- package/src/index.ts +5 -0
- package/src/mosaic/Container.tsx +137 -0
- package/src/mosaic/Debug.tsx +24 -0
- package/src/mosaic/DefaultComponent.tsx +26 -0
- package/src/mosaic/DragOverlay.tsx +136 -0
- package/src/mosaic/Mosaic.ts +84 -0
- package/src/mosaic/Root.tsx +255 -0
- package/src/mosaic/SortableContext.tsx +60 -0
- package/src/mosaic/Tile.tsx +189 -0
- package/src/mosaic/hooks/index.ts +8 -0
- package/src/mosaic/hooks/useContainer.ts +16 -0
- package/src/mosaic/hooks/useItemsWithPreview.ts +64 -0
- package/src/mosaic/hooks/useMosaic.ts +11 -0
- package/src/mosaic/hooks/useSortedItems.ts +14 -0
- package/src/mosaic/index.ts +9 -0
- package/src/mosaic/types.ts +13 -0
- package/src/mosaic/util/data.ts +19 -0
- package/src/mosaic/util/index.ts +7 -0
- package/src/mosaic/util/path.test.ts +26 -0
- package/src/mosaic/util/path.ts +21 -0
- package/src/mosaic/util/styles.ts +10 -0
- package/src/testing/Cards.tsx +88 -0
- package/src/testing/decorators.tsx +20 -0
- package/src/testing/generator.ts +135 -0
- package/src/testing/index.ts +9 -0
- package/src/testing/next-fractional-index.ts +36 -0
- package/src/views/Demo/Demo.stories.tsx +79 -0
- package/src/views/Demo/Echo.stories.tsx +74 -0
- package/src/views/Grid/Grid.stories.tsx +48 -0
- package/src/views/Grid/Grid.tsx +248 -0
- package/src/views/Grid/index.ts +7 -0
- package/src/views/Grid/layout.ts +42 -0
- package/src/views/Grid/testing.tsx +91 -0
- package/src/views/Kanban/Echo.stories.tsx +67 -0
- package/src/views/Kanban/Kanban.stories.tsx +48 -0
- package/src/views/Kanban/Kanban.tsx +139 -0
- package/src/views/Kanban/index.ts +5 -0
- package/src/views/Kanban/testing.tsx +231 -0
- package/src/views/Stack/Columns.stories.tsx +61 -0
- package/src/views/Stack/Stack.stories.tsx +62 -0
- package/src/views/Stack/Stack.tsx +58 -0
- package/src/views/Stack/index.ts +5 -0
- package/src/views/Stack/testing.tsx +77 -0
- package/src/views/Tree/Graph.stories.tsx +36 -0
- package/src/views/Tree/Tree.stories.tsx +92 -0
- package/src/views/Tree/Tree.tsx +128 -0
- package/src/views/Tree/index.ts +5 -0
- package/src/views/Tree/testing.tsx +156 -0
- package/src/views/index.ts +8 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { describe, test } from '@dxos/test';
|
|
8
|
+
|
|
9
|
+
import { Path } from './path';
|
|
10
|
+
|
|
11
|
+
describe('paths', () => {
|
|
12
|
+
test('create', () => {
|
|
13
|
+
const path = Path.create('a', 'b', 'c');
|
|
14
|
+
expect(Path.first(path)).to.eq('a');
|
|
15
|
+
expect(Path.last(path)).to.eq('c');
|
|
16
|
+
|
|
17
|
+
expect(Path.hasRoot(path, 'a')).to.be.true;
|
|
18
|
+
expect(Path.hasRoot(path, 'x')).to.be.false;
|
|
19
|
+
|
|
20
|
+
expect(Path.hasChild(Path.create('a', 'b'), path)).to.be.true;
|
|
21
|
+
expect(Path.hasChild(Path.create('a'), path)).to.be.false;
|
|
22
|
+
|
|
23
|
+
expect(Path.hasDescendent(Path.create('a', 'b'), path)).to.be.true;
|
|
24
|
+
expect(Path.hasDescendent(Path.create('a'), path)).to.be.true;
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
const SEPARATOR = '/';
|
|
6
|
+
|
|
7
|
+
export const Path = {
|
|
8
|
+
create: (...args: string[]) => args.join(SEPARATOR),
|
|
9
|
+
|
|
10
|
+
parts: (path: string) => path.split(SEPARATOR),
|
|
11
|
+
length: (path: string) => path.split(SEPARATOR).length,
|
|
12
|
+
first: (path: string) => path.split(SEPARATOR)[0] ?? path,
|
|
13
|
+
last: (path: string) => path.split(SEPARATOR).at(-1) ?? path,
|
|
14
|
+
parent: (path: string) => path.split(SEPARATOR).slice(0, -1).join(SEPARATOR),
|
|
15
|
+
|
|
16
|
+
hasRoot: (path: string, id: string) => Path.first(path) === id,
|
|
17
|
+
hasChild: (path: string, compare: string) => Path.parent(compare) === path,
|
|
18
|
+
hasDescendent: (path: string, compare: string) => compare !== path && compare.startsWith(path),
|
|
19
|
+
siblings: (path: string, compare: string) => Path.parent(path) === Path.parent(compare),
|
|
20
|
+
onPath: (path: string, id: string) => Path.parts(path).includes(id),
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// https://docs.dndkit.com/api-documentation/draggable/usedraggable#transform
|
|
6
|
+
|
|
7
|
+
import { CSS, type Transform } from '@dnd-kit/utilities';
|
|
8
|
+
|
|
9
|
+
export const getTransformCSS = (transform: Transform | null) =>
|
|
10
|
+
transform ? CSS.Transform.toString(Object.assign(transform, { scaleX: 1, scaleY: 1 })) : undefined;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { forwardRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Card } from '@dxos/react-ui';
|
|
8
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
import { Mosaic, type MosaicTileComponent } from '../mosaic';
|
|
11
|
+
|
|
12
|
+
// TODO(burdon): Menu options.
|
|
13
|
+
// TODO(burdon): Margin while dragging.
|
|
14
|
+
|
|
15
|
+
export type SimpleCardProps = { id: string; title?: string; body?: string; image?: string };
|
|
16
|
+
|
|
17
|
+
export const SimpleCard: MosaicTileComponent<SimpleCardProps> = forwardRef(
|
|
18
|
+
(
|
|
19
|
+
{ className, active, draggableStyle, draggableProps, item: { id, title }, path, position, grow, debug, onSelect },
|
|
20
|
+
forwardRef,
|
|
21
|
+
) => {
|
|
22
|
+
const full = !title;
|
|
23
|
+
return (
|
|
24
|
+
<div role='none' ref={forwardRef} className='flex w-full' style={draggableStyle}>
|
|
25
|
+
<Card.Root
|
|
26
|
+
noPadding={full}
|
|
27
|
+
classNames={mx(
|
|
28
|
+
className,
|
|
29
|
+
'w-full snap-center',
|
|
30
|
+
(active === 'origin' || active === 'rearrange') && 'opacity-0',
|
|
31
|
+
active === 'destination' && 'opacity-20',
|
|
32
|
+
)}
|
|
33
|
+
grow={grow}
|
|
34
|
+
>
|
|
35
|
+
<Card.Header floating={full} onDoubleClick={() => onSelect?.()}>
|
|
36
|
+
<Card.DragHandle position={full ? 'left' : undefined} {...draggableProps} />
|
|
37
|
+
{title && <Card.Title title={title} />}
|
|
38
|
+
<Card.Menu position={full ? 'right' : undefined} />
|
|
39
|
+
</Card.Header>
|
|
40
|
+
{debug && (
|
|
41
|
+
<Card.Body>
|
|
42
|
+
<Mosaic.Debug data={{ path, id, position }} />
|
|
43
|
+
</Card.Body>
|
|
44
|
+
)}
|
|
45
|
+
</Card.Root>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
SimpleCard.displayName = 'SimpleCard';
|
|
52
|
+
|
|
53
|
+
export const ComplexCard: MosaicTileComponent<SimpleCardProps> = forwardRef(
|
|
54
|
+
(
|
|
55
|
+
{ className, isDragging, draggableStyle, draggableProps, item: { id, title, body, image }, grow, debug, onSelect },
|
|
56
|
+
forwardRef,
|
|
57
|
+
) => {
|
|
58
|
+
const full = !title;
|
|
59
|
+
return (
|
|
60
|
+
<div role='none' ref={forwardRef} className='flex w-full' style={draggableStyle}>
|
|
61
|
+
<Card.Root
|
|
62
|
+
noPadding={full}
|
|
63
|
+
classNames={mx(className, 'w-full snap-center', isDragging && 'opacity-20')}
|
|
64
|
+
grow={grow}
|
|
65
|
+
>
|
|
66
|
+
<Card.Header floating={full} onDoubleClick={() => onSelect?.()}>
|
|
67
|
+
<Card.DragHandle position={full ? 'left' : undefined} {...draggableProps} />
|
|
68
|
+
{title && <Card.Title title={title} />}
|
|
69
|
+
<Card.Menu position={full ? 'right' : undefined} />
|
|
70
|
+
</Card.Header>
|
|
71
|
+
{image && <Card.Media src={image} />}
|
|
72
|
+
{title && body && (
|
|
73
|
+
<Card.Body gutter classNames='line-clamp-3 text-sm'>
|
|
74
|
+
{body}
|
|
75
|
+
</Card.Body>
|
|
76
|
+
)}
|
|
77
|
+
{debug && (
|
|
78
|
+
<Card.Body>
|
|
79
|
+
<Mosaic.Debug data={{ id }} />
|
|
80
|
+
</Card.Body>
|
|
81
|
+
)}
|
|
82
|
+
</Card.Root>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
ComplexCard.displayName = 'ComplexCard';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type DecoratorFunction } from '@storybook/csf';
|
|
6
|
+
import { type ReactRenderer } from '@storybook/react';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { DensityProvider } from '@dxos/react-ui';
|
|
10
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
export const FullscreenDecorator = (className?: string): DecoratorFunction<ReactRenderer> => {
|
|
13
|
+
return (Story) => (
|
|
14
|
+
<div className={mx('flex fixed inset-0 overflow-hidden', className)}>
|
|
15
|
+
<DensityProvider density='fine'>
|
|
16
|
+
<Story />
|
|
17
|
+
</DensityProvider>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// TODO(burdon): Reconcile with @dxos/plugin-debug, @dxos/react-ui/testing.
|
|
6
|
+
|
|
7
|
+
// TODO(burdon): Bug when adding stale objects to space (e.g., static objects already added in previous story invocation).
|
|
8
|
+
|
|
9
|
+
import { faker } from '@faker-js/faker';
|
|
10
|
+
|
|
11
|
+
import { Schema as SchemaType } from '@dxos/client/echo';
|
|
12
|
+
import { Expando, type TypedObject } from '@dxos/echo-schema';
|
|
13
|
+
|
|
14
|
+
// TODO(burdon): Util.
|
|
15
|
+
export const range = <T>(fn: (i: number) => T | undefined, length: number): T[] =>
|
|
16
|
+
Array.from({ length })
|
|
17
|
+
.map((_, i) => fn(i))
|
|
18
|
+
.filter(Boolean) as T[];
|
|
19
|
+
|
|
20
|
+
// TODO(burdon): Commit to using ECHO to generate all test data? Or convert from raw data?
|
|
21
|
+
export type TestItem = { id: string; type: string } & Record<string, any>;
|
|
22
|
+
|
|
23
|
+
type ObjectDataGenerator = {
|
|
24
|
+
createSchema?: () => SchemaType;
|
|
25
|
+
createData: () => any;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type ObjectFactory<T extends TypedObject> = {
|
|
29
|
+
schema?: SchemaType; // TODO(burdon): Support both typed and expando schema.
|
|
30
|
+
createObject: () => T;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type ObjectFactoryMap = { [type: string]: ObjectFactory<any> };
|
|
34
|
+
|
|
35
|
+
const createFactory = ({ createSchema, createData }: ObjectDataGenerator) => {
|
|
36
|
+
const schema = createSchema?.();
|
|
37
|
+
return {
|
|
38
|
+
schema,
|
|
39
|
+
createObject: () => new Expando(createData(), { schema }),
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// TODO(burdon): Handle restricted values.
|
|
44
|
+
export const Status = ['pending', 'active', 'done'];
|
|
45
|
+
export const Priority = [1, 2, 3, 4, 5];
|
|
46
|
+
|
|
47
|
+
export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
|
|
48
|
+
document: {
|
|
49
|
+
createData: () => ({
|
|
50
|
+
title: faker.lorem.sentence(3),
|
|
51
|
+
body: faker.lorem.sentences({ min: 1, max: faker.number.int({ min: 1, max: 3 }) }),
|
|
52
|
+
}),
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
image: {
|
|
56
|
+
createData: () => ({
|
|
57
|
+
title: faker.lorem.sentence(3),
|
|
58
|
+
image: faker.helpers.arrayElement(data.images),
|
|
59
|
+
body: faker.datatype.boolean() ? faker.lorem.sentences() : undefined,
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
project: {
|
|
64
|
+
createSchema: () =>
|
|
65
|
+
new SchemaType({
|
|
66
|
+
props: [
|
|
67
|
+
{
|
|
68
|
+
id: 'title',
|
|
69
|
+
type: SchemaType.PropType.STRING,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'repo',
|
|
73
|
+
type: SchemaType.PropType.STRING,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'status',
|
|
77
|
+
type: SchemaType.PropType.STRING,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'priority',
|
|
81
|
+
type: SchemaType.PropType.NUMBER,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
}),
|
|
85
|
+
createData: () => ({
|
|
86
|
+
title: faker.commerce.productName(),
|
|
87
|
+
repo: faker.datatype.boolean({ probability: 0.3 }) ? faker.internet.url() : undefined,
|
|
88
|
+
status: faker.helpers.arrayElement(Status),
|
|
89
|
+
priority: faker.helpers.arrayElement(Priority),
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Typed object generator.
|
|
96
|
+
*/
|
|
97
|
+
export class TestObjectGenerator {
|
|
98
|
+
public readonly factories: ObjectFactoryMap;
|
|
99
|
+
|
|
100
|
+
constructor({ types, factories }: { types?: string[]; factories?: ObjectFactoryMap } = {}) {
|
|
101
|
+
this.factories =
|
|
102
|
+
factories ??
|
|
103
|
+
(types ?? Object.keys(defaultGenerators)).reduce<ObjectFactoryMap>((acc, type) => {
|
|
104
|
+
acc[type] = createFactory(defaultGenerators[type]);
|
|
105
|
+
return acc;
|
|
106
|
+
}, {});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get schema(): SchemaType[] {
|
|
110
|
+
return Object.values(this.factories).map((f) => f.schema!);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
createObject({ types }: { types?: string[] } = {}) {
|
|
114
|
+
const type = faker.helpers.arrayElement(types ?? Object.keys(this.factories));
|
|
115
|
+
const factory = this.factories[type];
|
|
116
|
+
return factory?.createObject();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
createObjects({ types, length }: { types?: string[]; length: number }) {
|
|
120
|
+
return range(() => this.createObject({ types }), length);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// https://unsplash.com
|
|
125
|
+
// TODO(burdon): Use https://picsum.photos?
|
|
126
|
+
const data = {
|
|
127
|
+
images: [
|
|
128
|
+
'/images/image-1.png',
|
|
129
|
+
'/images/image-2.png',
|
|
130
|
+
'/images/image-3.png',
|
|
131
|
+
'/images/image-4.png',
|
|
132
|
+
'/images/image-5.png',
|
|
133
|
+
'/images/image-6.png',
|
|
134
|
+
],
|
|
135
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type UniqueIdentifier } from '@dnd-kit/core';
|
|
6
|
+
import { getIndexAbove, getIndexBelow, getIndexBetween } from '@tldraw/indices';
|
|
7
|
+
|
|
8
|
+
import { type Node } from '@braneframe/plugin-graph';
|
|
9
|
+
|
|
10
|
+
export const nextRearrangeIndex = (nodes: Node[], activeId: UniqueIdentifier, overId?: UniqueIdentifier) => {
|
|
11
|
+
const overOrderIndex = nodes.length > 0 ? nodes.findIndex(({ id }) => id === overId) : -1;
|
|
12
|
+
if (overOrderIndex >= 0) {
|
|
13
|
+
const activeOrderIndex = nodes.findIndex(({ id }) => id === activeId);
|
|
14
|
+
return overOrderIndex < 1
|
|
15
|
+
? getIndexBelow(nodes[overOrderIndex].properties.index)
|
|
16
|
+
: activeOrderIndex < overOrderIndex
|
|
17
|
+
? getIndexBetween(nodes[overOrderIndex].properties.index, nodes[overOrderIndex + 1]?.properties.index)
|
|
18
|
+
: getIndexBetween(nodes[overOrderIndex - 1].properties.index, nodes[overOrderIndex].properties.index);
|
|
19
|
+
} else {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const nextCopyIndex = (nodes: Node[], overId?: UniqueIdentifier) => {
|
|
25
|
+
const overOrderIndex = nodes.length > 0 ? nodes.findIndex(({ id }) => id === overId) : -1;
|
|
26
|
+
const previewOrderIndex = nodes.findIndex(({ id }) => id.startsWith('preview--'));
|
|
27
|
+
if (overOrderIndex >= 0) {
|
|
28
|
+
return overOrderIndex < 1
|
|
29
|
+
? getIndexBelow(nodes[overOrderIndex].properties.index)
|
|
30
|
+
: previewOrderIndex < overOrderIndex && previewOrderIndex >= 0
|
|
31
|
+
? getIndexBetween(nodes[overOrderIndex].properties.index, nodes[overOrderIndex + 1]?.properties.index)
|
|
32
|
+
: getIndexBetween(nodes[overOrderIndex - 1].properties.index, nodes[overOrderIndex]?.properties.index);
|
|
33
|
+
} else {
|
|
34
|
+
return nodes.length ? getIndexAbove(nodes[nodes.length - 1].properties.index) : 'a0';
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { faker } from '@faker-js/faker';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
import { Mosaic } from '../../mosaic';
|
|
11
|
+
import { ComplexCard, FullscreenDecorator, SimpleCard } from '../../testing';
|
|
12
|
+
import { DemoGrid } from '../Grid/testing';
|
|
13
|
+
import { DemoKanban } from '../Kanban/testing';
|
|
14
|
+
import { DemoStack } from '../Stack/testing';
|
|
15
|
+
import { DemoTree } from '../Tree/testing';
|
|
16
|
+
|
|
17
|
+
faker.seed(5);
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
title: 'Views/Demo',
|
|
21
|
+
decorators: [FullscreenDecorator()],
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: 'fullscreen',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const debug = false;
|
|
28
|
+
const types = ['document', 'image'];
|
|
29
|
+
|
|
30
|
+
export const Default = () => (
|
|
31
|
+
<Mosaic.Root debug={debug}>
|
|
32
|
+
<Mosaic.DragOverlay />
|
|
33
|
+
<div className='flex grow overflow-hidden'>
|
|
34
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden'>
|
|
35
|
+
<DemoStack id='stack-1' Component={SimpleCard} types={types} debug={debug} className='p-2' />
|
|
36
|
+
</div>
|
|
37
|
+
<div className='flex grow overflow-hidden bg-neutral-900'>
|
|
38
|
+
<DemoGrid id='grid-1' Component={ComplexCard} types={types} debug={debug} className='p-4' />
|
|
39
|
+
</div>
|
|
40
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden bg-neutral-900'>
|
|
41
|
+
<DemoStack id='stack-2' Component={ComplexCard} types={types} debug={debug} />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</Mosaic.Root>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
export const Tree = () => (
|
|
48
|
+
<Mosaic.Root debug={debug}>
|
|
49
|
+
<Mosaic.DragOverlay />
|
|
50
|
+
<div className='flex grow overflow-hidden'>
|
|
51
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden'>
|
|
52
|
+
<DemoTree id='tree-1' types={types} debug={debug} />
|
|
53
|
+
</div>
|
|
54
|
+
<div className='flex grow overflow-hidden'>
|
|
55
|
+
<DemoGrid id='grid-1' Component={ComplexCard} types={types} debug={debug} className='p-4 bg-neutral-900' />
|
|
56
|
+
</div>
|
|
57
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden bg-neutral-900'>
|
|
58
|
+
<DemoStack id='stack-2' Component={ComplexCard} types={types} debug={debug} />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</Mosaic.Root>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
export const Kanban = () => (
|
|
65
|
+
<Mosaic.Root debug={debug}>
|
|
66
|
+
<Mosaic.DragOverlay />
|
|
67
|
+
<div className='flex grow overflow-hidden'>
|
|
68
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden'>
|
|
69
|
+
<DemoTree id='tree-1' types={types} debug={debug} />
|
|
70
|
+
</div>
|
|
71
|
+
<div className='flex grow overflow-hidden'>
|
|
72
|
+
<DemoKanban id='kanban-1' Component={SimpleCard} debug={debug} className='p-4' />
|
|
73
|
+
</div>
|
|
74
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden bg-neutral-900 pl-2'>
|
|
75
|
+
<DemoStack id='stack-1' Component={ComplexCard} types={types} debug={debug} />
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</Mosaic.Root>
|
|
79
|
+
);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { faker } from '@faker-js/faker';
|
|
6
|
+
import React, { type FC } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type PublicKey } from '@dxos/react-client';
|
|
9
|
+
import { Expando } from '@dxos/react-client/echo';
|
|
10
|
+
import { ClientSpaceDecorator } from '@dxos/react-client/testing';
|
|
11
|
+
|
|
12
|
+
import { Mosaic } from '../../mosaic';
|
|
13
|
+
import { FullscreenDecorator, TestObjectGenerator, range, Status, Priority } from '../../testing';
|
|
14
|
+
import { EchoKanban } from '../Kanban/testing';
|
|
15
|
+
import { GraphTree } from '../Tree/testing';
|
|
16
|
+
|
|
17
|
+
faker.seed(3);
|
|
18
|
+
const debug = true;
|
|
19
|
+
const generator = new TestObjectGenerator();
|
|
20
|
+
|
|
21
|
+
// TODO(burdon): Compute this?
|
|
22
|
+
const columnValues: { [property: string]: any[] } = {
|
|
23
|
+
status: ['unknown', ...Status],
|
|
24
|
+
priority: ['unknown', ...Priority],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const Story: FC<{ spaceKey: PublicKey }> = ({ spaceKey }) => {
|
|
28
|
+
return (
|
|
29
|
+
<Mosaic.Root debug={debug}>
|
|
30
|
+
<Mosaic.DragOverlay />
|
|
31
|
+
<div className='flex grow overflow-hidden'>
|
|
32
|
+
<div className='flex shrink-0 w-[280px] overflow-hidden'>
|
|
33
|
+
<GraphTree id='graph' debug={debug} />
|
|
34
|
+
</div>
|
|
35
|
+
<div className='flex grow overflow-hidden'>
|
|
36
|
+
<EchoKanban id='projects' spaceKey={spaceKey} generator={generator} debug={debug} />
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</Mosaic.Root>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default {
|
|
44
|
+
title: 'Views/Demo',
|
|
45
|
+
render: Story,
|
|
46
|
+
decorators: [
|
|
47
|
+
FullscreenDecorator(),
|
|
48
|
+
ClientSpaceDecorator({
|
|
49
|
+
onCreateSpace: async (space) => {
|
|
50
|
+
const factory = generator.factories.project;
|
|
51
|
+
const objects = [
|
|
52
|
+
factory.schema,
|
|
53
|
+
...range(factory.createObject, 10),
|
|
54
|
+
new Expando({
|
|
55
|
+
type: 'kanban',
|
|
56
|
+
title: 'Projects',
|
|
57
|
+
schema: factory.schema,
|
|
58
|
+
// TODO(burdon): Standardize with other story.
|
|
59
|
+
columnProp: 'status',
|
|
60
|
+
columnValues: columnValues.status,
|
|
61
|
+
objectPosition: {}, // TODO(burdon): Make this a CRDT.
|
|
62
|
+
}),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// TODO(burdon): Batch API.
|
|
66
|
+
objects.forEach((object) => space.db.add(object));
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// TODO(wittjosiah): This currently has a bug where empty over events are fired when dragging from tree onto kanban.
|
|
73
|
+
// The bug didn't exist before the cleanup refactor so seems likely it's a side effect of that.
|
|
74
|
+
export const ECHO = {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { faker } from '@faker-js/faker';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
import { Grid, type GridLayout } from './Grid';
|
|
11
|
+
import { DemoGrid } from './testing';
|
|
12
|
+
import { Mosaic } from '../../mosaic';
|
|
13
|
+
import { FullscreenDecorator, TestObjectGenerator } from '../../testing';
|
|
14
|
+
|
|
15
|
+
faker.seed(99);
|
|
16
|
+
|
|
17
|
+
const debug = true;
|
|
18
|
+
|
|
19
|
+
// TODO(burdon): Create object.
|
|
20
|
+
// TODO(burdon): Delete object (delete/remove from grid).
|
|
21
|
+
// TODO(burdon): Editable cards (and other card stories).
|
|
22
|
+
// TODO(burdon): Search story (drag from search stack).
|
|
23
|
+
|
|
24
|
+
const size = { x: 4, y: 4 };
|
|
25
|
+
const generator = new TestObjectGenerator({ types: ['document', 'image'] });
|
|
26
|
+
const testItems = generator.createObjects({ length: 20 });
|
|
27
|
+
const testLayout = testItems.reduce<GridLayout>((map, item) => {
|
|
28
|
+
map[item.id] = { x: faker.number.int({ min: 0, max: size.x - 1 }), y: faker.number.int({ min: 0, max: size.y - 1 }) };
|
|
29
|
+
return map;
|
|
30
|
+
}, {});
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
component: Grid,
|
|
34
|
+
render: () => {
|
|
35
|
+
return (
|
|
36
|
+
<Mosaic.Root debug={debug}>
|
|
37
|
+
<Mosaic.DragOverlay />
|
|
38
|
+
<DemoGrid id='grid' options={{ size }} initialItems={testItems} initialLayout={testLayout} />
|
|
39
|
+
</Mosaic.Root>
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
decorators: [FullscreenDecorator()],
|
|
43
|
+
parameters: {
|
|
44
|
+
layout: 'fullscreen',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Default = {};
|