UNPKG

5.46 kBJavaScriptView Raw
1import { AttributorStore, BlockBlot, EmbedBlot, LeafBlot, Scope } from 'parchment';
2import Delta from 'quill-delta';
3import Break from './break.js';
4import Inline from './inline.js';
5import TextBlot from './text.js';
6const NEWLINE_LENGTH = 1;
7class Block extends BlockBlot {
8 cache = {};
9 delta() {
10 if (this.cache.delta == null) {
11 this.cache.delta = blockDelta(this);
12 }
13 return this.cache.delta;
14 }
15 deleteAt(index, length) {
16 super.deleteAt(index, length);
17 this.cache = {};
18 }
19 formatAt(index, length, name, value) {
20 if (length <= 0) return;
21 if (this.scroll.query(name, Scope.BLOCK)) {
22 if (index + length === this.length()) {
23 this.format(name, value);
24 }
25 } else {
26 super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);
27 }
28 this.cache = {};
29 }
30 insertAt(index, value, def) {
31 if (def != null) {
32 super.insertAt(index, value, def);
33 this.cache = {};
34 return;
35 }
36 if (value.length === 0) return;
37 const lines = value.split('\n');
38 const text = lines.shift();
39 if (text.length > 0) {
40 if (index < this.length() - 1 || this.children.tail == null) {
41 super.insertAt(Math.min(index, this.length() - 1), text);
42 } else {
43 this.children.tail.insertAt(this.children.tail.length(), text);
44 }
45 this.cache = {};
46 }
47 // TODO: Fix this next time the file is edited.
48 // eslint-disable-next-line @typescript-eslint/no-this-alias
49 let block = this;
50 lines.reduce((lineIndex, line) => {
51 // @ts-expect-error Fix me later
52 block = block.split(lineIndex, true);
53 block.insertAt(0, line);
54 return line.length;
55 }, index + text.length);
56 }
57 insertBefore(blot, ref) {
58 const {
59 head
60 } = this.children;
61 super.insertBefore(blot, ref);
62 if (head instanceof Break) {
63 head.remove();
64 }
65 this.cache = {};
66 }
67 length() {
68 if (this.cache.length == null) {
69 this.cache.length = super.length() + NEWLINE_LENGTH;
70 }
71 return this.cache.length;
72 }
73 moveChildren(target, ref) {
74 super.moveChildren(target, ref);
75 this.cache = {};
76 }
77 optimize(context) {
78 super.optimize(context);
79 this.cache = {};
80 }
81 path(index) {
82 return super.path(index, true);
83 }
84 removeChild(child) {
85 super.removeChild(child);
86 this.cache = {};
87 }
88 split(index) {
89 let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
90 if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {
91 const clone = this.clone();
92 if (index === 0) {
93 this.parent.insertBefore(clone, this);
94 return this;
95 }
96 this.parent.insertBefore(clone, this.next);
97 return clone;
98 }
99 const next = super.split(index, force);
100 this.cache = {};
101 return next;
102 }
103}
104Block.blotName = 'block';
105Block.tagName = 'P';
106Block.defaultChild = Break;
107Block.allowedChildren = [Break, Inline, EmbedBlot, TextBlot];
108class BlockEmbed extends EmbedBlot {
109 attach() {
110 super.attach();
111 this.attributes = new AttributorStore(this.domNode);
112 }
113 delta() {
114 return new Delta().insert(this.value(), {
115 ...this.formats(),
116 ...this.attributes.values()
117 });
118 }
119 format(name, value) {
120 const attribute = this.scroll.query(name, Scope.BLOCK_ATTRIBUTE);
121 if (attribute != null) {
122 // @ts-expect-error TODO: Scroll#query() should return Attributor when scope is attribute
123 this.attributes.attribute(attribute, value);
124 }
125 }
126 formatAt(index, length, name, value) {
127 this.format(name, value);
128 }
129 insertAt(index, value, def) {
130 if (def != null) {
131 super.insertAt(index, value, def);
132 return;
133 }
134 const lines = value.split('\n');
135 const text = lines.pop();
136 const blocks = lines.map(line => {
137 const block = this.scroll.create(Block.blotName);
138 block.insertAt(0, line);
139 return block;
140 });
141 const ref = this.split(index);
142 blocks.forEach(block => {
143 this.parent.insertBefore(block, ref);
144 });
145 if (text) {
146 this.parent.insertBefore(this.scroll.create('text', text), ref);
147 }
148 }
149}
150BlockEmbed.scope = Scope.BLOCK_BLOT;
151// It is important for cursor behavior BlockEmbeds use tags that are block level elements
152
153function blockDelta(blot) {
154 let filter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
155 return blot.descendants(LeafBlot).reduce((delta, leaf) => {
156 if (leaf.length() === 0) {
157 return delta;
158 }
159 return delta.insert(leaf.value(), bubbleFormats(leaf, {}, filter));
160 }, new Delta()).insert('\n', bubbleFormats(blot));
161}
162function bubbleFormats(blot) {
163 let formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
164 let filter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
165 if (blot == null) return formats;
166 if ('formats' in blot && typeof blot.formats === 'function') {
167 formats = {
168 ...formats,
169 ...blot.formats()
170 };
171 if (filter) {
172 // exclude syntax highlighting from deltas and getFormat()
173 delete formats['code-token'];
174 }
175 }
176 if (blot.parent == null || blot.parent.statics.blotName === 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {
177 return formats;
178 }
179 return bubbleFormats(blot.parent, formats, filter);
180}
181export { blockDelta, bubbleFormats, BlockEmbed, Block as default };
182//# sourceMappingURL=block.js.map
\No newline at end of file