lkb-fields-document 1.0.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.
Files changed (112) hide show
  1. package/component-blocks/dist/lkb-fields-document-component-blocks.cjs.d.ts +2 -0
  2. package/component-blocks/dist/lkb-fields-document-component-blocks.cjs.js +306 -0
  3. package/component-blocks/dist/lkb-fields-document-component-blocks.esm.js +300 -0
  4. package/component-blocks/dist/lkb-fields-document-component-blocks.node.cjs.js +306 -0
  5. package/component-blocks/dist/lkb-fields-document-component-blocks.node.esm.js +300 -0
  6. package/component-blocks/package.json +4 -0
  7. package/dist/Cell-0ac0ac66.node.cjs.js +21 -0
  8. package/dist/Cell-242f7404.esm.js +17 -0
  9. package/dist/Cell-3103f73d.node.esm.js +17 -0
  10. package/dist/Cell-bfb56d74.cjs.js +21 -0
  11. package/dist/Field-0e0f75ed.node.cjs.js +1628 -0
  12. package/dist/Field-28177061.cjs.js +1628 -0
  13. package/dist/Field-35b79e6b.node.esm.js +1619 -0
  14. package/dist/Field-92d13205.esm.js +1619 -0
  15. package/dist/api-2f524611.esm.js +502 -0
  16. package/dist/api-73636987.cjs.js +506 -0
  17. package/dist/api-8e2b20b8.node.cjs.js +506 -0
  18. package/dist/api-c32e360e.node.esm.js +502 -0
  19. package/dist/callout-ui-2aded278.cjs.js +131 -0
  20. package/dist/callout-ui-3e5ca544.node.esm.js +126 -0
  21. package/dist/callout-ui-8b5f2376.esm.js +126 -0
  22. package/dist/callout-ui-ad50f301.node.cjs.js +131 -0
  23. package/dist/declarations/src/component-blocks.d.ts +4 -0
  24. package/dist/declarations/src/component-blocks.d.ts.map +1 -0
  25. package/dist/declarations/src/document-editor/component-blocks/api.d.ts +120 -0
  26. package/dist/declarations/src/document-editor/component-blocks/api.d.ts.map +1 -0
  27. package/dist/declarations/src/document-editor/component-blocks/types.d.ts +241 -0
  28. package/dist/declarations/src/document-editor/component-blocks/types.d.ts.map +1 -0
  29. package/dist/declarations/src/document-editor/toolset/relationship/relationship-shared.d.ts +10 -0
  30. package/dist/declarations/src/document-editor/toolset/relationship/relationship-shared.d.ts.map +1 -0
  31. package/dist/declarations/src/index.d.ts +7 -0
  32. package/dist/declarations/src/index.d.ts.map +1 -0
  33. package/dist/declarations/src/my-component-blocks/index.d.ts +46 -0
  34. package/dist/declarations/src/my-component-blocks/index.d.ts.map +1 -0
  35. package/dist/declarations/src/structure/Cell.d.ts +5 -0
  36. package/dist/declarations/src/structure/Cell.d.ts.map +1 -0
  37. package/dist/declarations/src/structure/Field.d.ts +5 -0
  38. package/dist/declarations/src/structure/Field.d.ts.map +1 -0
  39. package/dist/declarations/src/structure/controller.d.ts +10 -0
  40. package/dist/declarations/src/structure/controller.d.ts.map +1 -0
  41. package/dist/declarations/src/structure/structure.d.ts +4 -0
  42. package/dist/declarations/src/structure/structure.d.ts.map +1 -0
  43. package/dist/declarations/src/structure-views.d.ts +5 -0
  44. package/dist/declarations/src/structure-views.d.ts.map +1 -0
  45. package/dist/declarations/src/types/DocumentFeatures.d.ts +33 -0
  46. package/dist/declarations/src/types/DocumentFeatures.d.ts.map +1 -0
  47. package/dist/declarations/src/types/DocumentFieldConfig.d.ts +18 -0
  48. package/dist/declarations/src/types/DocumentFieldConfig.d.ts.map +1 -0
  49. package/dist/declarations/src/types/FormattingConfig.d.ts +28 -0
  50. package/dist/declarations/src/types/FormattingConfig.d.ts.map +1 -0
  51. package/dist/declarations/src/types/RelationshipsConfig.d.ts +9 -0
  52. package/dist/declarations/src/types/RelationshipsConfig.d.ts.map +1 -0
  53. package/dist/declarations/src/types/StructureFieldConfig.d.ts +10 -0
  54. package/dist/declarations/src/types/StructureFieldConfig.d.ts.map +1 -0
  55. package/dist/declarations/src/validation/structure-validation.d.ts +218 -0
  56. package/dist/declarations/src/validation/structure-validation.d.ts.map +1 -0
  57. package/dist/declarations/src/views/Cell.d.ts +5 -0
  58. package/dist/declarations/src/views/Cell.d.ts.map +1 -0
  59. package/dist/declarations/src/views/Field.d.ts +5 -0
  60. package/dist/declarations/src/views/Field.d.ts.map +1 -0
  61. package/dist/declarations/src/views/controller.d.ts +15 -0
  62. package/dist/declarations/src/views/controller.d.ts.map +1 -0
  63. package/dist/declarations/src/views/document.d.ts +4 -0
  64. package/dist/declarations/src/views/document.d.ts.map +1 -0
  65. package/dist/declarations/src/views.d.ts +7 -0
  66. package/dist/declarations/src/views.d.ts.map +1 -0
  67. package/dist/editor-shared-a6e340e6.node.esm.js +1993 -0
  68. package/dist/editor-shared-a997ae98.node.cjs.js +2007 -0
  69. package/dist/editor-shared-cc1293ed.cjs.js +2007 -0
  70. package/dist/editor-shared-da518ba3.esm.js +1993 -0
  71. package/dist/form-from-preview-2042b9ef.cjs.js +512 -0
  72. package/dist/form-from-preview-5df6e492.node.esm.js +508 -0
  73. package/dist/form-from-preview-9e501058.node.cjs.js +512 -0
  74. package/dist/form-from-preview-b3a66f37.esm.js +508 -0
  75. package/dist/index-06c36775.cjs.js +14 -0
  76. package/dist/index-586adb8f.node.esm.js +11 -0
  77. package/dist/index-67d52357.esm.js +11 -0
  78. package/dist/index-c3223fdc.node.cjs.js +14 -0
  79. package/dist/layouts-6412fa2a.esm.js +189 -0
  80. package/dist/layouts-a4a3cf0b.node.cjs.js +196 -0
  81. package/dist/layouts-ba9a558b.cjs.js +196 -0
  82. package/dist/layouts-e653b908.node.esm.js +189 -0
  83. package/dist/lkb-fields-document.cjs.d.ts +2 -0
  84. package/dist/lkb-fields-document.cjs.js +1167 -0
  85. package/dist/lkb-fields-document.esm.js +1162 -0
  86. package/dist/lkb-fields-document.node.cjs.js +1167 -0
  87. package/dist/lkb-fields-document.node.esm.js +1162 -0
  88. package/dist/shared-0533009e.cjs.js +594 -0
  89. package/dist/shared-4684cc24.node.cjs.js +594 -0
  90. package/dist/shared-5e864055.node.esm.js +579 -0
  91. package/dist/shared-aaba5901.esm.js +579 -0
  92. package/dist/toolbar-state-3359e2f3.cjs.js +994 -0
  93. package/dist/toolbar-state-945823b8.node.esm.js +971 -0
  94. package/dist/toolbar-state-9611743f.node.cjs.js +994 -0
  95. package/dist/toolbar-state-bc8fe661.esm.js +971 -0
  96. package/dist/utils-06bcddc4.node.cjs.js +747 -0
  97. package/dist/utils-200ff260.node.esm.js +722 -0
  98. package/dist/utils-6409f730.cjs.js +747 -0
  99. package/dist/utils-bc6a0b82.esm.js +722 -0
  100. package/package.json +118 -0
  101. package/structure-views/dist/lkb-fields-document-structure-views.cjs.d.ts +2 -0
  102. package/structure-views/dist/lkb-fields-document-structure-views.cjs.js +138 -0
  103. package/structure-views/dist/lkb-fields-document-structure-views.esm.js +131 -0
  104. package/structure-views/dist/lkb-fields-document-structure-views.node.cjs.js +138 -0
  105. package/structure-views/dist/lkb-fields-document-structure-views.node.esm.js +131 -0
  106. package/structure-views/package.json +4 -0
  107. package/views/dist/lkb-fields-document-views.cjs.d.ts +2 -0
  108. package/views/dist/lkb-fields-document-views.cjs.js +114 -0
  109. package/views/dist/lkb-fields-document-views.esm.js +95 -0
  110. package/views/dist/lkb-fields-document-views.node.cjs.js +114 -0
  111. package/views/dist/lkb-fields-document-views.node.esm.js +95 -0
  112. package/views/package.json +4 -0
@@ -0,0 +1,2007 @@
1
+ 'use strict';
2
+
3
+ var slate = require('slate');
4
+ var slateHistory = require('slate-history');
5
+ var weakMemoize = require('@emotion/weak-memoize');
6
+ var utils = require('./utils-06bcddc4.node.cjs.js');
7
+ var shared = require('./shared-4684cc24.node.cjs.js');
8
+ var index = require('./index-c3223fdc.node.cjs.js');
9
+ var mdASTUtilFromMarkdown = require('mdast-util-from-markdown');
10
+ var autoLinkLiteralFromMarkdownExtension = require('mdast-util-gfm-autolink-literal/from-markdown');
11
+ var autoLinkLiteralMarkdownSyntax = require('micromark-extension-gfm-autolink-literal');
12
+ var gfmStrikethroughFromMarkdownExtension = require('mdast-util-gfm-strikethrough/from-markdown');
13
+ var gfmStrikethroughMarkdownSyntax = require('micromark-extension-gfm-strikethrough');
14
+
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
16
+
17
+ var weakMemoize__default = /*#__PURE__*/_interopDefault(weakMemoize);
18
+ var mdASTUtilFromMarkdown__default = /*#__PURE__*/_interopDefault(mdASTUtilFromMarkdown);
19
+ var autoLinkLiteralFromMarkdownExtension__default = /*#__PURE__*/_interopDefault(autoLinkLiteralFromMarkdownExtension);
20
+ var autoLinkLiteralMarkdownSyntax__default = /*#__PURE__*/_interopDefault(autoLinkLiteralMarkdownSyntax);
21
+ var gfmStrikethroughFromMarkdownExtension__default = /*#__PURE__*/_interopDefault(gfmStrikethroughFromMarkdownExtension);
22
+ var gfmStrikethroughMarkdownSyntax__default = /*#__PURE__*/_interopDefault(gfmStrikethroughMarkdownSyntax);
23
+
24
+ function getAncestorComponentBlock(editor) {
25
+ if (editor.selection) {
26
+ const ancestorEntry = slate.Editor.above(editor, {
27
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node) && node.type !== 'paragraph'
28
+ });
29
+ if (ancestorEntry && (ancestorEntry[0].type === 'component-block-prop' || ancestorEntry[0].type === 'component-inline-prop')) {
30
+ return {
31
+ isInside: true,
32
+ componentBlock: slate.Editor.parent(editor, ancestorEntry[1]),
33
+ prop: ancestorEntry
34
+ };
35
+ }
36
+ }
37
+ return {
38
+ isInside: false
39
+ };
40
+ }
41
+ const alreadyNormalizedThings = new WeakMap();
42
+ function normalizeNodeWithinComponentProp([node, path], editor, fieldOptions, relationships) {
43
+ let alreadyNormalizedNodes = alreadyNormalizedThings.get(fieldOptions);
44
+ if (!alreadyNormalizedNodes) {
45
+ alreadyNormalizedNodes = new WeakSet();
46
+ alreadyNormalizedThings.set(fieldOptions, alreadyNormalizedNodes);
47
+ }
48
+ if (alreadyNormalizedNodes.has(node)) return false;
49
+ let didNormalization = false;
50
+ if (fieldOptions.inlineMarks !== 'inherit' && slate.Text.isText(node)) {
51
+ didNormalization = shared.normalizeTextBasedOnInlineMarksAndSoftBreaks([node, path], editor, fieldOptions.inlineMarks, fieldOptions.softBreaks);
52
+ }
53
+ if (slate.Element.isElement(node)) {
54
+ for (const [i, child] of node.children.entries()) {
55
+ if (normalizeNodeWithinComponentProp([child, [...path, i]], editor, fieldOptions, relationships)) {
56
+ return true;
57
+ }
58
+ }
59
+ if (fieldOptions.kind === 'block') {
60
+ if (node.type === 'component-block') {
61
+ if (!fieldOptions.componentBlocks) {
62
+ slate.Transforms.unwrapNodes(editor, {
63
+ at: path
64
+ });
65
+ didNormalization = true;
66
+ }
67
+ } else {
68
+ didNormalization = shared.normalizeElementBasedOnDocumentFeatures([node, path], editor, fieldOptions.documentFeatures, relationships);
69
+ }
70
+ } else {
71
+ didNormalization = shared.normalizeInlineBasedOnLinksAndRelationships([node, path], editor, fieldOptions.documentFeatures.links, fieldOptions.documentFeatures.relationships, relationships);
72
+ }
73
+ }
74
+ if (didNormalization === false) {
75
+ alreadyNormalizedNodes.add(node);
76
+ }
77
+ return didNormalization;
78
+ }
79
+ function canSchemaContainChildField(rootSchema) {
80
+ const queue = new Set([rootSchema]);
81
+ for (const schema of queue) {
82
+ if (schema.kind === 'form' || schema.kind === 'relationship') continue;
83
+ if (schema.kind === 'child') return true;
84
+ if (schema.kind === 'array') {
85
+ queue.add(schema.element);
86
+ continue;
87
+ }
88
+ if (schema.kind === 'object') {
89
+ for (const innerProp of Object.values(schema.fields)) {
90
+ queue.add(innerProp);
91
+ }
92
+ continue;
93
+ }
94
+ if (schema.kind === 'conditional') {
95
+ for (const innerProp of Object.values(schema.values)) {
96
+ queue.add(innerProp);
97
+ }
98
+ continue;
99
+ }
100
+ utils.assertNever(schema);
101
+ }
102
+ return false;
103
+ }
104
+ function doesSchemaOnlyEverContainASingleChildField(rootSchema) {
105
+ const queue = new Set([rootSchema]);
106
+ let hasFoundChildField = false;
107
+ for (const schema of queue) {
108
+ if (schema.kind === 'form' || schema.kind === 'relationship') continue;
109
+ if (schema.kind === 'child') {
110
+ if (hasFoundChildField) return false;
111
+ hasFoundChildField = true;
112
+ continue;
113
+ }
114
+ if (schema.kind === 'array') {
115
+ if (canSchemaContainChildField(schema.element)) return false;
116
+ continue;
117
+ }
118
+ if (schema.kind === 'object') {
119
+ for (const innerProp of Object.values(schema.fields)) {
120
+ queue.add(innerProp);
121
+ }
122
+ continue;
123
+ }
124
+ if (schema.kind === 'conditional') {
125
+ for (const innerProp of Object.values(schema.values)) {
126
+ queue.add(innerProp);
127
+ }
128
+ continue;
129
+ }
130
+ utils.assertNever(schema);
131
+ }
132
+ return hasFoundChildField;
133
+ }
134
+ function findArrayFieldsWithSingleChildField(schema, value) {
135
+ const propPaths = [];
136
+ utils.traverseProps(schema, value, (schema, value, path) => {
137
+ if (schema.kind === 'array' && doesSchemaOnlyEverContainASingleChildField(schema.element)) {
138
+ propPaths.push([path, schema]);
139
+ }
140
+ });
141
+ return propPaths;
142
+ }
143
+ function isEmptyChildFieldNode(element) {
144
+ const firstChild = element.children[0];
145
+ return element.children.length === 1 && (element.type === 'component-inline-prop' && firstChild.type === undefined && firstChild.text === '' || element.type === 'component-block-prop' && firstChild.type === 'paragraph' && firstChild.children.length === 1 && firstChild.children[0].type === undefined && firstChild.children[0].text === '');
146
+ }
147
+ function withComponentBlocks(blockComponents, editorDocumentFeatures, relationships, editor) {
148
+ // note that conflicts between the editor document features
149
+ // and the child field document features are dealt with elsewhere
150
+ const memoizedGetDocumentFeaturesForChildField = weakMemoize__default["default"](options => {
151
+ return utils.getDocumentFeaturesForChildField(editorDocumentFeatures, options);
152
+ });
153
+ const {
154
+ normalizeNode,
155
+ deleteBackward,
156
+ insertBreak
157
+ } = editor;
158
+ editor.deleteBackward = unit => {
159
+ if (editor.selection) {
160
+ const ancestorComponentBlock = getAncestorComponentBlock(editor);
161
+ if (ancestorComponentBlock.isInside && slate.Range.isCollapsed(editor.selection) && slate.Editor.isStart(editor, editor.selection.anchor, ancestorComponentBlock.prop[1]) && ancestorComponentBlock.prop[1][ancestorComponentBlock.prop[1].length - 1] === 0) {
162
+ slate.Transforms.unwrapNodes(editor, {
163
+ at: ancestorComponentBlock.componentBlock[1]
164
+ });
165
+ return;
166
+ }
167
+ }
168
+ deleteBackward(unit);
169
+ };
170
+ editor.insertBreak = () => {
171
+ const ancestorComponentBlock = getAncestorComponentBlock(editor);
172
+ if (editor.selection && ancestorComponentBlock.isInside) {
173
+ const {
174
+ prop: [componentPropNode, componentPropPath],
175
+ componentBlock: [componentBlockNode, componentBlockPath]
176
+ } = ancestorComponentBlock;
177
+ const isLastProp = componentPropPath[componentPropPath.length - 1] === componentBlockNode.children.length - 1;
178
+ if (componentPropNode.type === 'component-block-prop') {
179
+ const [[paragraphNode, paragraphPath]] = slate.Editor.nodes(editor, {
180
+ match: node => node.type === 'paragraph'
181
+ });
182
+ const isLastParagraph = paragraphPath[paragraphPath.length - 1] === componentPropNode.children.length - 1;
183
+ if (slate.Node.string(paragraphNode) === '' && isLastParagraph) {
184
+ if (isLastProp) {
185
+ slate.Transforms.moveNodes(editor, {
186
+ at: paragraphPath,
187
+ to: slate.Path.next(ancestorComponentBlock.componentBlock[1])
188
+ });
189
+ } else {
190
+ slate.Transforms.move(editor, {
191
+ distance: 1,
192
+ unit: 'line'
193
+ });
194
+ slate.Transforms.removeNodes(editor, {
195
+ at: paragraphPath
196
+ });
197
+ }
198
+ return;
199
+ }
200
+ }
201
+ if (componentPropNode.type === 'component-inline-prop') {
202
+ slate.Editor.withoutNormalizing(editor, () => {
203
+ const componentBlock = blockComponents[componentBlockNode.component];
204
+ if (componentPropNode.propPath !== undefined && componentBlock !== undefined) {
205
+ const rootSchema = {
206
+ kind: 'object',
207
+ fields: componentBlock.schema
208
+ };
209
+ const ancestorFields = utils.getAncestorSchemas(rootSchema, componentPropNode.propPath, componentBlockNode.props);
210
+ const idx = [...ancestorFields].reverse().findIndex(item => item.kind === 'array');
211
+ if (idx !== -1) {
212
+ const arrayFieldIdx = ancestorFields.length - 1 - idx;
213
+ const arrayField = ancestorFields[arrayFieldIdx];
214
+ utils.assert(arrayField.kind === 'array');
215
+ const val = utils.getValueAtPropPath(componentBlockNode.props, componentPropNode.propPath.slice(0, arrayFieldIdx));
216
+ if (doesSchemaOnlyEverContainASingleChildField(arrayField.element)) {
217
+ if (slate.Node.string(componentPropNode) === '' && val.length - 1 === componentPropNode.propPath[arrayFieldIdx]) {
218
+ slate.Transforms.removeNodes(editor, {
219
+ at: componentPropPath
220
+ });
221
+ if (isLastProp) {
222
+ slate.Transforms.insertNodes(editor, {
223
+ type: 'paragraph',
224
+ children: [{
225
+ text: ''
226
+ }]
227
+ }, {
228
+ at: slate.Path.next(componentBlockPath)
229
+ });
230
+ slate.Transforms.select(editor, slate.Path.next(componentBlockPath));
231
+ } else {
232
+ slate.Transforms.move(editor, {
233
+ distance: 1,
234
+ unit: 'line'
235
+ });
236
+ }
237
+ } else {
238
+ insertBreak();
239
+ }
240
+ return;
241
+ }
242
+ }
243
+ }
244
+ slate.Transforms.splitNodes(editor, {
245
+ always: true
246
+ });
247
+ const splitNodePath = slate.Path.next(componentPropPath);
248
+ if (isLastProp) {
249
+ slate.Transforms.moveNodes(editor, {
250
+ at: splitNodePath,
251
+ to: slate.Path.next(componentBlockPath)
252
+ });
253
+ } else {
254
+ utils.moveChildren(editor, splitNodePath, [...slate.Path.next(splitNodePath), 0]);
255
+ slate.Transforms.removeNodes(editor, {
256
+ at: splitNodePath
257
+ });
258
+ }
259
+ });
260
+ return;
261
+ }
262
+ }
263
+ insertBreak();
264
+ };
265
+ editor.normalizeNode = entry => {
266
+ const [node, path] = entry;
267
+ if (node.type === 'component-inline-prop' && !node.propPath && (node.children.length !== 1 || !slate.Text.isText(node.children[0]) || node.children[0].text !== '')) {
268
+ slate.Transforms.removeNodes(editor, {
269
+ at: path
270
+ });
271
+ return;
272
+ }
273
+ if (node.type === 'component-block') {
274
+ const componentBlock = blockComponents[node.component];
275
+ if (componentBlock) {
276
+ const rootSchema = {
277
+ kind: 'object',
278
+ fields: componentBlock.schema
279
+ };
280
+ const updatedProps = addMissingFields(node.props, rootSchema);
281
+ if (updatedProps !== node.props) {
282
+ slate.Transforms.setNodes(editor, {
283
+ props: updatedProps
284
+ }, {
285
+ at: path
286
+ });
287
+ return;
288
+ }
289
+ for (const [propPath, arrayField] of findArrayFieldsWithSingleChildField(rootSchema, node.props)) {
290
+ if (node.children.length === 1 && node.children[0].type === 'component-inline-prop' && node.children[0].propPath === undefined) {
291
+ break;
292
+ }
293
+ const nodesWithin = [];
294
+ for (const [idx, childNode] of node.children.entries()) {
295
+ if ((childNode.type === 'component-block-prop' || childNode.type === 'component-inline-prop') && childNode.propPath !== undefined) {
296
+ const subPath = childNode.propPath.concat();
297
+ while (subPath.length) {
298
+ if (typeof subPath.pop() === 'number') break;
299
+ }
300
+ if (shared.areArraysEqual(propPath, subPath)) {
301
+ nodesWithin.push([idx, childNode]);
302
+ }
303
+ }
304
+ }
305
+ const arrVal = utils.getValueAtPropPath(node.props, propPath);
306
+ const prevKeys = utils.getKeysForArrayValue(arrVal);
307
+ const prevKeysSet = new Set(prevKeys);
308
+ const alreadyUsedIndicies = new Set();
309
+ const newVal = [];
310
+ const newKeys = [];
311
+ const getNewKey = () => {
312
+ let key = utils.getNewArrayElementKey();
313
+ while (prevKeysSet.has(key)) {
314
+ key = utils.getNewArrayElementKey();
315
+ }
316
+ return key;
317
+ };
318
+ for (const [, node] of nodesWithin) {
319
+ const idxFromValue = node.propPath[propPath.length];
320
+ utils.assert(typeof idxFromValue === 'number');
321
+ if (arrVal.length <= idxFromValue || alreadyUsedIndicies.has(idxFromValue) && isEmptyChildFieldNode(node)) {
322
+ newVal.push(utils.getInitialPropsValue(arrayField.element));
323
+ newKeys.push(getNewKey());
324
+ } else {
325
+ alreadyUsedIndicies.add(idxFromValue);
326
+ newVal.push(arrVal[idxFromValue]);
327
+ newKeys.push(alreadyUsedIndicies.has(idxFromValue) ? getNewKey() : prevKeys[idxFromValue]);
328
+ }
329
+ }
330
+ utils.setKeysForArrayValue(newVal, newKeys);
331
+ if (!shared.areArraysEqual(arrVal, newVal)) {
332
+ const transformedProps = utils.replaceValueAtPropPath(rootSchema, node.props, newVal, propPath);
333
+ slate.Transforms.setNodes(editor, {
334
+ props: transformedProps
335
+ }, {
336
+ at: path
337
+ });
338
+ for (const [idx, [idxInChildrenOfBlock, nodeWithin]] of nodesWithin.entries()) {
339
+ const newPropPath = [...nodeWithin.propPath];
340
+ newPropPath[propPath.length] = idx;
341
+ slate.Transforms.setNodes(editor, {
342
+ propPath: newPropPath
343
+ }, {
344
+ at: [...path, idxInChildrenOfBlock]
345
+ });
346
+ }
347
+ return;
348
+ }
349
+ }
350
+ const missingKeys = new Map(utils.findChildPropPaths(node.props, componentBlock.schema).map(x => [JSON.stringify(x.path), x.options.kind]));
351
+ node.children.forEach(node => {
352
+ utils.assert(node.type === 'component-block-prop' || node.type === 'component-inline-prop');
353
+ missingKeys.delete(JSON.stringify(node.propPath));
354
+ });
355
+ if (missingKeys.size) {
356
+ slate.Transforms.insertNodes(editor, [...missingKeys].map(([prop, kind]) => ({
357
+ type: `component-${kind}-prop`,
358
+ propPath: prop ? JSON.parse(prop) : prop,
359
+ children: [{
360
+ text: ''
361
+ }]
362
+ })), {
363
+ at: [...path, node.children.length]
364
+ });
365
+ return;
366
+ }
367
+ const foundProps = new Set();
368
+ const stringifiedInlinePropPaths = {};
369
+ utils.findChildPropPaths(node.props, blockComponents[node.component].schema).forEach((x, index) => {
370
+ stringifiedInlinePropPaths[JSON.stringify(x.path)] = {
371
+ options: x.options,
372
+ index
373
+ };
374
+ });
375
+ for (const [index, childNode] of node.children.entries()) {
376
+ if (
377
+ // children that are not these will be handled by
378
+ // the generic allowedChildren normalization
379
+ childNode.type !== 'component-inline-prop' && childNode.type !== 'component-block-prop') {
380
+ continue;
381
+ }
382
+ const childPath = [...path, index];
383
+ const stringifiedPropPath = JSON.stringify(childNode.propPath);
384
+ if (stringifiedInlinePropPaths[stringifiedPropPath] === undefined) {
385
+ slate.Transforms.removeNodes(editor, {
386
+ at: childPath
387
+ });
388
+ return;
389
+ }
390
+ if (foundProps.has(stringifiedPropPath)) {
391
+ slate.Transforms.removeNodes(editor, {
392
+ at: childPath
393
+ });
394
+ return;
395
+ }
396
+ foundProps.add(stringifiedPropPath);
397
+ const propInfo = stringifiedInlinePropPaths[stringifiedPropPath];
398
+ const expectedIndex = propInfo.index;
399
+ if (index !== expectedIndex) {
400
+ slate.Transforms.moveNodes(editor, {
401
+ at: childPath,
402
+ to: [...path, expectedIndex]
403
+ });
404
+ return;
405
+ }
406
+ const expectedChildNodeType = `component-${propInfo.options.kind}-prop`;
407
+ if (childNode.type !== expectedChildNodeType) {
408
+ slate.Transforms.setNodes(editor, {
409
+ type: expectedChildNodeType
410
+ }, {
411
+ at: childPath
412
+ });
413
+ return;
414
+ }
415
+ const documentFeatures = memoizedGetDocumentFeaturesForChildField(propInfo.options);
416
+ if (normalizeNodeWithinComponentProp([childNode, childPath], editor, documentFeatures, relationships)) {
417
+ return;
418
+ }
419
+ }
420
+ }
421
+ }
422
+ normalizeNode(entry);
423
+ };
424
+ return editor;
425
+ }
426
+
427
+ // the only thing that this will fix is a new field being added to an object field, nothing else.
428
+ function addMissingFields(value, schema) {
429
+ if (schema.kind === 'child' || schema.kind === 'form' || schema.kind === 'relationship') {
430
+ return value;
431
+ }
432
+ if (schema.kind === 'conditional') {
433
+ const conditionalValue = value;
434
+ const updatedInnerValue = addMissingFields(conditionalValue.value, schema.values[conditionalValue.discriminant.toString()]);
435
+ if (updatedInnerValue === conditionalValue.value) {
436
+ return value;
437
+ }
438
+ return {
439
+ discriminant: conditionalValue.discriminant,
440
+ value: updatedInnerValue
441
+ };
442
+ }
443
+ if (schema.kind === 'array') {
444
+ const arrValue = value;
445
+ const newArrValue = arrValue.map(x => addMissingFields(x, schema.element));
446
+ if (shared.areArraysEqual(arrValue, newArrValue)) {
447
+ return value;
448
+ }
449
+ return newArrValue;
450
+ }
451
+ if (schema.kind === 'object') {
452
+ const objectValue = value;
453
+ let hasChanged = false;
454
+ const newObjectValue = {};
455
+ for (const [key, innerSchema] of Object.entries(schema.fields)) {
456
+ const innerValue = objectValue[key];
457
+ if (innerValue === undefined) {
458
+ hasChanged = true;
459
+ newObjectValue[key] = utils.getInitialPropsValue(innerSchema);
460
+ continue;
461
+ }
462
+ const newInnerValue = addMissingFields(innerValue, innerSchema);
463
+ if (newInnerValue !== innerValue) {
464
+ hasChanged = true;
465
+ }
466
+ newObjectValue[key] = newInnerValue;
467
+ }
468
+ if (hasChanged) {
469
+ return newObjectValue;
470
+ }
471
+ return value;
472
+ }
473
+ utils.assertNever(schema);
474
+ }
475
+
476
+ const isLinkActive = editor => {
477
+ return utils.isElementActive(editor, 'link');
478
+ };
479
+ function wrapLink(editor, url) {
480
+ if (isLinkActive(editor)) {
481
+ slate.Transforms.unwrapNodes(editor, {
482
+ match: n => n.type === 'link'
483
+ });
484
+ return;
485
+ }
486
+ const {
487
+ selection
488
+ } = editor;
489
+ const isCollapsed = selection && slate.Range.isCollapsed(selection);
490
+ if (isCollapsed) {
491
+ slate.Transforms.insertNodes(editor, {
492
+ type: 'link',
493
+ href: url,
494
+ children: [{
495
+ text: url
496
+ }]
497
+ });
498
+ } else {
499
+ slate.Transforms.wrapNodes(editor, {
500
+ type: 'link',
501
+ href: url,
502
+ children: [{
503
+ text: ''
504
+ }]
505
+ }, {
506
+ split: true
507
+ });
508
+ }
509
+ }
510
+ const markdownLinkPattern = /(^|\s)\[(.+?)\]\((\S+)\)$/;
511
+ function withLink(editorDocumentFeatures, componentBlocks, editor) {
512
+ const {
513
+ insertText,
514
+ isInline,
515
+ normalizeNode
516
+ } = editor;
517
+ editor.isInline = element => {
518
+ return element.type === 'link' ? true : isInline(element);
519
+ };
520
+ if (editorDocumentFeatures.links) {
521
+ editor.insertText = text => {
522
+ insertText(text);
523
+ if (text !== ')' || !editor.selection) return;
524
+ const startOfBlock = slate.Editor.start(editor, slate.Editor.above(editor, {
525
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
526
+ })[1]);
527
+ const startOfBlockToEndOfShortcutString = slate.Editor.string(editor, {
528
+ anchor: editor.selection.anchor,
529
+ focus: startOfBlock
530
+ });
531
+ const match = markdownLinkPattern.exec(startOfBlockToEndOfShortcutString);
532
+ if (!match) return;
533
+ const ancestorComponentChildFieldDocumentFeatures = shared.getAncestorComponentChildFieldDocumentFeatures(editor, editorDocumentFeatures, componentBlocks);
534
+ if ((ancestorComponentChildFieldDocumentFeatures === null || ancestorComponentChildFieldDocumentFeatures === void 0 ? void 0 : ancestorComponentChildFieldDocumentFeatures.documentFeatures.links) === false) {
535
+ return;
536
+ }
537
+ const [, maybeWhitespace, linkText, href] = match;
538
+ // by doing this, the insertText(')') above will happen in a different undo than the link replacement
539
+ // so that means that when someone does an undo after this
540
+ // it will undo to the state of "[content](link)" rather than "[content](link" (note the missing closing bracket)
541
+ editor.writeHistory('undos', {
542
+ operations: [],
543
+ selectionBefore: null
544
+ });
545
+ const startOfShortcut = match.index === 0 ? startOfBlock : utils.EditorAfterButIgnoringingPointsWithNoContent(editor, startOfBlock, {
546
+ distance: match.index
547
+ });
548
+ const startOfLinkText = utils.EditorAfterButIgnoringingPointsWithNoContent(editor, startOfShortcut, {
549
+ distance: maybeWhitespace === '' ? 1 : 2
550
+ });
551
+ const endOfLinkText = utils.EditorAfterButIgnoringingPointsWithNoContent(editor, startOfLinkText, {
552
+ distance: linkText.length
553
+ });
554
+ slate.Transforms.delete(editor, {
555
+ at: {
556
+ anchor: endOfLinkText,
557
+ focus: editor.selection.anchor
558
+ }
559
+ });
560
+ slate.Transforms.delete(editor, {
561
+ at: {
562
+ anchor: startOfShortcut,
563
+ focus: startOfLinkText
564
+ }
565
+ });
566
+ slate.Transforms.wrapNodes(editor, {
567
+ type: 'link',
568
+ href,
569
+ children: []
570
+ }, {
571
+ at: {
572
+ anchor: editor.selection.anchor,
573
+ focus: startOfShortcut
574
+ },
575
+ split: true
576
+ });
577
+ const nextNode = slate.Editor.next(editor);
578
+ if (nextNode) {
579
+ slate.Transforms.select(editor, nextNode[1]);
580
+ }
581
+ };
582
+ }
583
+ editor.normalizeNode = ([node, path]) => {
584
+ if (node.type === 'link') {
585
+ if (slate.Node.string(node) === '') {
586
+ slate.Transforms.unwrapNodes(editor, {
587
+ at: path
588
+ });
589
+ return;
590
+ }
591
+ for (const [idx, child] of node.children.entries()) {
592
+ if (child.type === 'link') {
593
+ // links cannot contain links
594
+ slate.Transforms.unwrapNodes(editor, {
595
+ at: [...path, idx]
596
+ });
597
+ return;
598
+ }
599
+ }
600
+ }
601
+ if (isInlineContainer(node)) {
602
+ let lastMergableLink = null;
603
+ for (const [idx, child] of node.children.entries()) {
604
+ var _lastMergableLink;
605
+ if (child.type === 'link' && child.href === ((_lastMergableLink = lastMergableLink) === null || _lastMergableLink === void 0 ? void 0 : _lastMergableLink.node.href)) {
606
+ const firstLinkPath = [...path, lastMergableLink.index];
607
+ const secondLinkPath = [...path, idx];
608
+ const to = [...firstLinkPath, lastMergableLink.node.children.length];
609
+ // note this is going in reverse, js doesn't have double-ended iterators so it's a for(;;)
610
+ for (let i = child.children.length - 1; i >= 0; i--) {
611
+ const childPath = [...secondLinkPath, i];
612
+ slate.Transforms.moveNodes(editor, {
613
+ at: childPath,
614
+ to
615
+ });
616
+ }
617
+ slate.Transforms.removeNodes(editor, {
618
+ at: secondLinkPath
619
+ });
620
+ return;
621
+ }
622
+ if (!slate.Text.isText(child) || child.text !== '') {
623
+ lastMergableLink = null;
624
+ }
625
+ if (child.type === 'link') {
626
+ lastMergableLink = {
627
+ index: idx,
628
+ node: child
629
+ };
630
+ }
631
+ }
632
+ }
633
+ normalizeNode([node, path]);
634
+ };
635
+ return editor;
636
+ }
637
+
638
+ function withHeading(editor) {
639
+ const {
640
+ insertBreak
641
+ } = editor;
642
+ editor.insertBreak = () => {
643
+ insertBreak();
644
+ const entry = slate.Editor.above(editor, {
645
+ match: n => n.type === 'heading'
646
+ });
647
+ if (!entry || !editor.selection || !slate.Range.isCollapsed(editor.selection)) return;
648
+ const path = entry[1];
649
+ if (
650
+ // we want to unwrap the heading when the user inserted a break at the end of the heading
651
+ // when the user inserts a break at the end of a heading, the new heading
652
+ // that we want to unwrap will be empty so the end will be equal to the selection
653
+ slate.Point.equals(slate.Editor.end(editor, path), editor.selection.anchor)) {
654
+ slate.Transforms.unwrapNodes(editor, {
655
+ at: path
656
+ });
657
+ return;
658
+ }
659
+ // we also want to unwrap the _previous_ heading when the user inserted a break
660
+ // at the start of the heading, essentially just inserting an empty paragraph above the heading
661
+ if (!slate.Path.hasPrevious(path)) return;
662
+ const previousPath = slate.Path.previous(path);
663
+ const previousNode = slate.Node.get(editor, previousPath);
664
+ if (previousNode.type === 'heading' && previousNode.children.length === 1 && slate.Text.isText(previousNode.children[0]) && previousNode.children[0].text === '') {
665
+ slate.Transforms.unwrapNodes(editor, {
666
+ at: previousPath
667
+ });
668
+ }
669
+ };
670
+ return editor;
671
+ }
672
+
673
+ function insertBlockquote(editor) {
674
+ const isActive = utils.isElementActive(editor, 'blockquote');
675
+ if (isActive) {
676
+ slate.Transforms.unwrapNodes(editor, {
677
+ match: node => node.type === 'blockquote'
678
+ });
679
+ } else {
680
+ slate.Transforms.wrapNodes(editor, {
681
+ type: 'blockquote',
682
+ children: []
683
+ });
684
+ }
685
+ }
686
+ function getDirectBlockquoteParentFromSelection(editor) {
687
+ if (!editor.selection) return {
688
+ isInside: false
689
+ };
690
+ const [, parentPath] = slate.Editor.parent(editor, editor.selection);
691
+ if (!parentPath.length) {
692
+ return {
693
+ isInside: false
694
+ };
695
+ }
696
+ const [maybeBlockquoteParent, maybeBlockquoteParentPath] = slate.Editor.parent(editor, parentPath);
697
+ const isBlockquote = maybeBlockquoteParent.type === 'blockquote';
698
+ return isBlockquote ? {
699
+ isInside: true,
700
+ path: maybeBlockquoteParentPath
701
+ } : {
702
+ isInside: false
703
+ };
704
+ }
705
+ function withBlockquote(editor) {
706
+ const {
707
+ insertBreak,
708
+ deleteBackward
709
+ } = editor;
710
+ editor.deleteBackward = unit => {
711
+ if (editor.selection) {
712
+ const parentBlockquote = getDirectBlockquoteParentFromSelection(editor);
713
+ if (parentBlockquote.isInside && slate.Range.isCollapsed(editor.selection) &&
714
+ // the selection is at the start of the paragraph
715
+ editor.selection.anchor.offset === 0 &&
716
+ // it's the first paragraph in the panel
717
+ editor.selection.anchor.path[editor.selection.anchor.path.length - 2] === 0) {
718
+ slate.Transforms.unwrapNodes(editor, {
719
+ match: node => node.type === 'blockquote',
720
+ split: true
721
+ });
722
+ return;
723
+ }
724
+ }
725
+ deleteBackward(unit);
726
+ };
727
+ editor.insertBreak = () => {
728
+ const panel = getDirectBlockquoteParentFromSelection(editor);
729
+ if (editor.selection && panel.isInside) {
730
+ const [node, nodePath] = slate.Editor.node(editor, editor.selection);
731
+ if (slate.Path.isDescendant(nodePath, panel.path) && slate.Node.string(node) === '') {
732
+ slate.Transforms.unwrapNodes(editor, {
733
+ match: node => node.type === 'blockquote',
734
+ split: true
735
+ });
736
+ return;
737
+ }
738
+ }
739
+ insertBreak();
740
+ };
741
+ return editor;
742
+ }
743
+
744
+ function withRelationship(editor) {
745
+ const {
746
+ isVoid,
747
+ isInline
748
+ } = editor;
749
+ editor.isVoid = element => element.type === 'relationship' || isVoid(element);
750
+ editor.isInline = element => element.type === 'relationship' || isInline(element);
751
+ return editor;
752
+ }
753
+
754
+ function insertDivider(editor) {
755
+ utils.insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading(editor, {
756
+ type: 'divider',
757
+ children: [{
758
+ text: ''
759
+ }]
760
+ });
761
+ slate.Editor.insertNode(editor, {
762
+ type: 'paragraph',
763
+ children: [{
764
+ text: ''
765
+ }]
766
+ });
767
+ }
768
+ function withDivider(editor) {
769
+ const {
770
+ isVoid
771
+ } = editor;
772
+ editor.isVoid = node => {
773
+ return node.type === 'divider' || isVoid(node);
774
+ };
775
+ return editor;
776
+ }
777
+
778
+ function withCodeBlock(editor) {
779
+ const {
780
+ insertBreak,
781
+ normalizeNode
782
+ } = editor;
783
+ editor.insertBreak = () => {
784
+ const [node, path] = slate.Editor.above(editor, {
785
+ match: n => slate.Element.isElement(n) && slate.Editor.isBlock(editor, n)
786
+ }) || [editor, []];
787
+ if (node.type === 'code' && slate.Text.isText(node.children[0])) {
788
+ const text = node.children[0].text;
789
+ if (text[text.length - 1] === '\n' && editor.selection && slate.Range.isCollapsed(editor.selection) && slate.Point.equals(slate.Editor.end(editor, path), editor.selection.anchor)) {
790
+ insertBreak();
791
+ slate.Transforms.setNodes(editor, {
792
+ type: 'paragraph',
793
+ children: []
794
+ });
795
+ slate.Transforms.delete(editor, {
796
+ distance: 1,
797
+ at: {
798
+ path: [...path, 0],
799
+ offset: text.length - 1
800
+ }
801
+ });
802
+ return;
803
+ }
804
+ editor.insertText('\n');
805
+ return;
806
+ }
807
+ insertBreak();
808
+ };
809
+ editor.normalizeNode = ([node, path]) => {
810
+ if (node.type === 'code' && slate.Element.isElement(node)) {
811
+ for (const [index, childNode] of node.children.entries()) {
812
+ if (!slate.Text.isText(childNode)) {
813
+ if (editor.isVoid(childNode)) {
814
+ slate.Transforms.removeNodes(editor, {
815
+ at: [...path, index]
816
+ });
817
+ } else {
818
+ slate.Transforms.unwrapNodes(editor, {
819
+ at: [...path, index]
820
+ });
821
+ }
822
+ return;
823
+ }
824
+ const marks = Object.keys(childNode).filter(x => x !== 'text');
825
+ if (marks.length) {
826
+ slate.Transforms.unsetNodes(editor, marks, {
827
+ at: [...path, index]
828
+ });
829
+ return;
830
+ }
831
+ }
832
+ }
833
+ normalizeNode([node, path]);
834
+ };
835
+ return editor;
836
+ }
837
+
838
+ const allMarkdownShortcuts = {
839
+ bold: ['**', '__'],
840
+ italic: ['*', '_'],
841
+ strikethrough: ['~~'],
842
+ code: ['`']
843
+ };
844
+ function applyMark(editor, mark, shortcutText, startOfStartPoint) {
845
+ // so that this starts a new undo group
846
+ editor.writeHistory('undos', {
847
+ operations: [],
848
+ selectionBefore: null
849
+ });
850
+ const startPointRef = slate.Editor.pointRef(editor, startOfStartPoint);
851
+ slate.Transforms.delete(editor, {
852
+ at: editor.selection.anchor,
853
+ distance: shortcutText.length,
854
+ reverse: true
855
+ });
856
+ slate.Transforms.delete(editor, {
857
+ at: startOfStartPoint,
858
+ distance: shortcutText.length
859
+ });
860
+ slate.Transforms.setNodes(editor, {
861
+ [mark]: true
862
+ }, {
863
+ match: slate.Text.isText,
864
+ split: true,
865
+ at: {
866
+ anchor: startPointRef.unref(),
867
+ focus: editor.selection.anchor
868
+ }
869
+ });
870
+ // once you've ended the shortcut, you're done with the mark
871
+ // so we need to remove it so the text you insert after doesn't have it
872
+ editor.removeMark(mark);
873
+ }
874
+ function withMarks(editorDocumentFeatures, componentBlocks, editor) {
875
+ const {
876
+ insertText,
877
+ insertBreak
878
+ } = editor;
879
+ editor.insertBreak = () => {
880
+ insertBreak();
881
+ const marksAfterInsertBreak = slate.Editor.marks(editor);
882
+ if (!marksAfterInsertBreak || !editor.selection) return;
883
+ const parentBlock = slate.Editor.above(editor, {
884
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
885
+ });
886
+ if (!parentBlock) return;
887
+ const point = utils.EditorAfterButIgnoringingPointsWithNoContent(editor, editor.selection.anchor);
888
+ const marksAfterInsertBreakArr = Object.keys(marksAfterInsertBreak);
889
+ if (!point || !slate.Path.isDescendant(point.path, parentBlock[1])) {
890
+ for (const mark of marksAfterInsertBreakArr) {
891
+ editor.removeMark(mark);
892
+ }
893
+ return;
894
+ }
895
+ const textNode = slate.Node.get(editor, point.path);
896
+ for (const mark of marksAfterInsertBreakArr) {
897
+ if (!textNode[mark]) {
898
+ editor.removeMark(mark);
899
+ }
900
+ }
901
+ };
902
+ const selectedMarkdownShortcuts = {};
903
+ const enabledMarks = editorDocumentFeatures.formatting.inlineMarks;
904
+ Object.keys(allMarkdownShortcuts).forEach(mark => {
905
+ if (enabledMarks[mark]) {
906
+ selectedMarkdownShortcuts[mark] = allMarkdownShortcuts[mark];
907
+ }
908
+ });
909
+ if (Object.keys(selectedMarkdownShortcuts).length === 0) return editor;
910
+ editor.insertText = text => {
911
+ insertText(text);
912
+ if (editor.selection && slate.Range.isCollapsed(editor.selection)) {
913
+ for (const [mark, shortcuts] of Object.entries(selectedMarkdownShortcuts)) {
914
+ for (const shortcutText of shortcuts) {
915
+ if (text === shortcutText[shortcutText.length - 1]) {
916
+ // this function is not inlined because
917
+ // https://github.com/swc-project/swc/issues/2622
918
+ const startOfBlock = getStartOfBlock(editor);
919
+ const startOfBlockToEndOfShortcutString = slate.Editor.string(editor, {
920
+ anchor: editor.selection.anchor,
921
+ focus: startOfBlock
922
+ });
923
+ const hasWhitespaceBeforeEndOfShortcut = /\s/.test(startOfBlockToEndOfShortcutString.slice(-shortcutText.length - 1, -shortcutText.length));
924
+ const endOfShortcutContainsExpectedContent = shortcutText === startOfBlockToEndOfShortcutString.slice(-shortcutText.length);
925
+ if (hasWhitespaceBeforeEndOfShortcut || !endOfShortcutContainsExpectedContent) {
926
+ continue;
927
+ }
928
+ const strToMatchOn = startOfBlockToEndOfShortcutString.slice(0, -shortcutText.length - 1);
929
+ // TODO: use regex probs
930
+ for (const [offsetFromStartOfBlock] of [...strToMatchOn].reverse().entries()) {
931
+ const expectedShortcutText = strToMatchOn.slice(offsetFromStartOfBlock, offsetFromStartOfBlock + shortcutText.length);
932
+ if (expectedShortcutText !== shortcutText) {
933
+ continue;
934
+ }
935
+ const startOfStartOfShortcut = offsetFromStartOfBlock === 0 ? startOfBlock : utils.EditorAfterButIgnoringingPointsWithNoContent(editor, startOfBlock, {
936
+ distance: offsetFromStartOfBlock
937
+ });
938
+ const endOfStartOfShortcut = slate.Editor.after(editor, startOfStartOfShortcut, {
939
+ distance: shortcutText.length
940
+ });
941
+ if (offsetFromStartOfBlock !== 0 && !/\s/.test(slate.Editor.string(editor, {
942
+ anchor: slate.Editor.before(editor, startOfStartOfShortcut, {
943
+ unit: 'character'
944
+ }),
945
+ focus: startOfStartOfShortcut
946
+ }))) {
947
+ continue;
948
+ }
949
+ const contentBetweenShortcuts = slate.Editor.string(editor, {
950
+ anchor: endOfStartOfShortcut,
951
+ focus: editor.selection.anchor
952
+ }).slice(0, -shortcutText.length);
953
+ if (contentBetweenShortcuts === '' || /\s/.test(contentBetweenShortcuts[0])) {
954
+ continue;
955
+ }
956
+
957
+ // this is a bit of a weird one
958
+ // let's say you had <text>__thing _<cursor /></text> and you insert `_`.
959
+ // without the below, that would turn into <text italic>_thing _<cursor /></text>
960
+ // but it's probably meant to be bold but it's not because of the space before the ending _
961
+ // there's probably a better way to do this but meh, this works
962
+ if (mark === 'italic' && (contentBetweenShortcuts[0] === '_' || contentBetweenShortcuts[0] === '*')) {
963
+ continue;
964
+ }
965
+ const ancestorComponentChildFieldDocumentFeatures = shared.getAncestorComponentChildFieldDocumentFeatures(editor, editorDocumentFeatures, componentBlocks);
966
+ if (ancestorComponentChildFieldDocumentFeatures && ancestorComponentChildFieldDocumentFeatures.inlineMarks !== 'inherit' && ancestorComponentChildFieldDocumentFeatures.inlineMarks[mark] === false) {
967
+ continue;
968
+ }
969
+ applyMark(editor, mark, shortcutText, startOfStartOfShortcut);
970
+ return;
971
+ }
972
+ }
973
+ }
974
+ }
975
+ }
976
+ };
977
+ return editor;
978
+ }
979
+ function getStartOfBlock(editor) {
980
+ return slate.Editor.start(editor, slate.Editor.above(editor, {
981
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
982
+ })[1]);
983
+ }
984
+
985
+ function withSoftBreaks(editor) {
986
+ // TODO: should soft breaks only work in particular places
987
+ editor.insertSoftBreak = () => {
988
+ slate.Transforms.insertText(editor, '\n');
989
+ };
990
+ return editor;
991
+ }
992
+
993
+ const shortcuts = {
994
+ '...': '…',
995
+ '-->': '→',
996
+ '->': '→',
997
+ '<-': '←',
998
+ '<--': '←',
999
+ '--': '–'
1000
+ };
1001
+ function withShortcuts(editor) {
1002
+ const {
1003
+ insertText
1004
+ } = editor;
1005
+ editor.insertText = text => {
1006
+ insertText(text);
1007
+ if (text === ' ' && editor.selection && slate.Range.isCollapsed(editor.selection)) {
1008
+ const selectionPoint = editor.selection.anchor;
1009
+ const ancestorBlock = slate.Editor.above(editor, {
1010
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
1011
+ });
1012
+ if (ancestorBlock) {
1013
+ Object.keys(shortcuts).forEach(shortcut => {
1014
+ const pointBefore = slate.Editor.before(editor, selectionPoint, {
1015
+ unit: 'character',
1016
+ distance: shortcut.length + 1
1017
+ });
1018
+ if (pointBefore && slate.Path.isDescendant(pointBefore.path, ancestorBlock[1])) {
1019
+ const range = {
1020
+ anchor: selectionPoint,
1021
+ focus: pointBefore
1022
+ };
1023
+ const str = slate.Editor.string(editor, range);
1024
+ if (str.slice(0, shortcut.length) === shortcut) {
1025
+ editor.writeHistory('undos', {
1026
+ operations: [],
1027
+ selectionBefore: null
1028
+ });
1029
+ slate.Transforms.select(editor, range);
1030
+ editor.insertText(shortcuts[shortcut] + ' ');
1031
+ }
1032
+ }
1033
+ });
1034
+ }
1035
+ }
1036
+ };
1037
+ return editor;
1038
+ }
1039
+
1040
+ const nodeListsWithoutInsertMenu = new WeakSet();
1041
+ const nodesWithoutInsertMenu = new WeakSet();
1042
+ function findPathWithInsertMenu(node, path) {
1043
+ if (slate.Text.isText(node)) return node.insertMenu ? path : undefined;
1044
+ if (nodeListsWithoutInsertMenu.has(node.children)) return;
1045
+ for (const [index, child] of node.children.entries()) {
1046
+ if (nodesWithoutInsertMenu.has(child)) continue;
1047
+ const maybePath = findPathWithInsertMenu(child, [...path, index]);
1048
+ if (maybePath) {
1049
+ return maybePath;
1050
+ }
1051
+ nodesWithoutInsertMenu.add(child);
1052
+ }
1053
+ nodeListsWithoutInsertMenu.add(node.children);
1054
+ }
1055
+ function removeInsertMenuMarkWhenOutsideOfSelection(editor) {
1056
+ var _Editor$marks;
1057
+ const path = findPathWithInsertMenu(editor, []);
1058
+ if (path && !((_Editor$marks = slate.Editor.marks(editor)) !== null && _Editor$marks !== void 0 && _Editor$marks.insertMenu) && (!editor.selection || !slate.Path.equals(editor.selection.anchor.path, path) || !slate.Path.equals(editor.selection.focus.path, path))) {
1059
+ slate.Transforms.unsetNodes(editor, 'insertMenu', {
1060
+ at: path
1061
+ });
1062
+ return true;
1063
+ }
1064
+ return false;
1065
+ }
1066
+ function withInsertMenu(editor) {
1067
+ const {
1068
+ normalizeNode,
1069
+ apply,
1070
+ insertText
1071
+ } = editor;
1072
+ editor.normalizeNode = ([node, path]) => {
1073
+ if (slate.Text.isText(node) && node.insertMenu) {
1074
+ if (node.text[0] !== '/') {
1075
+ slate.Transforms.unsetNodes(editor, 'insertMenu', {
1076
+ at: path
1077
+ });
1078
+ return;
1079
+ }
1080
+ const whitespaceMatch = /\s/.exec(node.text);
1081
+ if (whitespaceMatch) {
1082
+ slate.Transforms.unsetNodes(editor, 'insertMenu', {
1083
+ at: {
1084
+ anchor: {
1085
+ path,
1086
+ offset: whitespaceMatch.index
1087
+ },
1088
+ focus: slate.Editor.end(editor, path)
1089
+ },
1090
+ match: slate.Text.isText,
1091
+ split: true
1092
+ });
1093
+ return;
1094
+ }
1095
+ }
1096
+ if (slate.Editor.isEditor(editor) && removeInsertMenuMarkWhenOutsideOfSelection(editor)) {
1097
+ return;
1098
+ }
1099
+ normalizeNode([node, path]);
1100
+ };
1101
+ editor.apply = op => {
1102
+ apply(op);
1103
+ // we're calling this here AND in normalizeNode
1104
+ // because normalizeNode won't be called on selection changes
1105
+ // but apply will
1106
+ // we're still calling this from normalizeNode though because we want it to happen
1107
+ // when normalization happens
1108
+ if (op.type === 'set_selection') {
1109
+ removeInsertMenuMarkWhenOutsideOfSelection(editor);
1110
+ }
1111
+ };
1112
+ editor.insertText = text => {
1113
+ insertText(text);
1114
+ if (editor.selection && text === '/') {
1115
+ const startOfBlock = slate.Editor.start(editor, slate.Editor.above(editor, {
1116
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
1117
+ })[1]);
1118
+ const before = slate.Editor.before(editor, editor.selection.anchor, {
1119
+ unit: 'character'
1120
+ });
1121
+ if (before && (slate.Point.equals(startOfBlock, before) || before.offset !== 0 && /\s/.test(slate.Node.get(editor, before.path).text[before.offset - 1]))) {
1122
+ slate.Transforms.setNodes(editor, {
1123
+ insertMenu: true
1124
+ }, {
1125
+ at: {
1126
+ anchor: before,
1127
+ focus: editor.selection.anchor
1128
+ },
1129
+ match: slate.Text.isText,
1130
+ split: true
1131
+ });
1132
+ }
1133
+ }
1134
+ };
1135
+ return editor;
1136
+ }
1137
+
1138
+ function withBlockMarkdownShortcuts(documentFeatures, componentBlocks, editor) {
1139
+ const {
1140
+ insertText
1141
+ } = editor;
1142
+ const shortcuts = Object.create(null);
1143
+ const editorDocumentFeaturesForNormalizationToCheck = {
1144
+ ...documentFeatures,
1145
+ relationships: true
1146
+ };
1147
+ const addShortcut = (text, insert, shouldBeEnabledInComponentBlock, type = 'paragraph') => {
1148
+ if (!shouldBeEnabledInComponentBlock(editorDocumentFeaturesForNormalizationToCheck)) return;
1149
+ const trigger = text[text.length - 1];
1150
+ if (!shortcuts[trigger]) {
1151
+ shortcuts[trigger] = Object.create(null);
1152
+ }
1153
+ shortcuts[trigger][text] = {
1154
+ insert,
1155
+ type,
1156
+ shouldBeEnabledInComponentBlock
1157
+ };
1158
+ };
1159
+ addShortcut('1. ', () => {
1160
+ slate.Transforms.wrapNodes(editor, {
1161
+ type: 'ordered-list',
1162
+ children: []
1163
+ }, {
1164
+ match: n => slate.Element.isElement(n) && slate.Editor.isBlock(editor, n)
1165
+ });
1166
+ }, features => features.formatting.listTypes.ordered);
1167
+ addShortcut('- ', () => {
1168
+ slate.Transforms.wrapNodes(editor, {
1169
+ type: 'unordered-list',
1170
+ children: []
1171
+ }, {
1172
+ match: n => slate.Element.isElement(n) && slate.Editor.isBlock(editor, n)
1173
+ });
1174
+ }, features => features.formatting.listTypes.unordered);
1175
+ addShortcut('* ', () => {
1176
+ slate.Transforms.wrapNodes(editor, {
1177
+ type: 'unordered-list',
1178
+ children: []
1179
+ }, {
1180
+ match: n => slate.Element.isElement(n) && slate.Editor.isBlock(editor, n)
1181
+ });
1182
+ }, features => features.formatting.listTypes.unordered);
1183
+ documentFeatures.formatting.headingLevels.forEach(level => {
1184
+ addShortcut('#'.repeat(level) + ' ', () => {
1185
+ slate.Transforms.setNodes(editor, {
1186
+ type: 'heading',
1187
+ level
1188
+ }, {
1189
+ match: node => node.type === 'paragraph' || node.type === 'heading'
1190
+ });
1191
+ }, features => features.formatting.headingLevels.includes(level), 'heading-or-paragraph');
1192
+ });
1193
+ addShortcut('> ', () => {
1194
+ slate.Transforms.wrapNodes(editor, {
1195
+ type: 'blockquote',
1196
+ children: []
1197
+ }, {
1198
+ match: node => node.type === 'paragraph'
1199
+ });
1200
+ }, features => features.formatting.blockTypes.blockquote);
1201
+ addShortcut('```', () => {
1202
+ slate.Transforms.wrapNodes(editor, {
1203
+ type: 'code',
1204
+ children: []
1205
+ }, {
1206
+ match: node => node.type === 'paragraph'
1207
+ });
1208
+ }, features => features.formatting.blockTypes.code);
1209
+ addShortcut('---', () => {
1210
+ insertDivider(editor);
1211
+ }, features => features.dividers);
1212
+ editor.insertText = text => {
1213
+ insertText(text);
1214
+ const shortcutsForTrigger = shortcuts[text];
1215
+ if (shortcutsForTrigger && editor.selection && slate.Range.isCollapsed(editor.selection)) {
1216
+ const {
1217
+ anchor
1218
+ } = editor.selection;
1219
+ const block = slate.Editor.above(editor, {
1220
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
1221
+ });
1222
+ if (!block || block[0].type !== 'paragraph' && block[0].type !== 'heading') return;
1223
+ const start = slate.Editor.start(editor, block[1]);
1224
+ const range = {
1225
+ anchor,
1226
+ focus: start
1227
+ };
1228
+ const shortcutText = slate.Editor.string(editor, range);
1229
+ const shortcut = shortcutsForTrigger[shortcutText];
1230
+ if (!shortcut || shortcut.type === 'paragraph' && block[0].type !== 'paragraph') {
1231
+ return;
1232
+ }
1233
+ const locationDocumentFeatures = shared.getAncestorComponentChildFieldDocumentFeatures(editor, documentFeatures, componentBlocks);
1234
+ if (locationDocumentFeatures && (locationDocumentFeatures.kind === 'inline' || !shortcut.shouldBeEnabledInComponentBlock(locationDocumentFeatures.documentFeatures))) {
1235
+ return;
1236
+ }
1237
+ // so that this starts a new undo group
1238
+ editor.writeHistory('undos', {
1239
+ operations: [],
1240
+ selectionBefore: null
1241
+ });
1242
+ slate.Transforms.select(editor, range);
1243
+ slate.Transforms.delete(editor);
1244
+ shortcut.insert();
1245
+ }
1246
+ };
1247
+ return editor;
1248
+ }
1249
+
1250
+ // a v important note
1251
+ // marks in the markdown ast/html are represented quite differently to how they are in slate
1252
+ // if you had the markdown **something https://site.com something**
1253
+ // the bold node is the parent of the link node
1254
+ // but in slate, marks are only represented on text nodes
1255
+
1256
+ const currentlyActiveMarks = new Set();
1257
+ const currentlyDisabledMarks = new Set();
1258
+ let currentLink = null;
1259
+ function addMarkToChildren(mark, cb) {
1260
+ const wasPreviouslyActive = currentlyActiveMarks.has(mark);
1261
+ currentlyActiveMarks.add(mark);
1262
+ try {
1263
+ return cb();
1264
+ } finally {
1265
+ if (!wasPreviouslyActive) {
1266
+ currentlyActiveMarks.delete(mark);
1267
+ }
1268
+ }
1269
+ }
1270
+ function setLinkForChildren(href, cb) {
1271
+ // we'll only use the outer link
1272
+ if (currentLink !== null) {
1273
+ return cb();
1274
+ }
1275
+ currentLink = href;
1276
+ try {
1277
+ return cb();
1278
+ } finally {
1279
+ currentLink = null;
1280
+ }
1281
+ }
1282
+ function addMarksToChildren(marks, cb) {
1283
+ const marksToRemove = new Set();
1284
+ for (const mark of marks) {
1285
+ if (!currentlyActiveMarks.has(mark)) {
1286
+ marksToRemove.add(mark);
1287
+ }
1288
+ currentlyActiveMarks.add(mark);
1289
+ }
1290
+ try {
1291
+ return cb();
1292
+ } finally {
1293
+ for (const mark of marksToRemove) {
1294
+ currentlyActiveMarks.delete(mark);
1295
+ }
1296
+ }
1297
+ }
1298
+ function forceDisableMarkForChildren(mark, cb) {
1299
+ const wasPreviouslyDisabled = currentlyDisabledMarks.has(mark);
1300
+ currentlyDisabledMarks.add(mark);
1301
+ try {
1302
+ return cb();
1303
+ } finally {
1304
+ if (!wasPreviouslyDisabled) {
1305
+ currentlyDisabledMarks.delete(mark);
1306
+ }
1307
+ }
1308
+ }
1309
+
1310
+ /**
1311
+ * This type is more strict than `Element & { type: 'link'; }` because `children`
1312
+ * is constrained to only contain Text nodes. This can't be assumed generally around the editor
1313
+ * (because of inline relationships or nested links(which are normalized away but the editor needs to not break if it happens))
1314
+ * but where this type is used, we're only going to allow links to contain Text and that's important
1315
+ * so that we know a block will never be inside an inline because Slate gets unhappy when that happens
1316
+ * (really the link inline should probably be a mark rather than an inline,
1317
+ * non-void inlines are probably always bad but that would imply changing the document
1318
+ * structure which would be such unnecessary breakage)
1319
+ */
1320
+
1321
+ // inline relationships are not here because we never create them from handling a paste from html or markdown
1322
+
1323
+ function getInlineNodes(text) {
1324
+ const node = {
1325
+ text
1326
+ };
1327
+ for (const mark of currentlyActiveMarks) {
1328
+ if (!currentlyDisabledMarks.has(mark)) {
1329
+ node[mark] = true;
1330
+ }
1331
+ }
1332
+ if (currentLink !== null) {
1333
+ return [{
1334
+ text: ''
1335
+ }, {
1336
+ type: 'link',
1337
+ href: currentLink,
1338
+ children: [node]
1339
+ }, {
1340
+ text: ''
1341
+ }];
1342
+ }
1343
+ return [node];
1344
+ }
1345
+
1346
+ // very loosely based on https://github.com/ianstormtaylor/slate/blob/d22c76ae1313fe82111317417912a2670e73f5c9/site/examples/paste-html.tsx
1347
+ function getAlignmentFromElement(element) {
1348
+ const parent = element.parentElement;
1349
+ // confluence
1350
+ const attribute = parent === null || parent === void 0 ? void 0 : parent.getAttribute('data-align');
1351
+ // note: we don't show html that confluence would parse as alignment
1352
+ // we could change that but meh
1353
+ // (they match on div.fabric-editor-block-mark with data-align)
1354
+ if (attribute === 'center' || attribute === 'end') {
1355
+ return attribute;
1356
+ }
1357
+ if (element instanceof HTMLElement) {
1358
+ // Google docs
1359
+ const textAlign = element.style.textAlign;
1360
+ if (textAlign === 'center') {
1361
+ return 'center';
1362
+ }
1363
+ // TODO: RTL things?
1364
+ if (textAlign === 'right' || textAlign === 'end') {
1365
+ return 'end';
1366
+ }
1367
+ }
1368
+ }
1369
+ const headings = {
1370
+ H1: 1,
1371
+ H2: 2,
1372
+ H3: 3,
1373
+ H4: 4,
1374
+ H5: 5,
1375
+ H6: 6
1376
+ };
1377
+ const TEXT_TAGS = {
1378
+ CODE: 'code',
1379
+ DEL: 'strikethrough',
1380
+ S: 'strikethrough',
1381
+ STRIKE: 'strikethrough',
1382
+ EM: 'italic',
1383
+ I: 'italic',
1384
+ STRONG: 'bold',
1385
+ U: 'underline',
1386
+ SUP: 'superscript',
1387
+ SUB: 'subscript',
1388
+ KBD: 'keyboard'
1389
+ };
1390
+ function marksFromElementAttributes(element) {
1391
+ const marks = new Set();
1392
+ const style = element.style;
1393
+ const {
1394
+ nodeName
1395
+ } = element;
1396
+ const markFromNodeName = TEXT_TAGS[nodeName];
1397
+ if (markFromNodeName) {
1398
+ marks.add(markFromNodeName);
1399
+ }
1400
+ const {
1401
+ fontWeight,
1402
+ textDecoration,
1403
+ verticalAlign
1404
+ } = style;
1405
+ if (textDecoration === 'underline') {
1406
+ marks.add('underline');
1407
+ } else if (textDecoration === 'line-through') {
1408
+ marks.add('strikethrough');
1409
+ }
1410
+ // confluence
1411
+ if (nodeName === 'SPAN' && element.classList.contains('code')) {
1412
+ marks.add('code');
1413
+ }
1414
+ // Google Docs does weird things with <b>
1415
+ if (nodeName === 'B' && fontWeight !== 'normal') {
1416
+ marks.add('bold');
1417
+ } else if (typeof fontWeight === 'string' && (fontWeight === 'bold' || fontWeight === 'bolder' || fontWeight === '1000' || /^[5-9]\d{2}$/.test(fontWeight))) {
1418
+ marks.add('bold');
1419
+ }
1420
+ if (style.fontStyle === 'italic') {
1421
+ marks.add('italic');
1422
+ }
1423
+ // Google Docs uses vertical align for subscript and superscript instead of <sup> and <sub>
1424
+ if (verticalAlign === 'super') {
1425
+ marks.add('superscript');
1426
+ } else if (verticalAlign === 'sub') {
1427
+ marks.add('subscript');
1428
+ }
1429
+ return marks;
1430
+ }
1431
+ function deserializeHTML(html) {
1432
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
1433
+ return fixNodesForBlockChildren(deserializeNodes(parsed.body.childNodes));
1434
+ }
1435
+ function deserializeHTMLNode(el) {
1436
+ if (!(el instanceof globalThis.HTMLElement)) {
1437
+ const text = el.textContent;
1438
+ if (!text) {
1439
+ return [];
1440
+ }
1441
+ return getInlineNodes(text);
1442
+ }
1443
+ if (el.nodeName === 'BR') {
1444
+ return getInlineNodes('\n');
1445
+ }
1446
+ if (el.nodeName === 'IMG') {
1447
+ const alt = el.getAttribute('alt');
1448
+ return getInlineNodes(alt !== null && alt !== void 0 ? alt : '');
1449
+ }
1450
+ if (el.nodeName === 'HR') {
1451
+ return [{
1452
+ type: 'divider',
1453
+ children: [{
1454
+ text: ''
1455
+ }]
1456
+ }];
1457
+ }
1458
+ const marks = marksFromElementAttributes(el);
1459
+
1460
+ // Dropbox Paper displays blockquotes as lists for some reason
1461
+ if (el.classList.contains('listtype-quote')) {
1462
+ marks.delete('italic');
1463
+ return addMarksToChildren(marks, () => [{
1464
+ type: 'blockquote',
1465
+ children: fixNodesForBlockChildren(deserializeNodes(el.childNodes))
1466
+ }]);
1467
+ }
1468
+ return addMarksToChildren(marks, () => {
1469
+ const {
1470
+ nodeName
1471
+ } = el;
1472
+ if (nodeName === 'A') {
1473
+ const href = el.getAttribute('href');
1474
+ if (href) {
1475
+ return setLinkForChildren(href, () => forceDisableMarkForChildren('underline', () => deserializeNodes(el.childNodes)));
1476
+ }
1477
+ }
1478
+ if (nodeName === 'PRE' && el.textContent) {
1479
+ return [{
1480
+ type: 'code',
1481
+ children: [{
1482
+ text: el.textContent || ''
1483
+ }]
1484
+ }];
1485
+ }
1486
+ const deserialized = deserializeNodes(el.childNodes);
1487
+ const children = fixNodesForBlockChildren(deserialized);
1488
+ if (nodeName === 'LI') {
1489
+ let nestedList;
1490
+ const listItemContent = {
1491
+ type: 'list-item-content',
1492
+ children: children.filter(node => {
1493
+ if (nestedList === undefined && (node.type === 'ordered-list' || node.type === 'unordered-list')) {
1494
+ nestedList = node;
1495
+ return false;
1496
+ }
1497
+ return true;
1498
+ })
1499
+ };
1500
+ const listItemChildren = nestedList ? [listItemContent, nestedList] : [listItemContent];
1501
+ return [{
1502
+ type: 'list-item',
1503
+ children: listItemChildren
1504
+ }];
1505
+ }
1506
+ if (nodeName === 'P') {
1507
+ return [{
1508
+ type: 'paragraph',
1509
+ textAlign: getAlignmentFromElement(el),
1510
+ children
1511
+ }];
1512
+ }
1513
+ const headingLevel = headings[nodeName];
1514
+ if (typeof headingLevel === 'number') {
1515
+ return [{
1516
+ type: 'heading',
1517
+ level: headingLevel,
1518
+ textAlign: getAlignmentFromElement(el),
1519
+ children
1520
+ }];
1521
+ }
1522
+ if (nodeName === 'BLOCKQUOTE') {
1523
+ return [{
1524
+ type: 'blockquote',
1525
+ children
1526
+ }];
1527
+ }
1528
+ if (nodeName === 'OL') {
1529
+ return [{
1530
+ type: 'ordered-list',
1531
+ children
1532
+ }];
1533
+ }
1534
+ if (nodeName === 'UL') {
1535
+ return [{
1536
+ type: 'unordered-list',
1537
+ children
1538
+ }];
1539
+ }
1540
+ if (nodeName === 'DIV' && !isBlock(children[0])) {
1541
+ return [{
1542
+ type: 'paragraph',
1543
+ children
1544
+ }];
1545
+ }
1546
+ return deserialized;
1547
+ });
1548
+ }
1549
+ function deserializeNodes(nodes) {
1550
+ const outputNodes = [];
1551
+ for (const node of nodes) {
1552
+ outputNodes.push(...deserializeHTMLNode(node));
1553
+ }
1554
+ return outputNodes;
1555
+ }
1556
+ function fixNodesForBlockChildren(deserializedNodes) {
1557
+ if (!deserializedNodes.length) {
1558
+ // Slate also gets unhappy if an element has no children
1559
+ // the empty text nodes will get normalized away if they're not needed
1560
+ return [{
1561
+ text: ''
1562
+ }];
1563
+ }
1564
+ if (deserializedNodes.some(isBlock)) {
1565
+ const result = [];
1566
+ let queuedInlines = [];
1567
+ const flushInlines = () => {
1568
+ if (queuedInlines.length) {
1569
+ result.push({
1570
+ type: 'paragraph',
1571
+ children: queuedInlines
1572
+ });
1573
+ queuedInlines = [];
1574
+ }
1575
+ };
1576
+ for (const node of deserializedNodes) {
1577
+ if (isBlock(node)) {
1578
+ flushInlines();
1579
+ result.push(node);
1580
+ continue;
1581
+ }
1582
+ // we want to ignore whitespace between block level elements
1583
+ // useful info about whitespace in html:
1584
+ // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
1585
+ if (slate.Node.string(node).trim() !== '') {
1586
+ queuedInlines.push(node);
1587
+ }
1588
+ }
1589
+ flushInlines();
1590
+ return result;
1591
+ }
1592
+ return deserializedNodes;
1593
+ }
1594
+
1595
+ const markdownConfig = {
1596
+ mdastExtensions: [autoLinkLiteralFromMarkdownExtension__default["default"], gfmStrikethroughFromMarkdownExtension__default["default"]],
1597
+ extensions: [autoLinkLiteralMarkdownSyntax__default["default"], gfmStrikethroughMarkdownSyntax__default["default"]()]
1598
+ };
1599
+ function deserializeMarkdown(markdown) {
1600
+ const root = mdASTUtilFromMarkdown__default["default"](markdown, markdownConfig);
1601
+ let nodes = root.children;
1602
+ if (nodes.length === 1 && nodes[0].type === 'paragraph') {
1603
+ nodes = nodes[0].children;
1604
+ }
1605
+ return deserializeChildren(nodes, markdown);
1606
+ }
1607
+ function deserializeChildren(nodes, input) {
1608
+ const outputNodes = [];
1609
+ for (const node of nodes) {
1610
+ const result = deserializeMarkdownNode(node, input);
1611
+ if (result.length) {
1612
+ outputNodes.push(...result);
1613
+ }
1614
+ }
1615
+ if (!outputNodes.length) {
1616
+ outputNodes.push({
1617
+ text: ''
1618
+ });
1619
+ }
1620
+ return outputNodes;
1621
+ }
1622
+ function deserializeMarkdownNode(node, input) {
1623
+ switch (node.type) {
1624
+ case 'blockquote':
1625
+ return [{
1626
+ type: 'blockquote',
1627
+ children: deserializeChildren(node.children, input)
1628
+ }];
1629
+ case 'link':
1630
+ {
1631
+ // arguably this could just return a link node rather than use setLinkForChildren since the children _should_ only be inlines
1632
+ // but rather than relying on the markdown parser we use being correct in this way since it isn't nicely codified in types
1633
+ // let's be safe since we already have the code to do it the safer way because of html pasting
1634
+ return setLinkForChildren(node.url, () => deserializeChildren(node.children, input));
1635
+ }
1636
+ case 'code':
1637
+ return [{
1638
+ type: 'code',
1639
+ children: [{
1640
+ text: node.value
1641
+ }]
1642
+ }];
1643
+ case 'paragraph':
1644
+ return [{
1645
+ type: 'paragraph',
1646
+ children: deserializeChildren(node.children, input)
1647
+ }];
1648
+ case 'heading':
1649
+ {
1650
+ return [{
1651
+ type: 'heading',
1652
+ level: node.depth,
1653
+ children: deserializeChildren(node.children, input)
1654
+ }];
1655
+ }
1656
+ case 'list':
1657
+ {
1658
+ return [{
1659
+ type: node.ordered ? 'ordered-list' : 'unordered-list',
1660
+ children: deserializeChildren(node.children, input)
1661
+ }];
1662
+ }
1663
+ case 'listItem':
1664
+ return [{
1665
+ type: 'list-item',
1666
+ children: deserializeChildren(node.children, input)
1667
+ }];
1668
+ case 'thematicBreak':
1669
+ return [{
1670
+ type: 'divider',
1671
+ children: [{
1672
+ text: ''
1673
+ }]
1674
+ }];
1675
+ case 'break':
1676
+ return getInlineNodes('\n');
1677
+ case 'delete':
1678
+ return addMarkToChildren('strikethrough', () => deserializeChildren(node.children, input));
1679
+ case 'strong':
1680
+ return addMarkToChildren('bold', () => deserializeChildren(node.children, input));
1681
+ case 'emphasis':
1682
+ return addMarkToChildren('italic', () => deserializeChildren(node.children, input));
1683
+ case 'inlineCode':
1684
+ return addMarkToChildren('code', () => getInlineNodes(node.value));
1685
+ case 'text':
1686
+ return getInlineNodes(node.value);
1687
+ }
1688
+ return getInlineNodes(input.slice(node.position.start.offset, node.position.end.offset));
1689
+ }
1690
+
1691
+ const urlPattern = /https?:\/\//;
1692
+ function insertFragmentButDifferent(editor, nodes) {
1693
+ const firstNode = nodes[0];
1694
+ if (slate.Element.isElement(firstNode) && slate.Editor.isBlock(editor, firstNode)) {
1695
+ utils.insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading(editor, nodes);
1696
+ } else {
1697
+ slate.Transforms.insertFragment(editor, nodes);
1698
+ }
1699
+ }
1700
+ function withPasting(editor) {
1701
+ const {
1702
+ insertData,
1703
+ setFragmentData
1704
+ } = editor;
1705
+ editor.setFragmentData = data => {
1706
+ if (editor.selection) {
1707
+ data.setData('application/x-keystone-document-editor', 'true');
1708
+ }
1709
+ setFragmentData(data);
1710
+ };
1711
+ editor.insertData = data => {
1712
+ // this exists because behind the scenes, Slate sets the slate document
1713
+ // on the data transfer, this is great because it means when you copy and paste
1714
+ // something in the editor or between editors, it'll use the actual Slate data
1715
+ // rather than the serialized html so component blocks and etc. will work fine
1716
+ // we're setting application/x-keystone-document-editor
1717
+ // though so that we only accept slate data from Keystone's editor
1718
+ // because other editors will likely have a different structure
1719
+ // so we'll rely on the html deserialization instead
1720
+ // (note that yes, we do call insertData at the end of this function
1721
+ // which is where Slate's logic will run, it'll never do anything there though
1722
+ // since anything that will have slate data will also have text/html which we handle
1723
+ // before we call insertData)
1724
+ // TODO: handle the case of copying between editors with different components blocks
1725
+ // (right now, things will blow up in most cases)
1726
+ if (data.getData('application/x-keystone-document-editor') === 'true') {
1727
+ insertData(data);
1728
+ return;
1729
+ }
1730
+ const blockAbove = slate.Editor.above(editor, {
1731
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node)
1732
+ });
1733
+ if ((blockAbove === null || blockAbove === void 0 ? void 0 : blockAbove[0].type) === 'code') {
1734
+ const plain = data.getData('text/plain');
1735
+ editor.insertText(plain);
1736
+ return;
1737
+ }
1738
+ const vsCodeEditorData = data.getData('vscode-editor-data');
1739
+ if (vsCodeEditorData) {
1740
+ try {
1741
+ const vsCodeData = JSON.parse(vsCodeEditorData);
1742
+ if ((vsCodeData === null || vsCodeData === void 0 ? void 0 : vsCodeData.mode) === 'markdown' || (vsCodeData === null || vsCodeData === void 0 ? void 0 : vsCodeData.mode) === 'mdx') {
1743
+ const plain = data.getData('text/plain');
1744
+ if (plain) {
1745
+ const fragment = deserializeMarkdown(plain);
1746
+ insertFragmentButDifferent(editor, fragment);
1747
+ return;
1748
+ }
1749
+ }
1750
+ } catch (err) {
1751
+ console.log(err);
1752
+ }
1753
+ }
1754
+ const plain = data.getData('text/plain');
1755
+ if (
1756
+ // isValidURL is a bit more permissive than a user might expect
1757
+ // so for pasting, we'll constrain it to starting with https:// or http://
1758
+ urlPattern.test(plain) && index.isValidURL(plain) && editor.selection && !slate.Range.isCollapsed(editor.selection) &&
1759
+ // we only want to turn the selected text into a link if the selection is within the same block
1760
+ slate.Editor.above(editor, {
1761
+ match: node => slate.Element.isElement(node) && slate.Editor.isBlock(editor, node) && !(slate.Element.isElement(node.children[0]) && slate.Editor.isBlock(editor, node.children[0]))
1762
+ }) &&
1763
+ // and there is only text(potentially with marks) in the selection
1764
+ // no other links or inline relationships
1765
+ slate.Editor.nodes(editor, {
1766
+ match: node => slate.Element.isElement(node) && slate.Editor.isInline(editor, node)
1767
+ }).next().done) {
1768
+ slate.Transforms.wrapNodes(editor, {
1769
+ type: 'link',
1770
+ href: plain,
1771
+ children: []
1772
+ }, {
1773
+ split: true
1774
+ });
1775
+ return;
1776
+ }
1777
+ const html = data.getData('text/html');
1778
+ if (html) {
1779
+ const fragment = deserializeHTML(html);
1780
+ insertFragmentButDifferent(editor, fragment);
1781
+ return;
1782
+ }
1783
+ if (plain) {
1784
+ const fragment = deserializeMarkdown(plain);
1785
+ insertFragmentButDifferent(editor, fragment);
1786
+ return;
1787
+ }
1788
+ insertData(data);
1789
+ };
1790
+ return editor;
1791
+ }
1792
+
1793
+ const blockquoteChildren = ['paragraph', 'code', 'heading', 'ordered-list', 'unordered-list', 'divider'];
1794
+ const paragraphLike = [...blockquoteChildren, 'blockquote'];
1795
+ const insideOfLayouts = [...paragraphLike, 'component-block'];
1796
+ const editorSchema = {
1797
+ editor: blockContainer({
1798
+ allowedChildren: [...insideOfLayouts, 'layout'],
1799
+ invalidPositionHandleMode: 'move'
1800
+ }),
1801
+ layout: blockContainer({
1802
+ allowedChildren: ['layout-area'],
1803
+ invalidPositionHandleMode: 'move'
1804
+ }),
1805
+ 'layout-area': blockContainer({
1806
+ allowedChildren: insideOfLayouts,
1807
+ invalidPositionHandleMode: 'unwrap'
1808
+ }),
1809
+ blockquote: blockContainer({
1810
+ allowedChildren: blockquoteChildren,
1811
+ invalidPositionHandleMode: 'move'
1812
+ }),
1813
+ paragraph: inlineContainer({
1814
+ invalidPositionHandleMode: 'unwrap'
1815
+ }),
1816
+ code: inlineContainer({
1817
+ invalidPositionHandleMode: 'move'
1818
+ }),
1819
+ divider: inlineContainer({
1820
+ invalidPositionHandleMode: 'move'
1821
+ }),
1822
+ heading: inlineContainer({
1823
+ invalidPositionHandleMode: 'unwrap'
1824
+ }),
1825
+ 'component-block': blockContainer({
1826
+ allowedChildren: ['component-block-prop', 'component-inline-prop'],
1827
+ invalidPositionHandleMode: 'move'
1828
+ }),
1829
+ 'component-inline-prop': inlineContainer({
1830
+ invalidPositionHandleMode: 'unwrap'
1831
+ }),
1832
+ 'component-block-prop': blockContainer({
1833
+ allowedChildren: insideOfLayouts,
1834
+ invalidPositionHandleMode: 'unwrap'
1835
+ }),
1836
+ 'ordered-list': blockContainer({
1837
+ allowedChildren: ['list-item'],
1838
+ invalidPositionHandleMode: 'move'
1839
+ }),
1840
+ 'unordered-list': blockContainer({
1841
+ allowedChildren: ['list-item'],
1842
+ invalidPositionHandleMode: 'move'
1843
+ }),
1844
+ 'list-item': blockContainer({
1845
+ allowedChildren: ['list-item-content', 'ordered-list', 'unordered-list'],
1846
+ invalidPositionHandleMode: 'unwrap'
1847
+ }),
1848
+ 'list-item-content': inlineContainer({
1849
+ invalidPositionHandleMode: 'unwrap'
1850
+ })
1851
+ };
1852
+ function inlineContainer(args) {
1853
+ return {
1854
+ kind: 'inlines',
1855
+ invalidPositionHandleMode: args.invalidPositionHandleMode
1856
+ };
1857
+ }
1858
+ const inlineContainerTypes = new Set(Object.entries(editorSchema).filter(([, value]) => value.kind === 'inlines').map(([type]) => type));
1859
+ function isInlineContainer(node) {
1860
+ return node.type !== undefined && inlineContainerTypes.has(node.type);
1861
+ }
1862
+ function createDocumentEditor(documentFeatures, componentBlocks, relationships, slate$1) {
1863
+ var _slate$withReact;
1864
+ return withPasting(withSoftBreaks(withBlocksSchema(withLink(documentFeatures, componentBlocks, shared.withList(withHeading(withRelationship(withInsertMenu(withComponentBlocks(componentBlocks, documentFeatures, relationships, shared.withParagraphs(withShortcuts(withDivider(shared.withLayouts(withMarks(documentFeatures, componentBlocks, withCodeBlock(withBlockMarkdownShortcuts(documentFeatures, componentBlocks, withBlockquote(shared.withDocumentFeaturesNormalization(documentFeatures, relationships, slateHistory.withHistory((_slate$withReact = slate$1 === null || slate$1 === void 0 ? void 0 : slate$1.withReact(slate.createEditor())) !== null && _slate$withReact !== void 0 ? _slate$withReact : slate.createEditor())))))))))))))))))));
1865
+ }
1866
+ function blockContainer(args) {
1867
+ return {
1868
+ kind: 'blocks',
1869
+ allowedChildren: new Set(args.allowedChildren),
1870
+ blockToWrapInlinesIn: args.allowedChildren[0],
1871
+ invalidPositionHandleMode: args.invalidPositionHandleMode
1872
+ };
1873
+ }
1874
+ const blockTypes = new Set(Object.keys(editorSchema).filter(x => x !== 'editor'));
1875
+ function isBlock(node) {
1876
+ return blockTypes.has(node.type);
1877
+ }
1878
+ function withBlocksSchema(editor) {
1879
+ const {
1880
+ normalizeNode
1881
+ } = editor;
1882
+ editor.normalizeNode = ([node, path]) => {
1883
+ if (!slate.Text.isText(node) && node.type !== 'link' && node.type !== 'relationship') {
1884
+ const nodeType = slate.Editor.isEditor(node) ? 'editor' : node.type;
1885
+ if (typeof nodeType !== 'string' || editorSchema[nodeType] === undefined) {
1886
+ slate.Transforms.unwrapNodes(editor, {
1887
+ at: path
1888
+ });
1889
+ return;
1890
+ }
1891
+ const info = editorSchema[nodeType];
1892
+ if (info.kind === 'blocks' && node.children.length !== 0 && node.children.every(child => !(slate.Element.isElement(child) && slate.Editor.isBlock(editor, child)))) {
1893
+ slate.Transforms.wrapNodes(editor, {
1894
+ type: info.blockToWrapInlinesIn,
1895
+ children: []
1896
+ }, {
1897
+ at: path,
1898
+ match: node => !(slate.Element.isElement(node) && slate.Editor.isBlock(editor, node))
1899
+ });
1900
+ return;
1901
+ }
1902
+ for (const [index, childNode] of node.children.entries()) {
1903
+ const childPath = [...path, index];
1904
+ if (info.kind === 'inlines') {
1905
+ if (!slate.Text.isText(childNode) && !slate.Editor.isInline(editor, childNode) &&
1906
+ // these checks are implicit in Editor.isBlock
1907
+ // but that isn't encoded in types so these will make TS happy
1908
+ childNode.type !== 'link' && childNode.type !== 'relationship') {
1909
+ handleNodeInInvalidPosition(editor, [childNode, childPath], path);
1910
+ return;
1911
+ }
1912
+ } else {
1913
+ if (!(slate.Element.isElement(childNode) && slate.Editor.isBlock(editor, childNode)) ||
1914
+ // these checks are implicit in Editor.isBlock
1915
+ // but that isn't encoded in types so these will make TS happy
1916
+ childNode.type === 'link' || childNode.type === 'relationship') {
1917
+ slate.Transforms.wrapNodes(editor, {
1918
+ type: info.blockToWrapInlinesIn,
1919
+ children: []
1920
+ }, {
1921
+ at: childPath
1922
+ });
1923
+ return;
1924
+ }
1925
+ if (slate.Element.isElement(childNode) && slate.Editor.isBlock(editor, childNode) && !info.allowedChildren.has(childNode.type)) {
1926
+ handleNodeInInvalidPosition(editor, [childNode, childPath], path);
1927
+ return;
1928
+ }
1929
+ }
1930
+ }
1931
+ }
1932
+ normalizeNode([node, path]);
1933
+ };
1934
+ return editor;
1935
+ }
1936
+ function handleNodeInInvalidPosition(editor, [node, path], parentPath) {
1937
+ const nodeType = node.type;
1938
+ const childNodeInfo = editorSchema[nodeType];
1939
+ // the parent of a block will never be an inline so this casting is okay
1940
+ const parentNode = slate.Node.get(editor, parentPath);
1941
+ const parentNodeType = slate.Editor.isEditor(parentNode) ? 'editor' : parentNode.type;
1942
+ const parentNodeInfo = editorSchema[parentNodeType];
1943
+ if (!childNodeInfo || childNodeInfo.invalidPositionHandleMode === 'unwrap') {
1944
+ if (parentNodeInfo.kind === 'blocks' && parentNodeInfo.blockToWrapInlinesIn) {
1945
+ slate.Transforms.setNodes(editor, {
1946
+ type: parentNodeInfo.blockToWrapInlinesIn,
1947
+ ...Object.fromEntries(Object.keys(node).filter(key => key !== 'type' && key !== 'children').map(key => [key, null])) // the Slate types don't understand that null is allowed and it will unset properties with setNodes
1948
+ }, {
1949
+ at: path
1950
+ });
1951
+ return;
1952
+ }
1953
+ slate.Transforms.unwrapNodes(editor, {
1954
+ at: path
1955
+ });
1956
+ return;
1957
+ }
1958
+ const info = editorSchema[parentNode.type || 'editor'];
1959
+ if ((info === null || info === void 0 ? void 0 : info.kind) === 'blocks' && info.allowedChildren.has(nodeType)) {
1960
+ if (parentPath.length === 0) {
1961
+ slate.Transforms.moveNodes(editor, {
1962
+ at: path,
1963
+ to: [path[0] + 1]
1964
+ });
1965
+ } else {
1966
+ slate.Transforms.moveNodes(editor, {
1967
+ at: path,
1968
+ to: slate.Path.next(parentPath)
1969
+ });
1970
+ }
1971
+ return;
1972
+ }
1973
+ if (slate.Editor.isEditor(parentNode)) {
1974
+ slate.Transforms.moveNodes(editor, {
1975
+ at: path,
1976
+ to: [path[0] + 1]
1977
+ });
1978
+ slate.Transforms.unwrapNodes(editor, {
1979
+ at: [path[0] + 1]
1980
+ });
1981
+ return;
1982
+ }
1983
+ handleNodeInInvalidPosition(editor, [node, path], parentPath.slice(0, -1));
1984
+ }
1985
+
1986
+ // to print the editor schema in Graphviz if you want to visualize it
1987
+ // function printEditorSchema(editorSchema: EditorSchema) {
1988
+ // return `digraph G {
1989
+ // concentrate=true;
1990
+ // ${Object.keys(editorSchema)
1991
+ // .map(key => {
1992
+ // let val = editorSchema[key];
1993
+ // if (val.kind === 'inlines') {
1994
+ // return `"${key}" -> inlines`;
1995
+ // }
1996
+ // if (val.kind === 'blocks') {
1997
+ // return `"${key}" -> {${[...val.allowedChildren].map(x => JSON.stringify(x)).join(' ')}}`;
1998
+ // }
1999
+ // })
2000
+ // .join('\n ')}
2001
+ // }`;
2002
+ // }
2003
+
2004
+ exports.createDocumentEditor = createDocumentEditor;
2005
+ exports.insertBlockquote = insertBlockquote;
2006
+ exports.insertDivider = insertDivider;
2007
+ exports.wrapLink = wrapLink;