@remirror/extension-react-tables
Version:
Create tables with nested react components.
1,375 lines (1,354 loc) • 66.3 kB
JavaScript
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