@dxos/react-ui-mosaic 0.3.3-main.6b9536c → 0.3.3-main.76bfae3

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.
Files changed (45) hide show
  1. package/dist/lib/browser/index.mjs +93 -49
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/mosaic/Container.d.ts +9 -1
  5. package/dist/types/src/mosaic/Container.d.ts.map +1 -1
  6. package/dist/types/src/mosaic/Root.d.ts.map +1 -1
  7. package/dist/types/src/mosaic/hooks/index.d.ts +0 -1
  8. package/dist/types/src/mosaic/hooks/index.d.ts.map +1 -1
  9. package/dist/types/src/mosaic/hooks/useItemsWithPreview.d.ts +2 -1
  10. package/dist/types/src/mosaic/hooks/useItemsWithPreview.d.ts.map +1 -1
  11. package/dist/types/src/stories/Home.stories.d.ts +11 -0
  12. package/dist/types/src/stories/Home.stories.d.ts.map +1 -0
  13. package/dist/types/src/testing/decorators.d.ts.map +1 -1
  14. package/dist/types/src/testing/generator.d.ts +4 -4
  15. package/dist/types/src/testing/generator.d.ts.map +1 -1
  16. package/dist/types/src/testing/index.d.ts +0 -1
  17. package/dist/types/src/testing/index.d.ts.map +1 -1
  18. package/dist/types/src/views/Grid/Grid.d.ts +1 -1
  19. package/dist/types/src/views/Grid/Grid.d.ts.map +1 -1
  20. package/dist/types/src/views/Grid/Grid.stories.d.ts +1 -1
  21. package/dist/types/src/views/Stack/Columns.stories.d.ts +1 -1
  22. package/dist/types/src/views/Stack/Columns.stories.d.ts.map +1 -1
  23. package/dist/types/src/views/Tree/testing.d.ts +1 -1
  24. package/dist/types/src/views/Tree/testing.d.ts.map +1 -1
  25. package/package.json +14 -15
  26. package/src/mosaic/Container.tsx +10 -2
  27. package/src/mosaic/Root.tsx +1 -2
  28. package/src/mosaic/hooks/index.ts +0 -1
  29. package/src/mosaic/hooks/useItemsWithPreview.ts +56 -20
  30. package/src/stories/Home.stories.tsx +87 -0
  31. package/src/testing/decorators.tsx +1 -0
  32. package/src/testing/generator.ts +9 -9
  33. package/src/testing/index.ts +0 -1
  34. package/src/views/Grid/Grid.tsx +4 -2
  35. package/src/views/Kanban/Kanban.tsx +1 -1
  36. package/src/views/Stack/Columns.stories.tsx +1 -1
  37. package/src/views/Stack/testing.tsx +1 -1
  38. package/src/views/Tree/Tree.tsx +17 -11
  39. package/src/views/Tree/testing.tsx +13 -12
  40. package/dist/types/src/mosaic/hooks/useSortedItems.d.ts +0 -6
  41. package/dist/types/src/mosaic/hooks/useSortedItems.d.ts.map +0 -1
  42. package/dist/types/src/testing/next-fractional-index.d.ts +0 -5
  43. package/dist/types/src/testing/next-fractional-index.d.ts.map +0 -1
  44. package/src/mosaic/hooks/useSortedItems.ts +0 -14
  45. package/src/testing/next-fractional-index.ts +0 -36
@@ -0,0 +1,87 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+
7
+ import React from 'react';
8
+
9
+ import { FullscreenDecorator } from '../testing';
10
+
11
+ const org = '@dxos-mosaic';
12
+
13
+ const data = {
14
+ quote: `"I've written a few thousand words on why traditional “semantic class names” are the reason CSS is hard to ' +
15
+ 'maintain, but the truth is you’re never going to believe me until you actually try it. If you can suppress the ' +
16
+ 'urge to retch long enough to give it a chance, I really think you’ll wonder how you ever worked with CSS any other ' +
17
+ 'way." [User]`,
18
+ };
19
+
20
+ const packages = [
21
+ {
22
+ name: `${org}/ui`,
23
+ description: 'Component primitives.',
24
+ },
25
+ {
26
+ name: `${org}/dnd`,
27
+ description: 'Drag-and-drop containers and tiles.',
28
+ },
29
+ {
30
+ name: `${org}/echo`,
31
+ description: 'DXOS ECHO bindings.',
32
+ },
33
+ {
34
+ name: `${org}/editor`,
35
+ description: 'Rich text editor.',
36
+ },
37
+ {
38
+ name: `${org}/table`,
39
+ description: 'Blazingly fast virtualized tables.',
40
+ },
41
+ {
42
+ name: `${org}/stack`,
43
+ description: 'Flexible block editor.',
44
+ },
45
+ {
46
+ name: `${org}/grid`,
47
+ description: 'Hierarchical and zoomable grid.',
48
+ },
49
+ ];
50
+
51
+ const Home = () => {
52
+ return (
53
+ <div className='flex flex-col w-[800px] bg-white px-16 py-20 gap-4 text-lg font-light'>
54
+ <b className='text-4xl font-medium text-black'>Mosaic</b>
55
+ <p>
56
+ Mosaic is an extensible user interface framework and component library built using{' '}
57
+ <span className='font-mono text-purple-700'>radix</span> and{' '}
58
+ <span className='font-mono text-purple-700'>tailwindcss</span>. It enables the rapid development of beautiful
59
+ data-driven applications.
60
+ </p>
61
+ <p>Mosaic is an extensible family of packages, which include:</p>
62
+ <table>
63
+ <tbody>
64
+ {packages.map(({ name, description }) => (
65
+ <tr key={name}>
66
+ <td className='font-mono text-purple-700 w-[260px]'>{name}</td>
67
+ <td>{description}</td>
68
+ </tr>
69
+ ))}
70
+ </tbody>
71
+ </table>
72
+ <br />
73
+ <h1 className='text-lg flex justify-center font-medium text-blue-700'>Data-drive components, done right.</h1>
74
+ <p className='px-8'>{data.quote}</p>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ export default {
80
+ component: Home,
81
+ decorators: [FullscreenDecorator('flex justify-center bg-[url(/images/bg-mosaic.png)]')],
82
+ parameters: {
83
+ layout: 'fullscreen',
84
+ },
85
+ };
86
+
87
+ export const Default = {};
@@ -12,6 +12,7 @@ import { mx } from '@dxos/react-ui-theme';
12
12
  export const FullscreenDecorator = (className?: string): DecoratorFunction<ReactRenderer> => {
13
13
  return (Story) => (
14
14
  <div className={mx('flex fixed inset-0 overflow-hidden', className)}>
15
+ {/* TODO(burdon): Remove DensityProvider from generic decorator. */}
15
16
  <DensityProvider density='fine'>
16
17
  <Story />
17
18
  </DensityProvider>
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { faker } from '@faker-js/faker';
10
10
 
11
- import { Schema as SchemaType } from '@dxos/client/echo';
11
+ import { Schema } from '@dxos/client/echo';
12
12
  import { Expando, type TypedObject } from '@dxos/echo-schema';
13
13
 
14
14
  // TODO(burdon): Util.
@@ -21,12 +21,12 @@ export const range = <T>(fn: (i: number) => T | undefined, length: number): T[]
21
21
  export type TestItem = { id: string; type: string } & Record<string, any>;
22
22
 
23
23
  type ObjectDataGenerator = {
24
- createSchema?: () => SchemaType;
24
+ createSchema?: () => Schema;
25
25
  createData: () => any;
26
26
  };
27
27
 
28
28
  type ObjectFactory<T extends TypedObject> = {
29
- schema?: SchemaType; // TODO(burdon): Support both typed and expando schema.
29
+ schema?: Schema; // TODO(burdon): Support both typed and expando schema.
30
30
  createObject: () => T;
31
31
  };
32
32
 
@@ -62,23 +62,23 @@ export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
62
62
 
63
63
  project: {
64
64
  createSchema: () =>
65
- new SchemaType({
65
+ new Schema({
66
66
  props: [
67
67
  {
68
68
  id: 'title',
69
- type: SchemaType.PropType.STRING,
69
+ type: Schema.PropType.STRING,
70
70
  },
71
71
  {
72
72
  id: 'repo',
73
- type: SchemaType.PropType.STRING,
73
+ type: Schema.PropType.STRING,
74
74
  },
75
75
  {
76
76
  id: 'status',
77
- type: SchemaType.PropType.STRING,
77
+ type: Schema.PropType.STRING,
78
78
  },
79
79
  {
80
80
  id: 'priority',
81
- type: SchemaType.PropType.NUMBER,
81
+ type: Schema.PropType.NUMBER,
82
82
  },
83
83
  ],
84
84
  }),
@@ -106,7 +106,7 @@ export class TestObjectGenerator {
106
106
  }, {});
107
107
  }
108
108
 
109
- get schema(): SchemaType[] {
109
+ get schema(): Schema[] {
110
110
  return Object.values(this.factories).map((f) => f.schema!);
111
111
  }
112
112
 
@@ -4,6 +4,5 @@
4
4
 
5
5
  export * from './decorators';
6
6
  export * from './generator';
7
- export * from './next-fractional-index';
8
7
 
9
8
  export * from './Cards';
@@ -52,8 +52,8 @@ type GridOptions = {
52
52
 
53
53
  const defaultGridOptions: GridOptions = {
54
54
  size: { x: 8, y: 8 },
55
- cellBounds: { width: 280, height: 280 },
56
- spacing: 8,
55
+ cellBounds: { width: 272, height: 272 },
56
+ spacing: 6,
57
57
  };
58
58
 
59
59
  //
@@ -90,6 +90,7 @@ export const Grid = ({
90
90
  Component = Mosaic.DefaultComponent,
91
91
  className,
92
92
  onDrop,
93
+ onOver,
93
94
  onSelect,
94
95
  onCreate,
95
96
  onAction,
@@ -155,6 +156,7 @@ export const Grid = ({
155
156
  getOverlayProps: () => ({ grow: true }),
156
157
  getOverlayStyle: () => getDimension(cellBounds, options.spacing),
157
158
  onDrop,
159
+ onOver,
158
160
  }}
159
161
  >
160
162
  <div className={mx('flex grow overflow-auto', className)}>
@@ -51,7 +51,7 @@ export const Kanban = ({
51
51
  // Restrict to objects from other columns.
52
52
  // TODO(burdon): Consider objects from other containers.
53
53
  // TODO(wittjosiah): Allow override.
54
- onOver: ({ active, over }) => (Path.length(active.path) >= Path.length(over.path) ? 'adopt' : 'reject'),
54
+ onOver: ({ active, over }) => (Path.length(active.path) >= Path.length(over.path) ? 'transfer' : 'reject'),
55
55
  onDrop,
56
56
  }}
57
57
  >
@@ -36,7 +36,7 @@ export default {
36
36
  },
37
37
  };
38
38
 
39
- export const Adopt = {
39
+ export const Transfer = {
40
40
  args: {
41
41
  Component: SimpleCard,
42
42
  debug: true,
@@ -26,7 +26,7 @@ export const DemoStack = ({
26
26
  types,
27
27
  count = 8,
28
28
  direction = 'vertical',
29
- operation = 'adopt',
29
+ operation = 'transfer',
30
30
  debug,
31
31
  }: DemoStackProps) => {
32
32
  const [items, setItems] = useState<MosaicDataItem[]>(() => {
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { forwardRef } from 'react';
5
+ import React, { forwardRef, useMemo } from 'react';
6
6
 
7
7
  import { Card, Tree as TreeComponent, TreeItem as TreeItemComponent } from '@dxos/react-ui';
8
8
  import { dropRing, mx } from '@dxos/react-ui-theme';
@@ -15,7 +15,7 @@ import {
15
15
  useContainer,
16
16
  useMosaic,
17
17
  Path,
18
- useSortedItems,
18
+ useItemsWithPreview,
19
19
  type MosaicCompareDataItem,
20
20
  } from '../../mosaic';
21
21
 
@@ -55,13 +55,16 @@ export const Tree = ({ id, Component = TreeItem, onOver, onDrop, items = [], deb
55
55
  };
56
56
 
57
57
  const TreeRoot = ({ items }: { items: TreeData[] }) => {
58
- const { id, Component } = useContainer();
59
- const sortedItems = useSortedItems(items);
58
+ const { id, Component, compare } = useContainer();
59
+ const sortedItems = useMemo(() => {
60
+ return compare ? [...items].sort(compare) : items;
61
+ }, [items, compare]);
62
+ const itemsWithPreview = useItemsWithPreview({ items: sortedItems, path: id, strategy: 'layout-stable' });
60
63
 
61
64
  return (
62
65
  <TreeComponent.Root classNames='flex flex-col'>
63
- <Mosaic.SortableContext id={id} items={sortedItems} direction='vertical'>
64
- {sortedItems.map((item, index) => (
66
+ <Mosaic.SortableContext id={id} items={itemsWithPreview} direction='vertical'>
67
+ {itemsWithPreview.map((item, index) => (
65
68
  <TreeItemComponent.Root key={item.id} collapsible defaultOpen>
66
69
  <Mosaic.SortableTile item={item} path={id} position={index} Component={Component!} />
67
70
  </TreeItemComponent.Root>
@@ -101,13 +104,16 @@ const TreeItem: MosaicTileComponent<TreeData> = forwardRef(
101
104
 
102
105
  const TreeBranch = ({ path, items }: { path: string; items: TreeData[] }) => {
103
106
  const { operation, overItem } = useMosaic();
104
- const { Component } = useContainer();
105
- const sortedItems = useSortedItems(items);
107
+ const { Component, compare } = useContainer();
108
+ const sortedItems = useMemo(() => {
109
+ return compare ? [...items].sort(compare) : items;
110
+ }, [items, compare]);
111
+ const itemsWithPreview = useItemsWithPreview({ items: sortedItems, path, strategy: 'layout-stable' });
106
112
 
107
113
  return (
108
114
  <TreeItemComponent.Body className='pis-4'>
109
- <Mosaic.SortableContext id={path} items={sortedItems} direction='vertical'>
110
- {sortedItems.map((child, index) => (
115
+ <Mosaic.SortableContext id={path} items={itemsWithPreview} direction='vertical'>
116
+ {itemsWithPreview.map((child, index) => (
111
117
  <TreeComponent.Branch key={child.id}>
112
118
  <TreeItemComponent.Root collapsible defaultOpen>
113
119
  <Mosaic.SortableTile
@@ -116,7 +122,7 @@ const TreeBranch = ({ path, items }: { path: string; items: TreeData[] }) => {
116
122
  position={index}
117
123
  Component={Component!}
118
124
  isOver={
119
- overItem?.path === Path.create(path, child.id) && (operation === 'adopt' || operation === 'copy')
125
+ overItem?.path === Path.create(path, child.id) && (operation === 'transfer' || operation === 'copy')
120
126
  }
121
127
  />
122
128
  </TreeItemComponent.Root>
@@ -5,13 +5,13 @@
5
5
  import { faker } from '@faker-js/faker';
6
6
  import React, { useCallback, useState } from 'react';
7
7
 
8
- import { type Graph, GraphBuilder, type Node } from '@braneframe/plugin-graph';
9
- import { buildGraph } from '@braneframe/plugin-graph/testing';
8
+ import { type Graph, GraphBuilder, type Node } from '@dxos/app-graph';
9
+ import { buildGraph } from '@dxos/app-graph/testing';
10
10
  import { arrayMove } from '@dxos/util';
11
11
 
12
12
  import { Tree, type TreeData, type TreeProps } from './Tree';
13
- import { type MosaicDropEvent, type MosaicMoveEvent, Path } from '../../mosaic';
14
- import { TestObjectGenerator, nextRearrangeIndex } from '../../testing';
13
+ import { type MosaicDropEvent, type MosaicMoveEvent, Path, type MosaicOperation } from '../../mosaic';
14
+ import { TestObjectGenerator } from '../../testing';
15
15
 
16
16
  const fake = faker.helpers.fake;
17
17
 
@@ -47,8 +47,8 @@ export const DemoTree = ({ id = 'tree', initialItems, types, debug }: DemoTreePr
47
47
  }),
48
48
  );
49
49
 
50
- const handleOver = useCallback(({ active, over }: MosaicMoveEvent<number>) => {
51
- return !(active.path === id && over.path !== id) ? 'adopt' : 'reject';
50
+ const handleOver = useCallback(({ over }: MosaicMoveEvent<number>) => {
51
+ return over && Path.hasChild(id, over.path) ? 'transfer' : 'reject';
52
52
  }, []);
53
53
 
54
54
  // NOTE: Does not handle deep operations.
@@ -67,8 +67,8 @@ export const DemoTree = ({ id = 'tree', initialItems, types, debug }: DemoTreePr
67
67
  if (Path.last(Path.parent(active.path)) === item.id) {
68
68
  children.splice(active.position!, 1);
69
69
  }
70
- if (Path.last(Path.parent(over.path)) === item.id) {
71
- children.splice(over.position!, 0, active.item as TreeData);
70
+ if (Path.last(over.path) === item.id) {
71
+ children.push(active.item as TreeData);
72
72
  }
73
73
  return { ...item, children };
74
74
  }),
@@ -117,7 +117,6 @@ const graphNodeCompare = (a: Node, b: Node) => {
117
117
  };
118
118
 
119
119
  export const GraphTree = ({ id, graph = createGraph(), debug }: { id: string; graph?: Graph; debug: boolean }) => {
120
- // TODO(wittjosiah): This graph does not handle order currently.
121
120
  const handleDrop = ({ operation, active, over }: MosaicDropEvent<number>) => {
122
121
  // Moving within the tree.
123
122
  if (Path.hasDescendent(id, active.path) && Path.hasDescendent(id, over.path)) {
@@ -135,20 +134,22 @@ export const GraphTree = ({ id, graph = createGraph(), debug }: { id: string; gr
135
134
  operation === 'rearrange'
136
135
  ) {
137
136
  // This is a rearrange operation
138
- const nextIndex = nextRearrangeIndex(activeParent.children.sort(graphNodeCompare), activeNode.id, overNode.id);
139
- activeNode.properties.index = nextIndex ?? 'a0';
140
- } else if (activeNode && activeParent && overParent && operation === 'adopt') {
137
+ console.warn('[react-ui-mosaic]', 'Tree', 'rearrange', 'needs implementation');
138
+ } else if (activeNode && activeParent && overParent && operation === 'transfer') {
141
139
  activeParent.removeNode(active.item.id);
142
140
  overNode.addNode('tree', { ...activeNode });
143
141
  }
144
142
  }
145
143
  };
146
144
 
145
+ const handleOver = (): MosaicOperation => 'transfer';
146
+
147
147
  return (
148
148
  <Tree
149
149
  id={id}
150
150
  items={graph.root.children as TreeData[]}
151
151
  onDrop={handleDrop}
152
+ onOver={handleOver}
152
153
  debug={debug}
153
154
  compare={graphNodeCompare}
154
155
  />
@@ -1,6 +0,0 @@
1
- import { type MosaicDataItem } from '../types';
2
- /**
3
- * Returns a sorted collection of items if a compare function is provided.
4
- */
5
- export declare const useSortedItems: <T extends MosaicDataItem>(items: T[]) => T[];
6
- //# sourceMappingURL=useSortedItems.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useSortedItems.d.ts","sourceRoot":"","sources":["../../../../../src/mosaic/hooks/useSortedItems.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,cAAc,+CAG1B,CAAC"}
@@ -1,5 +0,0 @@
1
- import { type UniqueIdentifier } from '@dnd-kit/core';
2
- import { type Node } from '@braneframe/plugin-graph';
3
- export declare const nextRearrangeIndex: (nodes: Node[], activeId: UniqueIdentifier, overId?: UniqueIdentifier) => string | null;
4
- export declare const nextCopyIndex: (nodes: Node[], overId?: UniqueIdentifier) => string;
5
- //# sourceMappingURL=next-fractional-index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"next-fractional-index.d.ts","sourceRoot":"","sources":["../../../../src/testing/next-fractional-index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,eAAO,MAAM,kBAAkB,UAAW,IAAI,EAAE,YAAY,gBAAgB,WAAW,gBAAgB,kBAYtG,CAAC;AAEF,eAAO,MAAM,aAAa,UAAW,IAAI,EAAE,WAAW,gBAAgB,WAYrE,CAAC"}
@@ -1,14 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { useContainer } from './useContainer';
6
- import { type MosaicDataItem } from '../types';
7
-
8
- /**
9
- * Returns a sorted collection of items if a compare function is provided.
10
- */
11
- export const useSortedItems = <T extends MosaicDataItem>(items: T[]): T[] => {
12
- const { compare } = useContainer();
13
- return compare ? [...items].sort(compare) : items;
14
- };
@@ -1,36 +0,0 @@
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
- };