UNPKG

@cairn214/fluent-editor

Version:

A rich text editor based on Quill 2.0, which extends rich modules and formats on the basis of Quill. It's powerful and out-of-the-box.

486 lines (485 loc) 19.5 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const Quill = require("quill"); const editor_utils = require("../config/editor.utils.cjs.js"); const header = require("./formats/header.cjs.js"); const list = require("./formats/list.cjs.js"); const table = require("./formats/table.cjs.js"); const tableColumnTool = require("./modules/table-column-tool.cjs.js"); const tableOperationMenu = require("./modules/table-operation-menu.cjs.js"); const tableScrollBar = require("./modules/table-scroll-bar.cjs.js"); const tableSelection = require("./modules/table-selection.cjs.js"); const tableSelector = require("./modules/table-selector.cjs.js"); const index = require("./utils/index.cjs.js"); const nodeMatchers = require("./utils/node-matchers.cjs.js"); const Block = Quill.imports["blots/block"]; const Delta = Quill.imports.delta; const Module = Quill.imports["core/module"]; class BetterTable extends Module { static register() { Quill.register(table.TableCol, true); Quill.register(table.TableColGroup, true); Quill.register(table.TableCellLine, true); Quill.register(table.TableCell, true); Quill.register(table.TableRow, true); Quill.register(table.TableBody, true); Quill.register(table.TableContainer, true); Quill.register(table.TableViewWrapper, true); Quill.register("formats/header", header.default, true); Quill.register("formats/list", list.default, true); } constructor(quill, options) { super(quill, options); this.isComposition = false; this.isTableSelectorVisible = false; this.quill.root.addEventListener("mousedown", (event) => this.handleMouseDown(event, quill), false); this.quill.root.addEventListener("compositionend", () => this.handleCompositionend(quill), false); this.quill.root.addEventListener("compositionstart", () => this.handleCompositionstart(quill), false); this.quill.root.addEventListener("keypress", (event) => this.handleKeyDown(event, quill), false); this.quill.root.addEventListener( "contextmenu", (evt) => { if (!this.table) return true; evt.preventDefault(); const path = editor_utils.getEventComposedPath(evt); if (!path || path.length <= 0) return; const tableNode = path.filter((node) => { return node.tagName && node.tagName.toUpperCase() === "TABLE" && node.classList.contains("quill-better-table"); })[0]; const rowNode = path.filter((node) => { return node.tagName && node.tagName.toUpperCase() === "TR" && node.getAttribute("data-row"); })[0]; const cellNode = path.filter((node) => { return node.tagName && node.tagName.toUpperCase() === "TD" && node.getAttribute("data-row"); })[0]; const isTargetCellSelected = this.tableSelection.selectedTds.map((tableCell) => tableCell.domNode).includes(cellNode); if (this.tableSelection.selectedTds.length <= 0 || !isTargetCellSelected) { this.tableSelection.setSelection(cellNode.getBoundingClientRect(), cellNode.getBoundingClientRect()); } if (this.tableOperationMenu) { this.tableOperationMenu = this.tableOperationMenu.destroy(); } if (tableNode) { this.tableOperationMenu = new tableOperationMenu.default( { table: tableNode, row: rowNode, cell: cellNode, left: evt.x + 15, top: evt.y }, quill, options.operationMenu ); } }, false ); quill.keyboard.addBinding({ key: "Backspace" }, {}, (range, context) => { if (range.index === 0 || this.quill.getLength() <= 1) return true; if (context.offset === 0 && range.length === 0) { const [prev] = this.quill.getLine(range.index - 1); const [line] = this.quill.getLine(range.index); if (!editor_utils.isNullOrUndefined(prev)) { if (prev.statics.blotName === "table-cell-line" && line.statics.blotName !== "table-cell-line") { const betterTableModule = this.quill.getModule("better-table"); let tableBlot; try { tableBlot = prev.parent.parent.parent.parent; } catch (_e) { } if (tableBlot && tableBlot.domNode !== betterTableModule.table) { betterTableModule.hideTableTools(); tableBlot.remove(); this.quill.update(Quill.sources.USER); } return false; } } } return true; }); const thisBinding = quill.keyboard.bindings.Backspace.pop(); quill.keyboard.bindings.Backspace.splice(1, 0, thisBinding); quill.clipboard.addMatcher("td", nodeMatchers.matchTableCell); quill.clipboard.addMatcher("th", nodeMatchers.matchTableHeader); quill.clipboard.addMatcher("table", nodeMatchers.matchTable); quill.clipboard.addMatcher("h1, h2, h3, h4, h5, h6", nodeMatchers.matchHeader); quill.clipboard.addMatcher("ol, ul", nodeMatchers.matchList); quill.clipboard.addMatcher("span", nodeMatchers.matchMentionLink); quill.clipboard.addMatcher("v:imageData", nodeMatchers.matchWordShapeImage); quill.clipboard.addMatcher(Node.ELEMENT_NODE, nodeMatchers.matchInline); quill.clipboard.matchers = quill.clipboard.matchers.filter((matcher) => { return matcher[0] !== "tr"; }); quill.clipboard.addMatcher("tr", nodeMatchers.matchTableRow); this.quill.on(Quill.events.EDITOR_CHANGE, () => { const tableContainer = Quill.find(this.table); if (!tableContainer) { this.hideTableTools(); } }); this.quill.on(Quill.events.SELECTION_CHANGE, (range, _oldRange, source) => { if (!range) return; const selectionStart = range.index; const selectionEnd = range.index + range.length; if (source === Quill.sources.USER) { const tables = this.quill.root.getElementsByTagName("table"); if (tables && tables.length) { [].forEach.call(tables, (table2) => { const tableContainer = Quill.find(table2); if (tableContainer) { const tableStart = tableContainer.offset(this.quill.scroll); const tableEnd = tableStart + tableContainer.length(); const classes = Array.from(table2.parentNode.classList); if (selectionStart <= tableStart && tableEnd <= selectionEnd) { table2.parentNode.classList.add("quill-better-table-selected"); } else if (classes.includes("quill-better-table-selected")) { table2.parentNode.classList.remove("quill-better-table-selected"); } } }); } } }); this.initTableSelector(); } initTableSelector() { const toolbar = this.quill.getModule("toolbar"); if (!toolbar) return; const tableButton = toolbar.container.querySelector(".ql-better-table"); if (!tableButton) return; const tableSelectorWrapper = this.tableSelectorWrapperCreator(); tableButton.parentNode.insertBefore(tableSelectorWrapper, tableButton); tableSelectorWrapper.appendChild(tableButton); this.tableSelectorWrapper = tableSelectorWrapper; tableSelectorWrapper.addEventListener("mouseenter", this.handleTableSelectorHover.bind(this), false); tableSelectorWrapper.addEventListener("mouseleave", this.handleTableSelectorMouseOut.bind(this), false); } tableSelectorWrapperCreator() { const wrapper = document.createElement("div"); wrapper.className = "ql-better-table-wrapper"; const cssContent = { position: "relative", display: "flex", alignItems: "center", justifyContent: "center", boxSizing: "border-box" }; index.css(wrapper, cssContent); return wrapper; } handleMouseDown(evt, quill) { const path = editor_utils.getEventComposedPath(evt); if (!path || path.length <= 0) return; const tableNode = path.filter((node) => { return node.tagName && node.tagName.toUpperCase() === "TABLE" && node.classList.contains("quill-better-table"); })[0]; if (tableNode) { tableNode.parentNode.classList.remove("quill-better-table-selected"); if (this.table === tableNode) return; if (this.table) { this.hideTableTools(); } if (!quill.options.readOnly) { this.showTableTools(tableNode, quill); } } else if (this.table) { this.hideTableTools(); } } handleKeyDown(evt, quill) { const key = evt.key; const range = quill.getSelection(); if (this.isComposition || !range) return; const [col] = quill.getLine(range.index); const [td] = quill.getLine(range.index - 1); if (key && key !== "Delete" && col && range) { if (col.statics.blotName === "table-col") { const betterTableModule = this.quill.getModule("better-table"); betterTableModule.hideTableTools(); quill.insertText(range.index - 1, "\n", Quill.sources.USER); quill.setSelection(range.index, 0, Quill.sources.USER); } else if (td && td.statics.blotName === "table-cell-line" && col.statics.blotName === "block") { quill.insertText(range.index, "\n", Quill.sources.USER); quill.setSelection(range.index, 0, Quill.sources.USER); } } } handleCompositionstart(quill) { this.isComposition = true; const range = quill.getSelection(); const [col] = quill.getLine(range.index); const [td] = quill.getLine(range.index - 1); if (col && range) { if (col.statics.blotName === "table-col") { const betterTableModule = this.quill.getModule("better-table"); betterTableModule.hideTableTools(); quill.insertText(range.index - 1, "\n ", Quill.sources.USER); quill.setSelection(range.index, 0, Quill.sources.USER); } else if (td && td.statics.blotName === "table-cell-line" && col.statics.blotName === "block") { quill.setSelection(range.index, 0, Quill.sources.USER); } } } handleCompositionend(quill) { const range = quill.getSelection(); const [line] = quill.getLine(range.index); const domNode = line.domNode; const isInParagraph = domNode.nodeName === "P"; const isInTableCell = line.statics.blotName === "table-cell-line"; if ((isInParagraph || isInTableCell) && domNode.childNodes.length > 1 && domNode.childNodes[0].nodeName === "BR") { domNode.childNodes[0].remove(); } if (editor_utils.isPureIE && line instanceof Block) { quill.setSelection(range.index, 0, Quill.sources.SILENT); } this.isComposition = false; } // 触发table selector handleTableSelectorHover() { if (this.isTableSelectorVisible) return; if (!this.tableSelectorWrapper) return; this.isTableSelectorVisible = true; this.tableSelector = new tableSelector.default({ onSelect: (rows, cols) => this.insertTable(rows, cols) }); this.tableSelectorWrapper.appendChild(this.tableSelector.container); const buttonRect = this.tableSelectorWrapper.getBoundingClientRect(); this.tableSelector.show( 0, buttonRect.height ); } // table selector 鼠标离开 handleTableSelectorMouseOut() { if (this.tableSelector) { this.tableSelector.destroy(); } this.tableSelector = null; this.isTableSelectorVisible = false; } getTable(range = this.quill.getSelection()) { if (editor_utils.isNullOrUndefined(range)) return [null, null, null, -1]; const [cellLine, offset] = this.quill.getLine(range.index); if (editor_utils.isNullOrUndefined(cellLine) || cellLine.statics.blotName !== table.TableCellLine.blotName) { return [null, null, null, -1]; } const cell = cellLine.tableCell(); const row = cell.row(); const table$1 = row.table(); return [table$1, row, cell, offset]; } insertTable(rows, columns) { const range = this.quill.getSelection(true); const isInsideTable = editor_utils.insideTable(range, this.quill); if (editor_utils.isNullOrUndefined(range) || isInsideTable) return; let delta = new Delta().retain(range.index); delta.insert("\n"); delta = new Array(columns).fill("\n").reduce((memo, text) => { memo.insert(text, { "table-col": true }); return memo; }, delta); delta = new Array(rows).fill(0).reduce((memo) => { const tableRowId = table.rowId(); return new Array(columns).fill("\n").reduce((op, text) => { op.insert(text, { "table-cell-line": { row: tableRowId, cell: table.cellId() } }); return op; }, memo); }, delta); const [col] = this.quill.getLine(range.index); if (col && col.statics.blotName === "table-col") { delta.insert("\n"); } this.quill.updateContents(delta, Quill.sources.USER); const [cellLine] = this.quill.getLine(range.index + columns + 1); if (cellLine) { const cell = cellLine.tableCell(); const td = cell.domNode; const mousedownEvent = document.createEvent("MouseEvent"); const mouseupEvent = document.createEvent("MouseEvent"); const keyEvent = document.createEvent("Event"); mousedownEvent.initEvent("mousedown", true, false); mouseupEvent.initEvent("mouseup", true, false); keyEvent.initEvent("keypress", true, false); td.dispatchEvent(mousedownEvent); td.dispatchEvent(mouseupEvent); td.dispatchEvent(keyEvent); this.tableSelection.endTd = td; } } showTableTools(table2, quill) { this.table = table2; this.modulesContainer = document.createElement("div"); this.modulesContainer.classList.add("qlbt-modules-container"); const linkContainer = this.quill.root.parentNode.querySelector(".ql-tooltip "); this.quill.root.parentNode.insertBefore(this.modulesContainer, linkContainer); this.tableSelection = new tableSelection.default(table2, quill, this.modulesContainer); this.columnTool = new tableColumnTool.default(table2, quill, this.modulesContainer); this.tableScrollBar = new tableScrollBar.default(table2, quill, this.modulesContainer); let timeoutID; this.subscriber = () => { clearTimeout(timeoutID); timeoutID = setTimeout(this.hideTableTools, 300); }; window.addEventListener("resize", this.subscriber, false); } hideTableTools() { if (this.columnTool) { this.columnTool.destroy(); } if (this.tableSelection) { this.tableSelection.destroy(); } if (this.tableOperationMenu) { this.tableOperationMenu.destroy(); } if (this.tableScrollBar) { this.tableScrollBar.destroy(); } if (this.modulesContainer) { this.modulesContainer.remove(); } this.columnTool = null; this.tableSelection = null; this.tableOperationMenu = null; this.tableScrollBar = null; this.modulesContainer = null; this.table = null; if (this.subscriber) { window.removeEventListener("resize", this.subscriber); } } } BetterTable.keyboardBindings = { "line delete": { key: "Delete", shiftKey: null, format: { "table-col": false, "table-cell-line": false }, collapsed: true, offset: 0, handler(range, context) { if (range.index === 0 && context.line.next && context.line.next.statics.blotName === "table-view") { const [p] = this.quill.getLine(range.index - 1); const [col] = this.quill.getLine(range.index + 1); if (p && p.statics.blotName === "block" && col && col.statics.blotName === "table-col") { return false; } } return true; } }, "table-col enter": { key: "Enter", shiftKey: null, format: ["table-col"], collapsed: true, offset: 0, handler(range, context) { if (context.offset === 0 && range.length === 0) { const [col] = this.quill.getLine(range.index); if (col && col.statics.blotName === "table-col") { const betterTableModule = this.quill.getModule("better-table"); betterTableModule.hideTableTools(); this.quill.insertText(range.index - 1, "\n", Quill.sources.USER); this.quill.setSelection(range.index, 0, Quill.sources.USER); } } } }, "table-col delete": { key: "Delete", shiftKey: null, format: ["table-col"], collapsed: true, offset: 0, handler(range, context) { if (context.offset === 0 && range.length === 0) { const [col] = this.quill.getLine(range.index); if (col && col.statics.blotName === "table-col") { const betterTableModule = this.quill.getModule("better-table"); let tableBlot; try { tableBlot = col.parent.parent.parent; } catch (_e) { } if (tableBlot && tableBlot.domNode !== betterTableModule.table) { betterTableModule.hideTableTools(); tableBlot.remove(); this.quill.update(Quill.sources.USER); } } } } }, "table-col backspace": { key: "Backspace", shiftKey: null, format: ["table-col"], collapsed: true, offset: 0, handler() { } }, "table-cell-line backspace": { key: "Backspace", format: ["table-cell-line"], collapsed: true, offset: 0, handler(range) { const [line] = this.quill.getLine(range.index); if (!line.prev) { return false; } return true; } }, "table-cell-line delete": { key: "Delete", format: ["table-cell-line"], handler(range, _context) { const [line] = this.quill.getLine(range.index); const index2 = this.quill.getIndex(line); const length = line.length(); if (!line.next && (!line.prev && length === 1 || range.index !== index2 && range.index + 1 >= index2 + length)) { return false; } return true; } }, "table-cell-line enter": { key: "Enter", shiftKey: null, format: ["table-cell-line"], handler(range, context) { const node = window.getSelection().anchorNode; if (node instanceof HTMLDivElement && node.classList.contains("quill-better-table-wrapper")) { this.quill.setSelection(range.index + 1, 0, Quill.sources.USER); this.quill.focus(); return; } if (this.quill.selection && this.quill.selection.composing) return; const Scope = Quill.imports.parchment.Scope; if (range.length > 0) { this.quill.scroll.deleteAt(range.index, range.length); } const lineFormats = Object.keys(context.format).reduce((formats, format) => { if (this.quill.scroll.query(format, Scope.BLOCK) && !Array.isArray(context.format[format])) { formats[format] = context.format[format]; } return formats; }, {}); this.quill.insertText(range.index, "\n", lineFormats["table-cell-line"], Quill.sources.USER); this.quill.setSelection(range.index + 1, Quill.sources.SILENT); this.quill.focus(); Object.keys(context.format).forEach((name) => { if (!editor_utils.isNullOrUndefined(lineFormats[name])) return; if (Array.isArray(context.format[name])) return; if (name === "link") return; this.quill.format(name, context.format[name], Quill.sources.USER); }); } } }; exports.default = BetterTable; //# sourceMappingURL=better-table.cjs.js.map