UNPKG

@remirror/extension-react-tables

Version:
1,375 lines (1,354 loc) 66.3 kB
var __create = Object.create; var __freeze = Object.freeze; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name); var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key3, value) => key3 in obj ? __defProp(obj, key3, { enumerable: true, configurable: true, writable: true, value }) : obj[key3] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __decoratorStart = (base) => { var _a3; return [, , , __create((_a3 = base == null ? void 0 : base[__knownSymbol("metadata")]) != null ? _a3 : null)]; }; var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn; var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) }); var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]); var __runInitializers = (array, flags, self, value) => { for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value); return value; }; var __decorateElement = (array, flags, name, decorators, target, extra) => { var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16); var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key3 = __decoratorStrings[k + 5]; var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []); var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() { return __privateGet(this, extra); }, set [name](x) { return __privateSet(this, extra, x); } }, name)); k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name); for (var i = decorators.length - 1; i >= 0; i--) { ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers); if (k) { ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x }; if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name]; if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y; } it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key3] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key3] = it : target = it); else if (typeof it !== "object" || it === null) __typeError("Object expected"); else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn); } return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target; }; var __publicField = (obj, key3, value) => __defNormalProp(obj, typeof key3 !== "symbol" ? key3 + "" : key3, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) })); // src/components/table-cell-menu.tsx import React, { useState } from "react"; import { PositionerPortal } from "@remirror/react-components"; import { useCommands } from "@remirror/react-core"; import { useEditorEvent, usePositioner } from "@remirror/react-hooks"; import { ComponentsTheme } from "@remirror/theme"; // src/block-positioner.ts import { findParentNodeOfType } from "@remirror/core"; import { blockNodePositioner, Positioner } from "@remirror/extension-positioner"; import { CellSelection } from "@remirror/pm/tables"; var cellNodeTypes = ["tableCell", "tableHeaderCell"]; function findMenuTableCell(selection) { if (selection instanceof CellSelection) { return findParentNodeOfType({ selection: selection.$head, types: cellNodeTypes }); } return findParentNodeOfType({ selection, types: cellNodeTypes }); } var menuCellPositioner = blockNodePositioner.clone(() => ({ getActive: (props) => { const result = findMenuTableCell(props.state.selection); return result ? [result] : Positioner.EMPTY; } })); // src/const.ts var borderWidth = 1; // src/components/table-cell-menu.tsx var DefaultTableCellMenuButton = ({ setPopupOpen }) => /* @__PURE__ */ React.createElement( "button", { onClick: () => { setPopupOpen(true); }, onMouseDown: (event) => { event.preventDefault(); event.stopPropagation(); }, style: { position: "relative", right: "0px", top: "0px", height: "16px", width: "16px", border: "1px solid blue", fontSize: "10px", lineHeight: "10px", cursor: "pointer" }, className: ComponentsTheme.BUTTON }, "v" ); var DefaultTableCellMenuItem = (props) => /* @__PURE__ */ React.createElement("button", { disabled: props.disabled, onClick: props.onClick, className: ComponentsTheme.MENU_ITEM }, props.label); var DefaultTableCellMenuPopup = ({ setPopupOpen, popupOpen }) => { const commands = useCommands(); const handleClick = (command2) => () => { command2(); setPopupOpen(false); }; const setTableCellBackground = (color) => () => { commands.setTableCellBackground(color); }; return /* @__PURE__ */ React.createElement( "div", { style: { position: "absolute", backgroundColor: "white", width: "200px", border: "1px solid lightgray", display: popupOpen ? "flex" : "none", flexDirection: "column" }, className: ComponentsTheme.MENU }, /* @__PURE__ */ React.createElement(DefaultTableCellMenuItem, { label: "Color teal", onClick: setTableCellBackground("teal") }), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Color pink", onClick: setTableCellBackground("rgba(255,100,100,0.3)") } ), /* @__PURE__ */ React.createElement(DefaultTableCellMenuItem, { label: "Remove color", onClick: setTableCellBackground(null) }), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Add row above", onClick: handleClick(commands.addTableRowBefore) } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Add row below", onClick: handleClick(commands.addTableRowAfter) } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Add column before", onClick: handleClick(commands.addTableColumnBefore) } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Add column after", onClick: handleClick(commands.addTableColumnAfter) } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Merge cells", onClick: handleClick(commands.mergeTableCells), disabled: !commands.mergeTableCells.enabled() } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Split cells", onClick: handleClick(commands.splitTableCell), disabled: !commands.splitTableCell.enabled() } ), /* @__PURE__ */ React.createElement( DefaultTableCellMenuItem, { label: "Remove column", onClick: handleClick(commands.deleteTableColumn) } ), /* @__PURE__ */ React.createElement(DefaultTableCellMenuItem, { label: "Remove row", onClick: handleClick(commands.deleteTableRow) }), /* @__PURE__ */ React.createElement(DefaultTableCellMenuItem, { label: "Remove table", onClick: handleClick(commands.deleteTable) }) ); }; var DefaultTableCellMenuComponent = (props) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DefaultTableCellMenuButton, __spreadValues({}, props)), /* @__PURE__ */ React.createElement(DefaultTableCellMenuPopup, __spreadValues({}, props))); var TableCellMenu = ({ Component = DefaultTableCellMenuComponent }) => { const position = usePositioner(menuCellPositioner, []); const { ref, width, height, x, y } = position; const [popupOpen, setPopupOpen] = useState(false); useEditorEvent("mousedown", () => { if (popupOpen) { setPopupOpen(false); } return false; }); return /* @__PURE__ */ React.createElement(PositionerPortal, null, /* @__PURE__ */ React.createElement( "div", { ref, style: { position: "absolute", left: x, top: y, width: width + borderWidth, height: height + borderWidth, zIndex: 100, pointerEvents: "none", // place the child into the top-left corner display: "flex", justifyContent: "flex-end", alignItems: "flex-start" // for debug: // backgroundColor: 'lightpink', // opacity: 0.5, } }, /* @__PURE__ */ React.createElement( "div", { style: { zIndex: 100, pointerEvents: "initial" } }, /* @__PURE__ */ React.createElement(Component, { popupOpen, setPopupOpen }) ) )); }; // src/components/table-components.tsx import React4 from "react"; import { useHelpers } from "@remirror/react-core"; // src/components/table-delete-row-column-button.tsx import React2, { useCallback } from "react"; import { cx as cx2, findParentNodeOfType as findParentNodeOfType3, isElementDomNode, mergeDOMRects } from "@remirror/core"; import { defaultAbsolutePosition, hasStateChanged, isPositionVisible, Positioner as Positioner2 } from "@remirror/extension-positioner"; import { deleteColumn, deleteRow, isCellSelection, TableMap as TableMap5 } from "@remirror/pm/tables"; import { Icon, PositionerPortal as PositionerPortal2 } from "@remirror/react-components"; import { useRemirrorContext } from "@remirror/react-core"; import { usePositioner as usePositioner2 } from "@remirror/react-hooks"; import { ExtensionTablesTheme as ExtensionTablesTheme4 } from "@remirror/theme"; // src/utils/controller.ts import { Fragment as Fragment2 } from "@remirror/pm/model"; import { cellAround as cellAround2, CellSelection as CellSelection2, TableMap as TableMap4 } from "@remirror/pm/tables"; // src/table-column-resizing.ts import { Plugin, PluginKey } from "@remirror/pm/state"; import { cellAround, TableMap as TableMap3, updateColumnsOnResize as updateColumns } from "@remirror/pm/tables"; import { Decoration, DecorationSet } from "@remirror/pm/view"; // src/views/table-view.tsx import { range, throttle } from "@remirror/core"; import { TableMap as TableMap2, updateColumnsOnResize } from "@remirror/pm/tables"; import { ExtensionTablesTheme as ExtensionTablesTheme2 } from "@remirror/theme"; // src/components/table-insert-button.ts import { addFill } from "@remirror/icons"; import { ExtensionTablesTheme } from "@remirror/theme"; // src/react-table-commands.ts import { addColSpan, isInTable, selectedRect, tableNodeTypes } from "@remirror/pm/tables"; function addColumn(tr, { map, tableStart, table }, col) { const refColumn = col < map.width ? 0 : -1; for (let row = 0; row < map.height; row++) { const index = row * map.width + col; if (col > 0 && col < map.width && map.map[index - 1] === map.map[index]) { const pos = map.map[index]; const cell = table.nodeAt(pos); tr.setNodeMarkup( tr.mapping.map(tableStart + pos), void 0, // @ts-expect-error: cell.attrs needs stricter types addColSpan(cell.attrs, col - map.colCount(pos)) ); row += cell.attrs.rowspan - 1; } else { const type = table.nodeAt(map.map[index + refColumn]).type; const pos = map.positionAt(row, col, table); tr.insert(tr.mapping.map(tableStart + pos), type.createAndFill()); } } return tr; } function addColumnBefore(state, dispatch) { if (!isInTable(state)) { return false; } if (dispatch) { const rect = selectedRect(state); dispatch(addColumn(state.tr, rect, rect.left)); } return true; } function addColumnAfter(state, dispatch) { if (!isInTable(state)) { return false; } if (dispatch) { const rect = selectedRect(state); dispatch(addColumn(state.tr, rect, rect.right)); } return true; } function addRow(tr, { map, tableStart, table }, row) { let rowPos = tableStart; for (let i = 0; i < row; i++) { rowPos += table.child(i).nodeSize; } const refRow = row < map.height - 1 ? 0 : -1; const cells = []; for (let col = 0, index = map.width * row; col < map.width; col++, index++) { if (row > 0 && row < map.height && map.map[index] === map.map[index - map.width]) { const pos = map.map[index]; const attrs = table.nodeAt(pos).attrs; tr.setNodeMarkup(tableStart + pos, void 0, setAttr(attrs, "rowspan", attrs.rowspan + 1)); col += attrs.colspan - 1; } else { const type = table.nodeAt(map.map[index + refRow * map.width]).type; cells.push(type.createAndFill()); } } tr.insert(rowPos, tableNodeTypes(table.type.schema).row.create(null, cells)); return tr; } function addRowBefore(state, dispatch) { if (!isInTable(state)) { return false; } if (dispatch) { const rect = selectedRect(state); dispatch(addRow(state.tr, rect, rect.top)); } return true; } function addRowAfter(state, dispatch) { if (!isInTable(state)) { return false; } if (dispatch) { const rect = selectedRect(state); dispatch(addRow(state.tr, rect, rect.bottom)); } return true; } // src/utils/dom.ts import { cx, isNullOrUndefined } from "@remirror/core"; function h(tagName, attrs, ...children) { const element = document.createElement(tagName); if (attrs) { for (let [key3, value] of Object.entries(attrs)) { if (isNullOrUndefined(value)) { continue; } key3 = key3.toLowerCase(); if (/^on[a-z]+/.test(key3) && typeof value === "function") { element.addEventListener(key3.slice(2), value); } else if (["class", "classname"].includes(key3)) { element.className = cx(value); } else if (key3 === "dataset") { for (const [dataKey, dataValue] of Object.entries(value)) { element.dataset[dataKey] = dataValue; } } else if (key3 === "style" && typeof value === "object") { Object.assign(element.style, value); } else if (["number", "boolean", "string"].includes(typeof value)) { element.setAttribute(key3, "".concat(value)); } else { throw new TypeError("Unexpected ".concat(typeof value, ' value for attribute "').concat(key3, '"')); } } } element.append(...children); return element; } function getRelativeCoord(absoluteCoord, parent) { const parentRect = parent.getBoundingClientRect(); return { x: absoluteCoord.x + parent.scrollLeft - parentRect.left, y: absoluteCoord.y + parent.scrollTop - parentRect.top }; } // src/components/table-insert-button.ts function shouldHideInsertButton(attrs, e) { if (attrs.col !== -1) { return e.clientX < attrs.triggerRect.left - 400 || e.clientX > attrs.triggerRect.right + 400 || e.clientY < attrs.triggerRect.top - 60 || e.clientY > attrs.triggerRect.bottom; } else if (attrs.row !== -1) { return e.clientX < attrs.triggerRect.left - 40 || e.clientX > attrs.triggerRect.right || e.clientY < attrs.triggerRect.top - 100 || e.clientY > attrs.triggerRect.bottom + 100; } return true; } var addFillIconCache = null; function AddFillIcon() { if (addFillIconCache) { return addFillIconCache; } const xmlns = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(xmlns, "svg"); svg.setAttribute("xmlns", xmlns); svg.setAttribute("viewBox", "0 0 24 24"); const g = document.createElementNS(xmlns, "g"); for (const tree of addFill) { const path = document.createElementNS(xmlns, tree.tag); for (const [key3, value] of Object.entries(tree.attr)) { path.setAttribute(key3, value); } g.append(path); } svg.append(g); addFillIconCache = svg; return svg; } function InnerTableInsertButton(attrs) { const size = 18; return h( "div", { className: "".concat(ExtensionTablesTheme.TABLE_INSERT_BUTTON, " ").concat(ExtensionTablesTheme.CONTROLLERS_TOGGLE), style: { top: "".concat(attrs.y - size / 2 - 5, "px"), left: "".concat(attrs.x - size / 2, "px") } }, AddFillIcon() ); } function TableInsertButton({ view, tableRect, attrs, removeInsertButton }) { const button = InnerTableInsertButton(attrs); const insertRolOrColumn = () => { let tr = view.state.tr; if (attrs.col !== -1) { tr = addColumn(tr, tableRect, attrs.col); } else if (attrs.row !== -1) { tr = addRow(tr, tableRect, attrs.row); } else { return; } view.dispatch(removeInsertButton(tr)); }; button.addEventListener("mousedown", () => { insertRolOrColumn(); }); return button; } var table_insert_button_default = TableInsertButton; // src/utils/prosemirror.ts import { Fragment, Slice } from "@remirror/pm/model"; import { ReplaceAroundStep } from "@remirror/pm/transform"; function setNodeAttrs(tr, pos, attrs, node) { node = node || tr.doc.nodeAt(pos); if (!node) { throw new RangeError("No node at given position"); } const type = node.type; const newNode = type.create(__spreadValues(__spreadValues({}, node.attrs), attrs), void 0, node.marks); if (node.isLeaf) { return tr.replaceWith(pos, pos + node.nodeSize, newNode); } if (!type.validContent(node.content)) { throw new RangeError("Invalid content for node type ".concat(type.name)); } return tr.step( new ReplaceAroundStep( pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1, new Slice(Fragment.from(newNode), 0, 0), 1, true ) ); } function cellSelectionToSelection(selection) { return selection; } // src/views/table-view.tsx var TableView = class { constructor(node, cellMinWidth, decorations, view, getPos) { this.node = node; this.cellMinWidth = cellMinWidth; this.decorations = decorations; this.view = view; this.getPos = getPos; __publicField(this, "root"); __publicField(this, "table"); __publicField(this, "colgroup"); __publicField(this, "tbody"); __publicField(this, "insertButtonWrapper"); __publicField(this, "handleMouseMove"); __publicField(this, "showInsertButton"); __publicField(this, "removeInsertButton"); __publicField(this, "map"); this.map = TableMap2.get(this.node); this.tbody = h("tbody", { className: ExtensionTablesTheme2.TABLE_TBODY_WITH_CONTROLLERS }); this.colgroup = h( "colgroup", { className: ExtensionTablesTheme2.TABLE_COLGROUP }, ...range(this.map.width).map(() => h("col")) ); this.table = h( "table", { className: ExtensionTablesTheme2.TABLE, dataset: { controllersInjected: "" } }, this.colgroup, this.tbody ); this.insertButtonWrapper = h("div"); this.root = h("div", null, this.table, this.insertButtonWrapper); this.render(); this.showInsertButton = false; this.handleMouseMove = throttle(100, (e) => { if (this.showInsertButton) { const attrs = this.attrs().insertButtonAttrs; if (attrs && shouldHideInsertButton(attrs, e)) { this.showInsertButton = false; replaceChildren(this.insertButtonWrapper, []); if (this.removeInsertButton) { this.view.dispatch(this.removeInsertButton(this.view.state.tr)); } } } }); document.addEventListener("mousemove", this.handleMouseMove); } get dom() { return this.root; } get contentDOM() { return this.tbody; } update(node, decorations) { if (node.type !== this.node.type) { return false; } this.decorations = decorations; this.node = node; this.map = TableMap2.get(this.node); this.render(); return true; } render() { this.renderTable(); if (!this.attrs().isControllersInjected) { return; } this.renderInsertButton(); } renderTable() { if (this.colgroup.children.length !== this.map.width) { const cols = range(this.map.width).map(() => h("col")); replaceChildren(this.colgroup, cols); } const className = [ ExtensionTablesTheme2.TABLE, // Hide the table before controllers injected this.attrs().isControllersInjected ? ExtensionTablesTheme2.TABLE_WITH_CONTROLLERS : ExtensionTablesTheme2.TABLE_WAITTING_CONTROLLERS ].join(" "); if (this.table.className !== className) { this.table.className = className; } updateColumnsOnResize(this.node, this.colgroup, this.table, this.cellMinWidth); } renderInsertButton() { const attrs = this.attrs().insertButtonAttrs; if (attrs) { const tableRect = { map: this.map, table: this.node, tableStart: this.getPos() + 1, // The following properties are not actually used left: 0, top: 0, right: 0, bottom: 0 }; this.removeInsertButton = (tr) => { const attrsPatch = { insertButtonAttrs: null }; return setNodeAttrs(tr, tableRect.tableStart - 1, attrsPatch); }; const button = table_insert_button_default({ view: this.view, attrs, tableRect, removeInsertButton: this.removeInsertButton }); replaceChildren(this.insertButtonWrapper, [button]); this.showInsertButton = true; } else { replaceChildren(this.insertButtonWrapper, []); this.showInsertButton = false; } } attrs() { return this.node.attrs; } ignoreMutation() { return true; } destroy() { document.removeEventListener("mousemove", this.handleMouseMove); } }; function replaceChildren(parent, children) { while (parent.firstChild) { parent.firstChild.remove(); } for (const child of children) { parent.append(child); } } // src/table-column-resizing.ts function pointsAtCell($pos) { return $pos.parent.type.spec.tableRole == "row" && $pos.nodeAfter; } function setAttr(attrs, name, value) { let result = {}; for (let prop in attrs) result[prop] = attrs[prop]; result[name] = value; return result; } var key = new PluginKey("tableColumnResizing"); function columnResizing({ handleWidth = 5, cellMinWidth = 25, View = TableView, lastColumnResizable = true, firstResizableColumn = 0 } = {}) { let plugin = new Plugin({ key, state: { init(_, state) { return new ResizeState(-1, false); }, apply(tr, prev) { return prev.apply(tr); } }, props: { attributes(state) { let pluginState = key.getState(state); return pluginState.activeHandle > -1 ? { class: "resize-cursor" } : null; }, handleDOMEvents: { mousemove(view, event) { handleMouseMove( view, event, handleWidth, cellMinWidth, lastColumnResizable, firstResizableColumn ); }, mouseleave(view) { handleMouseLeave(view); }, mousedown(view, event) { handleMouseDown(view, event, cellMinWidth); } }, decorations(state) { let pluginState = key.getState(state); if (pluginState.activeHandle > -1) return handleDecorations(state, pluginState.activeHandle); }, nodeViews: {} } }); return plugin; } var ResizeState = class _ResizeState { constructor(activeHandle, dragging) { this.activeHandle = activeHandle; this.dragging = dragging; } apply(tr) { let state = this, action = tr.getMeta(key); if (action && action.setHandle != null) return new _ResizeState(action.setHandle, null); if (action && action.setDragging !== void 0) return new _ResizeState(state.activeHandle, action.setDragging); if (state.activeHandle > -1 && tr.docChanged) { let handle = tr.mapping.map(state.activeHandle, -1); if (!pointsAtCell(tr.doc.resolve(handle))) handle = null; state = new _ResizeState(handle, state.dragging); } return state; } }; function handleMouseMove(view, event, handleWidth, cellMinWidth, lastColumnResizable, firstResizableColumn) { let pluginState = key.getState(view.state); if (!pluginState.dragging) { let target = domCellAround(event.target), cell = -1; if (target) { let { left, right } = target.getBoundingClientRect(); if (event.clientX - left <= handleWidth) cell = edgeCell(view, event, "left"); else if (right - event.clientX <= handleWidth) cell = edgeCell(view, event, "right"); } if (cell != pluginState.activeHandle) { if (cell !== -1) { let $cell = view.state.doc.resolve(cell); let table = $cell.node(-1), map = TableMap3.get(table), start = $cell.start(-1); let col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1; if (col < firstResizableColumn) { return; } if (!lastColumnResizable && col == map.width - 1) { return; } } updateHandle(view, cell); } } } function handleMouseLeave(view) { let pluginState = key.getState(view.state); if (pluginState.activeHandle > -1 && !pluginState.dragging) updateHandle(view, -1); } function handleMouseDown(view, event, cellMinWidth) { let pluginState = key.getState(view.state); if (pluginState.activeHandle == -1 || pluginState.dragging) return false; let cell = view.state.doc.nodeAt(pluginState.activeHandle); let width = currentColWidth(view, pluginState.activeHandle, cell.attrs); view.dispatch( view.state.tr.setMeta(key, { setDragging: { startX: event.clientX, startWidth: width } }) ); function finish(event2) { window.removeEventListener("mouseup", finish); window.removeEventListener("mousemove", move); let pluginState2 = key.getState(view.state); if (pluginState2.dragging) { updateColumnWidth( view, pluginState2.activeHandle, draggedWidth(pluginState2.dragging, event2, cellMinWidth) ); view.dispatch(view.state.tr.setMeta(key, { setDragging: null })); } } function move(event2) { if (!event2.which) return finish(event2); let pluginState2 = key.getState(view.state); let dragged = draggedWidth(pluginState2.dragging, event2, cellMinWidth); displayColumnWidth(view, pluginState2.activeHandle, dragged, cellMinWidth); } window.addEventListener("mouseup", finish); window.addEventListener("mousemove", move); event.preventDefault(); return true; } function currentColWidth(view, cellPos, { colspan, colwidth }) { let width = colwidth && colwidth[colwidth.length - 1]; if (width) return width; let dom = view.domAtPos(cellPos); let node = dom.node.childNodes[dom.offset]; let domWidth = node.offsetWidth, parts = colspan; if (colwidth) { for (let i = 0; i < colspan; i++) if (colwidth[i]) { domWidth -= colwidth[i]; parts--; } } return domWidth / parts; } function domCellAround(target) { while (target && target.nodeName != "TD" && target.nodeName != "TH") target = target.classList.contains("ProseMirror") ? null : target.parentNode; return target; } function edgeCell(view, event, side) { let found = view.posAtCoords({ left: event.clientX, top: event.clientY }); if (!found) return -1; let { pos } = found; let $cell = cellAround(view.state.doc.resolve(pos)); if (!$cell) return -1; if (side == "right") return $cell.pos; let map = TableMap3.get($cell.node(-1)), start = $cell.start(-1); let index = map.map.indexOf($cell.pos - start); return index % map.width == 0 ? -1 : start + map.map[index - 1]; } function draggedWidth(dragging, event, cellMinWidth) { let offset = event.clientX - dragging.startX; return Math.max(cellMinWidth, dragging.startWidth + offset); } function updateHandle(view, value) { view.dispatch(view.state.tr.setMeta(key, { setHandle: value })); } function updateColumnWidth(view, cell, width) { let $cell = view.state.doc.resolve(cell); let table = $cell.node(-1), map = TableMap3.get(table), start = $cell.start(-1); let col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1; let tr = view.state.tr; for (let row = 0; row < map.height; row++) { let mapIndex = row * map.width + col; if (row && map.map[mapIndex] == map.map[mapIndex - map.width]) continue; let pos = map.map[mapIndex], { attrs } = table.nodeAt(pos); let index = attrs.colspan == 1 ? 0 : col - map.colCount(pos); if (attrs.colwidth && attrs.colwidth[index] == width) continue; let colwidth = attrs.colwidth ? attrs.colwidth.slice() : zeroes(attrs.colspan); colwidth[index] = width; tr.setNodeMarkup(start + pos, null, setAttr(attrs, "colwidth", colwidth)); } if (tr.docChanged) view.dispatch(tr); } function displayColumnWidth(view, cell, width, cellMinWidth) { let $cell = view.state.doc.resolve(cell); let table = $cell.node(-1), start = $cell.start(-1); let col = TableMap3.get(table).colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1; let dom = view.domAtPos($cell.start(-1)).node; while (dom.nodeName != "TABLE") dom = dom.parentNode; updateColumns(table, dom.firstChild, dom, cellMinWidth, col, width); } function zeroes(n) { let result = []; for (let i = 0; i < n; i++) result.push(0); return result; } function handleDecorations(state, cell) { let decorations = []; let $cell = state.doc.resolve(cell); let table = $cell.node(-1), map = TableMap3.get(table), start = $cell.start(-1); let col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan; for (let row = 0; row < map.height; row++) { let index = col + row * map.width - 1; if ((col == map.width || map.map[index] != map.map[index + 1]) && (row == 0 || map.map[index - 1] != map.map[index - 1 - map.width])) { let cellPos = map.map[index]; let pos = start + cellPos + table.nodeAt(cellPos).nodeSize - 1; let dom = document.createElement("div"); dom.className = "column-resize-handle"; decorations.push(Decoration.widget(pos, dom)); } } return DecorationSet.create(state.doc, decorations); } // src/table-plugins.ts import { css } from "@emotion/css"; import { findParentNodeOfType as findParentNodeOfType2 } from "@remirror/core"; import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@remirror/pm/state"; import { Decoration as Decoration2, DecorationSet as DecorationSet2 } from "@remirror/pm/view"; import { ExtensionTablesTheme as ExtensionTablesTheme3, getThemeVar } from "@remirror/theme"; var preselectBorderColor = getThemeVar("color", "table", "preselect", "border"); var preselectControllerBackgroundColor = getThemeVar("color", "table", "preselect", "controller"); var _a, _b, _c, _d, _e; function getTableStyle(attrs) { const preselectClass = css(_a || (_a = __template(["\n /* Make the border-style 'double' instead of 'solid'. This works because 'double' has a higher priority than 'solid' */\n border-style: double;\n border-color: ", ";\n "])), preselectBorderColor); const preselectControllerClass = css(_b || (_b = __template(["\n ", "\n background-color: ", ";\n "])), preselectClass, preselectControllerBackgroundColor); let classNames = ""; if (attrs.preselectColumn !== -1) { classNames = css(_c || (_c = __template(["\n & table.", " tbody tr {\n th,\n td {\n &:nth-child(", ") {\n ", ";\n }\n }\n th.", ":nth-child(", ") {\n ", "\n }\n }\n "])), ExtensionTablesTheme3.TABLE, attrs.preselectColumn + 1, preselectClass, ExtensionTablesTheme3.TABLE_CONTROLLER, attrs.preselectColumn + 1, preselectControllerClass); } else if (attrs.preselectRow !== -1) { classNames = css(_d || (_d = __template(["\n & table.", " tbody tr:nth-child(", ") {\n td,\n th {\n ", ";\n }\n th.", " {\n ", "\n }\n }\n "])), ExtensionTablesTheme3.TABLE, attrs.preselectRow + 1, preselectClass, ExtensionTablesTheme3.TABLE_CONTROLLER, preselectControllerClass); } else if (attrs.preselectTable) { classNames = css(_e || (_e = __template(["\n &.", " table.", " tbody tr {\n td,\n th {\n ", ";\n }\n th.", " {\n ", "\n }\n }\n "])), ExtensionTablesTheme3.TABLE_PRESELECT_ALL, ExtensionTablesTheme3.TABLE, preselectClass, ExtensionTablesTheme3.TABLE_CONTROLLER, preselectControllerClass); } return classNames; } var key2 = new PluginKey2("remirrorTableControllerPluginKey"); function createTableControllerPlugin() { return new Plugin2({ key: key2, state: { init() { return new ControllerState({}); }, apply(tr, prev) { return prev.apply(tr); } }, props: { decorations: (state) => { const controllerState = key2.getState(state); if (!controllerState) { return null; } const { tableNodeResult, predelete, preselectTable } = controllerState.values; if (tableNodeResult) { const styleClassName = getTableStyle(controllerState.values); let className = "".concat(ExtensionTablesTheme3.TABLE_SHOW_CONTROLLERS, " ").concat(styleClassName); if (preselectTable) { className += " ".concat(ExtensionTablesTheme3.TABLE_PRESELECT_ALL); } if (predelete) { className += " ".concat(ExtensionTablesTheme3.TABLE_SHOW_PREDELETE); } const decorations = [ Decoration2.node(tableNodeResult.pos, tableNodeResult.end, { class: className }) ]; return DecorationSet2.create(state.doc, decorations); } return null; } } }); } var ControllerState = class _ControllerState { constructor(action) { this.action = action; __publicField(this, "values"); this.values = __spreadValues({ tableNodeResult: null, preselectTable: false, preselectColumn: -1, preselectRow: -1, predelete: false, insertButtonAttrs: null }, action); } apply(tr) { this.values.tableNodeResult = findParentNodeOfType2({ types: "table", selection: tr.selection }); const props = tr.getMeta(key2); if (props) { return new _ControllerState(__spreadValues(__spreadValues({}, this.values), props)); } return this; } }; function setControllerPluginMeta(tr, props) { return tr.setMeta(key2, props); } function resetControllerPluginMeta(tr) { return setControllerPluginMeta(tr, { preselectRow: -1, preselectColumn: -1, preselectTable: false, predelete: false }); } // src/utils/array.ts function repeat(val, times) { const result = []; for (let i = 0; i < times; i++) { result.push(val); } return result; } // src/utils/controller.ts function injectControllers({ schema, getMap, table: oldTable }) { const controllerCell = schema.nodes.tableControllerCell.create(); const headerControllerCells = repeat(controllerCell, getMap().width + 1); const controllerRow = schema.nodes.tableRow.create({}, headerControllerCells); const newRowsArray = [controllerRow]; const oldRows = oldTable.content; oldRows.forEach((oldRow) => { if (oldRow.content.child(0).type === schema.nodes.tableControllerCell) { newRowsArray.push(oldRow.copy()); return; } const oldCells = oldRow.content; const newCells = Fragment2.from(controllerCell).append(oldCells); const newRow = oldRow.copy(newCells); newRowsArray.push(newRow); }); const newRows = Fragment2.fromArray(newRowsArray); const newTable = oldTable.copy(newRows); newTable.attrs = __spreadProps(__spreadValues({}, newTable.attrs), { isControllersInjected: true }); return newTable; } function createControllerEvents({ view, findTable }) { return { onClick: (event) => { const axis = getCellAxisByMouseEvent(view, event); if (axis) { if (axis.row > 0) { selectRow(view, findTable, axis.row); } else if (axis.col > 0) { selectColumn(view, findTable, axis.col); } else { selectTable(view, findTable); } } }, onMouseEnter: (event) => { const axis = getCellAxisByMouseEvent(view, event); if (axis) { if (axis.row > 0) { setPreselectRow(view, axis.row); } else if (axis.col > 0) { setPreselectColumn(view, axis.col); } else { setPreselectTable(view, true); } } }, onMouseLeave: () => { resetPreselection(view); } }; } function onlyTableFound(func) { return (view, findTable, ...extra) => { const found = findTable(); if (!found) { return; } return func(view, found, ...extra); }; } var selectRow = onlyTableFound( (view, table, index) => { const map = TableMap4.get(table.node); const cellIndex = getCellIndex(map, index, 0); let tr = view.state.tr; const posInTable = map.map[cellIndex + 1]; const pos = table.pos + posInTable + 1; const $pos = tr.doc.resolve(pos); const selection = CellSelection2.rowSelection($pos); tr = tr.setSelection(cellSelectionToSelection(selection)); view.dispatch(tr); } ); var selectColumn = onlyTableFound( (view, table, index) => { const map = TableMap4.get(table.node); const cellIndex = getCellIndex(map, 0, index); let tr = view.state.tr; const posInTable = map.map[cellIndex]; const pos = table.pos + posInTable + 1; const $pos = tr.doc.resolve(pos); const selection = CellSelection2.colSelection($pos); tr = tr.setSelection(cellSelectionToSelection(selection)); view.dispatch(tr); } ); var selectTable = onlyTableFound((view, table) => { const map = TableMap4.get(table.node); if (map.map.length > 0) { let tr = view.state.tr; const firstCellPosInTable = map.map[0]; const lastCellPosInTable = map.map[map.map.length - 1]; const firstCellPos = table.pos + firstCellPosInTable + 1; const lastCellPos = table.pos + lastCellPosInTable + 1; const $firstCellPos = tr.doc.resolve(firstCellPos); const $lastCellPos = tr.doc.resolve(lastCellPos); const selection = new CellSelection2($firstCellPos, $lastCellPos); tr = tr.setSelection(cellSelectionToSelection(selection)); view.dispatch(tr); } }); function setPreselectRow(view, index) { view.dispatch(setControllerPluginMeta(view.state.tr, { preselectRow: index })); } function setPreselectColumn(view, index) { view.dispatch(setControllerPluginMeta(view.state.tr, { preselectColumn: index })); } function setPreselectTable(view, value) { view.dispatch(setControllerPluginMeta(view.state.tr, { preselectTable: value })); } function setPredelete(view, value) { view.dispatch(setControllerPluginMeta(view.state.tr, { predelete: value })); } function resetPreselection(view) { view.dispatch(resetControllerPluginMeta(view.state.tr)); } function getCellIndex(map, rowIndex, colIndex) { return map.width * rowIndex + colIndex; } function getCellAxisByMouseEvent(view, event) { const domCell = domCellAround(event.target); if (!domCell) { return null; } const domCellRect = domCell.getBoundingClientRect(); return getCellAxisByCoords(view, { left: domCellRect.left + 1, top: domCellRect.top + 1 }); } function getCellAxisByCoords(view, coords) { const cellPos = view.posAtCoords(coords); if (!cellPos) { return null; } const $cell = cellAround2(view.state.doc.resolve(cellPos.pos)); if (!$cell) { return null; } const map = TableMap4.get($cell.node(-1)); const start = $cell.start(-1); const rect = map.findCell($cell.pos - start); const { left: col, top: row } = rect; return { col, row }; } function getCellSelectionType(selection) { if (selection.isRowSelection()) { if (selection.isColSelection()) { return 3 /* table */; } return 1 /* row */; } else if (selection.isColSelection()) { return 2 /* col */; } return 4 /* other */; } // src/components/table-delete-row-column-button.tsx function createDeleteButtonPositioner() { return Positioner2.create({ hasChanged: hasStateChanged, getActive(props) { const { state } = props; const { selection } = state; if (isCellSelection(selection)) { const cellSelectionType = getCellSelectionType(selection); if (cellSelectionType === 2 /* col */ || cellSelectionType === 1 /* row */) { const tableResult = findParentNodeOfType3({ types: "table", selection }); if (tableResult) { const positionerData = { tableResult, cellSelectionType, anchorCellPos: selection.$anchorCell.pos, headCellPos: selection.$headCell.pos }; return [positionerData]; } } } return Positioner2.EMPTY; }, getPosition(props) { const { view, data } = props; const anchorCellDOM = view.nodeDOM(data.anchorCellPos); const headCellDOM = view.nodeDOM(data.headCellPos); if (!anchorCellDOM || !headCellDOM || !isElementDomNode(anchorCellDOM) || !isElementDomNode(headCellDOM)) { return defaultAbsolutePosition; } const map = TableMap5.get(data.tableResult.node); if (data.cellSelectionType === 2 /* col */ && map.width <= 2) { return defaultAbsolutePosition; } else if (data.cellSelectionType === 1 /* row */ && map.height <= 2) { return defaultAbsolutePosition; } const anchorCellRect = anchorCellDOM.getBoundingClientRect(); const headCellRect = headCellDOM.getBoundingClientRect(); const rect = mergeDOMRects(anchorCellRect, headCellRect); const editorRect = view.dom.getBoundingClientRect(); const height = rect.height; const width = rect.width; const left = view.dom.scrollLeft + rect.left - editorRect.left; const top = view.dom.scrollTop + rect.top - editorRect.top; const visible = isPositionVisible(rect, view.dom); const margin = 16; return data.cellSelectionType === 1 /* row */ ? { rect, visible, height: 0, width: 0, x: left - margin, y: top + height / 2 } : { rect, visible, height: 0, width: 0, x: left + width / 2, y: top - margin }; } }); } var TableDeleteRowColumnInnerButton = ({ position, onClick, onMouseDown, onMouseOver, onMouseOut }) => { const size = 18; return /* @__PURE__ */ React2.createElement( "button", { ref: position.ref, onClick, onMouseDown, onMouseOver, onMouseOut, style: { "--remirror-table-delete-row-column-button-y": "".concat(position.y, "px"), "--remirror-table-delete-row-column-button-x": "".concat(position.x, "px") }, className: cx2( ExtensionTablesTheme4.TABLE_DELETE_INNER_BUTTON, ExtensionTablesTheme4.TABLE_DELETE_ROW_COLUMN_INNER_BUTTON ) }, /* @__PURE__ */ React2.createElement(Icon, { name: "closeFill", size, color: "#ffffff" }) ); }; var deleteButtonPositioner = createDeleteButtonPositioner(); function usePosition() { const position = usePositioner2(deleteButtonPositioner, []); return position; } function useEvents(view) { const handleClick = useCallback(() => { const selection = view.state.selection; if (isCellSelection(selection)) { const cellSelectionType = getCellSelectionType(selection); if (cellSelectionType === 1 /* row */) { deleteRow(view.state, view.dispatch); } else if (cellSelectionType === 2 /* col */) { deleteColumn(view.state, view.dispatch); } } }, [view]); const handleMouseOver = useCallback(() => setPredelete(view, true), [view]); const handleMouseOut = useCallback(() => setPredelete(view, false), [view]); return { handleClick, handleMouseOver, handleMouseOut }; } var TableDeleteRowColumnButton = ({ Component }) => { const { view } = useRemirrorContext(); const position = usePosition(); const { handleClick, handleMouseOver, handleMouseOut } = useEvents(view); Component = Component != null ? Component : TableDeleteRowColumnInnerButton; const handleMouseDown2 = useCallback((e) => { e.preventDefault(); }, []); return /* @__PURE__ */ React2.createElement(PositionerPortal2, null, /* @__PURE__ */ React2.createElement( Component, { position, onClick: handleClick, onMouseDown: handleMouseDown2, onMouseOver: handleMouseOver, onMouseOut: handleMouseOut } )); }; // src/components/table-delete-table-button.tsx import React3, { useCallback as useCallback2 } from "react"; import { cx as cx3, findParentNodeOfType as findParentNodeOfType4, isElementDomNode as isElementDomNode2, last, mergeDOMRects as mergeDOMRects2 } from "@remirror/core"; import { defaultAbsolutePosition as defaultAbsolutePosition2, hasStateChanged as hasStateChanged2, isPositionVisible as isPositionVisible2, Positioner as Positioner3 } from "@remirror/extension-positioner"; import { TableMap as TableMap6 } from "@remirror/pm/tables"; import { Icon as Icon2, PositionerPortal as PositionerPortal3 } from "@remirror/react-components"; import { useCommands as useCommands2 } from "@remirror/react-core"; import { usePositioner as usePositioner3 } from "@remirror/react-hooks"; import { ExtensionTablesTheme as ExtensionTablesTheme5 } from "@remirror/theme"; var highlightTable = ({ tr, dispatch }) => { const node = findParentNodeOfType4({ types: "table", selection: tr.selection }); if (!node) { return false; } dispatch == null ? void 0 : dispatch(setControllerPluginMeta(tr, { preselectTable: true, predelete: true })); return true; }; var unhighlightTable = ({ tr, dispatch }) => { dispatch == null ? void 0 : dispatch(resetControllerPluginMeta(tr)); return true; }; function createDeleteTableButtonPositioner() { return Positioner3.create({ hasChanged: hasStateChanged2, getActive(props) { const { selection } = props.state; const tableResult = findParentNodeOfType4({ types: "table", selection }); if (tableResult) { const positionerData = { tableResult }; return [positionerData]; } return Positioner3.EMPTY; }, getPosition(props) { const { view, data } = props; const { node, pos } = data.tableResult; const map = TableMap6.get(node); const firstCellDOM = view.nodeDOM(pos + map.map[0] + 1); const lastCellDOM = view.nodeDOM(pos + last(map.map) + 1); if (!firstCellDOM || !lastCellDOM || !isElementDomNode2(firstCellDOM) || !isElementDomNode2(lastCellDOM)) { return defaultAbsolutePosition2; } const rect = mergeDOMRects2( firstCellDOM.getBoundingClientRect(), lastCellDOM.getBoundingClientRect() ); const editorRect = view.dom.getBoundingClientRect(); const left = view.dom.scrollLeft + rect.left - editorRect.left; const top = view.dom.scrollTop + rect.top - editorRect.top; const visible = isPositionVisible2(rect, view.dom); const margin = 16; return { rect, visible, height: 0, width: 0, x: left + rect.width / 2, y: top + rect.height + margin }; } }); } var TableDeleteInnerButton = ({ position, onClick, onMouseDown, onMouseEnter, onMouseLeave }) => { const size = 18; return /* @__PURE__ */ React3.createElement( "button", { ref: position.ref, onClick, onMouseDown, onMouseEnter, onMouseLeave, style: { "--remirror-table-delete-button-y": "".concat(position.y, "px