@squiz/formatted-text-editor 1.66.4 → 1.67.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/CHANGELOG.md +14 -0
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +34 -22
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +9 -1
- package/package.json +2 -2
- package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.spec.ts +2 -8
- package/src/utils/converters/mocks/squizNodeJson.mock.ts +8 -9
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +30 -22
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +57 -25
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +7 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.67.1
|
4
|
+
|
5
|
+
### Patch Changes
|
6
|
+
|
7
|
+
- 928cc88: Made children optional for formatted text nodes/tags
|
8
|
+
- Updated dependencies [928cc88]
|
9
|
+
- @squiz/dx-json-schema-lib@1.65.1
|
10
|
+
|
11
|
+
## 1.67.0
|
12
|
+
|
13
|
+
### Minor Changes
|
14
|
+
|
15
|
+
- 6867717: Changed output of FTE to use semantic HTML where applicable
|
16
|
+
|
3
17
|
## 1.66.4
|
4
18
|
|
5
19
|
### Patch Changes
|
@@ -48,12 +48,6 @@ const resolveFontOptions = (node) => {
|
|
48
48
|
const fontOptions = {};
|
49
49
|
node.marks.forEach((mark) => {
|
50
50
|
switch (mark.type.name) {
|
51
|
-
case 'bold':
|
52
|
-
fontOptions.bold = true;
|
53
|
-
break;
|
54
|
-
case 'italic':
|
55
|
-
fontOptions.italics = true;
|
56
|
-
break;
|
57
51
|
case 'underline':
|
58
52
|
fontOptions.underline = true;
|
59
53
|
break;
|
@@ -109,14 +103,7 @@ const transformNode = (node) => {
|
|
109
103
|
};
|
110
104
|
}
|
111
105
|
node.marks.forEach((mark) => {
|
112
|
-
|
113
|
-
case 'bold':
|
114
|
-
case 'italic':
|
115
|
-
case 'underline':
|
116
|
-
break;
|
117
|
-
default:
|
118
|
-
transformedNode = transformMark(mark, transformedNode);
|
119
|
-
}
|
106
|
+
transformedNode = transformMark(mark, transformedNode);
|
120
107
|
});
|
121
108
|
if (node.type.name === Extensions_1.NodeName.Unsupported) {
|
122
109
|
const unsupportedNode = node.attrs?.originalNode;
|
@@ -133,8 +120,10 @@ const transformNode = (node) => {
|
|
133
120
|
*
|
134
121
|
* @return {FormattedNode}
|
135
122
|
*/
|
136
|
-
const wrapNodeIfNeeded = (node, wrappingNode) => {
|
123
|
+
const wrapNodeIfNeeded = (node, wrappingNode, copyFont = true) => {
|
124
|
+
const wrappingNodeChildren = wrappingNode.children || [];
|
137
125
|
if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
|
126
|
+
const nodeChildren = node.children || [];
|
138
127
|
// if the node we are wrapping with is a DOM node, and the node being wrapped is
|
139
128
|
// a plain looking DOM node merge the 2 nodes.
|
140
129
|
return {
|
@@ -148,28 +137,51 @@ const wrapNodeIfNeeded = (node, wrappingNode) => {
|
|
148
137
|
...node.attributes,
|
149
138
|
...wrappingNode.attributes,
|
150
139
|
}),
|
151
|
-
font: (0, undefinedIfEmpty_1.undefinedIfEmpty)(
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
140
|
+
font: (0, undefinedIfEmpty_1.undefinedIfEmpty)(copyFont
|
141
|
+
? {
|
142
|
+
...node.font,
|
143
|
+
...wrappingNode.font,
|
144
|
+
}
|
145
|
+
: {}),
|
146
|
+
children: [...nodeChildren, ...wrappingNodeChildren],
|
156
147
|
};
|
157
148
|
}
|
158
149
|
// if the node we are wrapping or the wrapping nodes are not compatible merge them.
|
159
150
|
return {
|
160
151
|
...wrappingNode,
|
161
|
-
children: [node, ...
|
152
|
+
children: [node, ...wrappingNodeChildren],
|
162
153
|
};
|
163
154
|
};
|
164
155
|
const transformMark = (mark, node) => {
|
165
156
|
switch (mark.type.name) {
|
157
|
+
case 'bold':
|
158
|
+
return wrapNodeIfNeeded(node, {
|
159
|
+
type: 'tag',
|
160
|
+
tag: 'strong',
|
161
|
+
children: [],
|
162
|
+
}, false);
|
163
|
+
case 'italic':
|
164
|
+
return wrapNodeIfNeeded(node, {
|
165
|
+
type: 'tag',
|
166
|
+
tag: 'em',
|
167
|
+
children: [],
|
168
|
+
}, false);
|
169
|
+
case 'underline':
|
170
|
+
return wrapNodeIfNeeded(node, {
|
171
|
+
type: 'tag',
|
172
|
+
tag: 'span',
|
173
|
+
children: [],
|
174
|
+
font: {
|
175
|
+
underline: mark.type.name === 'underline',
|
176
|
+
},
|
177
|
+
});
|
166
178
|
case 'link':
|
167
179
|
return wrapNodeIfNeeded(node, {
|
168
180
|
type: 'tag',
|
169
181
|
tag: 'a',
|
170
182
|
attributes: transformAttributes(mark.attrs),
|
171
183
|
children: [],
|
172
|
-
});
|
184
|
+
}, false);
|
173
185
|
case 'assetLink':
|
174
186
|
return wrapNodeIfNeeded(node, {
|
175
187
|
type: 'link-to-matrix-asset',
|
@@ -24,7 +24,9 @@ const getNodeType = (node) => {
|
|
24
24
|
ul: 'bulletList',
|
25
25
|
hr: 'horizontalRule',
|
26
26
|
a: Extensions_1.NodeName.Text,
|
27
|
+
em: Extensions_1.NodeName.Text,
|
27
28
|
span: Extensions_1.NodeName.Text,
|
29
|
+
strong: Extensions_1.NodeName.Text,
|
28
30
|
code: Extensions_1.NodeName.CodeBlock,
|
29
31
|
};
|
30
32
|
if (typeMap[node.type]) {
|
@@ -96,6 +98,12 @@ const getNodeMarks = (node) => {
|
|
96
98
|
},
|
97
99
|
});
|
98
100
|
}
|
101
|
+
else if (node.type === 'tag' && node.tag === 'strong') {
|
102
|
+
marks.push({ type: 'bold' });
|
103
|
+
}
|
104
|
+
else if (node.type === 'tag' && node.tag === 'em') {
|
105
|
+
marks.push({ type: 'italic' });
|
106
|
+
}
|
99
107
|
// Handle font formatting
|
100
108
|
if ('font' in node && node.font !== undefined) {
|
101
109
|
for (const [type, enabled] of Object.entries(node.font)) {
|
@@ -125,7 +133,7 @@ const unwrapNodeIfNeeded = (node) => {
|
|
125
133
|
const formatNode = (node) => {
|
126
134
|
const children = [];
|
127
135
|
if ('children' in node) {
|
128
|
-
node.children
|
136
|
+
node.children?.forEach((child) => {
|
129
137
|
children.push(...formatNode(child));
|
130
138
|
});
|
131
139
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/formatted-text-editor",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.67.1",
|
4
4
|
"main": "lib/index.js",
|
5
5
|
"types": "lib/index.d.ts",
|
6
6
|
"private": false,
|
@@ -23,7 +23,7 @@
|
|
23
23
|
"@headlessui/react": "1.7.11",
|
24
24
|
"@mui/icons-material": "5.11.16",
|
25
25
|
"@remirror/react": "2.0.25",
|
26
|
-
"@squiz/dx-json-schema-lib": "^1.65.
|
26
|
+
"@squiz/dx-json-schema-lib": "^1.65.1",
|
27
27
|
"@squiz/resource-browser": "^1.66.3",
|
28
28
|
"clsx": "1.2.1",
|
29
29
|
"react-hook-form": "7.43.2",
|
@@ -74,16 +74,13 @@ describe('htmlToSquizNode', () => {
|
|
74
74
|
children: [
|
75
75
|
{
|
76
76
|
type: 'tag',
|
77
|
-
tag: '
|
77
|
+
tag: 'em',
|
78
78
|
children: [
|
79
79
|
{
|
80
80
|
type: 'text',
|
81
81
|
value: 'Italicised text',
|
82
82
|
},
|
83
83
|
],
|
84
|
-
font: {
|
85
|
-
italics: true,
|
86
|
-
},
|
87
84
|
},
|
88
85
|
{
|
89
86
|
type: 'text',
|
@@ -91,16 +88,13 @@ describe('htmlToSquizNode', () => {
|
|
91
88
|
},
|
92
89
|
{
|
93
90
|
type: 'tag',
|
94
|
-
tag: '
|
91
|
+
tag: 'strong',
|
95
92
|
children: [
|
96
93
|
{
|
97
94
|
type: 'text',
|
98
95
|
value: 'Bold text',
|
99
96
|
},
|
100
97
|
],
|
101
|
-
font: {
|
102
|
-
bold: true,
|
103
|
-
},
|
104
98
|
},
|
105
99
|
],
|
106
100
|
tag: 'p',
|
@@ -116,7 +116,6 @@ export const sharedNodeExamples: NodeExample[] = [
|
|
116
116
|
type: 'text',
|
117
117
|
text: 'Hello',
|
118
118
|
marks: [
|
119
|
-
{ type: 'bold' },
|
120
119
|
{
|
121
120
|
type: 'assetLink',
|
122
121
|
attrs: {
|
@@ -126,20 +125,20 @@ export const sharedNodeExamples: NodeExample[] = [
|
|
126
125
|
matrixIdentifier: 'matrix-api-identifier',
|
127
126
|
},
|
128
127
|
},
|
128
|
+
{ type: 'bold' },
|
129
129
|
],
|
130
130
|
},
|
131
131
|
squizNode: [
|
132
132
|
{
|
133
|
-
type: '
|
134
|
-
|
135
|
-
matrixAssetId: '123',
|
136
|
-
matrixDomain: 'https://my-matrix.squiz.net',
|
137
|
-
matrixIdentifier: 'matrix-api-identifier',
|
133
|
+
type: 'tag',
|
134
|
+
tag: 'strong',
|
138
135
|
children: [
|
139
136
|
{
|
140
|
-
type: '
|
141
|
-
|
142
|
-
|
137
|
+
type: 'link-to-matrix-asset',
|
138
|
+
target: '_blank',
|
139
|
+
matrixAssetId: '123',
|
140
|
+
matrixDomain: 'https://my-matrix.squiz.net',
|
141
|
+
matrixIdentifier: 'matrix-api-identifier',
|
143
142
|
children: [{ type: 'text', value: 'Hello' }],
|
144
143
|
},
|
145
144
|
],
|
@@ -329,10 +329,7 @@ describe('remirrorNodeToSquizNode', () => {
|
|
329
329
|
value: ' click',
|
330
330
|
},
|
331
331
|
],
|
332
|
-
|
333
|
-
bold: true,
|
334
|
-
},
|
335
|
-
tag: 'span',
|
332
|
+
tag: 'strong',
|
336
333
|
type: 'tag',
|
337
334
|
},
|
338
335
|
{
|
@@ -342,18 +339,22 @@ describe('remirrorNodeToSquizNode', () => {
|
|
342
339
|
{
|
343
340
|
children: [
|
344
341
|
{
|
345
|
-
type: '
|
346
|
-
|
342
|
+
type: 'tag',
|
343
|
+
tag: 'a',
|
344
|
+
children: [
|
345
|
+
{
|
346
|
+
type: 'text',
|
347
|
+
value: 'here',
|
348
|
+
},
|
349
|
+
],
|
350
|
+
attributes: {
|
351
|
+
href: 'https://www.google.com/',
|
352
|
+
title: '',
|
353
|
+
},
|
347
354
|
},
|
348
355
|
],
|
349
|
-
|
350
|
-
|
351
|
-
title: '',
|
352
|
-
},
|
353
|
-
font: {
|
354
|
-
italics: true,
|
355
|
-
},
|
356
|
-
tag: 'a',
|
356
|
+
|
357
|
+
tag: 'em',
|
357
358
|
type: 'tag',
|
358
359
|
},
|
359
360
|
{
|
@@ -363,10 +364,7 @@ describe('remirrorNodeToSquizNode', () => {
|
|
363
364
|
value: ' ',
|
364
365
|
},
|
365
366
|
],
|
366
|
-
|
367
|
-
italics: true,
|
368
|
-
},
|
369
|
-
tag: 'span',
|
367
|
+
tag: 'em',
|
370
368
|
type: 'tag',
|
371
369
|
},
|
372
370
|
{
|
@@ -376,13 +374,23 @@ describe('remirrorNodeToSquizNode', () => {
|
|
376
374
|
{
|
377
375
|
children: [
|
378
376
|
{
|
379
|
-
type: '
|
380
|
-
|
377
|
+
type: 'tag',
|
378
|
+
tag: 'em',
|
379
|
+
children: [
|
380
|
+
{
|
381
|
+
type: 'tag',
|
382
|
+
tag: 'strong',
|
383
|
+
children: [
|
384
|
+
{
|
385
|
+
type: 'text',
|
386
|
+
value: ' GOOGLE!',
|
387
|
+
},
|
388
|
+
],
|
389
|
+
},
|
390
|
+
],
|
381
391
|
},
|
382
392
|
],
|
383
393
|
font: {
|
384
|
-
bold: true,
|
385
|
-
italics: true,
|
386
394
|
underline: true,
|
387
395
|
},
|
388
396
|
tag: 'span',
|
@@ -13,7 +13,7 @@ type FontOptions = FormattedTextModels.v1.FormattedNodeFontProperties;
|
|
13
13
|
type FormattedText = FormattedTextModels.v1.FormattedText;
|
14
14
|
type FormattedNode = FormattedTextModels.v1.FormattedNodes;
|
15
15
|
type FormattedNodeFontProperties = FormattedTextModels.v1.FormattedNodeFontProperties;
|
16
|
-
type FormattedNodeWithChildren = Extract<FormattedNode, { children
|
16
|
+
type FormattedNodeWithChildren = Extract<FormattedNode, { children?: FormattedNode[] }>;
|
17
17
|
type RemirrorTextAlignment = Exclude<Remirror.Attributes['nodeTextAlignment'], undefined>;
|
18
18
|
type FormattedTextAlignment = FormattingOptions['alignment'];
|
19
19
|
|
@@ -71,12 +71,6 @@ const resolveFontOptions = (node: ProsemirrorNode): FormattedNodeFontProperties
|
|
71
71
|
|
72
72
|
node.marks.forEach((mark) => {
|
73
73
|
switch (mark.type.name) {
|
74
|
-
case 'bold':
|
75
|
-
fontOptions.bold = true;
|
76
|
-
break;
|
77
|
-
case 'italic':
|
78
|
-
fontOptions.italics = true;
|
79
|
-
break;
|
80
74
|
case 'underline':
|
81
75
|
fontOptions.underline = true;
|
82
76
|
break;
|
@@ -145,14 +139,7 @@ const transformNode = (node: ProsemirrorNode): FormattedNode => {
|
|
145
139
|
}
|
146
140
|
|
147
141
|
node.marks.forEach((mark) => {
|
148
|
-
|
149
|
-
case 'bold':
|
150
|
-
case 'italic':
|
151
|
-
case 'underline':
|
152
|
-
break;
|
153
|
-
default:
|
154
|
-
transformedNode = transformMark(mark, transformedNode);
|
155
|
-
}
|
142
|
+
transformedNode = transformMark(mark, transformedNode);
|
156
143
|
});
|
157
144
|
|
158
145
|
if (node.type.name === NodeName.Unsupported) {
|
@@ -172,8 +159,16 @@ const transformNode = (node: ProsemirrorNode): FormattedNode => {
|
|
172
159
|
*
|
173
160
|
* @return {FormattedNode}
|
174
161
|
*/
|
175
|
-
const wrapNodeIfNeeded = (
|
162
|
+
const wrapNodeIfNeeded = (
|
163
|
+
node: FormattedNode,
|
164
|
+
wrappingNode: FormattedNodeWithChildren,
|
165
|
+
copyFont: boolean = true,
|
166
|
+
): FormattedNode => {
|
167
|
+
const wrappingNodeChildren = wrappingNode.children || [];
|
168
|
+
|
176
169
|
if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
|
170
|
+
const nodeChildren = node.children || [];
|
171
|
+
|
177
172
|
// if the node we are wrapping with is a DOM node, and the node being wrapped is
|
178
173
|
// a plain looking DOM node merge the 2 nodes.
|
179
174
|
return {
|
@@ -187,30 +182,67 @@ const wrapNodeIfNeeded = (node: FormattedNode, wrappingNode: FormattedNodeWithCh
|
|
187
182
|
...node.attributes,
|
188
183
|
...wrappingNode.attributes,
|
189
184
|
}),
|
190
|
-
font: undefinedIfEmpty(
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
185
|
+
font: undefinedIfEmpty(
|
186
|
+
copyFont
|
187
|
+
? {
|
188
|
+
...node.font,
|
189
|
+
...wrappingNode.font,
|
190
|
+
}
|
191
|
+
: {},
|
192
|
+
),
|
193
|
+
children: [...nodeChildren, ...wrappingNodeChildren],
|
195
194
|
};
|
196
195
|
}
|
197
196
|
|
198
197
|
// if the node we are wrapping or the wrapping nodes are not compatible merge them.
|
199
198
|
return {
|
200
199
|
...wrappingNode,
|
201
|
-
children: [node, ...
|
200
|
+
children: [node, ...wrappingNodeChildren],
|
202
201
|
};
|
203
202
|
};
|
204
203
|
|
205
204
|
const transformMark = (mark: Mark, node: FormattedNode): FormattedNode => {
|
206
205
|
switch (mark.type.name) {
|
207
|
-
case '
|
206
|
+
case 'bold':
|
207
|
+
return wrapNodeIfNeeded(
|
208
|
+
node,
|
209
|
+
{
|
210
|
+
type: 'tag',
|
211
|
+
tag: 'strong',
|
212
|
+
children: [],
|
213
|
+
},
|
214
|
+
false,
|
215
|
+
);
|
216
|
+
case 'italic':
|
217
|
+
return wrapNodeIfNeeded(
|
218
|
+
node,
|
219
|
+
{
|
220
|
+
type: 'tag',
|
221
|
+
tag: 'em',
|
222
|
+
children: [],
|
223
|
+
},
|
224
|
+
false,
|
225
|
+
);
|
226
|
+
case 'underline':
|
208
227
|
return wrapNodeIfNeeded(node, {
|
209
228
|
type: 'tag',
|
210
|
-
tag: '
|
211
|
-
attributes: transformAttributes(mark.attrs),
|
229
|
+
tag: 'span',
|
212
230
|
children: [],
|
231
|
+
font: {
|
232
|
+
underline: mark.type.name === 'underline',
|
233
|
+
},
|
213
234
|
});
|
235
|
+
case 'link':
|
236
|
+
return wrapNodeIfNeeded(
|
237
|
+
node,
|
238
|
+
{
|
239
|
+
type: 'tag',
|
240
|
+
tag: 'a',
|
241
|
+
attributes: transformAttributes(mark.attrs),
|
242
|
+
children: [],
|
243
|
+
},
|
244
|
+
false,
|
245
|
+
);
|
214
246
|
case 'assetLink':
|
215
247
|
return wrapNodeIfNeeded(node, {
|
216
248
|
type: 'link-to-matrix-asset',
|
@@ -29,7 +29,9 @@ const getNodeType = (node: FormattedNodes): string => {
|
|
29
29
|
ul: 'bulletList',
|
30
30
|
hr: 'horizontalRule',
|
31
31
|
a: NodeName.Text,
|
32
|
+
em: NodeName.Text,
|
32
33
|
span: NodeName.Text,
|
34
|
+
strong: NodeName.Text,
|
33
35
|
code: NodeName.CodeBlock,
|
34
36
|
};
|
35
37
|
|
@@ -105,6 +107,10 @@ const getNodeMarks = (node: FormattedNodes): ObjectMark[] => {
|
|
105
107
|
target: node.target,
|
106
108
|
},
|
107
109
|
});
|
110
|
+
} else if (node.type === 'tag' && node.tag === 'strong') {
|
111
|
+
marks.push({ type: 'bold' });
|
112
|
+
} else if (node.type === 'tag' && node.tag === 'em') {
|
113
|
+
marks.push({ type: 'italic' });
|
108
114
|
}
|
109
115
|
|
110
116
|
// Handle font formatting
|
@@ -140,7 +146,7 @@ const formatNode = (node: FormattedNodes): RemirrorJSON[] => {
|
|
140
146
|
const children: RemirrorJSON[] = [];
|
141
147
|
|
142
148
|
if ('children' in node) {
|
143
|
-
node.children
|
149
|
+
node.children?.forEach((child: FormattedNodes) => {
|
144
150
|
children.push(...formatNode(child));
|
145
151
|
});
|
146
152
|
}
|