UNPKG

quill

Version:

Your powerful, rich text editor

182 lines (181 loc) 5.46 kB
import { AttributorStore, BlockBlot, EmbedBlot, LeafBlot, Scope } from 'parchment'; import Delta from 'quill-delta'; import Break from './break.js'; import Inline from './inline.js'; import TextBlot from './text.js'; const NEWLINE_LENGTH = 1; class Block extends BlockBlot { cache = {}; delta() { if (this.cache.delta == null) { this.cache.delta = blockDelta(this); } return this.cache.delta; } deleteAt(index, length) { super.deleteAt(index, length); this.cache = {}; } formatAt(index, length, name, value) { if (length <= 0) return; if (this.scroll.query(name, Scope.BLOCK)) { if (index + length === this.length()) { this.format(name, value); } } else { super.formatAt(index, Math.min(length, this.length() - index - 1), name, value); } this.cache = {}; } insertAt(index, value, def) { if (def != null) { super.insertAt(index, value, def); this.cache = {}; return; } if (value.length === 0) return; const lines = value.split('\n'); const text = lines.shift(); if (text.length > 0) { if (index < this.length() - 1 || this.children.tail == null) { super.insertAt(Math.min(index, this.length() - 1), text); } else { this.children.tail.insertAt(this.children.tail.length(), text); } this.cache = {}; } // TODO: Fix this next time the file is edited. // eslint-disable-next-line @typescript-eslint/no-this-alias let block = this; lines.reduce((lineIndex, line) => { // @ts-expect-error Fix me later block = block.split(lineIndex, true); block.insertAt(0, line); return line.length; }, index + text.length); } insertBefore(blot, ref) { const { head } = this.children; super.insertBefore(blot, ref); if (head instanceof Break) { head.remove(); } this.cache = {}; } length() { if (this.cache.length == null) { this.cache.length = super.length() + NEWLINE_LENGTH; } return this.cache.length; } moveChildren(target, ref) { super.moveChildren(target, ref); this.cache = {}; } optimize(context) { super.optimize(context); this.cache = {}; } path(index) { return super.path(index, true); } removeChild(child) { super.removeChild(child); this.cache = {}; } split(index) { let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) { const clone = this.clone(); if (index === 0) { this.parent.insertBefore(clone, this); return this; } this.parent.insertBefore(clone, this.next); return clone; } const next = super.split(index, force); this.cache = {}; return next; } } Block.blotName = 'block'; Block.tagName = 'P'; Block.defaultChild = Break; Block.allowedChildren = [Break, Inline, EmbedBlot, TextBlot]; class BlockEmbed extends EmbedBlot { attach() { super.attach(); this.attributes = new AttributorStore(this.domNode); } delta() { return new Delta().insert(this.value(), { ...this.formats(), ...this.attributes.values() }); } format(name, value) { const attribute = this.scroll.query(name, Scope.BLOCK_ATTRIBUTE); if (attribute != null) { // @ts-expect-error TODO: Scroll#query() should return Attributor when scope is attribute this.attributes.attribute(attribute, value); } } formatAt(index, length, name, value) { this.format(name, value); } insertAt(index, value, def) { if (def != null) { super.insertAt(index, value, def); return; } const lines = value.split('\n'); const text = lines.pop(); const blocks = lines.map(line => { const block = this.scroll.create(Block.blotName); block.insertAt(0, line); return block; }); const ref = this.split(index); blocks.forEach(block => { this.parent.insertBefore(block, ref); }); if (text) { this.parent.insertBefore(this.scroll.create('text', text), ref); } } } BlockEmbed.scope = Scope.BLOCK_BLOT; // It is important for cursor behavior BlockEmbeds use tags that are block level elements function blockDelta(blot) { let filter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; return blot.descendants(LeafBlot).reduce((delta, leaf) => { if (leaf.length() === 0) { return delta; } return delta.insert(leaf.value(), bubbleFormats(leaf, {}, filter)); }, new Delta()).insert('\n', bubbleFormats(blot)); } function bubbleFormats(blot) { let formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; let filter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; if (blot == null) return formats; if ('formats' in blot && typeof blot.formats === 'function') { formats = { ...formats, ...blot.formats() }; if (filter) { // exclude syntax highlighting from deltas and getFormat() delete formats['code-token']; } } if (blot.parent == null || blot.parent.statics.blotName === 'scroll' || blot.parent.statics.scope !== blot.statics.scope) { return formats; } return bubbleFormats(blot.parent, formats, filter); } export { blockDelta, bubbleFormats, BlockEmbed, Block as default }; //# sourceMappingURL=block.js.map