ckeditor5-elements-schema 0.0.0-stage → 49.0.0-alpha.4

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -2
  3. package/dist/augmentation.d.ts +25 -0
  4. package/dist/augmentation.d.ts.map +1 -0
  5. package/dist/augmentation.js +2 -0
  6. package/dist/augmentation.js.map +1 -0
  7. package/dist/commands/elementBlockCommand.d.ts +46 -0
  8. package/dist/commands/elementBlockCommand.d.ts.map +1 -0
  9. package/dist/commands/elementBlockCommand.js +71 -0
  10. package/dist/commands/elementBlockCommand.js.map +1 -0
  11. package/dist/commands/elementContainerCommand.d.ts +36 -0
  12. package/dist/commands/elementContainerCommand.d.ts.map +1 -0
  13. package/dist/commands/elementContainerCommand.js +67 -0
  14. package/dist/commands/elementContainerCommand.js.map +1 -0
  15. package/dist/commands/elementInlineCommand.d.ts +87 -0
  16. package/dist/commands/elementInlineCommand.d.ts.map +1 -0
  17. package/dist/commands/elementInlineCommand.js +253 -0
  18. package/dist/commands/elementInlineCommand.js.map +1 -0
  19. package/dist/commands/elementInsertCommand.d.ts +33 -0
  20. package/dist/commands/elementInsertCommand.d.ts.map +1 -0
  21. package/dist/commands/elementInsertCommand.js +61 -0
  22. package/dist/commands/elementInsertCommand.js.map +1 -0
  23. package/dist/commands/elementSchemaCommand.d.ts +62 -0
  24. package/dist/commands/elementSchemaCommand.d.ts.map +1 -0
  25. package/dist/commands/elementSchemaCommand.js +107 -0
  26. package/dist/commands/elementSchemaCommand.js.map +1 -0
  27. package/dist/conversion/convertElementSchema.d.ts +17 -0
  28. package/dist/conversion/convertElementSchema.d.ts.map +1 -0
  29. package/dist/conversion/convertElementSchema.js +55 -0
  30. package/dist/conversion/convertElementSchema.js.map +1 -0
  31. package/dist/conversion/inlineTextElement.d.ts +24 -0
  32. package/dist/conversion/inlineTextElement.d.ts.map +1 -0
  33. package/dist/conversion/inlineTextElement.js +28 -0
  34. package/dist/conversion/inlineTextElement.js.map +1 -0
  35. package/dist/definition/canHold.d.ts +10 -0
  36. package/dist/definition/canHold.d.ts.map +1 -0
  37. package/dist/definition/canHold.js +22 -0
  38. package/dist/definition/canHold.js.map +1 -0
  39. package/dist/definition/compileElementSchema.d.ts +41 -0
  40. package/dist/definition/compileElementSchema.d.ts.map +1 -0
  41. package/dist/definition/compileElementSchema.js +60 -0
  42. package/dist/definition/compileElementSchema.js.map +1 -0
  43. package/dist/definition/elementSchemaError.d.ts +12 -0
  44. package/dist/definition/elementSchemaError.d.ts.map +1 -0
  45. package/dist/definition/elementSchemaError.js +16 -0
  46. package/dist/definition/elementSchemaError.js.map +1 -0
  47. package/dist/definition/readDefinition.d.ts +18 -0
  48. package/dist/definition/readDefinition.d.ts.map +1 -0
  49. package/dist/definition/readDefinition.js +60 -0
  50. package/dist/definition/readDefinition.js.map +1 -0
  51. package/dist/definition/suggest.d.ts +5 -0
  52. package/dist/definition/suggest.d.ts.map +1 -0
  53. package/dist/definition/suggest.js +27 -0
  54. package/dist/definition/suggest.js.map +1 -0
  55. package/dist/definition/types.d.ts +105 -0
  56. package/dist/definition/types.d.ts.map +1 -0
  57. package/dist/definition/types.js +2 -0
  58. package/dist/definition/types.js.map +1 -0
  59. package/dist/definition/validateElementSchema.d.ts +26 -0
  60. package/dist/definition/validateElementSchema.d.ts.map +1 -0
  61. package/dist/definition/validateElementSchema.js +145 -0
  62. package/dist/definition/validateElementSchema.js.map +1 -0
  63. package/dist/elementSchema.d.ts +47 -0
  64. package/dist/elementSchema.d.ts.map +1 -0
  65. package/dist/elementSchema.js +108 -0
  66. package/dist/elementSchema.js.map +1 -0
  67. package/dist/elementSchemaCommands.d.ts +83 -0
  68. package/dist/elementSchemaCommands.d.ts.map +1 -0
  69. package/dist/elementSchemaCommands.js +137 -0
  70. package/dist/elementSchemaCommands.js.map +1 -0
  71. package/dist/index.d.ts +18 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +22 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/typing/twoStepCaretMovement.d.ts +11 -0
  76. package/dist/typing/twoStepCaretMovement.d.ts.map +1 -0
  77. package/dist/typing/twoStepCaretMovement.js +76 -0
  78. package/dist/typing/twoStepCaretMovement.js.map +1 -0
  79. package/dist/validate.d.ts +12 -0
  80. package/dist/validate.d.ts.map +1 -0
  81. package/dist/validate.js +11 -0
  82. package/dist/validate.js.map +1 -0
  83. package/package.json +49 -3
  84. package/src/augmentation.ts +28 -0
  85. package/src/commands/elementBlockCommand.ts +87 -0
  86. package/src/commands/elementContainerCommand.ts +83 -0
  87. package/src/commands/elementInlineCommand.ts +311 -0
  88. package/src/commands/elementInsertCommand.ts +74 -0
  89. package/src/commands/elementSchemaCommand.ts +140 -0
  90. package/src/conversion/convertElementSchema.ts +65 -0
  91. package/src/conversion/inlineTextElement.ts +31 -0
  92. package/src/definition/canHold.ts +29 -0
  93. package/src/definition/compileElementSchema.ts +88 -0
  94. package/src/definition/elementSchemaError.ts +21 -0
  95. package/src/definition/readDefinition.ts +98 -0
  96. package/src/definition/suggest.ts +38 -0
  97. package/src/definition/types.ts +123 -0
  98. package/src/definition/validateElementSchema.ts +238 -0
  99. package/src/elementSchema.ts +133 -0
  100. package/src/elementSchemaCommands.ts +202 -0
  101. package/src/index.ts +30 -0
  102. package/src/typing/twoStepCaretMovement.ts +119 -0
  103. package/src/validate.ts +20 -0
@@ -0,0 +1,145 @@
1
+ import { ROOT, whyNot } from './canHold.js';
2
+ import { ATTRIBUTE_NAME, ELEMENT_KEYS, ELEMENT_NAME, TAG_NAME, checkKeys, isRecord, readKind, readNames } from './readDefinition.js';
3
+ import { suggest } from './suggest.js';
4
+ /**
5
+ * Checks an element schema, so that it is not contradictory, and returns the problems. An empty array means that the
6
+ * schema is valid.
7
+ *
8
+ * The schema is rejected when it:
9
+ *
10
+ * * is not of the shape {@link ElementSchemaDefinition}: an object of elements, by name; has keys that are not part of
11
+ * it (typos are problems), or has values of the wrong type;
12
+ * * declares an element with an unknown `type`, or an `empty` element with `allowChildren`;
13
+ * * has attribute names that are not valid, or that differ only in case (HTML attribute names are not case sensitive),
14
+ * or an `editingTag` that is not the name of a tag;
15
+ * * refers to elements that do not exist (a name that is neither declared in the schema, nor in `external`), or lists the
16
+ * same name twice;
17
+ * * allows what its type cannot hold: text or an inline element in a container or in the root, a block-level (not inline)
18
+ * element in a block, or anything in an `empty` element, whichever side says it: `allowChildren` of the parent, or
19
+ * `allowIn` of the child.
20
+ *
21
+ * `isInline` is about the position and the layout of an element, so it is valid for every type: an inline element is
22
+ * laid out in a line with its siblings. Inline elements are allowed only in blocks, which can hold several of them,
23
+ * e.g. inline containers, or inline images between text.
24
+ *
25
+ * It does not need an editor. The editor of `ElementSchema` runs it with the elements of the other plugins as `external`.
26
+ */
27
+ export function validateElementSchema(input, { external = {} } = {}) {
28
+ const issues = [];
29
+ const report = (path, message) => {
30
+ issues.push({ path, message });
31
+ };
32
+ if (!isRecord(input)) {
33
+ report('', 'The schema must be an object of element definitions, by name: { "note": { "type": "container" } }.');
34
+ return issues;
35
+ }
36
+ const elements = input;
37
+ if (!Object.keys(elements).length) {
38
+ report('', 'The schema declares no elements: declare at least one.');
39
+ return issues;
40
+ }
41
+ if (isRecord(elements.elements) && !('type' in elements.elements)) {
42
+ report('elements', 'the schema is the object of the elements itself: remove the "elements" wrapper, and put the elements at the top.');
43
+ return issues;
44
+ }
45
+ // The first pass checks every definition on its own, and collects the kinds and the lists to cross-check.
46
+ const kinds = new Map();
47
+ const lists = new Map();
48
+ for (const [name, definition] of Object.entries(elements)) {
49
+ const path = name;
50
+ if (!ELEMENT_NAME.test(name)) {
51
+ report(path, `"${name}" is not a valid element name: use camelCase letters and digits, starting with a lowercase letter.`);
52
+ }
53
+ if (name in external) {
54
+ report(path, `"${name}" already exists in the editor (it is declared by another plugin): remove it, or rename it.`);
55
+ }
56
+ if (!isRecord(definition)) {
57
+ report(path, 'must be an object, e.g. { "type": "block" }.');
58
+ continue;
59
+ }
60
+ checkKeys(definition, ELEMENT_KEYS, path, report);
61
+ const kind = readKind(definition, path, report);
62
+ if (kind) {
63
+ kinds.set(name, kind);
64
+ }
65
+ const allowChildren = readNames(definition, 'allowChildren', path, report);
66
+ const allowIn = readNames(definition, 'allowIn', path, report);
67
+ const allowAttributes = readNames(definition, 'allowAttributes', path, report);
68
+ if (allowIn && !allowIn.length) {
69
+ report(`${path}.allowIn`, 'is empty, so the element would be allowed nowhere: list its parents, or remove it for the default places.');
70
+ }
71
+ const lowercase = new Map();
72
+ for (const [index, attribute] of (allowAttributes ?? []).entries()) {
73
+ const attributePath = `${path}.allowAttributes[${String(index)}]`;
74
+ const collides = lowercase.get(attribute.toLowerCase());
75
+ if (!ATTRIBUTE_NAME.test(attribute)) {
76
+ report(attributePath, `"${attribute}" is not a valid attribute name.`);
77
+ }
78
+ else if (collides !== undefined) {
79
+ report(attributePath, `"${attribute}" collides with "${collides}": the names of HTML attributes are not case sensitive, so the data could not tell them apart.`);
80
+ }
81
+ else {
82
+ lowercase.set(attribute.toLowerCase(), attribute);
83
+ }
84
+ }
85
+ if (definition.editingTag !== undefined &&
86
+ (typeof definition.editingTag !== 'string' || !TAG_NAME.test(definition.editingTag))) {
87
+ report(`${path}.editingTag`, `must be the name of an HTML tag, in lowercase, e.g. "section", not ${JSON.stringify(definition.editingTag)}.`);
88
+ }
89
+ if (kind?.type === 'empty' && allowChildren?.length) {
90
+ report(`${path}.allowChildren`, 'an empty element has no content: remove allowChildren, or change the type.');
91
+ }
92
+ lists.set(name, { allowChildren: kind?.type === 'empty' ? [] : (allowChildren ?? []), allowIn: allowIn ?? [] });
93
+ }
94
+ // The second pass checks what the lists refer to, and whether the types can hold each other.
95
+ const known = (name) => kinds.get(name) ?? external[name];
96
+ const candidates = [...kinds.keys(), ...Object.keys(external)];
97
+ for (const [name, { allowChildren, allowIn }] of lists) {
98
+ const kind = kinds.get(name);
99
+ if (!kind) {
100
+ continue;
101
+ }
102
+ allowChildren.forEach((child, index) => {
103
+ const path = `${name}.allowChildren[${String(index)}]`;
104
+ const childKind = child === '$text' ? child : resolveReference(child, known, candidates, path, report);
105
+ if (childKind) {
106
+ const reason = whyNot(kind, childKind);
107
+ if (reason) {
108
+ report(path, `"${child}" cannot be a child of "${name}": ${reason}.`);
109
+ }
110
+ }
111
+ });
112
+ allowIn.forEach((parent, index) => {
113
+ const path = `${name}.allowIn[${String(index)}]`;
114
+ const parentKind = parent === '$root' ? ROOT : resolveReference(parent, known, candidates, path, report);
115
+ if (parentKind) {
116
+ const reason = whyNot(parentKind, kind);
117
+ if (reason) {
118
+ report(path, `"${name}" cannot be allowed in ${parent === '$root' ? 'the root' : `"${parent}"`}: ${reason}.`);
119
+ }
120
+ }
121
+ });
122
+ }
123
+ // In the order of the file: the problems of an element together, whichever pass found them.
124
+ const names = Object.keys(elements);
125
+ const position = ({ path }) => names.findIndex(name => path === name || path.startsWith(`${name}.`));
126
+ return issues.sort((a, b) => position(a) - position(b));
127
+ }
128
+ /**
129
+ * Returns the kind of the element the list entry names. Reports an unknown name (with the closest known one), and the
130
+ * names that cannot be used there: `$text` and `$root` are not elements that can be both parents and children.
131
+ */
132
+ function resolveReference(name, known, candidates, path, report) {
133
+ if (name === '$text' || name === '$root') {
134
+ report(path, name === '$text'
135
+ ? '"$text" is not an element: it is text, and cannot have children or parents.'
136
+ : '"$root" is the root of the editor: it can be a parent (allowIn), not a child.');
137
+ return undefined;
138
+ }
139
+ const kind = known(name);
140
+ if (!kind) {
141
+ report(path, `unknown element "${name}"${suggest(name, candidates)}. It must be declared in the schema, or be an element of another plugin that is loaded.`);
142
+ }
143
+ return kind;
144
+ }
145
+ //# sourceMappingURL=validateElementSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateElementSchema.js","sourceRoot":"","sources":["../../src/definition/validateElementSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACN,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,EAET,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,qBAAqB,CACpC,KAAc,EACd,EAAE,QAAQ,GAAG,EAAE,KAAmC,EAAE;IAEpD,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,OAAe,EAAQ,EAAE;QACtD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,CACL,EAAE,EACF,oGAAoG,CACpG,CAAC;QAEF,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC;IAEvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QACnC,MAAM,CAAC,EAAE,EAAE,wDAAwD,CAAC,CAAC;QAErE,OAAO,MAAM,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnE,MAAM,CACL,UAAU,EACV,kHAAkH,CAClH,CAAC;QAEF,OAAO,MAAM,CAAC;IACf,CAAC;IAED,0GAA0G;IAC1G,MAAM,KAAK,GAAG,IAAI,GAAG,EAA6B,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0D,CAAC;IAEhF,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,CACL,IAAI,EACJ,IAAI,IAAI,oGAAoG,CAC5G,CAAC;QACH,CAAC;QAED,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC;YACtB,MAAM,CACL,IAAI,EACJ,IAAI,IAAI,6FAA6F,CACrG,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,EAAE,8CAA8C,CAAC,CAAC;YAC7D,SAAS;QACV,CAAC;QAED,SAAS,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAElD,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAEhD,IAAI,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,eAAe,GAAG,SAAS,CAAC,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAE/E,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,CACL,GAAG,IAAI,UAAU,EACjB,2GAA2G,CAC3G,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE5C,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YACpE,MAAM,aAAa,GAAG,GAAG,IAAI,oBAAoB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;YAClE,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;YAExD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrC,MAAM,CAAC,aAAa,EAAE,IAAI,SAAS,kCAAkC,CAAC,CAAC;YACxE,CAAC;iBAAM,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACnC,MAAM,CACL,aAAa,EACb,IAAI,SAAS,oBAAoB,QAAQ,gGAAgG,CACzI,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,CAAC;YACnD,CAAC;QACF,CAAC;QAED,IACC,UAAU,CAAC,UAAU,KAAK,SAAS;YACnC,CAAC,OAAO,UAAU,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EACnF,CAAC;YACF,MAAM,CACL,GAAG,IAAI,aAAa,EACpB,sEAAsE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAC9G,CAAC;QACH,CAAC;QAED,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO,IAAI,aAAa,EAAE,MAAM,EAAE,CAAC;YACrD,MAAM,CACL,GAAG,IAAI,gBAAgB,EACvB,4EAA4E,CAC5E,CAAC;QACH,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;IACjH,CAAC;IAED,6FAA6F;IAC7F,MAAM,KAAK,GAAG,CAAC,IAAY,EAAiC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjG,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE/D,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,SAAS;QACV,CAAC;QAED,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACtC,MAAM,IAAI,GAAG,GAAG,IAAI,kBAAkB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;YACvD,MAAM,SAAS,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAEvG,IAAI,SAAS,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBAEvC,IAAI,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,2BAA2B,IAAI,MAAM,MAAM,GAAG,CAAC,CAAC;gBACvE,CAAC;YACF,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,GAAG,IAAI,YAAY,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;YACjD,MAAM,UAAU,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAEzG,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBAExC,IAAI,MAAM,EAAE,CAAC;oBACZ,MAAM,CACL,IAAI,EACJ,IAAI,IAAI,0BAA0B,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,MAAM,GAAG,KAAK,MAAM,GAAG,CAC/F,CAAC;gBACH,CAAC;YACF,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,4FAA4F;IAC5F,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAsB,EAAU,EAAE,CACzD,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAEvE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CACxB,IAAY,EACZ,KAAsD,EACtD,UAA6B,EAC7B,IAAY,EACZ,MAAc;IAEd,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1C,MAAM,CACL,IAAI,EACJ,IAAI,KAAK,OAAO;YACf,CAAC,CAAC,6EAA6E;YAC/E,CAAC,CAAC,+EAA+E,CAClF,CAAC;QAEF,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAEzB,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,CACL,IAAI,EACJ,oBAAoB,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,yFAAyF,CAC9I,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { Plugin } from 'ckeditor5';
2
+ /**
3
+ * Declares elements in the model schema of the editor from a simple definition, the `elementSchema` editor option: a
4
+ * plain object, e.g. a JSON file.
5
+ *
6
+ * ```json
7
+ * {
8
+ * "note": { "type": "container", "allowAttributes": ["type"] },
9
+ * "title": { "type": "block", "allowIn": ["note"] },
10
+ * "badge": { "type": "empty", "isInline": true, "allowAttributes": ["color"] }
11
+ * }
12
+ * ```
13
+ *
14
+ * Every element has one of three types, instead of CKEditor's many `is*` flags and generic items:
15
+ *
16
+ * * `empty`: has no content, like an image. It is an object, selected as a whole. With `isInline`, it is in a line of text.
17
+ * * `block`: can contain text and inline elements, like a paragraph. With `isInline`, it is an inline element with text,
18
+ * like a bold that is an element with attributes.
19
+ * * `container`: cannot contain text, but blocks, containers and empty elements, like a block quote.
20
+ *
21
+ * `allowAttributes` and `allowChildren` work as in CKEditor's schema. `allowIn` too, but it replaces the default places.
22
+ * See {@link ElementSchemaElement}.
23
+ *
24
+ * The schema is validated first, and a schema that is contradictory or refers to unknown elements is not accepted: the
25
+ * editor is not created, and the {@link ElementSchemaError} lists all the problems (see {@link validateElementSchema}).
26
+ *
27
+ * The elements are registered after the plugins are initialized (in `afterInit()`), so they can refer to the elements
28
+ * of other plugins, e.g. `paragraph`. Plugins that extend or convert the elements must do it in `afterInit()` too, or
29
+ * later; the converters can be declared at any time.
30
+ *
31
+ * The `empty` and `container` elements can be selected as a whole, by a click on them and by the arrow keys, when
32
+ * the `ElementSelectable` plugin is loaded too: the element schema does not depend on it, and uses it when it is there
33
+ * (with `elementSchemaConverters`, it warns when it is not).
34
+ *
35
+ * The plugin declares the schema only: how the elements look in the editor and in the data is declared with
36
+ * converters, or by the plugin when the `elementSchemaConverters` option is `true`: one to one, as `div` and `span`.
37
+ */
38
+ export default class ElementSchema extends Plugin {
39
+ static get pluginName(): 'ElementSchema';
40
+ afterInit(): void;
41
+ /**
42
+ * Makes the `empty` elements and the containers selectable as a whole, by a click on them and by the arrow keys, with
43
+ * `ElementSelectable`. Not the blocks: a click in a block puts the cursor in its text.
44
+ */
45
+ private _makeSelectable;
46
+ }
47
+ //# sourceMappingURL=elementSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementSchema.d.ts","sourceRoot":"","sources":["../src/elementSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoB,MAAM,WAAW,CAAC;AAQrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,MAAM;IAChD,WAAkB,UAAU,IAAI,eAAe,CAE9C;IAEM,SAAS,IAAI,IAAI;IA+BxB;;;OAGG;IACH,OAAO,CAAC,eAAe;CAiBvB"}
@@ -0,0 +1,108 @@
1
+ import { Plugin } from 'ckeditor5';
2
+ import { findPlugin } from 'ckeditor5-elements-core';
3
+ import { compileElementSchema } from './definition/compileElementSchema.js';
4
+ import { convertElementSchema } from './conversion/convertElementSchema.js';
5
+ import ElementSchemaError from './definition/elementSchemaError.js';
6
+ import { validateElementSchema } from './definition/validateElementSchema.js';
7
+ /**
8
+ * Declares elements in the model schema of the editor from a simple definition, the `elementSchema` editor option: a
9
+ * plain object, e.g. a JSON file.
10
+ *
11
+ * ```json
12
+ * {
13
+ * "note": { "type": "container", "allowAttributes": ["type"] },
14
+ * "title": { "type": "block", "allowIn": ["note"] },
15
+ * "badge": { "type": "empty", "isInline": true, "allowAttributes": ["color"] }
16
+ * }
17
+ * ```
18
+ *
19
+ * Every element has one of three types, instead of CKEditor's many `is*` flags and generic items:
20
+ *
21
+ * * `empty`: has no content, like an image. It is an object, selected as a whole. With `isInline`, it is in a line of text.
22
+ * * `block`: can contain text and inline elements, like a paragraph. With `isInline`, it is an inline element with text,
23
+ * like a bold that is an element with attributes.
24
+ * * `container`: cannot contain text, but blocks, containers and empty elements, like a block quote.
25
+ *
26
+ * `allowAttributes` and `allowChildren` work as in CKEditor's schema. `allowIn` too, but it replaces the default places.
27
+ * See {@link ElementSchemaElement}.
28
+ *
29
+ * The schema is validated first, and a schema that is contradictory or refers to unknown elements is not accepted: the
30
+ * editor is not created, and the {@link ElementSchemaError} lists all the problems (see {@link validateElementSchema}).
31
+ *
32
+ * The elements are registered after the plugins are initialized (in `afterInit()`), so they can refer to the elements
33
+ * of other plugins, e.g. `paragraph`. Plugins that extend or convert the elements must do it in `afterInit()` too, or
34
+ * later; the converters can be declared at any time.
35
+ *
36
+ * The `empty` and `container` elements can be selected as a whole, by a click on them and by the arrow keys, when
37
+ * the `ElementSelectable` plugin is loaded too: the element schema does not depend on it, and uses it when it is there
38
+ * (with `elementSchemaConverters`, it warns when it is not).
39
+ *
40
+ * The plugin declares the schema only: how the elements look in the editor and in the data is declared with
41
+ * converters, or by the plugin when the `elementSchemaConverters` option is `true`: one to one, as `div` and `span`.
42
+ */
43
+ export default class ElementSchema extends Plugin {
44
+ static get pluginName() {
45
+ return 'ElementSchema';
46
+ }
47
+ afterInit() {
48
+ const input = this.editor.config.get('elementSchema');
49
+ if (input === undefined) {
50
+ return;
51
+ }
52
+ const schema = this.editor.model.schema;
53
+ const issues = validateElementSchema(input, { external: getExternalKinds(schema) });
54
+ if (issues.length) {
55
+ throw new ElementSchemaError(issues);
56
+ }
57
+ const { items, children } = compileElementSchema(input);
58
+ for (const [name, definition] of items) {
59
+ schema.register(name, definition);
60
+ }
61
+ for (const { parent, child } of children) {
62
+ schema.extend(child, { allowIn: parent });
63
+ }
64
+ this._makeSelectable(input);
65
+ if (this.editor.config.get('elementSchemaConverters')) {
66
+ convertElementSchema(this.editor, input);
67
+ }
68
+ }
69
+ /**
70
+ * Makes the `empty` elements and the containers selectable as a whole, by a click on them and by the arrow keys, with
71
+ * `ElementSelectable`. Not the blocks: a click in a block puts the cursor in its text.
72
+ */
73
+ _makeSelectable(input) {
74
+ const names = Object.entries(input)
75
+ .filter(([, element]) => element.type === 'empty' || element.type === 'container')
76
+ .map(([name]) => name);
77
+ const selectable = findPlugin(this.editor, 'ElementSelectable', isSelectablePlugin);
78
+ if (selectable) {
79
+ selectable.add(...names);
80
+ }
81
+ else if (names.length && this.editor.config.get('elementSchemaConverters')) {
82
+ // The converters make the elements, and nothing selects them: say it, as it is a silent loss. Not CKEditor's
83
+ // `logWarning()`: it links to CKEditor's list of errors, which does not have this one.
84
+ // eslint-disable-next-line no-console -- A warning for the developer, once, when the editor is created.
85
+ console.warn(`ElementSchema: load ElementSelectable to select the elements of the schema as a whole: ${names.join(', ')}.`);
86
+ }
87
+ }
88
+ }
89
+ function isSelectablePlugin(plugin) {
90
+ return 'add' in plugin && typeof plugin.add === 'function';
91
+ }
92
+ /**
93
+ * Returns the kinds of the elements of other plugins: inline or not, and `empty` for objects, `block` for what can
94
+ * contain text, `container` for the rest. The generic items (`$block`, …) are not elements.
95
+ */
96
+ function getExternalKinds(schema) {
97
+ const kinds = {};
98
+ for (const name of Object.keys(schema.getDefinitions())) {
99
+ if (!name.startsWith('$')) {
100
+ kinds[name] = {
101
+ isInline: schema.isInline(name),
102
+ type: schema.isObject(name) ? 'empty' : schema.checkChild(name, '$text') ? 'block' : 'container'
103
+ };
104
+ }
105
+ }
106
+ return kinds;
107
+ }
108
+ //# sourceMappingURL=elementSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementSchema.js","sourceRoot":"","sources":["../src/elementSchema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { Plugin } from 'ckeditor5';
2
+ import ElementSchema from './elementSchema.js';
3
+ /**
4
+ * An element of {@link ElementSchemaCommands}: its command and its button.
5
+ */
6
+ export interface ElementSchemaCommandDefinition {
7
+ /**
8
+ * The model element: of the element schema, or of another plugin. Default: the name of the command.
9
+ */
10
+ element?: string;
11
+ /**
12
+ * The title of the command, and the label of its button, e.g. `'Bold'`.
13
+ */
14
+ title: string;
15
+ /**
16
+ * The icon of the command and of its button (an SVG string), e.g. `IconBold` of CKEditor.
17
+ */
18
+ icon?: string;
19
+ /**
20
+ * The keystroke that executes the command, e.g. `'Ctrl+B'`.
21
+ */
22
+ keystroke?: string;
23
+ /**
24
+ * The tag of the element in the data and in the editing view, e.g. `'strong'` or `'hr'`. The converters of the plugin
25
+ * replace the ones of the element schema (`elementSchemaConverters`) in the downcast, and the upcast takes both.
26
+ * Without it, the element is converted by the schema, or by your converters.
27
+ */
28
+ view?: string;
29
+ }
30
+ /**
31
+ * The `elementSchemaCommands` option: the elements, by the name of their command and of their button.
32
+ */
33
+ export type ElementSchemaCommandsConfig = Record<string, ElementSchemaCommandDefinition>;
34
+ /**
35
+ * Commands and toolbar buttons for the elements of the element schema, as CKEditor's features have for theirs: the
36
+ * command of an element is the one of its kind (see {@link ElementSchemaCommand}).
37
+ *
38
+ * | Element | Command | Works as CKEditor's |
39
+ * | ----------------------------- | --------------------------- | ----------------------------- |
40
+ * | inline `block` | {@link ElementInlineCommand} | bold: wraps text |
41
+ * | `block` | {@link ElementBlockCommand} | heading: renames blocks |
42
+ * | `container` | {@link ElementContainerCommand} | block quote: wraps blocks |
43
+ * | `empty`, inline `container` | {@link ElementInsertCommand} | horizontal line: inserts |
44
+ *
45
+ * ```ts
46
+ * elementSchema: schema,
47
+ * elementSchemaCommands: {
48
+ * bold: { title: 'Bold', icon: IconBold, keystroke: 'Ctrl+B', view: 'strong' },
49
+ * callout: { title: 'Callout', icon: IconQuote },
50
+ * divider: { title: 'Divider', icon: IconHorizontalLine, view: 'hr' }
51
+ * },
52
+ * toolbar: ['bold', 'callout', 'divider']
53
+ * ```
54
+ *
55
+ * Each key is the name of the command and of its toolbar button. The commands have titles and icons, so they can be
56
+ * menu items too, e.g. of the context menu.
57
+ *
58
+ * At the ends of the inline elements with text, the left and right arrow keys move in two steps, as CKEditor's two-step
59
+ * caret movement does at the ends of a bold: in the element, then in the text.
60
+ *
61
+ * The commands are made when the elements are registered, after `ElementSchema` and the other plugins are initialized.
62
+ * The editor is not created when an element is unknown.
63
+ */
64
+ export default class ElementSchemaCommands extends Plugin {
65
+ static get pluginName(): 'ElementSchemaCommands';
66
+ static get requires(): readonly [typeof ElementSchema];
67
+ /**
68
+ * The commands, by their name.
69
+ */
70
+ private readonly _commands;
71
+ init(): void;
72
+ /**
73
+ * Makes the commands of the elements, which are registered then, and their converters.
74
+ */
75
+ afterInit(): void;
76
+ private _getDefinitions;
77
+ /**
78
+ * Declares the converters of an element with the tag of its `view`: the downcast with the highest priority, so it
79
+ * replaces the ones of the element schema, and the upcast.
80
+ */
81
+ private _convert;
82
+ }
83
+ //# sourceMappingURL=elementSchemaCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementSchemaCommands.d.ts","sourceRoot":"","sources":["../src/elementSchemaCommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA+D,MAAM,WAAW,CAAC;AAOhG,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAI/C;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAsB,SAAQ,MAAM;IACxD,WAAkB,UAAU,IAAI,uBAAuB,CAEtD;IAED,WAAkB,QAAQ,IAAI,SAAS,CAAC,OAAO,aAAa,CAAC,CAE5D;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2C;IAE9D,IAAI,IAAI,IAAI;IA2BnB;;OAEG;IACI,SAAS,IAAI,IAAI;IA0CxB,OAAO,CAAC,eAAe;IAIvB;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAWhB"}
@@ -0,0 +1,137 @@
1
+ import { Plugin } from 'ckeditor5';
2
+ import { ElementOrPositionButtonView } from 'ckeditor5-elements-core';
3
+ import { createInlineTextElement } from './conversion/inlineTextElement.js';
4
+ import ElementBlockCommand from './commands/elementBlockCommand.js';
5
+ import ElementContainerCommand from './commands/elementContainerCommand.js';
6
+ import ElementInlineCommand from './commands/elementInlineCommand.js';
7
+ import ElementInsertCommand from './commands/elementInsertCommand.js';
8
+ import ElementSchema from './elementSchema.js';
9
+ import { enableTwoStepCaretMovement } from './typing/twoStepCaretMovement.js';
10
+ /**
11
+ * Commands and toolbar buttons for the elements of the element schema, as CKEditor's features have for theirs: the
12
+ * command of an element is the one of its kind (see {@link ElementSchemaCommand}).
13
+ *
14
+ * | Element | Command | Works as CKEditor's |
15
+ * | ----------------------------- | --------------------------- | ----------------------------- |
16
+ * | inline `block` | {@link ElementInlineCommand} | bold: wraps text |
17
+ * | `block` | {@link ElementBlockCommand} | heading: renames blocks |
18
+ * | `container` | {@link ElementContainerCommand} | block quote: wraps blocks |
19
+ * | `empty`, inline `container` | {@link ElementInsertCommand} | horizontal line: inserts |
20
+ *
21
+ * ```ts
22
+ * elementSchema: schema,
23
+ * elementSchemaCommands: {
24
+ * bold: { title: 'Bold', icon: IconBold, keystroke: 'Ctrl+B', view: 'strong' },
25
+ * callout: { title: 'Callout', icon: IconQuote },
26
+ * divider: { title: 'Divider', icon: IconHorizontalLine, view: 'hr' }
27
+ * },
28
+ * toolbar: ['bold', 'callout', 'divider']
29
+ * ```
30
+ *
31
+ * Each key is the name of the command and of its toolbar button. The commands have titles and icons, so they can be
32
+ * menu items too, e.g. of the context menu.
33
+ *
34
+ * At the ends of the inline elements with text, the left and right arrow keys move in two steps, as CKEditor's two-step
35
+ * caret movement does at the ends of a bold: in the element, then in the text.
36
+ *
37
+ * The commands are made when the elements are registered, after `ElementSchema` and the other plugins are initialized.
38
+ * The editor is not created when an element is unknown.
39
+ */
40
+ export default class ElementSchemaCommands extends Plugin {
41
+ static get pluginName() {
42
+ return 'ElementSchemaCommands';
43
+ }
44
+ static get requires() {
45
+ return [ElementSchema];
46
+ }
47
+ /**
48
+ * The commands, by their name.
49
+ */
50
+ _commands = new Map();
51
+ init() {
52
+ const editor = this.editor;
53
+ for (const [name, { title, keystroke }] of this._getDefinitions()) {
54
+ if (keystroke) {
55
+ editor.keystrokes.set(keystroke, name);
56
+ editor.accessibility.addKeystrokeInfos({ keystrokes: [{ label: title, keystroke }] });
57
+ }
58
+ editor.ui.componentFactory.add(name, locale => {
59
+ const command = this._commands.get(name);
60
+ // The commands are made in `afterInit()`, and the toolbars after it.
61
+ if (!command) {
62
+ throw new Error(`elementSchemaCommands.${name}: the button is created before the command.`);
63
+ }
64
+ const button = new ElementOrPositionButtonView(locale, command);
65
+ button.bind('isOn').to(command, 'isOn');
66
+ button.keystroke = keystroke;
67
+ return button;
68
+ });
69
+ }
70
+ }
71
+ /**
72
+ * Makes the commands of the elements, which are registered then, and their converters.
73
+ */
74
+ afterInit() {
75
+ const editor = this.editor;
76
+ const schema = editor.model.schema;
77
+ const inlineNames = new Set();
78
+ for (const [name, { element, title, icon, view }] of this._getDefinitions()) {
79
+ const elementName = element ?? name;
80
+ if (!schema.isRegistered(elementName)) {
81
+ throw new Error(`elementSchemaCommands.${name}: unknown element "${elementName}". Declare it in the element schema, or ` +
82
+ `set "element" to an element of another plugin.`);
83
+ }
84
+ const CommandClass = getCommandClass(schema, elementName);
85
+ const command = new CommandClass(editor, elementName);
86
+ command.set({ title, icon });
87
+ editor.commands.add(name, command);
88
+ this._commands.set(name, command);
89
+ if (command instanceof ElementInlineCommand) {
90
+ inlineNames.add(elementName);
91
+ }
92
+ if (view) {
93
+ this._convert(elementName, view, (writer) => {
94
+ if (command instanceof ElementInlineCommand) {
95
+ return createInlineTextElement(writer, view);
96
+ }
97
+ return schema.isObject(elementName)
98
+ ? writer.createEmptyElement(view)
99
+ : writer.createContainerElement(view);
100
+ });
101
+ }
102
+ }
103
+ enableTwoStepCaretMovement(this, inlineNames);
104
+ }
105
+ _getDefinitions() {
106
+ return Object.entries(this.editor.config.get('elementSchemaCommands') ?? {});
107
+ }
108
+ /**
109
+ * Declares the converters of an element with the tag of its `view`: the downcast with the highest priority, so it
110
+ * replaces the ones of the element schema, and the upcast.
111
+ */
112
+ _convert(elementName, tag, createView) {
113
+ const conversion = this.editor.conversion;
114
+ conversion.for('upcast').elementToElement({ view: tag, model: elementName });
115
+ conversion.for('downcast').elementToElement({
116
+ model: elementName,
117
+ view: (_element, { writer }) => createView(writer),
118
+ // Over the ones of the schema, some of which are `high` already.
119
+ converterPriority: 'highest'
120
+ });
121
+ }
122
+ }
123
+ /**
124
+ * Returns the command of an element, by its kind: an object, or an inline element with no text, is inserted; an inline
125
+ * element with text wraps text; a block-level one with text is a block; the others are containers.
126
+ */
127
+ function getCommandClass(schema, name) {
128
+ const holdsText = schema.checkChild(name, '$text');
129
+ if (schema.isObject(name) || (schema.isInline(name) && !holdsText)) {
130
+ return ElementInsertCommand;
131
+ }
132
+ if (schema.isInline(name)) {
133
+ return ElementInlineCommand;
134
+ }
135
+ return holdsText ? ElementBlockCommand : ElementContainerCommand;
136
+ }
137
+ //# sourceMappingURL=elementSchemaCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementSchemaCommands.js","sourceRoot":"","sources":["../src/elementSchemaCommands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Element schema: declare elements in the model schema from a simple definition, e.g. a JSON file, with three types of
3
+ * elements (`empty`, `block`, `container`), and a validation that rejects contradictory definitions. `ElementSchemaCommands`
4
+ * makes commands and buttons for its elements.
5
+ *
6
+ * @module ckeditor5-elements-schema
7
+ */
8
+ export { default as ElementSchema } from './elementSchema.js';
9
+ export { default as ElementSchemaCommands, type ElementSchemaCommandDefinition, type ElementSchemaCommandsConfig } from './elementSchemaCommands.js';
10
+ export { default as ElementSchemaCommand } from './commands/elementSchemaCommand.js';
11
+ export { default as ElementInlineCommand } from './commands/elementInlineCommand.js';
12
+ export { default as ElementBlockCommand } from './commands/elementBlockCommand.js';
13
+ export { default as ElementContainerCommand } from './commands/elementContainerCommand.js';
14
+ export { default as ElementInsertCommand } from './commands/elementInsertCommand.js';
15
+ export { withoutBlockFiller } from './conversion/inlineTextElement.js';
16
+ export * from './validate.js';
17
+ import './augmentation.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EACN,OAAO,IAAI,qBAAqB,EAChC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EAChC,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAC3F,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAGrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAGvE,cAAc,eAAe,CAAC;AAE9B,OAAO,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Element schema: declare elements in the model schema from a simple definition, e.g. a JSON file, with three types of
3
+ * elements (`empty`, `block`, `container`), and a validation that rejects contradictory definitions. `ElementSchemaCommands`
4
+ * makes commands and buttons for its elements.
5
+ *
6
+ * @module ckeditor5-elements-schema
7
+ */
8
+ // Plugins.
9
+ export { default as ElementSchema } from './elementSchema.js';
10
+ export { default as ElementSchemaCommands } from './elementSchemaCommands.js';
11
+ // Commands.
12
+ export { default as ElementSchemaCommand } from './commands/elementSchemaCommand.js';
13
+ export { default as ElementInlineCommand } from './commands/elementInlineCommand.js';
14
+ export { default as ElementBlockCommand } from './commands/elementBlockCommand.js';
15
+ export { default as ElementContainerCommand } from './commands/elementContainerCommand.js';
16
+ export { default as ElementInsertCommand } from './commands/elementInsertCommand.js';
17
+ // Utils.
18
+ export { withoutBlockFiller } from './conversion/inlineTextElement.js';
19
+ // Validation and compiling, which do not need an editor (also in `validate.ts`, for Node.js).
20
+ export * from './validate.js';
21
+ import './augmentation.js';
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EACN,OAAO,IAAI,qBAAqB,EAGhC,MAAM,4BAA4B,CAAC;AAEpC,YAAY;AACZ,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAC3F,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAErF,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,8FAA8F;AAC9F,cAAc,eAAe,CAAC;AAE9B,OAAO,mBAAmB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type Plugin } from 'ckeditor5';
2
+ /**
3
+ * Makes the left and right arrow keys move in two steps at the ends of inline elements, as CKEditor's two-step caret
4
+ * movement does at the ends of a bold: the first step goes into the element (or out of it), where the cursor looks the
5
+ * same, the next one moves in the text. `a[]<b>bc</b>` is `a<b>[]bc</b>`, then `a<b>b[]c</b>`. The browser would move by
6
+ * a character at once, and the positions on both sides of the end of an element would be out of reach.
7
+ *
8
+ * What is typed then goes where the cursor is. The listeners are the plugin's.
9
+ */
10
+ export declare function enableTwoStepCaretMovement(plugin: Plugin, names: ReadonlySet<string>): void;
11
+ //# sourceMappingURL=twoStepCaretMovement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twoStepCaretMovement.d.ts","sourceRoot":"","sources":["../../src/typing/twoStepCaretMovement.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,KAAK,MAAM,EAGX,MAAM,WAAW,CAAC;AAEnB;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CA8C3F"}