@stesura/react-wordgard 0.0.0-stage → 0.2.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 (111) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE.txt +21 -0
  3. package/README.md +912 -2
  4. package/THIRD_PARTY_LICENSES.txt +93 -0
  5. package/dist/ReactWordgard.d.ts +13 -0
  6. package/dist/ReactWordgard.js +94 -0
  7. package/dist/changes.d.ts +14 -0
  8. package/dist/changes.js +163 -0
  9. package/dist/components/ChildNodeViews.d.ts +8 -0
  10. package/dist/components/ChildNodeViews.js +36 -0
  11. package/dist/components/DecorationRoot.d.ts +13 -0
  12. package/dist/components/DecorationRoot.js +31 -0
  13. package/dist/components/ErrorBoundary.d.ts +18 -0
  14. package/dist/components/ErrorBoundary.js +14 -0
  15. package/dist/components/SeparatorHackView.d.ts +2 -0
  16. package/dist/components/SeparatorHackView.js +4 -0
  17. package/dist/components/ShapeView.d.ts +12 -0
  18. package/dist/components/ShapeView.js +24 -0
  19. package/dist/components/TextNodeView.d.ts +5 -0
  20. package/dist/components/TextNodeView.js +4 -0
  21. package/dist/components/TrailingHackView.d.ts +2 -0
  22. package/dist/components/TrailingHackView.js +4 -0
  23. package/dist/components/WidgetView.d.ts +10 -0
  24. package/dist/components/WidgetView.js +32 -0
  25. package/dist/components/WordgardDoc.d.ts +9 -0
  26. package/dist/components/WordgardDoc.js +36 -0
  27. package/dist/components/WordgardEditor.d.ts +15 -0
  28. package/dist/components/WordgardEditor.js +17 -0
  29. package/dist/components/WordgardScope.d.ts +14 -0
  30. package/dist/components/WordgardScope.js +14 -0
  31. package/dist/components/WrapperView.d.ts +12 -0
  32. package/dist/components/WrapperView.js +23 -0
  33. package/dist/components/marks/MarkView.d.ts +11 -0
  34. package/dist/components/marks/MarkView.js +10 -0
  35. package/dist/components/marks/ReactMarkView.d.ts +10 -0
  36. package/dist/components/marks/ReactMarkView.js +30 -0
  37. package/dist/components/nodes/DefaultNodeView.d.ts +16 -0
  38. package/dist/components/nodes/DefaultNodeView.js +112 -0
  39. package/dist/components/nodes/DocNodeView.d.ts +7 -0
  40. package/dist/components/nodes/DocNodeView.js +5 -0
  41. package/dist/composition.d.ts +23 -0
  42. package/dist/composition.js +95 -0
  43. package/dist/contexts/DecorationContext.d.ts +15 -0
  44. package/dist/contexts/DecorationContext.js +35 -0
  45. package/dist/contexts/DecorationRootContext.d.ts +6 -0
  46. package/dist/contexts/DecorationRootContext.js +4 -0
  47. package/dist/contexts/EditorContext.d.ts +27 -0
  48. package/dist/contexts/EditorContext.js +87 -0
  49. package/dist/contexts/RenderStoreContext.d.ts +15 -0
  50. package/dist/contexts/RenderStoreContext.js +41 -0
  51. package/dist/contexts/editorEvents.d.ts +12 -0
  52. package/dist/contexts/editorEvents.js +92 -0
  53. package/dist/decorations/DecoIterator.d.ts +43 -0
  54. package/dist/decorations/DecoIterator.js +462 -0
  55. package/dist/decorations/ReactMarkType.d.ts +41 -0
  56. package/dist/decorations/ReactMarkType.js +47 -0
  57. package/dist/decorations/ReactShapeType.d.ts +65 -0
  58. package/dist/decorations/ReactShapeType.js +202 -0
  59. package/dist/decorations/ReactWidgetType.d.ts +29 -0
  60. package/dist/decorations/ReactWidgetType.js +67 -0
  61. package/dist/decorations/ReactWrapperType.d.ts +31 -0
  62. package/dist/decorations/ReactWrapperType.js +41 -0
  63. package/dist/decorations/iterDeco.d.ts +82 -0
  64. package/dist/decorations/iterDeco.js +406 -0
  65. package/dist/helpers/attributes.d.ts +2 -0
  66. package/dist/helpers/attributes.js +11 -0
  67. package/dist/helpers/index.d.ts +2 -0
  68. package/dist/helpers/index.js +2 -0
  69. package/dist/helpers/props.d.ts +8 -0
  70. package/dist/helpers/props.js +248 -0
  71. package/dist/helpers/refs.d.ts +6 -0
  72. package/dist/helpers/refs.js +20 -0
  73. package/dist/hooks/useEditorEffect.d.ts +10 -0
  74. package/dist/hooks/useEditorEffect.js +28 -0
  75. package/dist/hooks/useEditorEventCallback.d.ts +8 -0
  76. package/dist/hooks/useEditorEventCallback.js +17 -0
  77. package/dist/hooks/useEditorEventListener.d.ts +10 -0
  78. package/dist/hooks/useEditorEventListener.js +26 -0
  79. package/dist/hooks/useEditorLayoutEffect.d.ts +15 -0
  80. package/dist/hooks/useEditorLayoutEffect.js +34 -0
  81. package/dist/hooks/useEditorState.d.ts +6 -0
  82. package/dist/hooks/useEditorState.js +12 -0
  83. package/dist/hooks/useEditorStateSelector.d.ts +7 -0
  84. package/dist/hooks/useEditorStateSelector.js +11 -0
  85. package/dist/hooks/useIgnoreMutation.d.ts +6 -0
  86. package/dist/hooks/useIgnoreMutation.js +19 -0
  87. package/dist/hooks/useIsNodeSelected.d.ts +2 -0
  88. package/dist/hooks/useIsNodeSelected.js +5 -0
  89. package/dist/hooks/useMergedDOMRefs.d.ts +6 -0
  90. package/dist/hooks/useMergedDOMRefs.js +18 -0
  91. package/dist/hooks/useNodePos.d.ts +4 -0
  92. package/dist/hooks/useNodePos.js +7 -0
  93. package/dist/hooks/useSelectNode.d.ts +6 -0
  94. package/dist/hooks/useSelectNode.js +25 -0
  95. package/dist/hooks/useStopEvent.d.ts +8 -0
  96. package/dist/hooks/useStopEvent.js +21 -0
  97. package/dist/hooks/useWordgard.d.ts +2 -0
  98. package/dist/hooks/useWordgard.js +3 -0
  99. package/dist/index.d.ts +21 -0
  100. package/dist/index.js +21 -0
  101. package/dist/reactKeys.d.ts +39 -0
  102. package/dist/reactKeys.js +188 -0
  103. package/dist/tile.d.ts +32 -0
  104. package/dist/tile.js +1319 -0
  105. package/dist/viewRoots.d.ts +2 -0
  106. package/dist/viewRoots.js +7 -0
  107. package/dist/wordgardInternals.d.ts +211 -0
  108. package/dist/wordgardInternals.js +82 -0
  109. package/package.json +119 -4
  110. package/skills/react-wordgard/SKILL.md +525 -0
  111. package/skills/react-wordgard/references/wordgard.md +189 -0
@@ -0,0 +1,462 @@
1
+ // Copied from Wordgard 0.6.2 (src/editor/decoration.ts in its repo: DecoIterator,
2
+ // SpanIterator, getDecoSet and the shape helpers), which doesn't export them.
3
+ // Changes, so React can lay out one plot at a time:
4
+ // - `walker.enter` says whether to descend. When it doesn't, the walk goes
5
+ // past the plot as a whole, emitting its `after` widgets once past.
6
+ // - The walker gets each node's position, and is told which widgets come from
7
+ // point decorations.
8
+ // - A walk can start before the previous one ended.
9
+ // - Decoration sets come from the DecoSet, not from calling sources again, and
10
+ // range sets are split into layers a cursor can seek into.
11
+ // - `nodeWrappers` without wrappers returns a shared empty array, as upstream.
12
+ // - It records the document's mark behind a mark of an overridden shape
13
+ // (`documentMark`), for React marks.
14
+ // - The element it wraps a React widget in to add attributes is marked
15
+ // (`attributeWidget`): the view draws the widget with them as props.
16
+ // The internal classes and facets come from src/wordgardInternals.ts.
17
+ import { Attributes, Elt, Leaf } from "wordgard/doc";
18
+ import { Decoration, Widget } from "wordgard/editor";
19
+ import { PointSet, RangeSet } from "wordgard/set";
20
+ import { getDecorationInternals, } from "../wordgardInternals.js";
21
+ import { attributeWidget } from "./ReactWidgetType.js";
22
+ var DecorationScope;
23
+ (function (DecorationScope) {
24
+ DecorationScope[DecorationScope["Atom"] = 1] = "Atom";
25
+ DecorationScope[DecorationScope["InlineAtom"] = 2] = "InlineAtom";
26
+ DecorationScope[DecorationScope["All"] = 4] = "All";
27
+ })(DecorationScope || (DecorationScope = {}));
28
+ var WidgetPlace;
29
+ (function (WidgetPlace) {
30
+ WidgetPlace[WidgetPlace["Before"] = 0] = "Before";
31
+ WidgetPlace[WidgetPlace["After"] = 1] = "After";
32
+ WidgetPlace[WidgetPlace["Start"] = 2] = "Start";
33
+ WidgetPlace[WidgetPlace["End"] = 3] = "End";
34
+ })(WidgetPlace || (WidgetPlace = {}));
35
+ var Side;
36
+ (function (Side) {
37
+ Side[Side["After"] = 1000000000] = "After";
38
+ })(Side || (Side = {}));
39
+ export const getDecoSet = (state) => {
40
+ const set = { points: new Map(), ranges: new Map() };
41
+ for (const src of state.facet(Decoration.Point.source))
42
+ set.points.set(src, src(state));
43
+ for (const src of state.facet(Decoration.Range.source))
44
+ set.ranges.set(src, src(state));
45
+ return set;
46
+ };
47
+ const memo = (f) => {
48
+ const map = new WeakMap();
49
+ return (arg) => {
50
+ let found = map.get(arg);
51
+ if (found === undefined)
52
+ map.set(arg, (found = f(arg)));
53
+ return found;
54
+ };
55
+ };
56
+ const addMarkAttributes = (overrides, shape, tag) => {
57
+ let attrs;
58
+ for (const mark of tag.marks) {
59
+ const type = overrides.get(mark.type) || mark.type;
60
+ if (type.attribute && (mark.type.spanning || !tag.isText)) {
61
+ const { get, target } = type.attribute;
62
+ const markAttrs = get(mark.value);
63
+ if (markAttrs.length) {
64
+ if (target && shape instanceof Elt)
65
+ shape = shape.addAttrs(markAttrs, target);
66
+ else
67
+ attrs = attrs ? Attributes.merge(attrs, markAttrs) : markAttrs;
68
+ }
69
+ }
70
+ }
71
+ return attrs ? addAttrs(shape, attrs, tag.type.isInline) : shape;
72
+ };
73
+ const addAttrs = (shape, attrs, inline) => shape instanceof Elt
74
+ ? shape.addAttrs(attrs)
75
+ : attributeWidget(shape, attrs, inline ? "span" : "div");
76
+ const applyDeco = (shape, deco, tag) => {
77
+ const { AttributeDecoration, WrapperDecoration } = getDecorationInternals();
78
+ if (deco instanceof AttributeDecoration) {
79
+ return deco.selector && shape instanceof Elt
80
+ ? shape.addAttrs(deco.attrs, deco.selector)
81
+ : addAttrs(shape, deco.attrs, tag.type.isInline);
82
+ }
83
+ else if (deco instanceof WrapperDecoration) {
84
+ return deco.selector && shape instanceof Elt
85
+ ? shape.wrap(deco.elt, deco.selector)
86
+ : deco.elt.fill([shape]);
87
+ }
88
+ return shape;
89
+ };
90
+ const baseTagShape = memo((tag) => tag.is(Leaf.Text) ? Widget.editableText.of(tag.param) : tag.type.shape.create(tag.param));
91
+ class SpanIterator {
92
+ ranges;
93
+ points;
94
+ end;
95
+ active = [];
96
+ activeEnd = [];
97
+ from;
98
+ to;
99
+ point = null;
100
+ pointSource = null;
101
+ done = false;
102
+ constructor(ranges, points, start, end) {
103
+ this.ranges = ranges;
104
+ this.points = points;
105
+ this.end = end;
106
+ this.from = this.to = start;
107
+ }
108
+ next() {
109
+ if (this.done)
110
+ return this;
111
+ if (this.point)
112
+ this.point = null;
113
+ const { ranges, points, active, activeEnd } = this;
114
+ for (;;) {
115
+ const [startPos, startSide] = ranges.value
116
+ ? [ranges.from, ranges.value.inclusiveStart ? -1 : 1]
117
+ : [1e9, 0];
118
+ let endPos = 1e9;
119
+ let endSide = 0;
120
+ let nextActive = -1;
121
+ for (let i = 0; i < active.length; i++) {
122
+ const pos = activeEnd[i] ?? 0;
123
+ const side = active[i]?.inclusiveEnd ? 1 : -1;
124
+ if ((pos - endPos || side - endSide) < 0) {
125
+ endPos = pos;
126
+ endSide = side;
127
+ nextActive = i;
128
+ }
129
+ }
130
+ const [pointPos, pointSide] = points.value ? [points.from, points.value.side] : [1e9, 1];
131
+ const nextPos = Math.min(startPos, endPos, pointPos);
132
+ if (this.to == this.end && nextPos > this.to) {
133
+ this.done = true;
134
+ break;
135
+ }
136
+ else if (nextPos > this.to) {
137
+ this.from = this.to;
138
+ this.to = Math.min(this.end, nextPos);
139
+ break;
140
+ }
141
+ else if (pointPos == nextPos &&
142
+ (startPos > pointPos || pointSide < 0) &&
143
+ (endPos > pointPos || pointSide < 0)) {
144
+ this.point = points.value;
145
+ this.pointSource = points.set;
146
+ this.from = this.to = points.from;
147
+ points.next();
148
+ break;
149
+ }
150
+ else if ((startPos - endPos || startSide - endSide) < 0) {
151
+ if (ranges.value)
152
+ active.push(ranges.value);
153
+ activeEnd.push(ranges.to);
154
+ ranges.next();
155
+ }
156
+ else {
157
+ active.splice(nextActive, 1);
158
+ activeEnd.splice(nextActive, 1);
159
+ }
160
+ }
161
+ return this;
162
+ }
163
+ }
164
+ const tagScope = (tag, atom) => DecorationScope.All |
165
+ (atom ? DecorationScope.Atom | (tag.type.isInline ? DecorationScope.InlineAtom : 0) : 0);
166
+ export const renderMarkWrapper = memo((mark) => {
167
+ const shape = mark.type.element;
168
+ if (!shape)
169
+ throw new Error(`${mark.name} has no element shape`);
170
+ return Elt.create(shape.name, shape.attrs(mark.value), [0]);
171
+ });
172
+ // The document's mark a wrapper draws: `Decoration.Mark.shape` draws one of
173
+ // a type it clones.
174
+ const documentMarks = new WeakMap();
175
+ export const documentMark = (mark) => documentMarks.get(mark) ?? mark;
176
+ export const renderWrapper = (src) => src instanceof getDecorationInternals().WrapperRangeDecoration ? src.elt : renderMarkWrapper(src);
177
+ const compareSetPrec = (setA, setB, array) => {
178
+ if (setA != setB)
179
+ for (const set of array) {
180
+ if (set == setA)
181
+ return -1;
182
+ if (set == setB)
183
+ return 1;
184
+ }
185
+ return 0;
186
+ };
187
+ // Where a walk ending at `target` has to stop: past the whole plot `target`
188
+ // falls inside, among the content after `pos`.
189
+ const pastPlotAt = (pos, target) => {
190
+ const { content } = pos.parent.node;
191
+ let start = pos.pos - pos.inText;
192
+ for (let i = pos.index; i < content.length; i++) {
193
+ const child = content[i];
194
+ if (!child)
195
+ break;
196
+ const end = start + child.length;
197
+ if (target < end)
198
+ return child.isPlot && target > start ? end : target;
199
+ start = end;
200
+ }
201
+ return target;
202
+ };
203
+ // Wordgard bug (docs/wordgard-upstream-bugs.md): a range set's second layer can
204
+ // hold overlapping ranges, which its cursor can't seek into. Wordgard walks
205
+ // forward only; React's per-plot walks seek, so they get the set's ranges in
206
+ // layers that don't overlap.
207
+ const seekable = memo((set) => {
208
+ const layers = [];
209
+ for (const cursor = set.cursor(0); cursor.value; cursor.next()) {
210
+ const { value, from, to } = cursor;
211
+ let layer = layers.find((layer) => layer.to <= from);
212
+ if (!layer)
213
+ layers.push((layer = { to, ranges: [] }));
214
+ layer.ranges.push([value, from, to]);
215
+ layer.to = to;
216
+ }
217
+ return layers.length > 1 ? layers.map(({ ranges }) => Decoration.Range.set(ranges)) : [set];
218
+ });
219
+ const none = [];
220
+ export class DecoIterator {
221
+ state;
222
+ decoSet;
223
+ tagShapes;
224
+ globalWidgets;
225
+ globalWrappers;
226
+ globalAttrs;
227
+ markShapes;
228
+ pos;
229
+ rangeCursor;
230
+ pointSets;
231
+ pointCursor;
232
+ constructor(state, decoSet) {
233
+ this.state = state;
234
+ this.decoSet = decoSet;
235
+ const { tagShape, tagWidget, tagWrapper, tagAttribute, markShape } = getDecorationInternals();
236
+ this.tagShapes = state.facet(tagShape);
237
+ this.globalWidgets = state.facet(tagWidget);
238
+ this.globalWrappers = state.facet(tagWrapper);
239
+ this.globalAttrs = state.facet(tagAttribute);
240
+ this.markShapes = state.facet(markShape);
241
+ this.pos = state.doc.resolve(0);
242
+ this.rangeCursor = RangeSet.cursor([...decoSet.ranges.values()].flatMap(seekable));
243
+ this.pointSets = [...decoSet.points.values()];
244
+ this.pointCursor = PointSet.cursor(this.pointSets);
245
+ }
246
+ widgets(tag, place, walker) {
247
+ if (place == WidgetPlace.Start && tag.type.isInline)
248
+ walker.widget(Widget.img, -1, false);
249
+ for (const src of this.globalWidgets) {
250
+ if (src.place == place && tag.type == src.type) {
251
+ const widget = typeof src.widget == "function" ? src.widget(tag) : src.widget;
252
+ if (widget)
253
+ walker.widget(widget, place == WidgetPlace.Before || place == WidgetPlace.End ? 1 : -1, false);
254
+ }
255
+ }
256
+ if (place == WidgetPlace.End && tag.type.isInline)
257
+ walker.widget(Widget.img, 1, false);
258
+ }
259
+ // Enumerate all wrapper elements for a given node. Spanning wrappers
260
+ // are always moved to the front of the result. Within the
261
+ // spanning/non-spanning wrappers, the ordering is determined by rank.
262
+ nodeWrappers(tag, active, atom) {
263
+ const { WrapperRangeDecoration } = getDecorationInternals();
264
+ const wrappers = [];
265
+ for (const mark of tag.marks) {
266
+ const shape = this.markShapes.get(mark.type) || mark.type;
267
+ if (shape.element) {
268
+ const drawn = shape == mark.type ? mark : shape.of(mark.value);
269
+ if (drawn != mark)
270
+ documentMarks.set(drawn, mark);
271
+ wrappers.push(drawn);
272
+ }
273
+ }
274
+ for (const val of active) {
275
+ if (val instanceof WrapperRangeDecoration &&
276
+ tagScope(tag, atom) & val.scope &&
277
+ (!val.query || this.state.schema.matchNode(tag.type, val.query)))
278
+ wrappers.push(val);
279
+ }
280
+ if (!wrappers.length)
281
+ return none;
282
+ if (wrappers.length > 1)
283
+ wrappers.sort((a, b) => (spanningOf(a) == spanningOf(b) ? 0 : spanningOf(a) ? -1 : 1) || rankOf(a) - rankOf(b));
284
+ return wrappers;
285
+ }
286
+ walk(from, inclusiveStart, to, walker) {
287
+ const { WidgetDecoration, ShapeDecoration } = getDecorationInternals();
288
+ this.rangeCursor.goto(from);
289
+ this.pointCursor.goto(from, inclusiveStart ? -1e9 : Side.After);
290
+ const iter = new SpanIterator(this.rangeCursor, this.pointCursor, from, to);
291
+ let pos = from >= this.pos.pos ? this.pos.advance(from - this.pos.pos) : this.state.doc.resolve(from);
292
+ let started = inclusiveStart;
293
+ let atomParent;
294
+ for (let p = pos.parent; p; p = p.parent)
295
+ if (this.state.isAtom(p.node.type))
296
+ atomParent = p;
297
+ // Track points that may apply to the node at the start of the next range
298
+ const pendingDeco = [];
299
+ let pendingPos = -1;
300
+ let pendingShape = null;
301
+ let pendingShapeSet = null;
302
+ // A plot the walker didn't enter: its `after` widgets come once past it.
303
+ let skipped = null;
304
+ const flushSkipped = () => {
305
+ if (!skipped)
306
+ return;
307
+ const tag = skipped;
308
+ skipped = null;
309
+ this.widgets(tag, WidgetPlace.After, walker);
310
+ };
311
+ const wrap = {
312
+ skip: (node, pos) => {
313
+ // Only done for leaf nodes.
314
+ flushSkipped();
315
+ if (started)
316
+ this.widgets(node.tag, WidgetPlace.Before, walker);
317
+ else
318
+ started = true;
319
+ const hasPending = pendingPos == pos && !node.isText;
320
+ let shape = hasPending && pendingShape ? pendingShape : this.tagShape(node.tag, iter.active);
321
+ if (hasPending)
322
+ for (const deco of pendingDeco)
323
+ shape = applyDeco(shape, deco, node.tag);
324
+ if (shape.hasContent)
325
+ throw new Error("Leaf nodes shapes shouldn't have a content hole");
326
+ walker.node(node, shape, this.nodeWrappers(node.tag, iter.active, true), undefined, pos);
327
+ this.widgets(node.tag, WidgetPlace.After, walker);
328
+ },
329
+ enterPlot: (node, pos) => {
330
+ flushSkipped();
331
+ if (started)
332
+ this.widgets(node.tag, WidgetPlace.Before, walker);
333
+ else
334
+ started = true;
335
+ let shape = pendingShape && pendingPos == pos ? pendingShape : this.tagShape(node.tag, iter.active);
336
+ if (pendingPos == pos)
337
+ for (const deco of pendingDeco)
338
+ shape = applyDeco(shape, deco, node.tag);
339
+ const wrappers = this.nodeWrappers(node.tag, iter.active, !shape.hasContent);
340
+ const atom = !shape.hasContent;
341
+ if (atom) {
342
+ walker.node(node, shape, wrappers, pos + node.length > to ? to - pos : undefined, pos);
343
+ }
344
+ else if (shape instanceof Elt && !walker.enter(node, shape, wrappers, pos)) {
345
+ skipped = node.tag;
346
+ return false;
347
+ }
348
+ this.widgets(node.tag, WidgetPlace.Start, walker);
349
+ return !atom;
350
+ },
351
+ leavePlot: (tag) => {
352
+ flushSkipped();
353
+ if (started)
354
+ this.widgets(tag, WidgetPlace.End, walker);
355
+ else
356
+ started = true;
357
+ walker.leave();
358
+ this.widgets(tag, WidgetPlace.After, walker);
359
+ },
360
+ };
361
+ if (inclusiveStart) {
362
+ const before = pos.nodeBefore;
363
+ if (before)
364
+ this.widgets(before.tag, WidgetPlace.After, walker);
365
+ else
366
+ this.widgets(pos.parent.node.tag, WidgetPlace.Start, walker);
367
+ }
368
+ for (; !iter.next().done;) {
369
+ if (atomParent) {
370
+ const end = Math.min(to, atomParent.after);
371
+ const done = atomParent.after <= to;
372
+ walker.nodePart(atomParent.node, end - pos.pos, done);
373
+ pos = pos.advance(end - pos.pos);
374
+ if (done)
375
+ this.widgets(atomParent.node.tag, WidgetPlace.After, walker);
376
+ atomParent = undefined;
377
+ while (iter.point && iter.from < end)
378
+ iter.next();
379
+ }
380
+ else if (iter.point) {
381
+ // Inside a plot the walk went past.
382
+ if (iter.from < pos.pos)
383
+ continue;
384
+ flushSkipped();
385
+ const value = iter.point;
386
+ if (value instanceof WidgetDecoration) {
387
+ walker.widget(value.widget, value.side, true);
388
+ }
389
+ else {
390
+ if (pendingPos < pos.pos) {
391
+ pendingDeco.length = 0;
392
+ pendingShape = null;
393
+ pendingPos = pos.pos;
394
+ }
395
+ if (value instanceof ShapeDecoration &&
396
+ (!pendingShape ||
397
+ (pendingShapeSet &&
398
+ iter.pointSource &&
399
+ compareSetPrec(pendingShapeSet, iter.pointSource, this.pointSets)))) {
400
+ pendingShape = value.shape;
401
+ pendingShapeSet = iter.pointSource;
402
+ }
403
+ else {
404
+ pendingDeco.push(value);
405
+ }
406
+ }
407
+ }
408
+ else if (iter.to > pos.pos) {
409
+ pos = pos.walk(pastPlotAt(pos, iter.to) - pos.pos, wrap);
410
+ }
411
+ }
412
+ if (pos.pos < to)
413
+ pos = pos.walk(pastPlotAt(pos, to) - pos.pos, wrap);
414
+ flushSkipped();
415
+ if (atomParent) {
416
+ walker.nodePart(atomParent.node, 0, atomParent.after == to);
417
+ }
418
+ else {
419
+ const after = pos.nodeAfter;
420
+ if (after)
421
+ this.widgets(after.tag, WidgetPlace.Before, walker);
422
+ else
423
+ this.widgets(pos.parent.node.tag, WidgetPlace.End, walker);
424
+ }
425
+ this.pos = pos;
426
+ }
427
+ tagShape(tag, active) {
428
+ const { AttributeRangeDecoration } = getDecorationInternals();
429
+ const override = this.tagShapes.get(tag.type);
430
+ let shape = override ? override(tag) : baseTagShape(tag);
431
+ if (tag.marks.length)
432
+ shape = addMarkAttributes(this.markShapes, shape, tag);
433
+ let add;
434
+ for (const src of this.globalAttrs)
435
+ if (tag.type == src.type) {
436
+ if (src.target && shape instanceof Elt)
437
+ shape = shape.addAttrs([src.attr, src.value(tag)], src.target);
438
+ else
439
+ Attributes.push((add ||= []), src.attr, src.value(tag));
440
+ }
441
+ const scope = tagScope(tag, !shape.hasContent);
442
+ for (const { type, elt, target } of this.globalWrappers)
443
+ if (tag.type == type) {
444
+ shape = target && shape instanceof Elt ? shape.wrap(elt, target) : elt.fill([shape]);
445
+ }
446
+ for (const deco of active) {
447
+ if (deco instanceof AttributeRangeDecoration &&
448
+ scope & deco.scope &&
449
+ (!deco.query || this.state.schema.matchNode(tag.type, deco.query)))
450
+ Attributes.push((add ||= []), deco.attribute, deco.value);
451
+ }
452
+ if (add) {
453
+ if (shape instanceof Elt)
454
+ shape = Elt.create(shape.tagName, Attributes.merge(shape.attrs, add), shape.children);
455
+ else
456
+ shape = attributeWidget(shape, add, tag.type.isBlock ? "div" : "span");
457
+ }
458
+ return shape;
459
+ }
460
+ }
461
+ const rankOf = (src) => src instanceof getDecorationInternals().WrapperRangeDecoration ? src.rank : src.type.rank;
462
+ export const spanningOf = (src) => src instanceof getDecorationInternals().WrapperRangeDecoration ? src.spanning : src.type.spanning;
@@ -0,0 +1,41 @@
1
+ import { type AllHTMLAttributes, type ComponentType, type HTMLProps, type ReactElement, type ReactNode, type RefAttributes, type RefCallback } from "react";
2
+ import type { Elt, Mark } from "wordgard/doc";
3
+ import type { Tile } from "../wordgardInternals.js";
4
+ /**
5
+ * A React mark's props, for each run of an equal mark: `markProps.mark` is the document's mark.
6
+ */
7
+ export type MarkComponentProps<E extends Element = HTMLElement> = AllHTMLAttributes<HTMLElement> & {
8
+ markProps: {
9
+ mark: Mark;
10
+ getPos: () => number;
11
+ contentDOMRef: RefCallback<Element>;
12
+ };
13
+ children: ReactNode;
14
+ } & RefAttributes<E>;
15
+ /** `createMarkShape`'s options. */
16
+ export type MarkShapeOptions = {
17
+ /**
18
+ * The component's root element, `span` by default, which plain Wordgard draws. It can't come from
19
+ * the mark type: a mark type's own `shape` is written before the type exists.
20
+ */
21
+ element?: string;
22
+ };
23
+ type DrawProps = {
24
+ attrs: HTMLProps<HTMLElement>;
25
+ markProps: MarkComponentProps["markProps"];
26
+ children: ReactNode;
27
+ ref: RefCallback<Element>;
28
+ };
29
+ type MarkShape = {
30
+ element: string;
31
+ attributes: Record<string, string>;
32
+ };
33
+ /**
34
+ * A mark's shape, for `Decoration.Mark.shape(MarkType, shape)` or a mark type's own `shape`.
35
+ */
36
+ export declare const createMarkShape: <E extends Element = HTMLElement>(component: ComponentType<MarkComponentProps<E>>, { element }?: MarkShapeOptions) => MarkShape;
37
+ export declare const isReactMark: (elt: Elt<unknown>) => boolean;
38
+ export declare const drawMark: (elt: Elt<unknown>, props: DrawProps) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
39
+ export declare const markTiles: WeakSet<Tile>;
40
+ export declare const markOfTile: WeakMap<Tile, Mark>;
41
+ export {};
@@ -0,0 +1,47 @@
1
+ // Named after ReactWidgetType. A React mark is a mark's element shape, registered as drawn by a
2
+ // component through the reserved attribute React shapes use: Wordgard's mark shapes are
3
+ // declarative. The props follow react-prosemirror's MarkComponentProps.
4
+ import { createElement, } from "react";
5
+ import { takesRef } from "../helpers/refs.js";
6
+ import { idOf, reserved } from "./ReactShapeType.js";
7
+ // Keyed apart from React shapes' and wrappers' ids, which share the attribute.
8
+ const draws = new Map();
9
+ // By component, then root element, as React shapes are.
10
+ const marks = new WeakMap();
11
+ /**
12
+ * A mark's shape, for `Decoration.Mark.shape(MarkType, shape)` or a mark type's own `shape`.
13
+ */
14
+ export const createMarkShape = (component, { element = "span" } = {}) => {
15
+ let registered = marks.get(component);
16
+ if (!registered) {
17
+ const id = `m${draws.size + 1}`;
18
+ draws.set(id, ({ attrs, markProps, children, ref }) => createElement(component, {
19
+ ...attrs,
20
+ ref: takesRef(component) ? ref : undefined,
21
+ markProps,
22
+ children,
23
+ }));
24
+ registered = { id, shapes: new Map() };
25
+ marks.set(component, registered);
26
+ }
27
+ let shape = registered.shapes.get(element);
28
+ if (!shape) {
29
+ shape = { element, attributes: { [reserved]: registered.id } };
30
+ registered.shapes.set(element, shape);
31
+ }
32
+ return shape;
33
+ };
34
+ export const isReactMark = (elt) => {
35
+ const id = idOf(elt);
36
+ return id != null && draws.has(id);
37
+ };
38
+ export const drawMark = (elt, props) => {
39
+ const draw = draws.get(idOf(elt) ?? "");
40
+ if (!draw)
41
+ throw new Error("Not a React mark");
42
+ return draw(props);
43
+ };
44
+ // The tiles over React marks' elements, and the mark each root's is for: a
45
+ // composition inside one sits in one wrapper, not in each of its elements.
46
+ export const markTiles = new WeakSet();
47
+ export const markOfTile = new WeakMap();
@@ -0,0 +1,65 @@
1
+ import { type AllHTMLAttributes, type ComponentType, type HTMLProps, type ReactElement, type ReactNode, type RefAttributes, type RefCallback } from "react";
2
+ import { type Node as DocNode, Elt } from "wordgard/doc";
3
+ /**
4
+ * A React shape's props: `nodeProps`, the content as `children`, and Wordgard's attributes and
5
+ * `ref` for its root. The content goes in the `contentDOMRef` element, else in the root; in an
6
+ * atom, `children` is empty and a `contentDOMRef` throws. With `<DecorationRoot>` as the root, the
7
+ * attributes and `ref` are taken care of; a root drawn by hand spreads them onto itself (see [A
8
+ * root drawn by hand](https://github.com/stesura/react-wordgard#a-root-drawn-by-hand)).
9
+ */
10
+ export type ShapeComponentProps<E extends Element = HTMLElement> = AllHTMLAttributes<HTMLElement> & {
11
+ nodeProps: {
12
+ node: DocNode;
13
+ getPos: () => number;
14
+ contentDOMRef: RefCallback<Element>;
15
+ };
16
+ children: ReactNode;
17
+ } & RefAttributes<E>;
18
+ /**
19
+ * What `createShape` returns: for `Decoration.Tag.shape`, a node's shape, its root the node type's
20
+ * own outer element, an atom where the type's shape is. `of` gives the shape with `element` for its
21
+ * root: for `Decoration.Point.shape`, or a root other than the type's.
22
+ */
23
+ export type ReactShape = ((tag: DocNode.Tag) => Elt) & {
24
+ of: (element: string, options?: {
25
+ atom?: boolean;
26
+ }) => Elt;
27
+ };
28
+ export declare const reserved = "data-wg-react";
29
+ type DrawProps = {
30
+ attrs: HTMLProps<HTMLElement>;
31
+ nodeProps: ShapeComponentProps["nodeProps"];
32
+ children: ReactNode;
33
+ ref: RefCallback<Element>;
34
+ };
35
+ /**
36
+ * A node's shape, for `Decoration.Tag.shape(NodeType, shape)`. `shape.of(element)` is the shape
37
+ * with another root element, also for `Decoration.Point.shape`.
38
+ */
39
+ export declare const createShape: <E extends Element = HTMLElement>(component: ComponentType<ShapeComponentProps<E>>) => ReactShape;
40
+ export declare const idOf: (elt: Elt<unknown>) => string | null;
41
+ export declare const isReactShape: (elt: Elt<unknown>) => boolean;
42
+ export declare const shapeAttributes: (elt: Elt<unknown>) => string[];
43
+ export declare const drawShape: (elt: Elt<unknown>, props: DrawProps) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
44
+ export type ShapeHandle = {
45
+ root: Element | null;
46
+ content: Element | null;
47
+ wrapper: boolean;
48
+ atom: boolean;
49
+ };
50
+ export declare const shapeOfRoot: (dom: Element) => ShapeHandle | undefined;
51
+ export declare const contentOf: (handle: ShapeHandle) => Element | null;
52
+ export declare const atomRef: (handle: ShapeHandle, atom: boolean) => () => void;
53
+ export declare const createShapeHandle: () => {
54
+ handle: ShapeHandle;
55
+ ref: (dom: Element | null) => void;
56
+ contentDOMRef: (dom: Element | null) => void;
57
+ };
58
+ export declare const createWrapperHandle: () => {
59
+ handle: ShapeHandle;
60
+ ref: (dom: Element | null) => void;
61
+ wrappedRef: (dom: Element | null) => void;
62
+ };
63
+ export declare const nodePos: (handle: ShapeHandle) => () => number;
64
+ export declare const sortRecords: (records: readonly MutationRecord[], changed: Set<Element>, inline: (root: Element) => boolean) => MutationRecord[];
65
+ export {};