@supernova-studio/client 0.3.0 → 0.4.1
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 +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +291 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +283 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/docs-editor/blocks-to-prosemirror.ts +110 -6
- package/src/docs-editor/prosemirror-to-blocks.ts +185 -1
- package/src/docs-editor/utils.ts +9 -1
package/package.json
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
PageBlockItemRichTextValue,
|
|
9
9
|
PageBlockCalloutType,
|
|
10
10
|
PageBlockItemMultiRichTextValue,
|
|
11
|
+
PageBlockTableCellAlignment,
|
|
12
|
+
PageBlockItemTableNode,
|
|
11
13
|
} from "@supernova-studio/model";
|
|
12
14
|
import { PageBlockEditorModel } from "./model/block";
|
|
13
15
|
import { ProsemirrorNode, ProsemirrorMark } from "./prosemirror/types";
|
|
@@ -96,6 +98,15 @@ export function blockToProsemirrorNode(
|
|
|
96
98
|
});
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
const tableProperty = BlockDefinitionUtils.firstTableProperty(definition);
|
|
102
|
+
if (tableProperty) {
|
|
103
|
+
return serializeAsTable({
|
|
104
|
+
block: block,
|
|
105
|
+
definition: definition,
|
|
106
|
+
property: tableProperty,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
// Embed
|
|
100
111
|
const embedProperty = BlockDefinitionUtils.firstEmbedProperty(definition);
|
|
101
112
|
const embedType = serializeEmbedType(block.data.packageId);
|
|
@@ -157,7 +168,7 @@ function serializeAsParagraph(input: RichTextInputWithValue): ProsemirrorNode {
|
|
|
157
168
|
return {
|
|
158
169
|
type: "paragraph",
|
|
159
170
|
attrs: serializeBlockNodeAttributes(input),
|
|
160
|
-
|
|
171
|
+
...serializeRichTextNodePart(input.richTextPropertyValue.value),
|
|
161
172
|
};
|
|
162
173
|
}
|
|
163
174
|
|
|
@@ -168,7 +179,7 @@ function serializeAsHeading(input: RichTextInputWithValue): ProsemirrorNode {
|
|
|
168
179
|
...serializeBlockNodeAttributes(input),
|
|
169
180
|
level: richTextHeadingLevel(input.property),
|
|
170
181
|
},
|
|
171
|
-
|
|
182
|
+
...serializeRichTextNodePart(input.richTextPropertyValue.value),
|
|
172
183
|
};
|
|
173
184
|
}
|
|
174
185
|
|
|
@@ -176,7 +187,7 @@ function serializeAsBlockquote(input: RichTextInputWithValue): ProsemirrorNode {
|
|
|
176
187
|
return {
|
|
177
188
|
type: "blockquote",
|
|
178
189
|
attrs: serializeBlockNodeAttributes(input),
|
|
179
|
-
|
|
190
|
+
...serializeRichTextNodePart(input.richTextPropertyValue.value),
|
|
180
191
|
};
|
|
181
192
|
}
|
|
182
193
|
|
|
@@ -189,7 +200,7 @@ function serializeAsCallout(input: RichTextInputWithValue): ProsemirrorNode {
|
|
|
189
200
|
...serializeBlockNodeAttributes(input),
|
|
190
201
|
type: serializeCalloutType(calloutType),
|
|
191
202
|
},
|
|
192
|
-
|
|
203
|
+
...serializeRichTextNodePart(input.richTextPropertyValue.value),
|
|
193
204
|
};
|
|
194
205
|
}
|
|
195
206
|
|
|
@@ -262,6 +273,91 @@ function serializeAsListItem(text: PageBlockText): ProsemirrorNode {
|
|
|
262
273
|
};
|
|
263
274
|
}
|
|
264
275
|
|
|
276
|
+
//
|
|
277
|
+
// Tables
|
|
278
|
+
//
|
|
279
|
+
|
|
280
|
+
function serializeAsTable(input: InputWithProperty): ProsemirrorNode {
|
|
281
|
+
const { block, definition, property: tableProperty } = input;
|
|
282
|
+
|
|
283
|
+
const blockItem = BlockParsingUtils.singleBlockItem(block);
|
|
284
|
+
const table = BlockParsingUtils.tablePropertyValue(blockItem, tableProperty.id);
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
type: "tableContainer",
|
|
288
|
+
attrs: {
|
|
289
|
+
...serializeBlockNodeAttributes(input),
|
|
290
|
+
hasBorder: table.showBorder ?? true,
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
content: [
|
|
294
|
+
{
|
|
295
|
+
type: "table",
|
|
296
|
+
content: table.value.map<ProsemirrorNode>((row, rowIndex) => {
|
|
297
|
+
const isHeaderRow = rowIndex === 0 && table.highlightHeaderRow === true;
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
type: "tableRow",
|
|
301
|
+
content: row.cells.map<ProsemirrorNode>((cell, cellIndex) => {
|
|
302
|
+
const isHeaderColumn = cellIndex === 0 && table.highlightHeaderColumn === true;
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
type: isHeaderRow || isHeaderColumn ? "tableHeader" : "tableCell",
|
|
306
|
+
attrs: {
|
|
307
|
+
id: cell.id,
|
|
308
|
+
textAlign: serializeTableCellAlignment(cell.alignment),
|
|
309
|
+
colspan: 1,
|
|
310
|
+
rowspan: 1,
|
|
311
|
+
...(cell.columnWidth && { colwidth: [cell.columnWidth] }),
|
|
312
|
+
},
|
|
313
|
+
content: cell.nodes.map<ProsemirrorNode>(serializeTableNode),
|
|
314
|
+
};
|
|
315
|
+
}),
|
|
316
|
+
};
|
|
317
|
+
}),
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function serializeTableCellAlignment(alignment: PageBlockTableCellAlignment) {
|
|
324
|
+
switch (alignment) {
|
|
325
|
+
case "Left":
|
|
326
|
+
return "left";
|
|
327
|
+
case "Center":
|
|
328
|
+
return "center";
|
|
329
|
+
case "Right":
|
|
330
|
+
return "right";
|
|
331
|
+
|
|
332
|
+
default:
|
|
333
|
+
return "left";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function serializeTableNode(node: PageBlockItemTableNode): ProsemirrorNode {
|
|
338
|
+
switch (node.type) {
|
|
339
|
+
case "RichText":
|
|
340
|
+
return {
|
|
341
|
+
type: "paragraph",
|
|
342
|
+
...serializeRichTextNodePart(node.value),
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
case "Image":
|
|
346
|
+
return {
|
|
347
|
+
type: "image",
|
|
348
|
+
attrs: {
|
|
349
|
+
src: node.value?.url,
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
case "MultiRichText":
|
|
354
|
+
// TODO
|
|
355
|
+
return {
|
|
356
|
+
type: "paragraph",
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
265
361
|
//
|
|
266
362
|
// Embeds
|
|
267
363
|
//
|
|
@@ -361,6 +457,12 @@ function serializeCalloutType(calloutType: PageBlockCalloutType) {
|
|
|
361
457
|
// Rich text
|
|
362
458
|
//
|
|
363
459
|
|
|
460
|
+
function serializeRichTextNodePart(richText: PageBlockText): Pick<ProsemirrorNode, "content"> {
|
|
461
|
+
const spans = serializeRichText(richText);
|
|
462
|
+
if (spans.length) return { content: spans };
|
|
463
|
+
return {};
|
|
464
|
+
}
|
|
465
|
+
|
|
364
466
|
function serializeRichText(richText: PageBlockText) {
|
|
365
467
|
return richText.spans.map(serializeTextSpan).flat();
|
|
366
468
|
}
|
|
@@ -373,7 +475,8 @@ function serializeTextSpan(span: PageBlockTextSpan): ProsemirrorNode[] {
|
|
|
373
475
|
{
|
|
374
476
|
type: "text",
|
|
375
477
|
text: textParts[0],
|
|
376
|
-
|
|
478
|
+
|
|
479
|
+
...(marks.length && { marks: marks }),
|
|
377
480
|
},
|
|
378
481
|
];
|
|
379
482
|
|
|
@@ -385,7 +488,8 @@ function serializeTextSpan(span: PageBlockTextSpan): ProsemirrorNode[] {
|
|
|
385
488
|
{
|
|
386
489
|
type: "text",
|
|
387
490
|
text: textParts[i],
|
|
388
|
-
|
|
491
|
+
|
|
492
|
+
...(marks.length && { marks: marks }),
|
|
389
493
|
}
|
|
390
494
|
);
|
|
391
495
|
}
|
|
@@ -34,6 +34,9 @@ import {
|
|
|
34
34
|
PageBlockItemStorybookValue,
|
|
35
35
|
PageBlockItemColorValue,
|
|
36
36
|
PageBlockItemUntypedValue,
|
|
37
|
+
PageBlockItemTableNode,
|
|
38
|
+
PageBlockItemTableCell,
|
|
39
|
+
PageBlockTableCellAlignment,
|
|
37
40
|
} from "@supernova-studio/model";
|
|
38
41
|
import { PageBlockEditorModel } from "./model/block";
|
|
39
42
|
import { DocumentationPageEditorModel } from "./model/page";
|
|
@@ -90,6 +93,11 @@ export function prosemirrorNodeToBlock(
|
|
|
90
93
|
return parseAsEmbed(prosemirrorNode, definition, embedProperty);
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
const tableProperty = BlockDefinitionUtils.firstTableProperty(definition);
|
|
97
|
+
if (tableProperty) {
|
|
98
|
+
return parseAsTable(prosemirrorNode, definition, tableProperty);
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
if (definition.id === "io.supernova.block.divider") {
|
|
94
102
|
return parseAsDivider(prosemirrorNode, definition);
|
|
95
103
|
}
|
|
@@ -248,6 +256,180 @@ function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribu
|
|
|
248
256
|
// Embed
|
|
249
257
|
//
|
|
250
258
|
|
|
259
|
+
function parseAsTable(
|
|
260
|
+
prosemirrorNode: ProsemirrorNode,
|
|
261
|
+
definition: PageBlockDefinition,
|
|
262
|
+
property: PageBlockDefinitionProperty
|
|
263
|
+
): PageBlockEditorModel {
|
|
264
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
265
|
+
const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
|
|
266
|
+
|
|
267
|
+
const hasBorder = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "hasBorder") !== false;
|
|
268
|
+
|
|
269
|
+
const tableChild = prosemirrorNode.content?.find(c => c.type === "table");
|
|
270
|
+
if (!tableChild) {
|
|
271
|
+
return emptyTable(id, variantId, 0);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Nodes can are considered rows if they have correct node type + their content is non-empty
|
|
275
|
+
const rows = tableChild.content?.filter(c => c.type === "tableRow" && !!c.content?.length) ?? [];
|
|
276
|
+
if (!rows.length) {
|
|
277
|
+
return emptyTable(id, variantId, 0);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const rowHeaderCells = rows[0].content?.filter(c => c.type === "tableHeader").length ?? 0;
|
|
281
|
+
const columnHeaderCells = rows.filter(r => r.content?.[0]?.type === "tableHeader").length;
|
|
282
|
+
|
|
283
|
+
const hasHeaderRow = rows[0].content?.length === rowHeaderCells;
|
|
284
|
+
const hasHeaderColumn = rows.length === columnHeaderCells;
|
|
285
|
+
|
|
286
|
+
const tableValue: PageBlockItemTableValue = {
|
|
287
|
+
showBorder: hasBorder,
|
|
288
|
+
highlightHeaderRow: hasHeaderRow,
|
|
289
|
+
highlightHeaderColumn: hasHeaderColumn,
|
|
290
|
+
value: [],
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
tableValue.value = rows.map(row => {
|
|
294
|
+
return {
|
|
295
|
+
cells: (row.content ?? []).map(parseAsTableCell),
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
id: id,
|
|
301
|
+
data: {
|
|
302
|
+
packageId: "io.supernova.block.table",
|
|
303
|
+
indentLevel: 0,
|
|
304
|
+
|
|
305
|
+
...(variantId && { variantId: variantId }),
|
|
306
|
+
|
|
307
|
+
items: [
|
|
308
|
+
{
|
|
309
|
+
id: id,
|
|
310
|
+
props: {
|
|
311
|
+
table: tableValue,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function parseAsTableCell(prosemirrorNode: ProsemirrorNode): PageBlockItemTableCell {
|
|
320
|
+
const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
|
|
321
|
+
const textAlign = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "textAlign");
|
|
322
|
+
|
|
323
|
+
let columnWidth: number | undefined;
|
|
324
|
+
const columnWidthArray = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "colwidth");
|
|
325
|
+
if (Array.isArray(columnWidthArray) && typeof columnWidthArray[0] === "number") {
|
|
326
|
+
columnWidth = columnWidthArray[0];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const tableNodes = (prosemirrorNode.content ?? []).map(parseAsTableNode).filter(nonNullFilter);
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
id: id,
|
|
333
|
+
alignment: parseTableCellAlignment(textAlign),
|
|
334
|
+
...(columnWidth && { columnWidth }),
|
|
335
|
+
nodes: tableNodes.length ? tableNodes : emptyTableCellContent(),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function parseTableCellAlignment(alignment?: string): PageBlockTableCellAlignment {
|
|
340
|
+
if (!alignment) return "Left";
|
|
341
|
+
|
|
342
|
+
switch (alignment) {
|
|
343
|
+
case "left":
|
|
344
|
+
return "Left";
|
|
345
|
+
case "center":
|
|
346
|
+
return "Center";
|
|
347
|
+
case "right":
|
|
348
|
+
return "Right";
|
|
349
|
+
|
|
350
|
+
default:
|
|
351
|
+
return "Left";
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function parseAsTableNode(prosemirrorNode: ProsemirrorNode): PageBlockItemTableNode | null {
|
|
356
|
+
switch (prosemirrorNode.type) {
|
|
357
|
+
case "paragraph":
|
|
358
|
+
return {
|
|
359
|
+
type: "RichText",
|
|
360
|
+
value: parseRichText(prosemirrorNode.content ?? []),
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
case "image":
|
|
364
|
+
const url = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "src");
|
|
365
|
+
if (!url || typeof url !== "string") return null;
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
type: "Image",
|
|
369
|
+
value: {
|
|
370
|
+
type: "Upload",
|
|
371
|
+
url: url,
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
default:
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function emptyTable(id: string, variantId?: string, indentLevel?: number): PageBlockEditorModel {
|
|
381
|
+
const tableValue: PageBlockItemTableValue = {
|
|
382
|
+
showBorder: true,
|
|
383
|
+
highlightHeaderColumn: false,
|
|
384
|
+
highlightHeaderRow: false,
|
|
385
|
+
value: [],
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
for (let i = 0; i < 2; i++) {
|
|
389
|
+
tableValue.value.push({
|
|
390
|
+
cells: [],
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
for (let j = 0; j < 3; j++) {
|
|
394
|
+
tableValue.value[i].cells.push({
|
|
395
|
+
id: "",
|
|
396
|
+
alignment: "Left",
|
|
397
|
+
nodes: emptyTableCellContent(),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
id: id,
|
|
404
|
+
data: {
|
|
405
|
+
packageId: "io.supernova.block.table",
|
|
406
|
+
indentLevel: indentLevel ?? 0,
|
|
407
|
+
variantId: variantId,
|
|
408
|
+
items: [
|
|
409
|
+
{
|
|
410
|
+
id: id,
|
|
411
|
+
props: {
|
|
412
|
+
table: tableValue,
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function emptyTableCellContent(): PageBlockItemTableNode[] {
|
|
421
|
+
return [
|
|
422
|
+
{
|
|
423
|
+
type: "RichText",
|
|
424
|
+
value: { spans: [] },
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
//
|
|
430
|
+
// Embed
|
|
431
|
+
//
|
|
432
|
+
|
|
251
433
|
function parseAsEmbed(
|
|
252
434
|
prosemirrorNode: ProsemirrorNode,
|
|
253
435
|
definition: PageBlockDefinition,
|
|
@@ -357,6 +539,8 @@ function parseItem(rawItem: unknown, definition: PageBlockDefinition): PageBlock
|
|
|
357
539
|
|
|
358
540
|
if (validationResult.success) {
|
|
359
541
|
sanitizedProps[property.id] = validationResult.data;
|
|
542
|
+
} else {
|
|
543
|
+
console.log(validationResult.error.flatten());
|
|
360
544
|
}
|
|
361
545
|
}
|
|
362
546
|
|
|
@@ -427,7 +611,7 @@ function valueSchemaForPropertyType(type: PageBlockDefinitionPropertyType) {
|
|
|
427
611
|
function parseProsemirrorBlockAttribute(prosemirrorNode: ProsemirrorNode, attributeName: string) {
|
|
428
612
|
const attributeValue = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, attributeName); //prosemirrorNode.attrs?.[attributeName];
|
|
429
613
|
if (!attributeValue) {
|
|
430
|
-
throw new Error(`Attribute ${attributeName} was not defined`);
|
|
614
|
+
throw new Error(`Attribute ${attributeName} was not defined on node ${prosemirrorNode.type}`);
|
|
431
615
|
}
|
|
432
616
|
|
|
433
617
|
return attributeValue;
|
package/src/docs-editor/utils.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
PageBlockItemEmbedValue,
|
|
6
6
|
PageBlockItemMultiRichTextValue,
|
|
7
7
|
PageBlockItemRichTextValue,
|
|
8
|
+
PageBlockItemTableValue,
|
|
8
9
|
PageBlockItemUntypedValue,
|
|
9
10
|
PageBlockItemV2,
|
|
10
11
|
PageBlockText,
|
|
@@ -32,6 +33,13 @@ export const BlockParsingUtils = {
|
|
|
32
33
|
return richText;
|
|
33
34
|
},
|
|
34
35
|
|
|
36
|
+
tablePropertyValue(item: PageBlockItemV2, propertyKey: string): PageBlockItemTableValue {
|
|
37
|
+
const objectValue = this.objectPropertyValue(item, propertyKey);
|
|
38
|
+
const table = PageBlockItemTableValue.parse(objectValue);
|
|
39
|
+
|
|
40
|
+
return table;
|
|
41
|
+
},
|
|
42
|
+
|
|
35
43
|
embedPropertyValue(item: PageBlockItemV2, propertyKey: string): PageBlockItemEmbedValue {
|
|
36
44
|
const objectValue = this.objectPropertyValue(item, propertyKey);
|
|
37
45
|
const embed = PageBlockItemEmbedValue.parse(objectValue);
|
|
@@ -71,7 +79,7 @@ export const BlockDefinitionUtils = {
|
|
|
71
79
|
},
|
|
72
80
|
|
|
73
81
|
firstTableProperty(definition: PageBlockDefinition) {
|
|
74
|
-
const property = definition.item.properties.find(p => p.type === "
|
|
82
|
+
const property = definition.item.properties.find(p => p.type === "Table");
|
|
75
83
|
if (property) return property;
|
|
76
84
|
return undefined;
|
|
77
85
|
},
|