@supernova-studio/client 0.1.0 → 0.2.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/dist/index.d.mts +86 -100
- package/dist/index.d.ts +86 -100
- package/dist/index.js +257 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +252 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
- package/src/docs-editor/blocks-to-prosemirror.ts +88 -114
- package/src/docs-editor/mock.ts +2 -2
- package/src/docs-editor/prosemirror/types.ts +3 -7
- package/src/docs-editor/prosemirror-to-blocks.ts +226 -3
- package/src/docs-editor/utils.ts +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supernova-studio/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Supernova Data Models",
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsc",
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
|
-
"test:unit": "vitest run"
|
|
29
|
-
"test:unit:watch": "vitest watch"
|
|
28
|
+
"test:unit": "vitest run"
|
|
30
29
|
},
|
|
31
30
|
"author": "",
|
|
32
31
|
"license": "ISC",
|
|
@@ -2,7 +2,6 @@ import * as Y from "yjs";
|
|
|
2
2
|
import {
|
|
3
3
|
PageBlockDefinition,
|
|
4
4
|
PageBlockDefinitionProperty,
|
|
5
|
-
PageBlockItemV2,
|
|
6
5
|
PageBlockText,
|
|
7
6
|
PageBlockTextSpan,
|
|
8
7
|
PageBlockTextSpanAttribute,
|
|
@@ -11,12 +10,7 @@ import {
|
|
|
11
10
|
PageBlockItemMultiRichTextPropertyValue,
|
|
12
11
|
} from "@supernova-studio/model";
|
|
13
12
|
import { PageBlockEditorModel } from "./model/block";
|
|
14
|
-
import {
|
|
15
|
-
ProsemirrorBlockItem,
|
|
16
|
-
ProsemirrorBlockItemPropertyValue,
|
|
17
|
-
ProsemirrorNode,
|
|
18
|
-
ProsemirrorMark,
|
|
19
|
-
} from "./prosemirror/types";
|
|
13
|
+
import { ProsemirrorNode, ProsemirrorMark } from "./prosemirror/types";
|
|
20
14
|
import { BlockDefinitionUtils, BlockParsingUtils } from "./utils";
|
|
21
15
|
import { DocumentationPageEditorModel } from "./model";
|
|
22
16
|
import { prosemirrorJSONToYXmlFragment } from "y-prosemirror";
|
|
@@ -26,17 +20,20 @@ import { pmSchema } from "./prosemirror";
|
|
|
26
20
|
// Types
|
|
27
21
|
//
|
|
28
22
|
|
|
29
|
-
type
|
|
23
|
+
type BaseInput = {
|
|
30
24
|
block: PageBlockEditorModel;
|
|
31
25
|
definition: PageBlockDefinition;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type InputWithProperty = BaseInput & {
|
|
32
29
|
property: PageBlockDefinitionProperty;
|
|
33
30
|
};
|
|
34
31
|
|
|
35
|
-
type RichTextInputWithValue =
|
|
32
|
+
type RichTextInputWithValue = InputWithProperty & {
|
|
36
33
|
richTextPropertyValue: PageBlockItemRichTextPropertyValue;
|
|
37
34
|
};
|
|
38
35
|
|
|
39
|
-
type MultiRichTextInputWithValue =
|
|
36
|
+
type MultiRichTextInputWithValue = InputWithProperty & {
|
|
40
37
|
multiRichTextPropertyValue: PageBlockItemMultiRichTextPropertyValue;
|
|
41
38
|
};
|
|
42
39
|
|
|
@@ -79,6 +76,7 @@ export function blockToProsemirrorNode(
|
|
|
79
76
|
block: PageBlockEditorModel,
|
|
80
77
|
definition: PageBlockDefinition
|
|
81
78
|
): ProsemirrorNode | null {
|
|
79
|
+
// Single rich text
|
|
82
80
|
const richTextProperty = BlockDefinitionUtils.firstRichTextProperty(definition);
|
|
83
81
|
if (richTextProperty) {
|
|
84
82
|
return serializeAsRichTextBlock({
|
|
@@ -88,6 +86,7 @@ export function blockToProsemirrorNode(
|
|
|
88
86
|
});
|
|
89
87
|
}
|
|
90
88
|
|
|
89
|
+
// Multi rich text
|
|
91
90
|
const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
|
|
92
91
|
if (multiRichTextProperty) {
|
|
93
92
|
return serializeAsMultiRichTextBlock({
|
|
@@ -97,6 +96,28 @@ export function blockToProsemirrorNode(
|
|
|
97
96
|
});
|
|
98
97
|
}
|
|
99
98
|
|
|
99
|
+
// Embed
|
|
100
|
+
const embedProperty = BlockDefinitionUtils.firstEmbedProperty(definition);
|
|
101
|
+
const embedType = serializeEmbedType(block.data.packageId);
|
|
102
|
+
if (embedProperty && embedType) {
|
|
103
|
+
return serializeAsEmbed(
|
|
104
|
+
{
|
|
105
|
+
block: block,
|
|
106
|
+
definition: definition,
|
|
107
|
+
property: embedProperty,
|
|
108
|
+
},
|
|
109
|
+
embedType
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Divider
|
|
114
|
+
if (block.data.packageId === "io.supernova.block.divider") {
|
|
115
|
+
return serializeAsDivider({
|
|
116
|
+
block: block,
|
|
117
|
+
definition: definition,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
return serializeAsCustomBlock(block, definition);
|
|
101
122
|
}
|
|
102
123
|
|
|
@@ -104,7 +125,7 @@ export function blockToProsemirrorNode(
|
|
|
104
125
|
// Single rich text blocks
|
|
105
126
|
//
|
|
106
127
|
|
|
107
|
-
|
|
128
|
+
function serializeAsRichTextBlock(input: InputWithProperty): ProsemirrorNode {
|
|
108
129
|
const { block, definition, property: richTextProperty } = input;
|
|
109
130
|
|
|
110
131
|
const blockItem = BlockParsingUtils.singleBlockItem(block);
|
|
@@ -176,7 +197,7 @@ function serializeAsCallout(input: RichTextInputWithValue): ProsemirrorNode {
|
|
|
176
197
|
// Multi rich text blocks
|
|
177
198
|
//
|
|
178
199
|
|
|
179
|
-
|
|
200
|
+
function serializeAsMultiRichTextBlock(input: InputWithProperty): ProsemirrorNode {
|
|
180
201
|
const { block, definition, property: richTextProperty } = input;
|
|
181
202
|
|
|
182
203
|
const blockItem = BlockParsingUtils.singleBlockItem(block);
|
|
@@ -241,11 +262,57 @@ function serializeAsListItem(text: PageBlockText): ProsemirrorNode {
|
|
|
241
262
|
};
|
|
242
263
|
}
|
|
243
264
|
|
|
265
|
+
//
|
|
266
|
+
// Embeds
|
|
267
|
+
//
|
|
268
|
+
|
|
269
|
+
function serializeAsEmbed(input: InputWithProperty, embedType: string): ProsemirrorNode {
|
|
270
|
+
const { block, definition, property: embedProperty } = input;
|
|
271
|
+
|
|
272
|
+
const blockItem = BlockParsingUtils.singleBlockItem(block);
|
|
273
|
+
const embedValue = BlockParsingUtils.embedPropertyValue(blockItem, embedProperty.id);
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
type: "embed",
|
|
277
|
+
attrs: {
|
|
278
|
+
...serializeBlockNodeAttributes(input),
|
|
279
|
+
type: embedType,
|
|
280
|
+
url: embedValue.value,
|
|
281
|
+
height: embedValue.height,
|
|
282
|
+
caption: embedValue.caption,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function serializeEmbedType(blockDefinitionId: string): string | undefined {
|
|
288
|
+
switch (blockDefinitionId) {
|
|
289
|
+
case "io.supernova.block.embed":
|
|
290
|
+
return "generic";
|
|
291
|
+
case "io.supernova.block.embed-youtube":
|
|
292
|
+
return "youtube";
|
|
293
|
+
case "io.supernova.block.embed-figma":
|
|
294
|
+
return "figma";
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
//
|
|
301
|
+
// Divider
|
|
302
|
+
//
|
|
303
|
+
|
|
304
|
+
function serializeAsDivider(input: BaseInput): ProsemirrorNode {
|
|
305
|
+
return {
|
|
306
|
+
type: "horizontalrule",
|
|
307
|
+
attrs: serializeBlockNodeAttributes(input),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
244
311
|
//
|
|
245
312
|
// Attributes
|
|
246
313
|
//
|
|
247
314
|
|
|
248
|
-
function serializeBlockNodeAttributes(input:
|
|
315
|
+
function serializeBlockNodeAttributes(input: BaseInput) {
|
|
249
316
|
const { block } = input;
|
|
250
317
|
|
|
251
318
|
return {
|
|
@@ -351,118 +418,25 @@ function serializeTextSpanAttribute(spanAttribute: PageBlockTextSpanAttribute):
|
|
|
351
418
|
}
|
|
352
419
|
|
|
353
420
|
export function serializeAsCustomBlock(block: PageBlockEditorModel, definition: PageBlockDefinition): ProsemirrorNode {
|
|
354
|
-
const items = block.data.items.map(i =>
|
|
421
|
+
const items = block.data.items.map(i => {
|
|
422
|
+
return {
|
|
423
|
+
id: i.id,
|
|
424
|
+
props: i.props,
|
|
425
|
+
linksTo: i.linksTo,
|
|
426
|
+
};
|
|
427
|
+
});
|
|
355
428
|
|
|
356
429
|
return {
|
|
357
430
|
type: "blockNode",
|
|
358
431
|
attrs: {
|
|
359
432
|
id: block.id,
|
|
360
433
|
definitionId: block.data.packageId,
|
|
361
|
-
variantId:
|
|
362
|
-
columns: 1,
|
|
434
|
+
variantId: block.data.variantId,
|
|
363
435
|
items: JSON.stringify(items),
|
|
364
|
-
|
|
365
|
-
// TODO Docs: variant, columns
|
|
366
436
|
},
|
|
367
437
|
};
|
|
368
438
|
}
|
|
369
439
|
|
|
370
|
-
function serializeItem(
|
|
371
|
-
item: PageBlockItemV2,
|
|
372
|
-
block: PageBlockEditorModel,
|
|
373
|
-
definition: PageBlockDefinition
|
|
374
|
-
): ProsemirrorBlockItem {
|
|
375
|
-
const result: ProsemirrorBlockItem = {
|
|
376
|
-
properties: [],
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
for (const property of definition.item.properties) {
|
|
380
|
-
const serializedValue = serializePropertyValue(item, property);
|
|
381
|
-
serializedValue && result.properties.push(serializedValue);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return result;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function serializePropertyValue(
|
|
388
|
-
item: PageBlockItemV2,
|
|
389
|
-
property: PageBlockDefinitionProperty
|
|
390
|
-
): ProsemirrorBlockItemPropertyValue | undefined {
|
|
391
|
-
const value = item.props[property.id] ?? undefined;
|
|
392
|
-
if (!value) {
|
|
393
|
-
return undefined;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// TODO Docs: actual property serialization
|
|
397
|
-
|
|
398
|
-
const result: ProsemirrorBlockItemPropertyValue = {
|
|
399
|
-
id: property.id,
|
|
400
|
-
data: {},
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
if (typeof value === "string") {
|
|
404
|
-
result.data.value = value;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return result;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
440
|
function nonNullFilter<T>(item: T | null): item is T {
|
|
411
441
|
return !!item;
|
|
412
442
|
}
|
|
413
|
-
|
|
414
|
-
// function serializePropertyValue(item: SDKBlockItem, property: SDKBlockDefinitionProperty) {
|
|
415
|
-
// switch (property.type) {
|
|
416
|
-
// case SDK.DocsBlockItemPropertyType.text: return value as string;
|
|
417
|
-
|
|
418
|
-
// case SDK.DocsBlockItemPropertyType.richText: return {}
|
|
419
|
-
|
|
420
|
-
// case SDK.DocsBlockItemPropertyType.boolean: return {}
|
|
421
|
-
// case SDK.DocsBlockItemPropertyType.number: return {}
|
|
422
|
-
// case SDK.DocsBlockItemPropertyType.singleSelect: return {}
|
|
423
|
-
// case SDK.DocsBlockItemPropertyType.multiSelect: return {}
|
|
424
|
-
// case SDK.DocsBlockItemPropertyType.image: return {}
|
|
425
|
-
// case SDK.DocsBlockItemPropertyType.token: return {}
|
|
426
|
-
// case SDK.DocsBlockItemPropertyType.tokenType: return {}
|
|
427
|
-
// case SDK.DocsBlockItemPropertyType.tokenProperty: return {}
|
|
428
|
-
// case SDK.DocsBlockItemPropertyType.component: return {}
|
|
429
|
-
// case SDK.DocsBlockItemPropertyType.componentProperty: return {}
|
|
430
|
-
// case SDK.DocsBlockItemPropertyType.asset: return {}
|
|
431
|
-
// case SDK.DocsBlockItemPropertyType.assetProperty: return {}
|
|
432
|
-
// case SDK.DocsBlockItemPropertyType.page: return {}
|
|
433
|
-
// case SDK.DocsBlockItemPropertyType.pageProperty: return {}
|
|
434
|
-
// case SDK.DocsBlockItemPropertyType.embedURL: return {}
|
|
435
|
-
// case SDK.DocsBlockItemPropertyType.embedFrame: return {}
|
|
436
|
-
// case SDK.DocsBlockItemPropertyType.markdown: return {}
|
|
437
|
-
// case SDK.DocsBlockItemPropertyType.code: return {}
|
|
438
|
-
// case SDK.DocsBlockItemPropertyType.codeSandbox: return {}
|
|
439
|
-
// case SDK.DocsBlockItemPropertyType.table: return {}
|
|
440
|
-
// case SDK.DocsBlockItemPropertyType.divider: return {}
|
|
441
|
-
// case SDK.DocsBlockItemPropertyType.storybook: return {}
|
|
442
|
-
// }
|
|
443
|
-
// }
|
|
444
|
-
|
|
445
|
-
// richText = "RichText",
|
|
446
|
-
// text = "Text",
|
|
447
|
-
// boolean = "Boolean",
|
|
448
|
-
// number = "Number",
|
|
449
|
-
// singleSelect = "SingleSelect",
|
|
450
|
-
// multiSelect = "MultiSelect",
|
|
451
|
-
// image = "Image",
|
|
452
|
-
// token = "Token",
|
|
453
|
-
// tokenType = "TokenType",
|
|
454
|
-
// tokenProperty = "TokenProperty",
|
|
455
|
-
// component = "Component",
|
|
456
|
-
// componentProperty = "ComponentProperty",
|
|
457
|
-
// asset = "Asset",
|
|
458
|
-
// assetProperty = "AssetProperty",
|
|
459
|
-
// page = "Page",
|
|
460
|
-
// pageProperty = "PageProperty",
|
|
461
|
-
// embedURL = "EmbedURL",
|
|
462
|
-
// embedFrame = "EmbedFrame",
|
|
463
|
-
// markdown = "Markdown",
|
|
464
|
-
// code = "Code",
|
|
465
|
-
// codeSandbox = "CodeSandbox",
|
|
466
|
-
// table = "Table",
|
|
467
|
-
// divider = "Divider",
|
|
468
|
-
// storybook = "Storybook"
|
package/src/docs-editor/mock.ts
CHANGED
|
@@ -591,7 +591,7 @@ const blocks: PageBlockDefinition[] = [
|
|
|
591
591
|
{
|
|
592
592
|
id: "block.links.property.title",
|
|
593
593
|
name: "Title",
|
|
594
|
-
type: "
|
|
594
|
+
type: "Text",
|
|
595
595
|
description: undefined,
|
|
596
596
|
options: { textStyle: "Title5" },
|
|
597
597
|
variantOptions: undefined,
|
|
@@ -599,7 +599,7 @@ const blocks: PageBlockDefinition[] = [
|
|
|
599
599
|
{
|
|
600
600
|
id: "block.links.property.description",
|
|
601
601
|
name: "Short description",
|
|
602
|
-
type: "
|
|
602
|
+
type: "Text",
|
|
603
603
|
description: undefined,
|
|
604
604
|
options: { textStyle: "Default", color: "NeutralFaded" },
|
|
605
605
|
variantOptions: undefined,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PageBlockItemUntypedPropertyValue } from "@supernova-studio/model";
|
|
2
|
+
|
|
1
3
|
export type ProsemirrorNode = {
|
|
2
4
|
type: string;
|
|
3
5
|
text?: string;
|
|
@@ -12,12 +14,6 @@ export type ProsemirrorMark = {
|
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export type ProsemirrorBlockItem = {
|
|
15
|
-
properties: ProsemirrorBlockItemPropertyValue[];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type ProsemirrorBlockItemPropertyValue = {
|
|
19
17
|
id: string;
|
|
20
|
-
|
|
21
|
-
value?: string;
|
|
22
|
-
};
|
|
18
|
+
props: Record<string, PageBlockItemUntypedPropertyValue>;
|
|
23
19
|
};
|
|
@@ -6,6 +6,14 @@ import {
|
|
|
6
6
|
PageBlockTextSpan,
|
|
7
7
|
PageBlockTextSpanAttribute,
|
|
8
8
|
PageBlockDefinitionProperty,
|
|
9
|
+
PageBlockCalloutType,
|
|
10
|
+
PageBlockItemRichTextPropertyValue,
|
|
11
|
+
PageBlockItemMultiRichTextPropertyValue,
|
|
12
|
+
PageBlockItemEmbedPropertyValue,
|
|
13
|
+
PageBlockItemV2,
|
|
14
|
+
PageBlockItemTextPropertyValue,
|
|
15
|
+
PageBlockItemUntypedPropertyValue,
|
|
16
|
+
PageBlockItemImageValue,
|
|
9
17
|
} from "@supernova-studio/model";
|
|
10
18
|
import { PageBlockEditorModel } from "./model/block";
|
|
11
19
|
import { DocumentationPageEditorModel } from "./model/page";
|
|
@@ -52,9 +60,27 @@ export function prosemirrorNodeToBlock(
|
|
|
52
60
|
return parseAsRichText(prosemirrorNode, definition, richTextProperty);
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
|
|
64
|
+
if (multiRichTextProperty) {
|
|
65
|
+
return parseAsMultiRichText(prosemirrorNode, definition, multiRichTextProperty);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const embedProperty = BlockDefinitionUtils.firstEmbedProperty(definition);
|
|
69
|
+
if (prosemirrorNode.type === "embed" && embedProperty) {
|
|
70
|
+
return parseAsEmbed(prosemirrorNode, definition, embedProperty);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (definition.id === "io.supernova.block.divider") {
|
|
74
|
+
return parseAsDivider(prosemirrorNode, definition);
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
return parseAsCustomBlock(prosemirrorNode, definition);
|
|
56
78
|
}
|
|
57
79
|
|
|
80
|
+
//
|
|
81
|
+
// Single rich text
|
|
82
|
+
//
|
|
83
|
+
|
|
58
84
|
function parseAsRichText(
|
|
59
85
|
prosemirrorNode: ProsemirrorNode,
|
|
60
86
|
definition: PageBlockDefinition,
|
|
@@ -62,21 +88,89 @@ function parseAsRichText(
|
|
|
62
88
|
): PageBlockEditorModel {
|
|
63
89
|
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
64
90
|
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
91
|
+
const calloutType = parseCalloutType(parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "type"));
|
|
65
92
|
|
|
66
93
|
return {
|
|
67
94
|
// TODO Artem: indent
|
|
68
95
|
id: id,
|
|
96
|
+
|
|
97
|
+
...(variantId && { variantId: variantId }),
|
|
98
|
+
|
|
69
99
|
data: {
|
|
70
100
|
packageId: definition.id,
|
|
71
101
|
indentLevel: 0,
|
|
72
|
-
variantId: variantId,
|
|
73
102
|
items: [
|
|
74
103
|
{
|
|
75
104
|
id: id,
|
|
76
105
|
props: {
|
|
77
106
|
[property.id]: {
|
|
107
|
+
// Required
|
|
78
108
|
value: parseRichText(prosemirrorNode.content ?? []),
|
|
79
|
-
|
|
109
|
+
|
|
110
|
+
// Optional
|
|
111
|
+
...(calloutType && { calloutType: calloutType }),
|
|
112
|
+
} satisfies PageBlockItemRichTextPropertyValue,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseCalloutType(prosemirrorCalloutType: string | undefined): PageBlockCalloutType | undefined {
|
|
121
|
+
if (!prosemirrorCalloutType) return undefined;
|
|
122
|
+
|
|
123
|
+
switch (prosemirrorCalloutType) {
|
|
124
|
+
case "error":
|
|
125
|
+
return "Error";
|
|
126
|
+
case "neutral":
|
|
127
|
+
return "Info";
|
|
128
|
+
case "success":
|
|
129
|
+
return "Success";
|
|
130
|
+
case "warning":
|
|
131
|
+
return "Warning";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//
|
|
136
|
+
// Multi rich text
|
|
137
|
+
//
|
|
138
|
+
|
|
139
|
+
function parseAsMultiRichText(
|
|
140
|
+
prosemirrorNode: ProsemirrorNode,
|
|
141
|
+
definition: PageBlockDefinition,
|
|
142
|
+
property: PageBlockDefinitionProperty
|
|
143
|
+
) {
|
|
144
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
145
|
+
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
// TODO Artem: indent
|
|
149
|
+
id: id,
|
|
150
|
+
data: {
|
|
151
|
+
packageId: definition.id,
|
|
152
|
+
indentLevel: 0,
|
|
153
|
+
|
|
154
|
+
...(variantId && { variantId: variantId }),
|
|
155
|
+
|
|
156
|
+
items: [
|
|
157
|
+
{
|
|
158
|
+
id: id,
|
|
159
|
+
props: {
|
|
160
|
+
[property.id]: {
|
|
161
|
+
// Required
|
|
162
|
+
value: (prosemirrorNode.content ?? [])
|
|
163
|
+
.map(listItem => {
|
|
164
|
+
if (listItem.type !== "listitem") return null;
|
|
165
|
+
if (!listItem.content?.length) return parseRichText([]);
|
|
166
|
+
|
|
167
|
+
const paragraph = listItem.content[0];
|
|
168
|
+
if (paragraph.type !== "paragraph") return parseRichText([]);
|
|
169
|
+
|
|
170
|
+
return parseRichText(paragraph.content ?? []);
|
|
171
|
+
})
|
|
172
|
+
.filter(nonNullFilter),
|
|
173
|
+
} satisfies PageBlockItemMultiRichTextPropertyValue,
|
|
80
174
|
},
|
|
81
175
|
},
|
|
82
176
|
],
|
|
@@ -84,6 +178,10 @@ function parseAsRichText(
|
|
|
84
178
|
};
|
|
85
179
|
}
|
|
86
180
|
|
|
181
|
+
//
|
|
182
|
+
// Rich text
|
|
183
|
+
//
|
|
184
|
+
|
|
87
185
|
function parseRichText(spans: ProsemirrorNode[]): PageBlockText {
|
|
88
186
|
return {
|
|
89
187
|
spans: spans.map(parseRichTextSpan).filter(nonNullFilter),
|
|
@@ -126,6 +224,67 @@ function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribu
|
|
|
126
224
|
return null;
|
|
127
225
|
}
|
|
128
226
|
|
|
227
|
+
//
|
|
228
|
+
// Embed
|
|
229
|
+
//
|
|
230
|
+
|
|
231
|
+
function parseAsEmbed(
|
|
232
|
+
prosemirrorNode: ProsemirrorNode,
|
|
233
|
+
definition: PageBlockDefinition,
|
|
234
|
+
property: PageBlockDefinitionProperty
|
|
235
|
+
): PageBlockEditorModel {
|
|
236
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
237
|
+
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
238
|
+
|
|
239
|
+
const url = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "url");
|
|
240
|
+
const caption = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "caption");
|
|
241
|
+
const height = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "height");
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
id: id,
|
|
245
|
+
data: {
|
|
246
|
+
packageId: definition.id,
|
|
247
|
+
indentLevel: 0,
|
|
248
|
+
|
|
249
|
+
...(variantId && { variantId: variantId }),
|
|
250
|
+
|
|
251
|
+
items: [
|
|
252
|
+
{
|
|
253
|
+
id: id,
|
|
254
|
+
props: {
|
|
255
|
+
[property.id]: {
|
|
256
|
+
value: url,
|
|
257
|
+
caption: caption,
|
|
258
|
+
height: height,
|
|
259
|
+
} as PageBlockItemEmbedPropertyValue,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
//
|
|
268
|
+
// Divider
|
|
269
|
+
//
|
|
270
|
+
|
|
271
|
+
function parseAsDivider(prosemirrorNode: ProsemirrorNode, definition: PageBlockDefinition): PageBlockEditorModel {
|
|
272
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
273
|
+
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
id: id,
|
|
277
|
+
data: {
|
|
278
|
+
packageId: definition.id,
|
|
279
|
+
indentLevel: 0,
|
|
280
|
+
|
|
281
|
+
...(variantId && { variantId: variantId }),
|
|
282
|
+
|
|
283
|
+
items: [{ id: id, props: {} }],
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
129
288
|
//
|
|
130
289
|
// Custom block
|
|
131
290
|
//
|
|
@@ -134,7 +293,71 @@ function parseAsCustomBlock(
|
|
|
134
293
|
prosemirrorNode: ProsemirrorNode,
|
|
135
294
|
definition: PageBlockDefinition
|
|
136
295
|
): PageBlockEditorModel | null {
|
|
137
|
-
|
|
296
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
297
|
+
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
298
|
+
|
|
299
|
+
const itemsString = parseProsemirrorBlockAttribute(prosemirrorNode, "items");
|
|
300
|
+
const itemsJson = JSON.parse(itemsString);
|
|
301
|
+
if (!Array.isArray(itemsJson)) {
|
|
302
|
+
console.error("Block `items` property must be a json array");
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const parsedItems = itemsJson.map(i => parseItem(i, definition));
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
id: id,
|
|
310
|
+
data: {
|
|
311
|
+
packageId: definition.id,
|
|
312
|
+
indentLevel: 0,
|
|
313
|
+
|
|
314
|
+
...(variantId && { variantId: variantId }),
|
|
315
|
+
|
|
316
|
+
items: parsedItems,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function parseItem(rawItem: unknown, definition: PageBlockDefinition): PageBlockItemV2 | null {
|
|
322
|
+
const parsedItem = PageBlockItemV2.safeParse(rawItem);
|
|
323
|
+
if (!parsedItem.success) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const sanitizedProps: Record<string, PageBlockItemUntypedPropertyValue> = {};
|
|
328
|
+
|
|
329
|
+
for (const property of definition.item.properties) {
|
|
330
|
+
const value = parsedItem.data.props[property.id];
|
|
331
|
+
if (!value) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Validate
|
|
336
|
+
switch (property.type) {
|
|
337
|
+
case "Text":
|
|
338
|
+
PageBlockItemTextPropertyValue.parse(value);
|
|
339
|
+
break;
|
|
340
|
+
|
|
341
|
+
case "EmbedURL":
|
|
342
|
+
PageBlockItemEmbedPropertyValue.parse(value);
|
|
343
|
+
break;
|
|
344
|
+
|
|
345
|
+
case "Image":
|
|
346
|
+
PageBlockItemImageValue.parse(value);
|
|
347
|
+
break;
|
|
348
|
+
|
|
349
|
+
default:
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
sanitizedProps[property.id] = value;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
id: parsedItem.data.id,
|
|
358
|
+
linksTo: parsedItem.data.linksTo,
|
|
359
|
+
props: sanitizedProps,
|
|
360
|
+
};
|
|
138
361
|
}
|
|
139
362
|
|
|
140
363
|
//
|
package/src/docs-editor/utils.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { PageBlockEditorModel } from "./model/block";
|
|
|
2
2
|
import {
|
|
3
3
|
PageBlockDefinition,
|
|
4
4
|
PageBlockDefinitionPropertyType,
|
|
5
|
+
PageBlockItemEmbedPropertyValue,
|
|
5
6
|
PageBlockItemMultiRichTextPropertyValue,
|
|
6
7
|
PageBlockItemRichTextPropertyValue,
|
|
7
8
|
PageBlockItemUntypedPropertyValue,
|
|
@@ -31,6 +32,13 @@ export const BlockParsingUtils = {
|
|
|
31
32
|
return richText;
|
|
32
33
|
},
|
|
33
34
|
|
|
35
|
+
embedPropertyValue(item: PageBlockItemV2, propertyKey: string): PageBlockItemEmbedPropertyValue {
|
|
36
|
+
const objectValue = this.objectPropertyValue(item, propertyKey);
|
|
37
|
+
const embed = PageBlockItemEmbedPropertyValue.parse(objectValue);
|
|
38
|
+
|
|
39
|
+
return embed;
|
|
40
|
+
},
|
|
41
|
+
|
|
34
42
|
objectPropertyValue(item: PageBlockItemV2, propertyKey: string) {
|
|
35
43
|
const value = this.propertyValueOrThrow(item, propertyKey);
|
|
36
44
|
|
|
@@ -68,6 +76,12 @@ export const BlockDefinitionUtils = {
|
|
|
68
76
|
return undefined;
|
|
69
77
|
},
|
|
70
78
|
|
|
79
|
+
firstEmbedProperty(definition: PageBlockDefinition) {
|
|
80
|
+
const property = definition.item.properties.find(p => p.type === "EmbedURL");
|
|
81
|
+
if (property) return property;
|
|
82
|
+
return undefined;
|
|
83
|
+
},
|
|
84
|
+
|
|
71
85
|
richTextProperty(definition: PageBlockDefinition, propertyKey: string) {
|
|
72
86
|
return this.property(definition, propertyKey, "RichText");
|
|
73
87
|
},
|