Skip to content

Commit 5fb49fe

Browse files
committed
fix(json-crdt): 🐛 add behavior to attrs of imported node
1 parent 3ec34f2 commit 5fb49fe

File tree

3 files changed

+75
-13
lines changed

3 files changed

+75
-13
lines changed

src/json-crdt-extensions/peritext/registry/SliceRegistry.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import {SliceBehavior} from '../slice/constants';
2+
import {CommonSliceType} from '../slice';
13
import type {PeritextMlElement} from '../block/types';
24
import type {NodeBuilder} from '../../../json-crdt-patch';
3-
import {SliceBehavior} from '../slice/constants';
45
import type {JsonMlElement} from 'very-small-parser/lib/html/json-ml/types';
56
import type {FromHtmlConverter, SliceTypeDefinition, ToHtmlConverter} from './types';
67

8+
/**
9+
* @todo Consider moving the registry under the `/transfer` directory.
10+
*/
711
export class SliceRegistry {
812
private map: Map<string | number, SliceTypeDefinition<any, any, any>> = new Map();
913
private toHtmlMap: Map<string | number, ToHtmlConverter<any>> = new Map();
@@ -14,17 +18,22 @@ export class SliceRegistry {
1418
def: SliceTypeDefinition<Type, Schema, Inline>,
1519
): void {
1620
const {type, toHtml, fromHtml} = def;
17-
this.map.set(type, def);
21+
const fromHtmlMap = this.fromHtmlMap;
1822
if (toHtml) this.toHtmlMap.set(type, toHtml);
1923
if (fromHtml) {
20-
const fromHtmlMap = this.fromHtmlMap;
2124
for (const htmlTag in fromHtml) {
2225
const converter = fromHtml[htmlTag];
2326
const converters = fromHtmlMap.get(htmlTag) ?? [];
2427
converters.push([def, converter]);
2528
fromHtmlMap.set(htmlTag, converters);
2629
}
2730
}
31+
const tag = CommonSliceType[type as any];
32+
if (tag && typeof tag === 'string') {
33+
fromHtmlMap.set(tag, [
34+
[def, () => [type, null]]
35+
])
36+
}
2837
}
2938

3039
public def<Type extends number | string, Schema extends NodeBuilder, Inline extends boolean = true>(

src/json-crdt-extensions/peritext/transfer/__tests__/import-html.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,57 @@ describe('.fromHtml()', () => {
3333
],
3434
]);
3535
});
36+
37+
test('can import a single <blockquote> block', () => {
38+
const html = '<blockquote>2b||!2b</blockquote>';
39+
const peritextMl = fromHtml(html);
40+
expect(peritextMl).toEqual(['', null, [CommonSliceType.blockquote, null, '2b||!2b']]);
41+
});
42+
43+
test('can import a single <blockquote> block with nested single <p>', () => {
44+
const html = '<blockquote><p>2b||!2b</p></blockquote>';
45+
const peritextMl = fromHtml(html);
46+
expect(peritextMl).toEqual(['', null,
47+
[CommonSliceType.blockquote, null,
48+
[CommonSliceType.p, null, '2b||!2b']
49+
],
50+
]);
51+
});
52+
53+
test('can import a single <blockquote> block after a <p> block', () => {
54+
const html = '<p>123</p><blockquote>2b||!2b</blockquote>';
55+
const peritextMl = fromHtml(html);
56+
expect(peritextMl).toEqual(['', null,
57+
[CommonSliceType.p, null, '123'],
58+
[CommonSliceType.blockquote, null, '2b||!2b'],
59+
]);
60+
});
61+
62+
test('can import a single <blockquote> block with nested single <p>, after a <p> block', () => {
63+
const html = '<p>123</p><blockquote><p>2b||!2b</p></blockquote>';
64+
const peritextMl = fromHtml(html);
65+
expect(peritextMl).toEqual(['', null,
66+
[CommonSliceType.p, null, '123'],
67+
[CommonSliceType.blockquote, null,
68+
[CommonSliceType.p, null, '2b||!2b']
69+
],
70+
]);
71+
});
72+
73+
test('can import a single <blockquote> block with nested single <p>, after a <p> block with inline formatting', () => {
74+
const html = '<p><b>1</b><code>2</code>3</p><blockquote><p>2b||!2b</p></blockquote>';
75+
const peritextMl = fromHtml(html);
76+
expect(peritextMl).toEqual(['', null,
77+
[CommonSliceType.p, null,
78+
[CommonSliceType.b, {behavior: SliceBehavior.One, inline: true}, '1'],
79+
[CommonSliceType.code, {behavior: SliceBehavior.One, inline: true}, '2'],
80+
'3'
81+
],
82+
[CommonSliceType.blockquote, null,
83+
[CommonSliceType.p, null, '2b||!2b']
84+
],
85+
]);
86+
});
3687
});
3788

3889
describe('.toViewRange()', () => {

src/json-crdt-extensions/peritext/transfer/import-html.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ import type {SliceRegistry} from '../registry/SliceRegistry';
1414
import type {ViewStyle, ViewRange, ViewSlice} from '../editor/types';
1515
import type {ClipboardData} from './export-html';
1616

17+
/**
18+
* @todo Implement HTML normalization function, ensure that:
19+
*
20+
* - <blockquote> and <p> nodes are treated correctly, especially when sole node
21+
* is nested.
22+
* - list nodes are treated correctly.
23+
* - <svg> nodes are converted to Base64 and inlined as data URL images.
24+
*/
25+
1726
/**
1827
* Flattens a {@link PeritextMlNode} tree structure into a {@link ViewRange}
1928
* flat string with annotation ranges.
@@ -115,7 +124,7 @@ export const fromJsonMl = (jsonml: JsonMlNode, registry: SliceRegistry = default
115124
node[0] = res[0];
116125
node[1] = res[1];
117126
} else {
118-
node[0] = SliceTypeName[tag as any] ?? tag;
127+
if (typeof tag === 'string') node[0] = SliceTypeName[tag as any] ?? tag;
119128
const attr = jsonml[1] || {};
120129
let data = null;
121130
if (attr['data-attr'] !== void 0) {
@@ -126,15 +135,8 @@ export const fromJsonMl = (jsonml: JsonMlNode, registry: SliceRegistry = default
126135
const inline = inlineHtmlTag || attr['data-inline'] === 'true';
127136
if (data || inline) node[1] = {data, inline};
128137
}
129-
if (typeof node[0] === 'number' && node[0] < 0) {
130-
const attr = node[1] || {};
131-
attr.inline = true;
132-
node[1] = attr;
133-
}
134-
if (node.length < 3) {
135-
const attr = node[1] || {};
136-
if (attr.inline) return '';
137-
}
138+
if (typeof node[0] === 'number' && node[0] < 0) (node[1] ||= {}).inline = true;
139+
if (node.length < 3 && (node[1] || {}).inline) return '';
138140
return node;
139141
};
140142

0 commit comments

Comments
 (0)