@nerd-bible/wordgard 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/doc.d.ts +9 -0
- package/dist/doc.js +20 -6
- package/dist/editor.d.ts +16 -6
- package/dist/editor.js +122 -40
- package/dist/schema.js +93 -2
- package/dist/state.js +1 -0
- package/dist/table.js +1 -1
- package/package.json +10 -2
package/dist/doc.d.ts
CHANGED
|
@@ -1536,6 +1536,15 @@ declare class Plot implements Node.Shared {
|
|
|
1536
1536
|
node that overlaps the given range, outer nodes before inner
|
|
1537
1537
|
nodes. When the function returns `false` for a node, descendents
|
|
1538
1538
|
of that node are not iterated.
|
|
1539
|
+
|
|
1540
|
+
When `from` is greater than `to`, iteration happens in inverted
|
|
1541
|
+
order, yielding later siblings before earlier ones.
|
|
1542
|
+
|
|
1543
|
+
Note that positions passed to the callback are relative to the
|
|
1544
|
+
start of the node this method is called on. That will usually be
|
|
1545
|
+
the document, in which case they are normal document positions,
|
|
1546
|
+
but you can also call it on an arbitrary plot, in which case
|
|
1547
|
+
they are local positions.
|
|
1539
1548
|
*/
|
|
1540
1549
|
iterate(from: number, to: number, f: (node: Node, pos: number, parent: Plot | null, index: number) => boolean | void): void;
|
|
1541
1550
|
iterate(f: (node: Node, pos: number, parent: Plot | null, index: number) => boolean | void): void;
|
package/dist/doc.js
CHANGED
|
@@ -1301,8 +1301,12 @@ class Plot {
|
|
|
1301
1301
|
}
|
|
1302
1302
|
iterate(a, b, c) {
|
|
1303
1303
|
let [from, to, f] = typeof a == "number" ? [a, b, c] : [0, this.length, a];
|
|
1304
|
-
if (this.isDoc || f(this, 0, null, 0) !== false)
|
|
1305
|
-
|
|
1304
|
+
if (this.isDoc || f(this, 0, null, 0) !== false) {
|
|
1305
|
+
if (to < from)
|
|
1306
|
+
this.iterBack(this.contentLength, to, from, f);
|
|
1307
|
+
else
|
|
1308
|
+
this.iterInner(0, from, to, f);
|
|
1309
|
+
}
|
|
1306
1310
|
}
|
|
1307
1311
|
nodeAt(pos) {
|
|
1308
1312
|
for (let node of this.content) {
|
|
@@ -1333,12 +1337,20 @@ class Plot {
|
|
|
1333
1337
|
break;
|
|
1334
1338
|
let node = this.content[i], start = pos;
|
|
1335
1339
|
pos += node.length;
|
|
1336
|
-
if (pos
|
|
1337
|
-
continue;
|
|
1338
|
-
if (f(node, start, this, i) !== false && node.isPlot)
|
|
1340
|
+
if (pos > from && f(node, start, this, i) !== false && node.isPlot)
|
|
1339
1341
|
node.iterInner(start + 1, from, to, f);
|
|
1340
1342
|
}
|
|
1341
1343
|
}
|
|
1344
|
+
iterBack(contentEnd, from, to, f) {
|
|
1345
|
+
for (let pos = contentEnd, i = this.content.length - 1; i >= 0; i--) {
|
|
1346
|
+
if (pos <= from)
|
|
1347
|
+
break;
|
|
1348
|
+
let node = this.content[i], end = pos;
|
|
1349
|
+
pos -= node.length;
|
|
1350
|
+
if (pos < to && f(node, pos, this, i) !== false && node.isPlot)
|
|
1351
|
+
node.iterBack(end - 1, from, to, f);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1342
1354
|
toString() {
|
|
1343
1355
|
return this.name + markString(this.tag.marks) + "(" + this.content.join() + ")";
|
|
1344
1356
|
}
|
|
@@ -2945,15 +2957,17 @@ class SectionIter {
|
|
|
2945
2957
|
}
|
|
2946
2958
|
next() {
|
|
2947
2959
|
let { sections } = this;
|
|
2960
|
+
this.off = 0;
|
|
2948
2961
|
if (this.i < sections.length) {
|
|
2949
2962
|
this.len = sections[this.i++];
|
|
2950
2963
|
this.ins = sections[this.i++];
|
|
2964
|
+
if (this.len == 0 && this.ins < 0)
|
|
2965
|
+
this.next();
|
|
2951
2966
|
}
|
|
2952
2967
|
else {
|
|
2953
2968
|
this.len = 0;
|
|
2954
2969
|
this.ins = -3;
|
|
2955
2970
|
}
|
|
2956
|
-
this.off = 0;
|
|
2957
2971
|
}
|
|
2958
2972
|
get keep() { return this.ins == -1 || this.ins == -2; }
|
|
2959
2973
|
get done() { return this.ins == -3; }
|
package/dist/editor.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ declare namespace Widget {
|
|
|
46
46
|
/**
|
|
47
47
|
How to render the widget as DOM content.
|
|
48
48
|
*/
|
|
49
|
-
render: (value: Param) => Element | Text;
|
|
49
|
+
render: (value: Param, wg: Wordgard) => Element | Text;
|
|
50
50
|
/**
|
|
51
51
|
Compare the widget value for equality. Will default to `===`.
|
|
52
52
|
*/
|
|
@@ -64,16 +64,22 @@ declare namespace Widget {
|
|
|
64
64
|
*/
|
|
65
65
|
disconnect?: (value: Param, dom: Element | Text) => void;
|
|
66
66
|
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
Used to determine whether events originating from the widget's
|
|
68
|
+
DOM should ignored by the editor. `false` or a function that
|
|
69
|
+
returns `false` for the event will prevent the editor's
|
|
70
|
+
regular event handling for the event.
|
|
70
71
|
*/
|
|
71
|
-
|
|
72
|
+
propagateEvent?: boolean | ((event: Event) => boolean);
|
|
72
73
|
/**
|
|
73
74
|
Set this to false for widgets that either aren't visible or
|
|
74
75
|
are positioned outside of the regular document flow.
|
|
75
76
|
*/
|
|
76
77
|
inFlow?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
By default, widgets are set to be ineditable. Set this to
|
|
80
|
+
`true` to suppress that.
|
|
81
|
+
*/
|
|
82
|
+
editable?: boolean;
|
|
77
83
|
};
|
|
78
84
|
/**
|
|
79
85
|
Each widget has an associated type that describes how it
|
|
@@ -506,7 +512,7 @@ declare abstract class Tile {
|
|
|
506
512
|
get boundary(): 0 | 1;
|
|
507
513
|
get firstChild(): Tile | null;
|
|
508
514
|
get lastChild(): Tile | null;
|
|
509
|
-
|
|
515
|
+
ignoreEvent(event: Event): boolean;
|
|
510
516
|
get ignoreMutations(): boolean;
|
|
511
517
|
toString(): string;
|
|
512
518
|
sync(): void;
|
|
@@ -606,6 +612,10 @@ declare class Wordgard {
|
|
|
606
612
|
private editorAttrs;
|
|
607
613
|
private contentAttrs;
|
|
608
614
|
private styleModules;
|
|
615
|
+
/**
|
|
616
|
+
True when the editor is connected to a DOM document.
|
|
617
|
+
*/
|
|
618
|
+
connected: boolean;
|
|
609
619
|
private flushing;
|
|
610
620
|
private willFlush;
|
|
611
621
|
private flushFunc;
|
package/dist/editor.js
CHANGED
|
@@ -24,32 +24,40 @@ class Widget {
|
|
|
24
24
|
return Widget.Type.new(spec).of(null);
|
|
25
25
|
}
|
|
26
26
|
type;
|
|
27
|
+
render(wg) {
|
|
28
|
+
return this.type.render(this.value, wg);
|
|
29
|
+
}
|
|
27
30
|
get hasContent() { return false; }
|
|
28
31
|
}
|
|
29
32
|
;Widget = /*@__PURE__*/(function (Widget) {
|
|
30
33
|
class Type {
|
|
31
34
|
render;
|
|
32
35
|
eq;
|
|
33
|
-
|
|
36
|
+
propagateEvent;
|
|
34
37
|
connect;
|
|
35
38
|
disconnect;
|
|
36
39
|
inFlow;
|
|
40
|
+
editable;
|
|
37
41
|
constructor(
|
|
38
42
|
render,
|
|
39
43
|
eq,
|
|
40
|
-
|
|
44
|
+
propagateEvent,
|
|
41
45
|
connect,
|
|
42
46
|
disconnect,
|
|
43
|
-
inFlow
|
|
47
|
+
inFlow,
|
|
48
|
+
editable) {
|
|
44
49
|
this.render = render;
|
|
45
50
|
this.eq = eq;
|
|
46
|
-
this.
|
|
51
|
+
this.propagateEvent = propagateEvent;
|
|
47
52
|
this.connect = connect;
|
|
48
53
|
this.disconnect = disconnect;
|
|
49
54
|
this.inFlow = inFlow;
|
|
55
|
+
this.editable = editable;
|
|
50
56
|
}
|
|
51
57
|
static new(spec) {
|
|
52
|
-
|
|
58
|
+
let prop = spec.propagateEvent;
|
|
59
|
+
let propEvent = typeof prop == "function" ? prop : prop == null ? () => true : () => prop;
|
|
60
|
+
return new Type(spec.render, spec.eq || ((a, b) => a === b), propEvent, spec.connect ?? null, spec.disconnect ?? null, spec.inFlow !== false, spec.editable === true);
|
|
53
61
|
}
|
|
54
62
|
of(value) { return Widget.new(this, value); }
|
|
55
63
|
}
|
|
@@ -106,7 +114,7 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
|
|
|
106
114
|
return tagWidget.of({
|
|
107
115
|
type: Node.Type.get(type),
|
|
108
116
|
place: getPlace(place),
|
|
109
|
-
widget: typeof widget == "function" ? memo(widget) :
|
|
117
|
+
widget: typeof widget == "function" ? memo(widget) : widget
|
|
110
118
|
});
|
|
111
119
|
}
|
|
112
120
|
Tag.widget = widget;
|
|
@@ -119,7 +127,7 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
|
|
|
119
127
|
return {
|
|
120
128
|
type: tp,
|
|
121
129
|
place: p,
|
|
122
|
-
widget: typeof w == "function" ? memo(w) :
|
|
130
|
+
widget: typeof w == "function" ? memo(w) : w
|
|
123
131
|
};
|
|
124
132
|
});
|
|
125
133
|
}
|
|
@@ -262,7 +270,7 @@ class ShapeDecoration extends Decoration.Point {
|
|
|
262
270
|
return this == other || other instanceof ShapeDecoration && other.shape.eq(this.shape);
|
|
263
271
|
}
|
|
264
272
|
get trackMode() { return "after"; }
|
|
265
|
-
get side() { return
|
|
273
|
+
get side() { return 1000000000; }
|
|
266
274
|
}
|
|
267
275
|
class WidgetDecoration extends Decoration.Point {
|
|
268
276
|
widget;
|
|
@@ -273,7 +281,7 @@ class WidgetDecoration extends Decoration.Point {
|
|
|
273
281
|
this.widget = widget;
|
|
274
282
|
this.side = side;
|
|
275
283
|
this.trackMode = trackMode;
|
|
276
|
-
if (side >=
|
|
284
|
+
if (side >= 1000000000)
|
|
277
285
|
throw new Error("Invalid widget side");
|
|
278
286
|
}
|
|
279
287
|
eq(other) {
|
|
@@ -297,7 +305,7 @@ class AttributeDecoration extends Decoration.Point {
|
|
|
297
305
|
selectorEq(other.selector, this.selector);
|
|
298
306
|
}
|
|
299
307
|
get trackMode() { return "after"; }
|
|
300
|
-
get side() { return
|
|
308
|
+
get side() { return 1000000000; }
|
|
301
309
|
}
|
|
302
310
|
class WrapperDecoration extends Decoration.Point {
|
|
303
311
|
elt;
|
|
@@ -312,7 +320,7 @@ class WrapperDecoration extends Decoration.Point {
|
|
|
312
320
|
selectorEq(other.selector, this.selector);
|
|
313
321
|
}
|
|
314
322
|
get trackMode() { return "after"; }
|
|
315
|
-
get side() { return
|
|
323
|
+
get side() { return 1000000000; }
|
|
316
324
|
}
|
|
317
325
|
const nodeSelectionDeco = /*@__PURE__*/Decoration.Point.attributes({ class: "wg-selected-node" });
|
|
318
326
|
function nodeSelection(state) {
|
|
@@ -494,9 +502,14 @@ class PointIterator {
|
|
|
494
502
|
get side() {
|
|
495
503
|
return this.done ? 1 : this.value.side;
|
|
496
504
|
}
|
|
497
|
-
goto(pos) {
|
|
505
|
+
goto(pos, inclusive) {
|
|
498
506
|
this.done = false;
|
|
499
|
-
|
|
507
|
+
let i = findAbove(this.set.positions, 0, pos - 1);
|
|
508
|
+
if (!inclusive) {
|
|
509
|
+
while (i < this.set.values.length && this.set.values[i].side < 1000000000)
|
|
510
|
+
i++;
|
|
511
|
+
}
|
|
512
|
+
this.fill(i);
|
|
500
513
|
}
|
|
501
514
|
}
|
|
502
515
|
function addDel(deleted, i) {
|
|
@@ -585,8 +598,8 @@ class RangeSet {
|
|
|
585
598
|
compareRange(fromA, b, fromB, len, change) {
|
|
586
599
|
let a = this, toB = fromB + len;
|
|
587
600
|
if (a != b || fromA != fromB) {
|
|
588
|
-
let iA = findAbove(a.
|
|
589
|
-
let iB = findAbove(b.
|
|
601
|
+
let iA = findAbove(a.to, 0, fromA - 1), lA = a.from.length;
|
|
602
|
+
let iB = findAbove(b.to, 0, fromB - 1), lB = b.from.length;
|
|
590
603
|
let off = fromB - fromA;
|
|
591
604
|
let sameVals = a.values == b.values;
|
|
592
605
|
for (;;) {
|
|
@@ -972,11 +985,14 @@ class DecoIterator {
|
|
|
972
985
|
pos;
|
|
973
986
|
rangeIter = [];
|
|
974
987
|
pointIter = [];
|
|
988
|
+
endWidgets;
|
|
975
989
|
constructor(state, decoSet) {
|
|
976
990
|
this.state = state;
|
|
977
991
|
this.decoSet = decoSet;
|
|
978
992
|
this.tagShapes = state.facet(tagShape);
|
|
979
993
|
this.globalWidgets = state.facet(tagWidget);
|
|
994
|
+
this.endWidgets = this.globalWidgets
|
|
995
|
+
.some(w => (w.place == 1 || w.place == 3) && typeof w.widget == "function");
|
|
980
996
|
this.globalWrappers = state.facet(tagWrapper);
|
|
981
997
|
this.globalAttrs = state.facet(tagAttribute);
|
|
982
998
|
this.pos = state.doc.resolve(0);
|
|
@@ -995,17 +1011,22 @@ class DecoIterator {
|
|
|
995
1011
|
widgets(tag, place, walker) {
|
|
996
1012
|
for (let src of this.globalWidgets) {
|
|
997
1013
|
if (src.place == place && tag.type == src.type) {
|
|
998
|
-
let widget = src.widget(tag);
|
|
1014
|
+
let widget = typeof src.widget == "function" ? src.widget(tag) : src.widget;
|
|
999
1015
|
if (widget)
|
|
1000
1016
|
walker.widget(widget, place == 0 || place == 3 ? 1 : -1);
|
|
1001
1017
|
}
|
|
1002
1018
|
}
|
|
1003
1019
|
}
|
|
1020
|
+
hasEndWidget(type) {
|
|
1021
|
+
return this.globalWidgets.some(tw => tw.type == type &&
|
|
1022
|
+
(tw.place == 3 || tw.place == 1) &&
|
|
1023
|
+
typeof tw.widget == "function");
|
|
1024
|
+
}
|
|
1004
1025
|
walk(from, inclusiveStart, to, walker) {
|
|
1005
1026
|
for (let i of this.rangeIter)
|
|
1006
1027
|
i.goto(from);
|
|
1007
1028
|
for (let i of this.pointIter)
|
|
1008
|
-
i.goto(
|
|
1029
|
+
i.goto(from, inclusiveStart);
|
|
1009
1030
|
let iter = new HeapIterator(this.rangeIter.filter(i => !i.done), this.pointIter.filter(i => !i.done), from, to);
|
|
1010
1031
|
let pos = this.pos.advance(from - this.pos.pos), started = inclusiveStart;
|
|
1011
1032
|
let atomParent;
|
|
@@ -1567,7 +1588,7 @@ class Tile {
|
|
|
1567
1588
|
let last = this.children.length - 1;
|
|
1568
1589
|
return last < 0 ? null : this.children[last];
|
|
1569
1590
|
}
|
|
1570
|
-
|
|
1591
|
+
ignoreEvent(event) { return false; }
|
|
1571
1592
|
get ignoreMutations() { return false; }
|
|
1572
1593
|
toString() { return this.dom.nodeName + (this.children.length ? `(${this.children})` : ""); }
|
|
1573
1594
|
sync() { }
|
|
@@ -1754,18 +1775,18 @@ class DocTile extends CompositeTile {
|
|
|
1754
1775
|
this.cursorWrapper = cursorWrapper;
|
|
1755
1776
|
this.decoSet = decoSet;
|
|
1756
1777
|
}
|
|
1757
|
-
static create(state, dom) {
|
|
1778
|
+
static create(state, dom, wg) {
|
|
1758
1779
|
return new DocTile(state, dom, null, { points: new Map, ranges: new Map })
|
|
1759
|
-
.updateRanges(state, getDecoSet(state), [0, state.doc.length],
|
|
1780
|
+
.updateRanges(state, getDecoSet(state), [0, state.doc.length], wg);
|
|
1760
1781
|
}
|
|
1761
1782
|
get isDoc() { return true; }
|
|
1762
1783
|
get node() { return this.state.doc; }
|
|
1763
|
-
update(state, changes,
|
|
1784
|
+
update(state, changes, wg, composition) {
|
|
1764
1785
|
let decoSet = getDecoSet(state);
|
|
1765
1786
|
let changed = findChangedRanges(this.state, this.decoSet, state, decoSet, changes);
|
|
1766
|
-
return this.updateRanges(state, decoSet, changed,
|
|
1787
|
+
return this.updateRanges(state, decoSet, changed, wg, composition);
|
|
1767
1788
|
}
|
|
1768
|
-
updateRanges(state, decoSet, sections,
|
|
1789
|
+
updateRanges(state, decoSet, sections, wg, composition) {
|
|
1769
1790
|
let wrapper = composition?.wrapCursor || null;
|
|
1770
1791
|
if ((!sections.length || sections.length == 2 && sections[1] == -1) && eqArray(wrapper, this.cursorWrapper))
|
|
1771
1792
|
return this;
|
|
@@ -1776,7 +1797,7 @@ class DocTile extends CompositeTile {
|
|
|
1776
1797
|
else
|
|
1777
1798
|
sections = separated;
|
|
1778
1799
|
}
|
|
1779
|
-
let builder = new ContentUpdate(state, this, new DecoIterator(state, decoSet), wrapper);
|
|
1800
|
+
let builder = new ContentUpdate(state, this, wg, new DecoIterator(state, decoSet), wrapper);
|
|
1780
1801
|
for (let i = 0, posB = 0, startCovered = false; i < sections.length;) {
|
|
1781
1802
|
let len = sections[i++], ins = sections[i++];
|
|
1782
1803
|
if (composition && posB == composition.fromB && ins >= 0) {
|
|
@@ -1802,7 +1823,7 @@ class DocTile extends CompositeTile {
|
|
|
1802
1823
|
}
|
|
1803
1824
|
let result = builder.finish();
|
|
1804
1825
|
result.sync();
|
|
1805
|
-
if (connected) {
|
|
1826
|
+
if (wg.connected) {
|
|
1806
1827
|
for (let ch of this.children)
|
|
1807
1828
|
ch.disconnect(builder.reused);
|
|
1808
1829
|
for (let tile of builder.toConnect)
|
|
@@ -2031,17 +2052,19 @@ class EltTile extends CompositeTile {
|
|
|
2031
2052
|
class WidgetTile extends Tile {
|
|
2032
2053
|
widget;
|
|
2033
2054
|
_node;
|
|
2034
|
-
constructor(widget, _node, flags, length = 0
|
|
2035
|
-
super(dom
|
|
2055
|
+
constructor(widget, _node, flags, dom, length = 0) {
|
|
2056
|
+
super(dom, flags);
|
|
2036
2057
|
this.widget = widget;
|
|
2037
2058
|
this._node = _node;
|
|
2038
2059
|
this.length = length;
|
|
2060
|
+
if (dom.nodeType == 1 && !widget.type.editable && dom.contentEditable == "inherit")
|
|
2061
|
+
dom.contentEditable = "false";
|
|
2039
2062
|
}
|
|
2040
2063
|
get isNodeOuter() { return !!this._node; }
|
|
2041
2064
|
get isAtom() { return true; }
|
|
2042
2065
|
get node() { return this._node; }
|
|
2043
2066
|
get children() { return noChildren; }
|
|
2044
|
-
|
|
2067
|
+
ignoreEvent(event) { return !this.widget.type.propagateEvent(event); }
|
|
2045
2068
|
connect() {
|
|
2046
2069
|
this.widget.type.connect?.(this.widget.value, this.dom);
|
|
2047
2070
|
}
|
|
@@ -2232,6 +2255,7 @@ class TilePointer {
|
|
|
2232
2255
|
}
|
|
2233
2256
|
class ContentUpdate {
|
|
2234
2257
|
state;
|
|
2258
|
+
wg;
|
|
2235
2259
|
deco;
|
|
2236
2260
|
old;
|
|
2237
2261
|
new;
|
|
@@ -2240,8 +2264,9 @@ class ContentUpdate {
|
|
|
2240
2264
|
keepWalker;
|
|
2241
2265
|
toConnect = [];
|
|
2242
2266
|
partialNode = null;
|
|
2243
|
-
constructor(state, old, deco, cursorWrapper) {
|
|
2267
|
+
constructor(state, old, wg, deco, cursorWrapper) {
|
|
2244
2268
|
this.state = state;
|
|
2269
|
+
this.wg = wg;
|
|
2245
2270
|
this.deco = deco;
|
|
2246
2271
|
this.old = new TilePointer(old, 0, null);
|
|
2247
2272
|
this.new = new DocTile(state, old.dom, cursorWrapper, deco.decoSet);
|
|
@@ -2321,6 +2346,54 @@ class ContentUpdate {
|
|
|
2321
2346
|
};
|
|
2322
2347
|
}
|
|
2323
2348
|
keep(len, includeStart, includeEnd) {
|
|
2349
|
+
let cut = [], end = this.posB + len;
|
|
2350
|
+
if (this.deco.endWidgets)
|
|
2351
|
+
for (let nw = this.new, { tile, index, parent } = this.old, pos = this.posB; pos < end;) {
|
|
2352
|
+
if (tile instanceof TextTile) {
|
|
2353
|
+
pos += tile.length - index;
|
|
2354
|
+
({ tile, index, parent } = parent);
|
|
2355
|
+
index++;
|
|
2356
|
+
}
|
|
2357
|
+
else if (index < tile.children.length) {
|
|
2358
|
+
pos += tile.children[index].length;
|
|
2359
|
+
index++;
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2362
|
+
if (tile.node) {
|
|
2363
|
+
while (!nw.node && nw.parent)
|
|
2364
|
+
nw = nw.parent;
|
|
2365
|
+
if (!nw.parent)
|
|
2366
|
+
break;
|
|
2367
|
+
if (!nw.node.tag.eq(tile.node.tag) &&
|
|
2368
|
+
(this.deco.hasEndWidget(tile.node.type) || this.deco.hasEndWidget(nw.node.type)))
|
|
2369
|
+
cut.push(pos);
|
|
2370
|
+
nw = nw.parent;
|
|
2371
|
+
if (!nw)
|
|
2372
|
+
break;
|
|
2373
|
+
pos++;
|
|
2374
|
+
}
|
|
2375
|
+
if (!parent)
|
|
2376
|
+
break;
|
|
2377
|
+
({ tile, index, parent } = parent);
|
|
2378
|
+
index++;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
for (let i = cut.length; i;) {
|
|
2382
|
+
let from = cut[--i], to = Math.min(from + 1, includeEnd ? end : end - 1);
|
|
2383
|
+
while (i && cut[i - 1] == from - 1) {
|
|
2384
|
+
from--;
|
|
2385
|
+
i--;
|
|
2386
|
+
}
|
|
2387
|
+
if (from > this.posB)
|
|
2388
|
+
this.keepInner(from - this.posB, includeStart, false);
|
|
2389
|
+
this.update(to - from, true);
|
|
2390
|
+
includeStart = false;
|
|
2391
|
+
len = end - to;
|
|
2392
|
+
}
|
|
2393
|
+
if (len)
|
|
2394
|
+
this.keepInner(len, includeStart, includeEnd);
|
|
2395
|
+
}
|
|
2396
|
+
keepInner(len, includeStart, includeEnd) {
|
|
2324
2397
|
if (!includeStart) {
|
|
2325
2398
|
this.old = this.old.walk(0, 1);
|
|
2326
2399
|
this.openOldWrappers();
|
|
@@ -2343,7 +2416,7 @@ class ContentUpdate {
|
|
|
2343
2416
|
if (mark.type.element) {
|
|
2344
2417
|
this.openWrapper(renderMarkWrapper(mark), mark.spanning, false);
|
|
2345
2418
|
}
|
|
2346
|
-
this.new.addChild(new WidgetTile(imgHack, null, 16 | 32));
|
|
2419
|
+
this.new.addChild(new WidgetTile(imgHack, null, 16 | 32, imgHack.render(this.wg)));
|
|
2347
2420
|
return;
|
|
2348
2421
|
}
|
|
2349
2422
|
let found = [];
|
|
@@ -2460,7 +2533,7 @@ class ContentUpdate {
|
|
|
2460
2533
|
: endOld && this.posB == end ? endOld.matchingWidget(widget, sideFlag, this.reused)
|
|
2461
2534
|
: null;
|
|
2462
2535
|
if (!tile) {
|
|
2463
|
-
tile = new WidgetTile(widget, null, 16 | sideFlag,
|
|
2536
|
+
tile = new WidgetTile(widget, null, 16 | sideFlag, widget.render(this.wg));
|
|
2464
2537
|
if (widget.type.connect)
|
|
2465
2538
|
this.toConnect.push(tile);
|
|
2466
2539
|
}
|
|
@@ -2497,6 +2570,9 @@ class ContentUpdate {
|
|
|
2497
2570
|
}
|
|
2498
2571
|
buildNodeShape(node, shape, reuse, afterContent = 0) {
|
|
2499
2572
|
if (shape instanceof Elt) {
|
|
2573
|
+
if (node && !shape.hasContent && Attributes.get(shape.attrs, "contenteditable") == null &&
|
|
2574
|
+
!/^(br|hr|img|input|wbr)$/i.test(shape.tagName))
|
|
2575
|
+
shape = Elt.create(shape.tagName, Attributes.merge(shape.attrs, ["contenteditable", "false"]), shape.children);
|
|
2500
2576
|
let reusable, dom, strict = true;
|
|
2501
2577
|
if (reusable = this.findReusableTile(shape, reuse, strict) || this.findReusableTile(shape, reuse, strict = false)) {
|
|
2502
2578
|
this.reused.set(reusable, 2);
|
|
@@ -2528,7 +2604,7 @@ class ContentUpdate {
|
|
|
2528
2604
|
dom = reusable.dom;
|
|
2529
2605
|
}
|
|
2530
2606
|
let flags = (node ? 512 : 16 | 1) | afterContent;
|
|
2531
|
-
let tile = new WidgetTile(shape, node, flags, node ? node.length : 0
|
|
2607
|
+
let tile = new WidgetTile(shape, node, flags, dom || shape.render(this.wg), node ? node.length : 0);
|
|
2532
2608
|
if (shape.type.connect)
|
|
2533
2609
|
this.toConnect.push(tile);
|
|
2534
2610
|
return tile;
|
|
@@ -2576,7 +2652,7 @@ class ContentUpdate {
|
|
|
2576
2652
|
this.new.children.splice(hasHack, 1);
|
|
2577
2653
|
}
|
|
2578
2654
|
else if (needsHack) {
|
|
2579
|
-
this.new.addChild(new WidgetTile(brHack, null, 16 | 64,
|
|
2655
|
+
this.new.addChild(new WidgetTile(brHack, null, 16 | 64, brHack.render(this.wg)));
|
|
2580
2656
|
}
|
|
2581
2657
|
}
|
|
2582
2658
|
up() {
|
|
@@ -2704,10 +2780,12 @@ function updateAttributes(dom, a, b) {
|
|
|
2704
2780
|
return changed;
|
|
2705
2781
|
}
|
|
2706
2782
|
const brHack = /*@__PURE__*/Widget.create({
|
|
2707
|
-
render() { return document.createElement("br"); }
|
|
2783
|
+
render() { return document.createElement("br"); },
|
|
2784
|
+
editable: true
|
|
2708
2785
|
});
|
|
2709
2786
|
const imgHack = /*@__PURE__*/Widget.create({
|
|
2710
|
-
render() { return document.createElement("img"); }
|
|
2787
|
+
render() { return document.createElement("img"); },
|
|
2788
|
+
editable: true
|
|
2711
2789
|
});
|
|
2712
2790
|
function separateComposition(sections, comp) {
|
|
2713
2791
|
let result = [], { fromB, toB } = comp;
|
|
@@ -4185,13 +4263,17 @@ function isInPrimarySelection(wg, event) {
|
|
|
4185
4263
|
}
|
|
4186
4264
|
return false;
|
|
4187
4265
|
}
|
|
4266
|
+
const focusEvents = /*@__PURE__*/(() => new Set(["input", "beforeinput", "keydown", "keyup", "keypress", "copy", "cut", "paste"]))();
|
|
4188
4267
|
function eventBelongsToEditor(wg, event) {
|
|
4189
|
-
if (!event.bubbles)
|
|
4190
|
-
return true;
|
|
4191
4268
|
if (event.defaultPrevented)
|
|
4192
4269
|
return false;
|
|
4270
|
+
if (!event.bubbles)
|
|
4271
|
+
return true;
|
|
4272
|
+
let active = wg.root.activeElement;
|
|
4193
4273
|
for (let node = event.target, tile; node != wg.contentDOM; node = node.parentNode)
|
|
4194
|
-
if (!node || node.nodeType == 11 ||
|
|
4274
|
+
if (!node || node.nodeType == 11 ||
|
|
4275
|
+
(tile = Tile.get(node)) && tile.ignoreEvent(event) ||
|
|
4276
|
+
node == active && focusEvents.has(event.type))
|
|
4195
4277
|
return false;
|
|
4196
4278
|
return true;
|
|
4197
4279
|
}
|
|
@@ -4756,7 +4838,7 @@ class Wordgard {
|
|
|
4756
4838
|
for (let plugin of this.plugins)
|
|
4757
4839
|
plugin.update(this);
|
|
4758
4840
|
this.inputState = new InputState(this);
|
|
4759
|
-
this.docTile = DocTile.create(this.state, this.contentDOM);
|
|
4841
|
+
this.docTile = DocTile.create(this.state, this.contentDOM, this);
|
|
4760
4842
|
this.updateAttrs();
|
|
4761
4843
|
this.observer = new DOMObserver(this);
|
|
4762
4844
|
if (spec.parent)
|
|
@@ -4899,7 +4981,7 @@ class Wordgard {
|
|
|
4899
4981
|
this.mountStyles();
|
|
4900
4982
|
this.updateAttrs();
|
|
4901
4983
|
}
|
|
4902
|
-
this.docTile = prevDocTile.update(update.state, changes, this
|
|
4984
|
+
this.docTile = prevDocTile.update(update.state, changes, this, composition);
|
|
4903
4985
|
if ((composition?.wrapCursor || !composition && (prevDocTile != this.docTile || update.selectionSet)) && this.hasFocus)
|
|
4904
4986
|
setDOMSelection(this);
|
|
4905
4987
|
this.observer.clear();
|
package/dist/schema.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Paragraph, Heading, Direction, Blockquote, HorizontalRule, Alignment, D
|
|
|
4
4
|
import { phrases, imagePhrases, colorNames } from 'wordgard/phrases';
|
|
5
5
|
import { Command, setTextblockType, Menu, setAlignment, setDirection, toggleBlock, listIsActive, toggleList, enter, toggleMark } from 'wordgard/command';
|
|
6
6
|
import { history } from 'wordgard/history';
|
|
7
|
-
import { KeyBinding, InputRule, Wordgard, Panel, Dialog,
|
|
7
|
+
import { KeyBinding, InputRule, Wordgard, Widget, Decoration, Panel, Dialog, PointSet, Tooltip } from 'wordgard/editor';
|
|
8
8
|
import cr from 'crelt';
|
|
9
9
|
|
|
10
10
|
function blockDoc() {
|
|
@@ -313,6 +313,97 @@ function codeBlock() {
|
|
|
313
313
|
};
|
|
314
314
|
}));
|
|
315
315
|
;return codeBlock})(codeBlock);
|
|
316
|
+
function codeBlockLanguage(options = {}) {
|
|
317
|
+
return [
|
|
318
|
+
GardState.schemaElement.of(CodeBlockLanguage),
|
|
319
|
+
options.languages ? [
|
|
320
|
+
languageStyles,
|
|
321
|
+
languageOptions.of(options.languages),
|
|
322
|
+
languageSelect,
|
|
323
|
+
codeBlockLanguage.selectLanguageBinding
|
|
324
|
+
] : []
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
const languageStyles = /*@__PURE__*/Wordgard.styles({
|
|
328
|
+
pre: {
|
|
329
|
+
position: "relative",
|
|
330
|
+
"& select.wg-code-language": {
|
|
331
|
+
font: "var(--wg-dialog-font)",
|
|
332
|
+
fontSize: "70%",
|
|
333
|
+
position: "absolute",
|
|
334
|
+
top: "2px",
|
|
335
|
+
right: "4px",
|
|
336
|
+
border: "none",
|
|
337
|
+
borderRadius: "4px"
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
const languageOptions = /*@__PURE__*/GardState.Facet.define({
|
|
342
|
+
combine: input => input.reduce((a, b) => a.concat(b), [])
|
|
343
|
+
});
|
|
344
|
+
function option(text, selected, value) {
|
|
345
|
+
let node = document.createElement("option");
|
|
346
|
+
node.textContent = text;
|
|
347
|
+
node.value = value;
|
|
348
|
+
if (selected)
|
|
349
|
+
node.selected = true;
|
|
350
|
+
return node;
|
|
351
|
+
}
|
|
352
|
+
const languageWidget = /*@__PURE__*/Widget.define({
|
|
353
|
+
render({ options, value }, wg) {
|
|
354
|
+
let sel = document.createElement("select");
|
|
355
|
+
sel.className = "wg-code-language";
|
|
356
|
+
sel.tabIndex = -1;
|
|
357
|
+
sel.appendChild(option("Plain", value == null, ""));
|
|
358
|
+
let selected = value && options.find(o => o.toLowerCase() == value);
|
|
359
|
+
if (value && !selected)
|
|
360
|
+
sel.appendChild(option(value, true, value));
|
|
361
|
+
for (let opt of options)
|
|
362
|
+
sel.appendChild(option(opt, opt == selected, opt.toLowerCase()));
|
|
363
|
+
sel.onchange = () => {
|
|
364
|
+
let value = sel.value || undefined;
|
|
365
|
+
let pos = wg.posAtDOM(sel.parentNode);
|
|
366
|
+
let block = pos > 0 && wg.state.doc.nodeAt(pos - 1);
|
|
367
|
+
if (block && block.type == CodeBlock.type && block.mark(CodeBlockLanguage) != value) {
|
|
368
|
+
wg.dispatch({
|
|
369
|
+
changes: value ? { from: pos - 1, add: CodeBlockLanguage.of(value) } :
|
|
370
|
+
{ from: pos - 1, remove: CodeBlockLanguage.isInSet(block.tag.marks) }
|
|
371
|
+
});
|
|
372
|
+
wg.focus();
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
return sel;
|
|
376
|
+
},
|
|
377
|
+
propagateEvent: false,
|
|
378
|
+
eq(a, b) {
|
|
379
|
+
return a.value == b.value && a.options.length == b.options.length &&
|
|
380
|
+
a.options.every((o, i) => o == b.options[i]);
|
|
381
|
+
},
|
|
382
|
+
inFlow: false
|
|
383
|
+
});
|
|
384
|
+
const languageSelect = /*@__PURE__*/Decoration.Tag.widget.dynamic(CodeBlock, "end", state => {
|
|
385
|
+
let options = state.facet(languageOptions);
|
|
386
|
+
return tag => {
|
|
387
|
+
return languageWidget.of({ options, value: tag.mark(CodeBlockLanguage) });
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
;codeBlockLanguage = /*@__PURE__*/(function (codeBlockLanguage) {
|
|
391
|
+
codeBlockLanguage.selectLanguage = wg => {
|
|
392
|
+
let blk = wg.state.sel.head.textblockParent;
|
|
393
|
+
if (!blk || blk.node.type != CodeBlock.type)
|
|
394
|
+
return false;
|
|
395
|
+
let sel = wg.nodeDOM(blk.before)?.querySelector("select.wg-code-language");
|
|
396
|
+
if (!sel)
|
|
397
|
+
return false;
|
|
398
|
+
sel.focus();
|
|
399
|
+
sel.showPicker();
|
|
400
|
+
return true;
|
|
401
|
+
};
|
|
402
|
+
codeBlockLanguage.selectLanguageBinding = KeyBinding.of({
|
|
403
|
+
key: "Shift-Mod-l",
|
|
404
|
+
run: codeBlockLanguage.selectLanguage
|
|
405
|
+
});
|
|
406
|
+
;return codeBlockLanguage})(codeBlockLanguage);
|
|
316
407
|
|
|
317
408
|
function strong() {
|
|
318
409
|
return [GardState.schemaElement.of(Strong), strong.button, strong.keyBinding];
|
|
@@ -1188,7 +1279,7 @@ function toggleLink(wg) {
|
|
|
1188
1279
|
let url = form && form.elements.namedItem("url")?.value;
|
|
1189
1280
|
if (url)
|
|
1190
1281
|
wg.dispatch({
|
|
1191
|
-
changes: selection.ranges.map(r => ({ from: r.from, to: r.to, add: Link.of(url) })),
|
|
1282
|
+
changes: wg.state.selection.ranges.map(r => ({ from: r.from, to: r.to, add: Link.of(url) })),
|
|
1192
1283
|
userEvent: "mark.add"
|
|
1193
1284
|
});
|
|
1194
1285
|
});
|
package/dist/state.js
CHANGED
package/dist/table.js
CHANGED
|
@@ -159,7 +159,7 @@ function sameCell(a, b) {
|
|
|
159
159
|
return a != 0 && a == b;
|
|
160
160
|
}
|
|
161
161
|
function computeMap(table) {
|
|
162
|
-
if (table.
|
|
162
|
+
if (table.type != Table.type)
|
|
163
163
|
throw new RangeError(`Not a table node: ${table.type.name}`);
|
|
164
164
|
let width = table.content[0].content.reduce((w, c) => w + (c.mark(ColSpan) ?? 1), 0);
|
|
165
165
|
let height = table.content.length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerd-bible/wordgard",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Semantic rich text editor system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
"./collab": "./dist/collab.js",
|
|
18
18
|
"./phrases": "./dist/phrases.js"
|
|
19
19
|
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "mocha test/test-*.ts",
|
|
22
|
+
"test-headless": "node bin/test-headless.ts",
|
|
23
|
+
"prepare": "node bin/build.ts",
|
|
24
|
+
"watch": "node bin/build.ts --watch",
|
|
25
|
+
"testserver": "node bin/run-testserver.ts",
|
|
26
|
+
"release": "node bin/release.ts"
|
|
27
|
+
},
|
|
20
28
|
"files": [
|
|
21
29
|
"/dist"
|
|
22
30
|
],
|
|
@@ -49,4 +57,4 @@
|
|
|
49
57
|
"repository": {
|
|
50
58
|
"url": "https://github.com/nerd-bible/wordgard"
|
|
51
59
|
}
|
|
52
|
-
}
|
|
60
|
+
}
|