@dxos/react-ui-mosaic 0.3.3-main.d5b74b2 → 0.3.3-main.e3fdda6

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 (49) hide show
  1. package/dist/lib/browser/index.mjs +48 -27
  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 +10 -7
  5. package/dist/types/src/mosaic/Container.d.ts.map +1 -1
  6. package/dist/types/src/mosaic/Mosaic.d.ts +1 -2
  7. package/dist/types/src/mosaic/Mosaic.d.ts.map +1 -1
  8. package/dist/types/src/mosaic/Root.d.ts.map +1 -1
  9. package/dist/types/src/mosaic/hooks/index.d.ts +1 -1
  10. package/dist/types/src/mosaic/hooks/index.d.ts.map +1 -1
  11. package/dist/types/src/mosaic/hooks/useItemsWithOrigin.d.ts +3 -0
  12. package/dist/types/src/mosaic/hooks/useItemsWithOrigin.d.ts.map +1 -0
  13. package/dist/types/src/mosaic/hooks/useItemsWithPreview.d.ts.map +1 -1
  14. package/dist/types/src/testing/generator.d.ts +3 -10
  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/Graph.stories.d.ts +1 -1
  24. package/dist/types/src/views/Tree/Tree.d.ts +2 -3
  25. package/dist/types/src/views/Tree/Tree.d.ts.map +1 -1
  26. package/dist/types/src/views/Tree/Tree.stories.d.ts +1 -1
  27. package/dist/types/src/views/Tree/testing.d.ts +1 -1
  28. package/dist/types/src/views/Tree/testing.d.ts.map +1 -1
  29. package/package.json +14 -15
  30. package/src/mosaic/Container.tsx +10 -11
  31. package/src/mosaic/Mosaic.ts +0 -3
  32. package/src/mosaic/Root.tsx +1 -2
  33. package/src/mosaic/hooks/index.ts +1 -1
  34. package/src/mosaic/hooks/useItemsWithOrigin.ts +33 -0
  35. package/src/mosaic/hooks/useItemsWithPreview.ts +8 -3
  36. package/src/testing/generator.ts +11 -14
  37. package/src/testing/index.ts +0 -1
  38. package/src/views/Grid/Grid.tsx +2 -0
  39. package/src/views/Kanban/Kanban.tsx +1 -1
  40. package/src/views/Stack/Columns.stories.tsx +1 -1
  41. package/src/views/Stack/testing.tsx +1 -1
  42. package/src/views/Tree/Tree.tsx +9 -12
  43. package/src/views/Tree/testing.tsx +13 -31
  44. package/dist/types/src/mosaic/hooks/useSortedItems.d.ts +0 -6
  45. package/dist/types/src/mosaic/hooks/useSortedItems.d.ts.map +0 -1
  46. package/dist/types/src/testing/next-fractional-index.d.ts +0 -5
  47. package/dist/types/src/testing/next-fractional-index.d.ts.map +0 -1
  48. package/src/mosaic/hooks/useSortedItems.ts +0 -14
  49. package/src/testing/next-fractional-index.ts +0 -36
@@ -24,7 +24,7 @@ export const useItemsWithPreview = <T extends MosaicDataItem>({ path, items }: {
24
24
  const [lastOverParent, setLastOverParent] = useState(overParent);
25
25
 
26
26
  useEffect(() => {
27
- if (operation === 'reject' || operation === 'rearrange' || !activeItem || !overItem) {
27
+ if (!activeItem || !overItem) {
28
28
  setLastOverParent(undefined);
29
29
  setItemsWithPreview(items);
30
30
  return;
@@ -34,12 +34,17 @@ export const useItemsWithPreview = <T extends MosaicDataItem>({ path, items }: {
34
34
  return;
35
35
  }
36
36
 
37
- setLastOverParent(overParent);
38
-
39
37
  const activeIsChild = Path.hasChild(path, activeItem.path);
40
38
  const overIsChild = Path.hasChild(path, overItem.path);
41
39
  const overSelf = overItem.path === path;
42
40
 
41
+ if (operation === 'reject' || operation === 'rearrange') {
42
+ setLastOverParent(undefined);
43
+ setItemsWithPreview(items);
44
+ return;
45
+ } else {
46
+ setLastOverParent(overParent);
47
+ }
43
48
  if (!activeIsChild && overIsChild) {
44
49
  // Insert item into sortable.
45
50
  setItemsWithPreview((items) => {
@@ -2,13 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
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).
5
+ // TODO(burdon): Reconcile with @dxos/echo-generator.
8
6
 
9
7
  import { faker } from '@faker-js/faker';
10
8
 
11
- import { Schema as SchemaType } from '@dxos/client/echo';
9
+ import { Schema } from '@dxos/client/echo';
12
10
  import { Expando, type TypedObject } from '@dxos/echo-schema';
13
11
 
14
12
  // TODO(burdon): Util.
@@ -17,16 +15,15 @@ export const range = <T>(fn: (i: number) => T | undefined, length: number): T[]
17
15
  .map((_, i) => fn(i))
18
16
  .filter(Boolean) as T[];
19
17
 
20
- // TODO(burdon): Commit to using ECHO to generate all test data? Or convert from raw data?
21
18
  export type TestItem = { id: string; type: string } & Record<string, any>;
22
19
 
23
20
  type ObjectDataGenerator = {
24
- createSchema?: () => SchemaType;
21
+ createSchema?: () => Schema;
25
22
  createData: () => any;
26
23
  };
27
24
 
28
25
  type ObjectFactory<T extends TypedObject> = {
29
- schema?: SchemaType; // TODO(burdon): Support both typed and expando schema.
26
+ schema?: Schema; // TODO(burdon): Support both typed and expando schema.
30
27
  createObject: () => T;
31
28
  };
32
29
 
@@ -44,7 +41,7 @@ const createFactory = ({ createSchema, createData }: ObjectDataGenerator) => {
44
41
  export const Status = ['pending', 'active', 'done'];
45
42
  export const Priority = [1, 2, 3, 4, 5];
46
43
 
47
- export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
44
+ const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
48
45
  document: {
49
46
  createData: () => ({
50
47
  title: faker.lorem.sentence(3),
@@ -62,23 +59,23 @@ export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
62
59
 
63
60
  project: {
64
61
  createSchema: () =>
65
- new SchemaType({
62
+ new Schema({
66
63
  props: [
67
64
  {
68
65
  id: 'title',
69
- type: SchemaType.PropType.STRING,
66
+ type: Schema.PropType.STRING,
70
67
  },
71
68
  {
72
69
  id: 'repo',
73
- type: SchemaType.PropType.STRING,
70
+ type: Schema.PropType.STRING,
74
71
  },
75
72
  {
76
73
  id: 'status',
77
- type: SchemaType.PropType.STRING,
74
+ type: Schema.PropType.STRING,
78
75
  },
79
76
  {
80
77
  id: 'priority',
81
- type: SchemaType.PropType.NUMBER,
78
+ type: Schema.PropType.NUMBER,
82
79
  },
83
80
  ],
84
81
  }),
@@ -106,7 +103,7 @@ export class TestObjectGenerator {
106
103
  }, {});
107
104
  }
108
105
 
109
- get schema(): SchemaType[] {
106
+ get schema(): Schema[] {
110
107
  return Object.values(this.factories).map((f) => f.schema!);
111
108
  }
112
109
 
@@ -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';
@@ -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[]>(() => {
@@ -15,8 +15,7 @@ import {
15
15
  useContainer,
16
16
  useMosaic,
17
17
  Path,
18
- useSortedItems,
19
- type MosaicCompareDataItem,
18
+ useItemsWithOrigin,
20
19
  } from '../../mosaic';
21
20
 
22
21
  // TODO(burdon): Tree data model that provides a pure abstraction of the plugin Graph.
@@ -27,7 +26,6 @@ import {
27
26
  export type TreeProps<TData extends MosaicDataItem = TreeData> = MosaicContainerProps<TData, number> & {
28
27
  items?: TData[];
29
28
  debug?: boolean;
30
- compare?: MosaicCompareDataItem;
31
29
  };
32
30
 
33
31
  export type TreeData = {
@@ -37,7 +35,7 @@ export type TreeData = {
37
35
  };
38
36
 
39
37
  // TODO(burdon): Make generic (and forwardRef).
40
- export const Tree = ({ id, Component = TreeItem, onOver, onDrop, items = [], debug, compare }: TreeProps) => {
38
+ export const Tree = ({ id, Component = TreeItem, onOver, onDrop, items = [], debug }: TreeProps) => {
41
39
  return (
42
40
  <Mosaic.Container
43
41
  {...{
@@ -46,7 +44,6 @@ export const Tree = ({ id, Component = TreeItem, onOver, onDrop, items = [], deb
46
44
  Component,
47
45
  onOver,
48
46
  onDrop,
49
- compare,
50
47
  }}
51
48
  >
52
49
  <TreeRoot items={items} />
@@ -56,12 +53,12 @@ export const Tree = ({ id, Component = TreeItem, onOver, onDrop, items = [], deb
56
53
 
57
54
  const TreeRoot = ({ items }: { items: TreeData[] }) => {
58
55
  const { id, Component } = useContainer();
59
- const sortedItems = useSortedItems(items);
56
+ const itemsWithOrigin = useItemsWithOrigin(id, items);
60
57
 
61
58
  return (
62
59
  <TreeComponent.Root classNames='flex flex-col'>
63
- <Mosaic.SortableContext id={id} items={sortedItems} direction='vertical'>
64
- {sortedItems.map((item, index) => (
60
+ <Mosaic.SortableContext id={id} items={itemsWithOrigin} direction='vertical'>
61
+ {itemsWithOrigin.map((item, index) => (
65
62
  <TreeItemComponent.Root key={item.id} collapsible defaultOpen>
66
63
  <Mosaic.SortableTile item={item} path={id} position={index} Component={Component!} />
67
64
  </TreeItemComponent.Root>
@@ -102,12 +99,12 @@ const TreeItem: MosaicTileComponent<TreeData> = forwardRef(
102
99
  const TreeBranch = ({ path, items }: { path: string; items: TreeData[] }) => {
103
100
  const { operation, overItem } = useMosaic();
104
101
  const { Component } = useContainer();
105
- const sortedItems = useSortedItems(items);
102
+ const itemsWithOrigin = useItemsWithOrigin(path, items);
106
103
 
107
104
  return (
108
105
  <TreeItemComponent.Body className='pis-4'>
109
- <Mosaic.SortableContext id={path} items={sortedItems} direction='vertical'>
110
- {sortedItems.map((child, index) => (
106
+ <Mosaic.SortableContext id={path} items={itemsWithOrigin} direction='vertical'>
107
+ {itemsWithOrigin.map((child, index) => (
111
108
  <TreeComponent.Branch key={child.id}>
112
109
  <TreeItemComponent.Root collapsible defaultOpen>
113
110
  <Mosaic.SortableTile
@@ -116,7 +113,7 @@ const TreeBranch = ({ path, items }: { path: string; items: TreeData[] }) => {
116
113
  position={index}
117
114
  Component={Component!}
118
115
  isOver={
119
- overItem?.path === Path.create(path, child.id) && (operation === 'adopt' || operation === 'copy')
116
+ overItem?.path === Path.create(path, child.id) && (operation === 'transfer' || operation === 'copy')
120
117
  }
121
118
  />
122
119
  </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 } 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
  }),
@@ -104,20 +104,7 @@ export const createGraph = () => {
104
104
  return buildGraph(new GraphBuilder().build(), 'tree', content);
105
105
  };
106
106
 
107
- const graphNodeCompare = (a: Node, b: Node) => {
108
- if (a.properties.index && b.properties.index) {
109
- if (a.properties.index < b.properties.index) {
110
- return -1;
111
- } else if (a.properties.index > b.properties.index) {
112
- return 1;
113
- }
114
- return 0;
115
- }
116
- return 0;
117
- };
118
-
119
107
  export const GraphTree = ({ id, graph = createGraph(), debug }: { id: string; graph?: Graph; debug: boolean }) => {
120
- // TODO(wittjosiah): This graph does not handle order currently.
121
108
  const handleDrop = ({ operation, active, over }: MosaicDropEvent<number>) => {
122
109
  // Moving within the tree.
123
110
  if (Path.hasDescendent(id, active.path) && Path.hasDescendent(id, over.path)) {
@@ -135,22 +122,17 @@ export const GraphTree = ({ id, graph = createGraph(), debug }: { id: string; gr
135
122
  operation === 'rearrange'
136
123
  ) {
137
124
  // 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') {
125
+ console.warn('[react-ui-mosaic]', 'Tree', 'rearrange', 'needs implementation');
126
+ } else if (activeNode && activeParent && overParent && operation === 'transfer') {
141
127
  activeParent.removeNode(active.item.id);
142
128
  overNode.addNode('tree', { ...activeNode });
143
129
  }
144
130
  }
145
131
  };
146
132
 
133
+ const handleOver = (): MosaicOperation => 'transfer';
134
+
147
135
  return (
148
- <Tree
149
- id={id}
150
- items={graph.root.children as TreeData[]}
151
- onDrop={handleDrop}
152
- debug={debug}
153
- compare={graphNodeCompare}
154
- />
136
+ <Tree id={id} items={graph.root.children as TreeData[]} onDrop={handleDrop} onOver={handleOver} debug={debug} />
155
137
  );
156
138
  };
@@ -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
- };