UNPKG

@opentiny/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.

306 lines (305 loc) 12.3 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const Quill = require("quill"); const table = require("../formats/table.cjs.js"); const tableConfig = require("../table-config.cjs.js"); const index = require("../utils/index.cjs.js"); class TableSelection { constructor(table2, quill, dom) { if (!table2) { return null; } this.table = table2; this.quill = quill; this.modulesContainer = dom; this.parent = this.quill.root.parentNode; this.boundary = {}; this.selectedTds = []; this.dragging = false; this.preSelectedTable = null; this.oldRootScrollTop = this.quill.root.scrollTop; this.selectingHandler = this.mouseDownHandler.bind(this); this.tdEditHandler = this.setTdEditable.bind(this); this.tdResizeHandler = this.resizeTd.bind(this); this.helpLinesInitial(); this.isEditorHasScrollBar(); this.quill.root.parentNode.addEventListener("mousedown", this.selectingHandler, false); this.quill.on("text-change", this.tdResizeHandler); if (this.quill.root === this.quill.scrollingContainer) { this.quill.root.addEventListener("scroll", this.handleQuillRootScroll.bind(this)); } } handleQuillRootScroll() { tableConfig.LINE_POSITIONS.forEach((direction) => { const selectionLine = this[direction]; if (selectionLine) { selectionLine.style.marginTop = `${this.oldRootScrollTop - this.quill.root.scrollTop}px`; } }); } isEditorHasScrollBar() { if (this.quill.root.scrollHeight > this.quill.root.clientHeight) { index.css(this.modulesContainer, { width: "calc(100% - 23px)" }); } else { this.modulesContainer.removeAttribute("style"); } } helpLinesInitial() { tableConfig.LINE_POSITIONS.forEach((direction) => { this[direction] = document.createElement("div"); this[direction].classList.add("qlbt-selection-line"); if (direction === "left") { this[direction].classList.add("qlbt-selection-line-first"); const ctrlPointLeft = document.createElement("div"); this[direction].appendChild(ctrlPointLeft); } if (direction === "bottom") { this[direction].classList.add("qlbt-selection-line-last"); const ctrlPointRight = document.createElement("div"); this[direction].appendChild(ctrlPointRight); } index.css(this[direction], { "position": "absolute", "display": "none", "background-color": tableConfig.PRIMARY_COLOR }); this.modulesContainer.appendChild(this[direction]); }); } mouseDownHandler(e) { if (e.button !== 0) { return; } const self = this; const betterTableModule = this.quill.getModule("better-table"); const tableColumnTool = betterTableModule.columnTool; if (tableColumnTool.activeToolCell) { tableColumnTool.activeToolCell.classList.remove("qlbt-tool-cell-on"); tableColumnTool.activeToolCell = null; } if (e.target.closest(".quill-better-table")) { if (e.shiftKey) { this.endTd = e.target.closest("td[data-row]"); this.drawSelection(this, true); if (this.startTd !== this.endTd) { this.quill.blur(); } } else { this.startTd = e.target.closest("td[data-row]"); this.drawSelection(this, false); this.dragging = true; this.parent.addEventListener("mousemove", mouseMoveHandler, false); document.addEventListener("mouseup", mouseUpHandler, false); } } else if (e.target.closest(".qlbt-selection-line")) { this.parent.addEventListener("mousemove", mouseMoveHandler, false); document.addEventListener("mouseup", mouseUpHandler, false); } function mouseMoveHandler(event) { event.stopPropagation(); if (event.button !== 0 || !event.target.closest(".quill-better-table")) { return; } self.endTd = event.target.closest("td[data-row]"); if (!self.endTd) { return; } self.drawSelection(self, true); if (self.startTd !== self.endTd) { self.quill.blur(); } } function mouseUpHandler() { self.parent.removeEventListener("mousemove", mouseMoveHandler, false); document.removeEventListener("mouseup", mouseUpHandler, false); self.dragging = false; } } drawSelection(self, end) { self.startTdRect = index.getRelativeRect(self.startTd.getBoundingClientRect(), self.quill.root.parentNode); let endTdRect = self.startTdRect; if (end) { self.endTdRect = index.getRelativeRect(self.endTd.getBoundingClientRect(), self.quill.root.parentNode); endTdRect = self.endTdRect; } self.boundary = computeBoundaryFromRects(self.startTdRect, endTdRect); self.selectedTds = self.computeSelectedTds(); self.repositionHelpLines(); } setTdEditable(e) { if (!this.editTd) { return; } this.parent.removeEventListener("keypress", this.tdEditHandler, false); const dom = this.editTd.domNode; dom.removeEventListener("click", this.tdEditHandler, false); dom.classList.add("editing-select-able"); dom.setAttribute("contenteditable", true); if (e.type === "keypress") { if (dom.childNodes.length > 1 || dom.firstChild.innerHTML !== "<br>") { const blank = dom.firstChild.cloneNode(); blank.innerHTML = "<br>"; dom.innerHTML = ""; dom.appendChild(blank); } dom.focus(); } } resizeTd() { if (!this.editTd || this.left && !this.left.offsetParent) { return; } if (this.selectedTds.length > 1) { this.refreshHelpLinesPosition(); } else { const dom = this.editTd.domNode; const domRect = index.getRelativeRect(dom.getBoundingClientRect(), this.quill.root.parentNode); this.boundary = computeBoundaryFromRects(domRect, domRect); this.repositionHelpLines(); } this.isEditorHasScrollBar(); const betterTableModule = this.quill.getModule("better-table"); const tableColumnTool = betterTableModule.columnTool; if (tableColumnTool) { tableColumnTool.updateRowToolCells(); const tableViewRect = this.table.parentNode.getBoundingClientRect(); if (tableColumnTool.leftMask) { index.css(tableColumnTool.leftMask, { height: `${tableViewRect.height + 50}px` }); } } const tableScrollBar = betterTableModule.tableScrollBar; tableScrollBar.resetTableHeight(this.table); } resetTd(e) { const dom = e.target; dom.classList.remove("editing-select-able"); dom.setAttribute("contenteditable", false); dom.removeAttribute("contenteditable"); } correctBoundary() { const tableContainer = Quill.find(this.table); const tableCells = tableContainer.descendants(table.TableCell); tableCells.forEach((tableCell) => { const { x, y, width, height } = index.getRelativeRect(tableCell.domNode.getBoundingClientRect(), this.quill.root.parentNode); const isCellIntersected = (x + tableConfig.ERROR_LIMIT >= this.boundary.x && x + tableConfig.ERROR_LIMIT <= this.boundary.x1 || x - tableConfig.ERROR_LIMIT + width >= this.boundary.x && x - tableConfig.ERROR_LIMIT + width <= this.boundary.x1) && (y + tableConfig.ERROR_LIMIT >= this.boundary.y && y + tableConfig.ERROR_LIMIT <= this.boundary.y1 || y - tableConfig.ERROR_LIMIT + height >= this.boundary.y && y - tableConfig.ERROR_LIMIT + height <= this.boundary.y1); if (isCellIntersected) { this.boundary = computeBoundaryFromRects(this.boundary, { x, y, width, height }); } }); } computeSelectedTds() { const tableContainer = Quill.find(this.table); const tableCells = tableContainer.descendants(table.TableCell); const result = tableCells.reduce((selectedCells, tableCell) => { const { x, y, width, height } = index.getRelativeRect(tableCell.domNode.getBoundingClientRect(), this.quill.root.parentNode); const isCellIncluded = x + tableConfig.ERROR_LIMIT >= this.boundary.x && x - tableConfig.ERROR_LIMIT + width <= this.boundary.x1 && y + tableConfig.ERROR_LIMIT >= this.boundary.y && y - tableConfig.ERROR_LIMIT + height <= this.boundary.y1; if (isCellIncluded) { selectedCells.push(tableCell); } return selectedCells; }, []); if (result.length === 1 && this.editTd !== result[0]) { const resetTds = this.table.querySelectorAll("td[contenteditable=true]"); resetTds.forEach((td) => this.resetTd({ target: td })); this.editTd = result[0]; this.editTd.domNode.addEventListener("mousedown", this.tdEditHandler, false); this.parent.addEventListener("keypress", this.tdEditHandler, false); } return result; } repositionHelpLines() { this.oldRootScrollTop = this.quill.root.scrollTop; if (!this.left) { return; } index.css(this.left, { "display": "block", "left": `${this.boundary.x - 1}px`, "top": `${this.boundary.y}px`, "height": `${this.boundary.height + 1}px`, "width": "1px", "margin-top": "0px" }); index.css(this.right, { "display": "block", "left": `${this.boundary.x1}px`, "top": `${this.boundary.y - 1}px`, "height": `${this.boundary.height + 1}px`, "width": "1px", "margin-top": "0px" }); index.css(this.top, { "display": "block", "left": `${this.boundary.x - 1}px`, "top": `${this.boundary.y - 1}px`, "width": `${this.boundary.width + 1}px`, "height": "1px", "margin-top": "0px" }); index.css(this.bottom, { "display": "block", "left": `${this.boundary.x - 1}px`, "top": `${this.boundary.y1}px`, "width": `${this.boundary.width + 1}px`, "height": "1px", "margin-top": "0px" }); } // based on selectedTds compute positions of help lines // It is useful when selectedTds are not changed refreshHelpLinesPosition() { const startRect = index.getRelativeRect(this.selectedTds[0].domNode.getBoundingClientRect(), this.quill.root.parentNode); const endRect = index.getRelativeRect( this.selectedTds[this.selectedTds.length - 1].domNode.getBoundingClientRect(), this.quill.root.parentNode ); this.boundary = computeBoundaryFromRects(startRect, endRect); this.repositionHelpLines(); } destroy() { const resetTds = this.table.querySelectorAll("td[contenteditable=true]"); resetTds.forEach((td) => this.resetTd({ target: td })); tableConfig.LINE_POSITIONS.forEach((direction) => { if (this[direction]) { this[direction].remove(); this[direction] = null; } }); this.quill.root.parentNode.removeEventListener("mousedown", this.selectingHandler, false); delete this.editTd; return null; } setSelection(startRect, endRect) { this.boundary = computeBoundaryFromRects( index.getRelativeRect(startRect, this.quill.root.parentNode), index.getRelativeRect(endRect, this.quill.root.parentNode) ); this.correctBoundary(); this.selectedTds = this.computeSelectedTds(); this.repositionHelpLines(); } clearSelection() { const resetTds = this.table.querySelectorAll("td[contenteditable=true]"); resetTds.forEach((td) => this.resetTd({ target: td })); this.boundary = {}; this.selectedTds = []; tableConfig.LINE_POSITIONS.forEach((direction) => { if (this[direction]) { index.css(this[direction], { display: "none" }); } }); } } function computeBoundaryFromRects(startRect, endRect) { const x = Math.min(startRect.x, endRect.x, startRect.x + startRect.width - 1, endRect.x + endRect.width - 1); const x1 = Math.max(startRect.x, endRect.x, startRect.x + startRect.width - 1, endRect.x + endRect.width - 1); const y = Math.min(startRect.y, endRect.y, startRect.y + startRect.height - 1, endRect.y + endRect.height - 1); const y1 = Math.max(startRect.y, endRect.y, startRect.y + startRect.height - 1, endRect.y + endRect.height - 1); const width = x1 - x; const height = y1 - y; return { x, x1, y, y1, width, height }; } exports.default = TableSelection; //# sourceMappingURL=table-selection.cjs.js.map