@pcg/dynamic-components 1.0.0-alpha.0
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/CHANGELOG.md +7 -0
- package/dist/index.d.ts +1816 -0
- package/dist/index.js +1564 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.cjs +14 -0
- package/package.json +30 -0
- package/src/assertions/basic.ts +58 -0
- package/src/assertions/containers.ts +76 -0
- package/src/assertions/index.ts +6 -0
- package/src/assertions/paths.ts +12 -0
- package/src/assertions/rich-text.ts +16 -0
- package/src/assertions/yjs.ts +25 -0
- package/src/data-objects/data-object.ts +34 -0
- package/src/data-objects/index.ts +3 -0
- package/src/data-objects/rich-text.ts +38 -0
- package/src/dynamic-components/fractional-indexing.ts +321 -0
- package/src/dynamic-components/index.ts +6 -0
- package/src/dynamic-components/paths.ts +194 -0
- package/src/dynamic-components/registry/chats.ts +24 -0
- package/src/dynamic-components/registry/content.ts +118 -0
- package/src/dynamic-components/registry/forms.ts +525 -0
- package/src/dynamic-components/registry/index.ts +6 -0
- package/src/dynamic-components/registry/layout.ts +86 -0
- package/src/dynamic-components/registry/uikit-dynamic-component.ts +84 -0
- package/src/dynamic-components/tools.ts +195 -0
- package/src/dynamic-components/types.ts +237 -0
- package/src/index.ts +7 -0
- package/src/paths/array-keys.ts +164 -0
- package/src/paths/array-ops.ts +124 -0
- package/src/paths/basic-ops.ts +181 -0
- package/src/paths/constants.ts +1 -0
- package/src/paths/index.ts +7 -0
- package/src/paths/tools.ts +42 -0
- package/src/paths/types.ts +133 -0
- package/src/y-components/index.ts +3 -0
- package/src/y-components/tools.ts +234 -0
- package/src/y-components/types.ts +19 -0
- package/src/y-tools/array-path-ops.ts +240 -0
- package/src/y-tools/basic-path-ops.ts +189 -0
- package/src/y-tools/index.ts +6 -0
- package/src/y-tools/tools.ts +122 -0
- package/src/y-tools/types.ts +32 -0
- package/src/y-tools/y-array-keys.ts +47 -0
- package/tests/assertions/basic-types.test.ts +78 -0
- package/tests/assertions/containers.test.ts +72 -0
- package/tests/assertions/paths.test.ts +23 -0
- package/tests/assertions/yjs.test.ts +33 -0
- package/tests/dynamic-components/paths.test.ts +171 -0
- package/tests/dynamic-components/tools.test.ts +121 -0
- package/tests/paths/array-keys.test.ts +182 -0
- package/tests/paths/array-ops.test.ts +164 -0
- package/tests/paths/basic-ops.test.ts +263 -0
- package/tests/paths/tools.test.ts +55 -0
- package/tests/y-components/tools.test.ts +198 -0
- package/tests/y-tools/array-base-ops.test.ts +55 -0
- package/tests/y-tools/array-path-ops.test.ts +95 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +13 -0
- package/tsdown.config.ts +18 -0
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { dynamicComponentToYMap, yComponentToDynamicComponent } from '@/y-components/index.js';
|
|
2
|
+
import * as Y from 'yjs';
|
|
3
|
+
|
|
4
|
+
import { DRichText } from '@/dynamic-components/index.js';
|
|
5
|
+
import { JSONContent, getSchema } from '@tiptap/core';
|
|
6
|
+
import { default as StarterKit } from '@tiptap/starter-kit';
|
|
7
|
+
import {
|
|
8
|
+
describe, expect, it,
|
|
9
|
+
} from 'vitest';
|
|
10
|
+
import { prosemirrorJSONToYXmlFragment } from 'y-prosemirror';
|
|
11
|
+
|
|
12
|
+
describe('dynamicComponentToYMap', () => {
|
|
13
|
+
it('should convert dynamic component to Y.Map', () => {
|
|
14
|
+
const yDoc = new Y.Doc();
|
|
15
|
+
|
|
16
|
+
const component = {
|
|
17
|
+
id: 'column',
|
|
18
|
+
component: 'column',
|
|
19
|
+
position: 'a0',
|
|
20
|
+
props: {
|
|
21
|
+
components: [
|
|
22
|
+
{
|
|
23
|
+
id: 'text-input',
|
|
24
|
+
position: 'a0',
|
|
25
|
+
component: 'text-input',
|
|
26
|
+
props: {
|
|
27
|
+
label: 'Text',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
34
|
+
|
|
35
|
+
expect(yComponent.toJSON()).toEqual({
|
|
36
|
+
id: 'column',
|
|
37
|
+
component: 'column',
|
|
38
|
+
position: 'a0',
|
|
39
|
+
props: {
|
|
40
|
+
components: [
|
|
41
|
+
{
|
|
42
|
+
id: 'text-input',
|
|
43
|
+
component: 'text-input',
|
|
44
|
+
position: 'a0',
|
|
45
|
+
props: {
|
|
46
|
+
label: 'Text',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should convert dynamic component to Y.Map with empty props', () => {
|
|
55
|
+
const yDoc = new Y.Doc();
|
|
56
|
+
|
|
57
|
+
const component = {
|
|
58
|
+
id: 'image',
|
|
59
|
+
component: 'image',
|
|
60
|
+
position: 'a0',
|
|
61
|
+
props: {
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
66
|
+
|
|
67
|
+
expect(yComponent.toJSON()).toEqual({
|
|
68
|
+
id: 'image',
|
|
69
|
+
component: 'image',
|
|
70
|
+
position: 'a0',
|
|
71
|
+
props: {
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should convert Y.Map with empty props to dynamic component', () => {
|
|
77
|
+
const yDoc = new Y.Doc();
|
|
78
|
+
|
|
79
|
+
const component = {
|
|
80
|
+
id: 'image',
|
|
81
|
+
component: 'image',
|
|
82
|
+
position: 'a0',
|
|
83
|
+
props: {
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
88
|
+
|
|
89
|
+
expect(yComponentToDynamicComponent(yComponent)).toEqual({
|
|
90
|
+
id: 'image',
|
|
91
|
+
component: 'image',
|
|
92
|
+
position: 'a0',
|
|
93
|
+
props: {
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should convert dynamic component to Y.Map without props', () => {
|
|
99
|
+
const yDoc = new Y.Doc();
|
|
100
|
+
|
|
101
|
+
const component = {
|
|
102
|
+
id: 'image',
|
|
103
|
+
component: 'image',
|
|
104
|
+
position: 'a0',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// @ts-expect-error props is required
|
|
108
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
109
|
+
|
|
110
|
+
expect(yComponent.toJSON()).toEqual({
|
|
111
|
+
id: 'image',
|
|
112
|
+
component: 'image',
|
|
113
|
+
position: 'a0',
|
|
114
|
+
props: {
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should convert Y.Map with empty props to dynamic component', () => {
|
|
120
|
+
const yDoc = new Y.Doc();
|
|
121
|
+
|
|
122
|
+
const component = {
|
|
123
|
+
id: 'image',
|
|
124
|
+
component: 'image',
|
|
125
|
+
position: 'a0',
|
|
126
|
+
props: {
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
131
|
+
yComponent.delete('props');
|
|
132
|
+
|
|
133
|
+
expect(yComponentToDynamicComponent(yComponent)).toEqual({
|
|
134
|
+
id: 'image',
|
|
135
|
+
component: 'image',
|
|
136
|
+
position: 'a0',
|
|
137
|
+
props: {
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should convert Y.Map with content to dynamic component', () => {
|
|
143
|
+
const jsonContent: JSONContent = {
|
|
144
|
+
type: 'doc',
|
|
145
|
+
content: [
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: 'text',
|
|
151
|
+
text: 'Hello World!',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const schema = getSchema([
|
|
159
|
+
StarterKit,
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
const id = 'xxx';
|
|
163
|
+
|
|
164
|
+
const yDoc = new Y.Doc();
|
|
165
|
+
prosemirrorJSONToYXmlFragment(schema, jsonContent, yDoc.getXmlFragment(`RichText:${id}`));
|
|
166
|
+
|
|
167
|
+
const component: DRichText = {
|
|
168
|
+
id: 'rich-text',
|
|
169
|
+
component: 'rich-text',
|
|
170
|
+
position: 'a0',
|
|
171
|
+
props: {
|
|
172
|
+
content: {
|
|
173
|
+
id,
|
|
174
|
+
tiptap: {
|
|
175
|
+
type: 'doc',
|
|
176
|
+
content: [],
|
|
177
|
+
},
|
|
178
|
+
type: 'RichText',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const yComponent = dynamicComponentToYMap(component, yDoc.getMap());
|
|
184
|
+
|
|
185
|
+
expect(yComponentToDynamicComponent(yComponent, true)).toEqual({
|
|
186
|
+
id: 'rich-text',
|
|
187
|
+
component: 'rich-text',
|
|
188
|
+
position: 'a0',
|
|
189
|
+
props: {
|
|
190
|
+
content: {
|
|
191
|
+
id,
|
|
192
|
+
tiptap: jsonContent,
|
|
193
|
+
type: 'RichText',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe, expect, test,
|
|
3
|
+
} from 'vitest';
|
|
4
|
+
import * as Y from 'yjs';
|
|
5
|
+
import { setInYPath, unsetYPath } from '../../src/index.js';
|
|
6
|
+
|
|
7
|
+
const createYMap = () => {
|
|
8
|
+
const yDoc = new Y.Doc();
|
|
9
|
+
|
|
10
|
+
const yMap = yDoc.getMap();
|
|
11
|
+
const talentCrew = new Y.Array();
|
|
12
|
+
|
|
13
|
+
const item = new Y.Map(Object.entries({
|
|
14
|
+
id: '89bc6',
|
|
15
|
+
name: 'Foo',
|
|
16
|
+
position: 'a0',
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
talentCrew.push([item]);
|
|
20
|
+
|
|
21
|
+
yMap.set('talentCrew', talentCrew);
|
|
22
|
+
|
|
23
|
+
return yMap;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('setInYPath', () => {
|
|
27
|
+
test('must set in array item', () => {
|
|
28
|
+
const yMap = createYMap();
|
|
29
|
+
|
|
30
|
+
setInYPath(yMap, ['talentCrew', 'id:89bc6', 'name'], 'Bar');
|
|
31
|
+
|
|
32
|
+
expect(yMap.toJSON()).toEqual({
|
|
33
|
+
talentCrew: [{
|
|
34
|
+
id: '89bc6',
|
|
35
|
+
name: 'Bar',
|
|
36
|
+
position: 'a0',
|
|
37
|
+
}],
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('unsetYPath', () => {
|
|
43
|
+
test('must unset array item property', () => {
|
|
44
|
+
const yMap = createYMap();
|
|
45
|
+
|
|
46
|
+
unsetYPath(yMap, ['talentCrew', 'id:89bc6', 'name']);
|
|
47
|
+
|
|
48
|
+
expect(yMap.toJSON()).toEqual({
|
|
49
|
+
talentCrew: [{
|
|
50
|
+
id: '89bc6',
|
|
51
|
+
position: 'a0',
|
|
52
|
+
}],
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isYMap } from '@/assertions/index.js';
|
|
2
|
+
import {
|
|
3
|
+
describe, expect, test,
|
|
4
|
+
} from 'vitest';
|
|
5
|
+
import * as Y from 'yjs';
|
|
6
|
+
import { pullFromYPath } from '../../src/index.js';
|
|
7
|
+
|
|
8
|
+
const createProps = () => {
|
|
9
|
+
const yDoc = new Y.Doc();
|
|
10
|
+
const yMap = yDoc.getMap('props');
|
|
11
|
+
const yArray = new Y.Array();
|
|
12
|
+
|
|
13
|
+
yArray.push([
|
|
14
|
+
new Y.Map([
|
|
15
|
+
['id', 'x'],
|
|
16
|
+
['position', 'a0'],
|
|
17
|
+
]),
|
|
18
|
+
new Y.Map([
|
|
19
|
+
['id', 'y'],
|
|
20
|
+
['position', 'a1'],
|
|
21
|
+
]),
|
|
22
|
+
new Y.Map([
|
|
23
|
+
['id', 'z'],
|
|
24
|
+
['position', 'a2'],
|
|
25
|
+
]),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
yMap.set('items', yArray);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
yMap,
|
|
32
|
+
yArray,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('pullFromYPath', () => {
|
|
37
|
+
test('must pull from yArray by index', () => {
|
|
38
|
+
const { yMap, yArray } = createProps();
|
|
39
|
+
pullFromYPath(yMap, ['items'], 1);
|
|
40
|
+
|
|
41
|
+
expect(yArray.length).toBe(2);
|
|
42
|
+
|
|
43
|
+
const firstYMap = yArray.get(0);
|
|
44
|
+
|
|
45
|
+
expect(isYMap(firstYMap)).toBeTruthy();
|
|
46
|
+
|
|
47
|
+
expect((firstYMap as Y.Map<unknown>).get('id')).toBe('x');
|
|
48
|
+
|
|
49
|
+
const secondYMap = yArray.get(1);
|
|
50
|
+
|
|
51
|
+
expect(isYMap(secondYMap)).toBeTruthy();
|
|
52
|
+
|
|
53
|
+
expect((secondYMap as Y.Map<unknown>).get('id')).toBe('z');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('must pull from yArray by position', () => {
|
|
57
|
+
const { yMap, yArray } = createProps();
|
|
58
|
+
|
|
59
|
+
pullFromYPath(yMap, ['items'], 'pos:a2');
|
|
60
|
+
|
|
61
|
+
expect(yArray.length).toBe(2);
|
|
62
|
+
|
|
63
|
+
const firstYMap = yArray.get(0);
|
|
64
|
+
|
|
65
|
+
expect(isYMap(firstYMap)).toBeTruthy();
|
|
66
|
+
|
|
67
|
+
expect((firstYMap as Y.Map<unknown>).get('id')).toBe('x');
|
|
68
|
+
|
|
69
|
+
const secondYMap = yArray.get(1);
|
|
70
|
+
|
|
71
|
+
expect(isYMap(secondYMap)).toBeTruthy();
|
|
72
|
+
|
|
73
|
+
expect((secondYMap as Y.Map<unknown>).get('id')).toBe('y');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('must pull from yArray by id', () => {
|
|
77
|
+
const { yMap, yArray } = createProps();
|
|
78
|
+
|
|
79
|
+
pullFromYPath(yMap, ['items'], 'id:z');
|
|
80
|
+
|
|
81
|
+
expect(yArray.length).toBe(2);
|
|
82
|
+
|
|
83
|
+
const firstYMap = yArray.get(0);
|
|
84
|
+
|
|
85
|
+
expect(isYMap(firstYMap)).toBeTruthy();
|
|
86
|
+
|
|
87
|
+
expect((firstYMap as Y.Map<unknown>).get('id')).toBe('x');
|
|
88
|
+
|
|
89
|
+
const secondYMap = yArray.get(1);
|
|
90
|
+
|
|
91
|
+
expect(isYMap(secondYMap)).toBeTruthy();
|
|
92
|
+
|
|
93
|
+
expect((secondYMap as Y.Map<unknown>).get('id')).toBe('y');
|
|
94
|
+
});
|
|
95
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"baseUrl": ".",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@/*": ["src/*"]
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*.ts", "tests/**/*.ts"],
|
|
12
|
+
"exclude": ["node_modules", "dist"],
|
|
13
|
+
}
|
package/tsdown.config.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { defineConfig } from 'tsdown';
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
entry: ['src/index.ts'],
|
|
9
|
+
tsconfig: './tsconfig.lib.json',
|
|
10
|
+
outDir: 'dist',
|
|
11
|
+
dts: true,
|
|
12
|
+
clean: true,
|
|
13
|
+
sourcemap: true,
|
|
14
|
+
format: 'esm',
|
|
15
|
+
alias: {
|
|
16
|
+
'@': resolve(__dirname, 'src'),
|
|
17
|
+
},
|
|
18
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// / <reference types="vitest" />
|
|
2
|
+
// eslint-disable-next-line node/no-unpublished-import
|
|
3
|
+
import { defineConfig } from 'vitest/config';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
test: {
|
|
7
|
+
environment: 'node',
|
|
8
|
+
include: ['tests/**/*.test.ts'],
|
|
9
|
+
globals: true,
|
|
10
|
+
typecheck: {
|
|
11
|
+
tsconfig: './tsconfig.json',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
resolve: {
|
|
15
|
+
alias: {
|
|
16
|
+
'@': new URL('./src', import.meta.url).pathname,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|