diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json
index 78bf59db7..86921af0c 100644
--- a/packages/editor/core/package.json
+++ b/packages/editor/core/package.json
@@ -32,10 +32,6 @@
"@tiptap/extension-color": "^2.1.11",
"@tiptap/extension-image": "^2.1.7",
"@tiptap/extension-link": "^2.1.7",
- "@tiptap/extension-table": "^2.1.6",
- "@tiptap/extension-table-cell": "^2.1.6",
- "@tiptap/extension-table-header": "^2.1.6",
- "@tiptap/extension-table-row": "^2.1.6",
"@tiptap/extension-task-item": "^2.1.7",
"@tiptap/extension-task-list": "^2.1.7",
"@tiptap/extension-text-style": "^2.1.11",
@@ -56,7 +52,9 @@
"tailwind-merge": "^1.14.0",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.2",
- "use-debounce": "^9.0.4"
+ "use-debounce": "^9.0.4",
+ "@tiptap/prosemirror-tables": "^1.1.4",
+ "jsx-dom-cjs": "^8.0.3"
},
"devDependencies": {
"eslint": "^7.32.0",
diff --git a/packages/editor/core/src/index.ts b/packages/editor/core/src/index.ts
index 590b17172..6268323e5 100644
--- a/packages/editor/core/src/index.ts
+++ b/packages/editor/core/src/index.ts
@@ -2,6 +2,8 @@
// import "./styles/tailwind.css";
// import "./styles/editor.css";
+export * from "./ui/extensions/table-new/Table";
+
// utils
export * from "./lib/utils";
export { startImageUpload } from "./ui/plugins/upload-image";
diff --git a/packages/editor/core/src/styles/editor.css b/packages/editor/core/src/styles/editor.css
index 85d881eeb..9987c5f06 100644
--- a/packages/editor/core/src/styles/editor.css
+++ b/packages/editor/core/src/styles/editor.css
@@ -212,9 +212,9 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
}
}
-.tableWrapper {
- overflow-x: auto;
-}
+/* .tableWrapper { */
+/* overflow-x: auto; */
+/* } */
.resize-cursor {
cursor: ew-resize;
diff --git a/packages/editor/core/src/ui/components/editor-container.tsx b/packages/editor/core/src/ui/components/editor-container.tsx
index 8de6298b5..bb35fef68 100644
--- a/packages/editor/core/src/ui/components/editor-container.tsx
+++ b/packages/editor/core/src/ui/components/editor-container.tsx
@@ -13,7 +13,7 @@ export const EditorContainer = ({ editor, editorClassNames, children }: EditorCo
onClick={() => {
editor?.chain().focus().run();
}}
- className={`cursor-text ${editorClassNames}`}
+ className={`cursor-text editorContainer ${editorClassNames}`}
>
{children}
diff --git a/packages/editor/core/src/ui/components/editor-content.tsx b/packages/editor/core/src/ui/components/editor-content.tsx
index 0675a5834..11468e757 100644
--- a/packages/editor/core/src/ui/components/editor-content.tsx
+++ b/packages/editor/core/src/ui/components/editor-content.tsx
@@ -10,10 +10,10 @@ interface EditorContentProps {
}
export const EditorContentWrapper = ({ editor, editorContentCustomClassNames = '', children }: EditorContentProps) => (
-
+
{/* @ts-ignore */}
-
+ {/*
*/}
{editor?.isActive("image") &&
}
{children}
diff --git a/packages/editor/core/src/ui/extensions/index.tsx b/packages/editor/core/src/ui/extensions/index.tsx
index 62d53e0e1..3c394205d 100644
--- a/packages/editor/core/src/ui/extensions/index.tsx
+++ b/packages/editor/core/src/ui/extensions/index.tsx
@@ -8,15 +8,19 @@ import TaskList from "@tiptap/extension-task-list";
import { Markdown } from "tiptap-markdown";
import Gapcursor from "@tiptap/extension-gapcursor";
-import { CustomTableCell } from "./table/table-cell";
-import { Table } from "./table";
-import { TableHeader } from "./table/table-header";
-import { TableRow } from "@tiptap/extension-table-row";
+// import { CustomTableCell } from "./table/table-cell";
+// import { Table } from "./table";
+// import { TableHeader } from "./table/table-header";
+// import { TableRow } from "@tiptap/extension-table-row";
import ImageExtension from "./image";
import { DeleteImage } from "../../types/delete-image";
import { isValidHttpUrl } from "../../lib/utils";
+import Table from "./table-new/Table";
+import TableHeader from "./table-new/TableHeader";
+import TableCell from "./table-new/TableCell";
+import TableRow from "./table-new/TableRow";
export const CoreEditorExtensions = (
@@ -92,6 +96,10 @@ export const CoreEditorExtensions = (
}),
Table,
TableHeader,
- CustomTableCell,
+ TableCell,
TableRow,
+ // Table,
+ // TableHeader,
+ // CustomTableCell,
+ // TableRow,
];
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/Table.ts b/packages/editor/core/src/ui/extensions/table-new/Table/Table.ts
new file mode 100644
index 000000000..1bded42af
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/Table.ts
@@ -0,0 +1,336 @@
+import { TextSelection } from "@tiptap/pm/state"
+
+import { callOrReturn, getExtensionField, mergeAttributes, Node, ParentConfig } from "@tiptap/core"
+import {
+ addColumnAfter,
+ addColumnBefore,
+ addRowAfter,
+ addRowBefore,
+ CellSelection,
+ columnResizing,
+ deleteColumn,
+ deleteRow,
+ deleteTable,
+ fixTables,
+ goToNextCell,
+ mergeCells,
+ setCellAttr,
+ splitCell,
+ tableEditing,
+ toggleHeader,
+ toggleHeaderCell
+} from "@tiptap/prosemirror-tables"
+
+import { tableControls } from "./tableControls"
+import { TableView } from "./TableView"
+import { createTable } from "./utilities/createTable"
+import { deleteTableWhenAllCellsSelected } from "./utilities/deleteTableWhenAllCellsSelected"
+
+/**
+ * Extension based on:
+ * - Tiptap TableExtension (https://github.com/ueberdosis/tiptap/blob/main/packages/extension-table/src/table.ts)
+ */
+
+export interface TableOptions {
+ HTMLAttributes: Record
+ resizable: boolean
+ handleWidth: number
+ cellMinWidth: number
+ lastColumnResizable: boolean
+ allowTableNodeSelection: boolean
+}
+
+declare module "@tiptap/core" {
+ interface Commands {
+ table: {
+ insertTable: (options?: {
+ rows?: number
+ cols?: number
+ withHeaderRow?: boolean
+ }) => ReturnType
+ addColumnBefore: () => ReturnType
+ addColumnAfter: () => ReturnType
+ deleteColumn: () => ReturnType
+ addRowBefore: () => ReturnType
+ addRowAfter: () => ReturnType
+ deleteRow: () => ReturnType
+ deleteTable: () => ReturnType
+ mergeCells: () => ReturnType
+ splitCell: () => ReturnType
+ toggleHeaderColumn: () => ReturnType
+ toggleHeaderRow: () => ReturnType
+ toggleHeaderCell: () => ReturnType
+ mergeOrSplit: () => ReturnType
+ setCellAttribute: (name: string, value: any) => ReturnType
+ goToNextCell: () => ReturnType
+ goToPreviousCell: () => ReturnType
+ fixTables: () => ReturnType
+ setCellSelection: (position: {
+ anchorCell: number
+ headCell?: number
+ }) => ReturnType
+ }
+ }
+
+ interface NodeConfig {
+ /**
+ * Table Role
+ */
+ tableRole?:
+ | string
+ | ((this: {
+ name: string
+ options: Options
+ storage: Storage
+ parent: ParentConfig>["tableRole"]
+ }) => string)
+ }
+}
+
+export default Node.create({
+ name: "table",
+
+ addOptions() {
+ return {
+ HTMLAttributes: {},
+ resizable: true,
+ handleWidth: 5,
+ cellMinWidth: 100,
+ lastColumnResizable: true,
+ allowTableNodeSelection: true
+ }
+ },
+
+ content: "tableRow+",
+
+ tableRole: "table",
+
+ isolating: true,
+
+ group: "block",
+
+ allowGapCursor: false,
+
+ parseHTML() {
+ return [{ tag: "table" }]
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return [
+ "table",
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
+ ["tbody", 0]
+ ]
+ },
+
+ addCommands() {
+ return {
+ insertTable:
+ ({ rows = 3, cols = 3, withHeaderRow = true} = {}) =>
+ ({ tr, dispatch, editor }) => {
+ const node = createTable(
+ editor.schema,
+ rows,
+ cols,
+ withHeaderRow
+ )
+
+ if (dispatch) {
+ const offset = tr.selection.anchor + 1
+
+ tr.replaceSelectionWith(node)
+ .scrollIntoView()
+ .setSelection(
+ TextSelection.near(tr.doc.resolve(offset))
+ )
+ }
+
+ return true
+ },
+ addColumnBefore:
+ () =>
+ ({ state, dispatch }) => {
+ return addColumnBefore(state, dispatch)
+ },
+ addColumnAfter:
+ () =>
+ ({ state, dispatch }) => {
+ return addColumnAfter(state, dispatch)
+ },
+ deleteColumn:
+ () =>
+ ({ state, dispatch }) => {
+ return deleteColumn(state, dispatch)
+ },
+ addRowBefore:
+ () =>
+ ({ state, dispatch }) => {
+ return addRowBefore(state, dispatch)
+ },
+ addRowAfter:
+ () =>
+ ({ state, dispatch }) => {
+ return addRowAfter(state, dispatch)
+ },
+ deleteRow:
+ () =>
+ ({ state, dispatch }) => {
+ return deleteRow(state, dispatch)
+ },
+ deleteTable:
+ () =>
+ ({ state, dispatch }) => {
+ return deleteTable(state, dispatch)
+ },
+ mergeCells:
+ () =>
+ ({ state, dispatch }) => {
+ return mergeCells(state, dispatch)
+ },
+ splitCell:
+ () =>
+ ({ state, dispatch }) => {
+ return splitCell(state, dispatch)
+ },
+ toggleHeaderColumn:
+ () =>
+ ({ state, dispatch }) => {
+ return toggleHeader("column")(state, dispatch)
+ },
+ toggleHeaderRow:
+ () =>
+ ({ state, dispatch }) => {
+ return toggleHeader("row")(state, dispatch)
+ },
+ toggleHeaderCell:
+ () =>
+ ({ state, dispatch }) => {
+ return toggleHeaderCell(state, dispatch)
+ },
+ mergeOrSplit:
+ () =>
+ ({ state, dispatch }) => {
+ if (mergeCells(state, dispatch)) {
+ return true
+ }
+
+ return splitCell(state, dispatch)
+ },
+ setCellAttribute:
+ (name, value) =>
+ ({ state, dispatch }) => {
+ return setCellAttr(name, value)(state, dispatch)
+ },
+ goToNextCell:
+ () =>
+ ({ state, dispatch }) => {
+ return goToNextCell(1)(state, dispatch)
+ },
+ goToPreviousCell:
+ () =>
+ ({ state, dispatch }) => {
+ return goToNextCell(-1)(state, dispatch)
+ },
+ fixTables:
+ () =>
+ ({ state, dispatch }) => {
+ if (dispatch) {
+ fixTables(state)
+ }
+
+ return true
+ },
+ setCellSelection:
+ (position) =>
+ ({ tr, dispatch }) => {
+ if (dispatch) {
+ const selection = CellSelection.create(
+ tr.doc,
+ position.anchorCell,
+ position.headCell
+ )
+
+ // @ts-ignore
+ tr.setSelection(selection)
+ }
+
+ return true
+ }
+ }
+ },
+
+ addKeyboardShortcuts() {
+ return {
+ Tab: () => {
+ if (this.editor.commands.goToNextCell()) {
+ return true
+ }
+
+ if (!this.editor.can().addRowAfter()) {
+ return false
+ }
+
+ return this.editor.chain().addRowAfter().goToNextCell().run()
+ },
+ "Shift-Tab": () => this.editor.commands.goToPreviousCell(),
+ Backspace: deleteTableWhenAllCellsSelected,
+ "Mod-Backspace": deleteTableWhenAllCellsSelected,
+ Delete: deleteTableWhenAllCellsSelected,
+ "Mod-Delete": deleteTableWhenAllCellsSelected
+ }
+ },
+
+ addNodeView() {
+ return ({ editor, getPos, node, decorations }) => {
+ const { cellMinWidth } = this.options
+
+ return new TableView(
+ node,
+ cellMinWidth,
+ decorations,
+ editor,
+ getPos as () => number
+ )
+ }
+ },
+
+ addProseMirrorPlugins() {
+ const isResizable = this.options.resizable && this.editor.isEditable
+
+ const plugins = [
+ tableEditing({
+ allowTableNodeSelection: this.options.allowTableNodeSelection
+ }),
+ tableControls()
+ ]
+
+ if (isResizable) {
+ plugins.unshift(
+ columnResizing({
+ handleWidth: this.options.handleWidth,
+ cellMinWidth: this.options.cellMinWidth,
+ // View: TableView,
+
+ // @ts-ignore
+ lastColumnResizable: this.options.lastColumnResizable
+ })
+ )
+ }
+
+ return plugins
+ },
+
+ extendNodeSchema(extension) {
+ const context = {
+ name: extension.name,
+ options: extension.options,
+ storage: extension.storage
+ }
+
+ return {
+ tableRole: callOrReturn(
+ getExtensionField(extension, "tableRole", context)
+ )
+ }
+ }
+})
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/TableView.ts b/packages/editor/core/src/ui/extensions/table-new/Table/TableView.ts
new file mode 100644
index 000000000..c11a69e31
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/TableView.ts
@@ -0,0 +1,500 @@
+import { h } from "jsx-dom-cjs"
+import { Node as ProseMirrorNode } from "@tiptap/pm/model"
+import { Decoration, NodeView } from "@tiptap/pm/view"
+import tippy, { Instance, Props, Tippy } from "tippy.js"
+
+import { Editor } from "@tiptap/core"
+import {
+ CellSelection,
+ TableMap,
+ updateColumnsOnResize
+} from "@tiptap/prosemirror-tables"
+
+import icons from "./icons"
+
+export function updateColumns(
+ node: ProseMirrorNode,
+ colgroup: HTMLElement,
+ table: HTMLElement,
+ cellMinWidth: number,
+ overrideCol?: number,
+ overrideValue?: any
+) {
+ let totalWidth = 0
+ let fixedWidth = true
+ let nextDOM = colgroup.firstChild as HTMLElement
+ const row = node.firstChild
+
+ if (!row) return
+
+ for (let i = 0, col = 0; i < row.childCount; i += 1) {
+ const { colspan, colwidth } = row.child(i).attrs
+
+ for (let j = 0; j < colspan; j += 1, col += 1) {
+ const hasWidth =
+ overrideCol === col ? overrideValue : colwidth && colwidth[j]
+ const cssWidth = hasWidth ? `${hasWidth}px` : ""
+
+ totalWidth += hasWidth || cellMinWidth
+
+ if (!hasWidth) {
+ fixedWidth = false
+ }
+
+ if (!nextDOM) {
+ colgroup.appendChild(
+ document.createElement("col")
+ ).style.width = cssWidth
+ } else {
+ if (nextDOM.style.width !== cssWidth) {
+ nextDOM.style.width = cssWidth
+ }
+
+ nextDOM = nextDOM.nextSibling as HTMLElement
+ }
+ }
+ }
+
+ while (nextDOM) {
+ const after = nextDOM.nextSibling
+
+ nextDOM.parentNode?.removeChild(nextDOM)
+ nextDOM = after as HTMLElement
+ }
+
+ if (fixedWidth) {
+ table.style.width = `${totalWidth}px`
+ table.style.minWidth = ""
+ } else {
+ table.style.width = ""
+ table.style.minWidth = `${totalWidth}px`
+ }
+}
+
+const defaultTippyOptions: Partial = {
+ allowHTML: true,
+ arrow: false,
+ trigger: "click",
+ animation: "scale-subtle",
+ theme: "light-border no-padding",
+ interactive: true,
+ hideOnClick: true,
+ placement: "right"
+}
+
+function setCellsBackgroundColor(editor: Editor, backgroundColor) {
+ return editor
+ .chain()
+ .focus()
+ .updateAttributes("tableCell", {
+ background: backgroundColor
+ })
+ .updateAttributes("tableHeader", {
+ background: backgroundColor
+ })
+ .run()
+}
+
+const columnsToolboxItems = [
+ {
+ label: "Add Column Before",
+ icon: icons.insertColumnLeft,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnBefore().run()
+ },
+ {
+ label: "Add Column After",
+ icon: icons.insertColumnRight,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnAfter().run()
+ },
+ {
+ label: "Pick Column Color",
+ icon: icons.colorPicker,
+ action: ({ editor, triggerButton, controlsContainer }) => {
+ createColorPickerToolbox({
+ triggerButton,
+ tippyOptions: {
+ appendTo: controlsContainer
+ },
+ onSelectColor: (color) => setCellsBackgroundColor(editor, color)
+ })
+ }
+ },
+ {
+ label: "Delete Column",
+ icon: icons.deleteColumn,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().deleteColumn().run()
+ }
+]
+
+const rowsToolboxItems = [
+ {
+ label: "Add Row Above",
+ icon: icons.insertRowTop,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowBefore().run()
+ },
+ {
+ label: "Add Row Below",
+ icon: icons.insertRowBottom,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowAfter().run()
+ },
+ {
+ label: "Pick a Color",
+ icon: icons.colorPicker,
+ action: ({ editor, triggerButton, controlsContainer }: { editor: Editor, triggerButton: HTMLElement, controlsContainer: any }) => {
+ createColorPickerToolbox({
+ triggerButton,
+ tippyOptions: {
+ appendTo: controlsContainer
+ },
+ onSelectColor: (color) => setCellsBackgroundColor(editor, color)
+ })
+ }
+ },
+ {
+ label: "Delete Row",
+ icon: icons.deleteRow,
+ action: ({ editor }: { editor: Editor }) => editor.chain().focus().deleteRow().run()
+ }
+]
+
+function createToolbox({
+ triggerButton,
+ items,
+ tippyOptions,
+ onClickItem
+}: { triggerButton: HTMLElement, items: { icon: string, label: string }[], tippyOptions: any, onClickItem: any }): Instance {
+ const toolbox = tippy(triggerButton, {
+ content: h(
+ "div",
+ { className: "tableToolbox" },
+ items.map((item) =>
+ h(
+ "div",
+ {
+ className: "toolboxItem",
+ onClick() {
+ onClickItem(item)
+ }
+ },
+ [
+ h("div", {
+ className: "iconContainer",
+ innerHTML: item.icon
+ }),
+ h("div", { className: "label" }, item.label)
+ ]
+ )
+ )
+ ),
+ ...tippyOptions
+ })
+
+ return Array.isArray(toolbox) ? toolbox[0] : toolbox
+}
+
+function createColorPickerToolbox({
+ triggerButton,
+ tippyOptions,
+ onSelectColor = () => { }
+}: {
+ triggerButton: HTMLElement
+ tippyOptions: Partial
+ onSelectColor?: (color: string) => void
+}) {
+ const items = {
+ "Fond par défault": "#ffffff",
+ "Fond gris clair": "#e7f3f8",
+ "Fond gris foncé": "#c7d2d7",
+ "Fond bleu": "#e7f3f8",
+ "Fond rouge": "#ffc4c7",
+ "Fond jaune": "#fbf3db"
+ }
+
+ const colorPicker = tippy(triggerButton, {
+ ...defaultTippyOptions,
+ content: h(
+ "div",
+ { className: "tableColorPickerToolbox" },
+ Object.entries(items).map(([key, value]) =>
+ h(
+ "div",
+ {
+ className: "toolboxItem",
+ onClick: () => {
+ onSelectColor(value)
+ colorPicker.hide()
+ }
+ },
+ [
+ h("div", {
+ className: "colorContainer",
+ style: {
+ backgroundColor: value
+ }
+ }),
+ h(
+ "div",
+ {
+ className: "label"
+ },
+ key
+ )
+ ]
+ )
+ )
+ ),
+ onHidden: (instance) => {
+ instance.destroy()
+ },
+ showOnCreate: true,
+ ...tippyOptions
+ })
+
+ return colorPicker
+}
+
+export class TableView implements NodeView {
+ node: ProseMirrorNode
+ cellMinWidth: number
+ decorations: Decoration[]
+ editor: Editor
+ getPos: () => number
+ hoveredCell
+ map: TableMap
+ root: HTMLElement
+ table: HTMLElement
+ colgroup: HTMLElement
+ tbody: HTMLElement
+ rowsControl: HTMLElement
+ columnsControl: HTMLElement
+ columnsToolbox: Instance
+ rowsToolbox: Instance
+ controls: HTMLElement
+
+ get dom() {
+ return this.root
+ }
+
+ get contentDOM() {
+ return this.tbody
+ }
+
+ constructor(
+ node: ProseMirrorNode,
+ cellMinWidth: number,
+ decorations: Decoration[],
+ editor: Editor,
+ getPos: () => number
+ ) {
+ this.node = node
+ this.cellMinWidth = cellMinWidth
+ this.decorations = decorations
+ this.editor = editor
+ this.getPos = getPos
+ this.hoveredCell = null
+ this.map = TableMap.get(node)
+
+ /**
+ * DOM
+ */
+
+ // Controllers
+ if (editor.isEditable) {
+ this.rowsControl = h(
+ "div",
+ { className: "rowsControl" },
+ h("button", {
+ onClick: () => this.selectRow()
+ })
+ )
+
+ this.columnsControl = h(
+ "div",
+ { className: "columnsControl" },
+ h("button", {
+ onClick: () => this.selectColumn()
+ })
+ )
+
+ this.controls = h(
+ "div",
+ { className: "tableControls", contentEditable: "false" },
+ this.rowsControl,
+ this.columnsControl
+ )
+
+ this.columnsToolbox = createToolbox({
+ triggerButton: this.columnsControl.querySelector("button"),
+ items: columnsToolboxItems,
+ tippyOptions: {
+ ...defaultTippyOptions,
+ appendTo: this.controls
+ },
+ onClickItem: (item) => {
+ item.action({
+ editor: this.editor,
+ triggerButton: this.columnsControl.firstElementChild,
+ controlsContainer: this.controls
+ })
+ this.columnsToolbox.hide()
+ }
+ })
+
+ this.rowsToolbox = createToolbox({
+ triggerButton: this.rowsControl.firstElementChild,
+ items: rowsToolboxItems,
+ tippyOptions: {
+ ...defaultTippyOptions,
+ appendTo: this.controls
+ },
+ onClickItem: (item) => {
+ item.action({
+ editor: this.editor,
+ triggerButton: this.rowsControl.firstElementChild,
+ controlsContainer: this.controls
+ })
+ this.rowsToolbox.hide()
+ }
+ })
+ }
+
+ // Table
+
+ this.colgroup = h(
+ "colgroup",
+ null,
+ Array.from({ length: this.map.width }, () => 1).map(() => h("col"))
+ )
+ this.tbody = h("tbody")
+ this.table = h("table", null, this.colgroup, this.tbody)
+
+ this.root = h(
+ "div",
+ {
+ className: "tableWrapper controls--disabled"
+ },
+ this.controls,
+ this.table
+ )
+
+ this.render()
+ }
+
+ update(node: ProseMirrorNode, decorations) {
+ if (node.type !== this.node.type) {
+ return false
+ }
+
+ this.node = node
+ this.decorations = decorations
+ this.map = TableMap.get(this.node)
+
+ if (this.editor.isEditable) {
+ this.updateControls()
+ }
+
+ this.render()
+
+ return true
+ }
+
+ render() {
+ if (this.colgroup.children.length !== this.map.width) {
+ const cols = Array.from({ length: this.map.width }, () => 1).map(
+ () => h("col")
+ )
+ this.colgroup.replaceChildren(...cols)
+ }
+
+ updateColumnsOnResize(
+ this.node,
+ this.colgroup,
+ this.table,
+ this.cellMinWidth
+ )
+ }
+
+ ignoreMutation() {
+ return true
+ }
+
+ updateControls() {
+ const { hoveredTable: table, hoveredCell: cell } = Object.values(
+ this.decorations
+ ).reduce((acc, curr) => {
+ if (curr.spec.hoveredCell !== undefined) {
+ acc["hoveredCell"] = curr.spec.hoveredCell
+ }
+
+ if (curr.spec.hoveredTable !== undefined) {
+ acc["hoveredTable"] = curr.spec.hoveredTable
+ }
+ return acc
+ }, {}) as any
+
+ if (table === undefined || cell === undefined) {
+ return this.root.classList.add("controls--disabled")
+ }
+
+ this.root.classList.remove("controls--disabled")
+ this.hoveredCell = cell
+
+ const cellDom = this.editor.view.nodeDOM(cell.pos) as HTMLElement
+
+ const tableRect = this.table.getBoundingClientRect()
+ const cellRect = cellDom.getBoundingClientRect()
+
+ this.columnsControl.style.left = `${cellRect.left -
+ tableRect.left -
+ this.table.parentElement!.scrollLeft
+ }px`
+ this.columnsControl.style.width = `${cellRect.width}px`
+
+ this.rowsControl.style.top = `${cellRect.top - tableRect.top}px`
+ this.rowsControl.style.height = `${cellRect.height}px`
+ }
+
+ selectColumn() {
+ if (!this.hoveredCell) return
+
+ const colIndex = this.map.colCount(
+ this.hoveredCell.pos - (this.getPos() + 1)
+ )
+ const anchorCellPos = this.hoveredCell.pos
+ const headCellPos =
+ this.map.map[colIndex + this.map.width * (this.map.height - 1)] +
+ (this.getPos() + 1)
+
+ const cellSelection = CellSelection.create(
+ this.editor.view.state.doc,
+ anchorCellPos,
+ headCellPos
+ )
+ this.editor.view.dispatch(
+ // @ts-ignore
+ this.editor.state.tr.setSelection(cellSelection)
+ )
+ }
+
+ selectRow() {
+ if (!this.hoveredCell) return
+
+ const anchorCellPos = this.hoveredCell.pos
+ const anchorCellIndex = this.map.map.indexOf(
+ anchorCellPos - (this.getPos() + 1)
+ )
+ const headCellPos =
+ this.map.map[anchorCellIndex + (this.map.width - 1)] +
+ (this.getPos() + 1)
+
+ const cellSelection = CellSelection.create(
+ this.editor.state.doc,
+ anchorCellPos,
+ headCellPos
+ )
+ this.editor.view.dispatch(
+ // @ts-ignore
+ this.editor.view.state.tr.setSelection(cellSelection)
+ )
+ }
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/icons.ts b/packages/editor/core/src/ui/extensions/table-new/Table/icons.ts
new file mode 100644
index 000000000..d24209c8a
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/icons.ts
@@ -0,0 +1,11 @@
+const icons = {
+ insertColumnLeft: ``,
+ insertColumnRight: ``,
+ insertRowTop: ``,
+ insertRowBottom: ``,
+ colorPicker: ``,
+ deleteColumn: ``,
+ deleteRow: ``
+}
+
+export default icons
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/index.ts b/packages/editor/core/src/ui/extensions/table-new/Table/index.ts
new file mode 100644
index 000000000..ccdae7fd2
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/index.ts
@@ -0,0 +1 @@
+export { default as default } from "./Table"
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/tableControls.ts b/packages/editor/core/src/ui/extensions/table-new/Table/tableControls.ts
new file mode 100644
index 000000000..afcb9103e
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/tableControls.ts
@@ -0,0 +1,118 @@
+import { Plugin, PluginKey, TextSelection } from "@tiptap/pm/state"
+import { findParentNode } from "@tiptap/core"
+import { DecorationSet, Decoration } from "@tiptap/pm/view"
+
+const key = new PluginKey("tableControls")
+
+export function tableControls() {
+ return new Plugin({
+ key,
+ state: {
+ init() {
+ return new TableControlsState()
+ },
+ apply(tr, prev) {
+ return prev.apply(tr)
+ }
+ },
+ props: {
+ handleDOMEvents: {
+ mousemove: (view, event) => {
+ const pluginState = key.getState(view.state)
+
+ if (
+ !(event.target as HTMLElement).closest(
+ ".tableWrapper"
+ ) &&
+ pluginState.values.hoveredTable
+ ) {
+ return view.dispatch(
+ view.state.tr.setMeta(key, {
+ setHoveredTable: null,
+ setHoveredCell: null
+ })
+ )
+ }
+
+ const pos = view.posAtCoords({
+ left: event.clientX,
+ top: event.clientY
+ })
+
+ if (!pos) return
+
+ const table = findParentNode(
+ (node) => node.type.name === "table"
+ )(TextSelection.create(view.state.doc, pos.pos))
+ const cell = findParentNode(
+ (node) =>
+ node.type.name === "tableCell" ||
+ node.type.name === "tableHeader"
+ )(TextSelection.create(view.state.doc, pos.pos))
+
+ if (!table || !cell) return
+
+ if (pluginState.values.hoveredCell?.pos !== cell.pos) {
+ return view.dispatch(
+ view.state.tr.setMeta(key, {
+ setHoveredTable: table,
+ setHoveredCell: cell
+ })
+ )
+ }
+ }
+ },
+ decorations: (state) => {
+ const pluginState = key.getState(state)
+ if (!pluginState) {
+ return null
+ }
+
+ const { hoveredTable, hoveredCell } = pluginState.values
+ if (hoveredTable) {
+ const decorations = [
+ Decoration.node(
+ hoveredTable.pos,
+ hoveredTable.pos + hoveredTable.node.nodeSize,
+ {},
+ {
+ hoveredTable,
+ hoveredCell
+ }
+ )
+ ]
+
+ return DecorationSet.create(state.doc, decorations)
+ }
+
+ return null
+ }
+ }
+ })
+}
+
+class TableControlsState {
+ values
+
+ constructor(props = {}) {
+ this.values = {
+ hoveredTable: null,
+ hoveredCell: null,
+ ...props
+ }
+ }
+
+ apply(tr: any) {
+ const actions = tr.getMeta(key)
+
+ if (actions?.setHoveredTable !== undefined) {
+ this.values.hoveredTable = actions.setHoveredTable
+ }
+
+ if (actions?.setHoveredCell !== undefined) {
+ this.values.hoveredCell = actions.setHoveredCell
+ }
+
+ return this
+ }
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createCell.ts b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createCell.ts
new file mode 100644
index 000000000..a3d7f2da8
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createCell.ts
@@ -0,0 +1,12 @@
+import { Fragment, Node as ProsemirrorNode, NodeType } from "prosemirror-model"
+
+export function createCell(
+ cellType: NodeType,
+ cellContent?: Fragment | ProsemirrorNode | Array
+): ProsemirrorNode | null | undefined {
+ if (cellContent) {
+ return cellType.createChecked(null, cellContent)
+ }
+
+ return cellType.createAndFill()
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createTable.ts b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createTable.ts
new file mode 100644
index 000000000..c59aadac1
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/createTable.ts
@@ -0,0 +1,47 @@
+import { Fragment, Node as ProsemirrorNode, Schema } from "@tiptap/pm/model"
+
+import { ReactNodeViewRenderer } from "@tiptap/react"
+
+import { createCell } from "./createCell"
+import { getTableNodeTypes } from "./getTableNodeTypes"
+
+export function createTable(
+ schema: Schema,
+ rowsCount: number,
+ colsCount: number,
+ withHeaderRow: boolean,
+ cellContent?: Fragment | ProsemirrorNode | Array
+): ProsemirrorNode {
+ const types = getTableNodeTypes(schema)
+ const headerCells: ProsemirrorNode[] = []
+ const cells: ProsemirrorNode[] = []
+
+ for (let index = 0; index < colsCount; index += 1) {
+ const cell = createCell(types.cell, cellContent)
+
+ if (cell) {
+ cells.push(cell)
+ }
+
+ if (withHeaderRow) {
+ const headerCell = createCell(types.header_cell, cellContent)
+
+ if (headerCell) {
+ headerCells.push(headerCell)
+ }
+ }
+ }
+
+ const rows: ProsemirrorNode[] = []
+
+ for (let index = 0; index < rowsCount; index += 1) {
+ rows.push(
+ types.row.createChecked(
+ null,
+ withHeaderRow && index === 0 ? headerCells : cells
+ )
+ )
+ }
+
+ return types.table.createChecked(null, rows)
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/utilities/deleteTableWhenAllCellsSelected.ts b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/deleteTableWhenAllCellsSelected.ts
new file mode 100644
index 000000000..b76cdca5d
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/deleteTableWhenAllCellsSelected.ts
@@ -0,0 +1,41 @@
+import { findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/core"
+
+import { isCellSelection } from "./isCellSelection"
+
+export const deleteTableWhenAllCellsSelected: KeyboardShortcutCommand = ({
+ editor
+}) => {
+ const { selection } = editor.state
+
+ if (!isCellSelection(selection)) {
+ return false
+ }
+
+ let cellCount = 0
+ const table = findParentNodeClosestToPos(
+ selection.ranges[0].$from,
+ (node) => {
+ return node.type.name === "table"
+ }
+ )
+
+ table?.node.descendants((node) => {
+ if (node.type.name === "table") {
+ return false
+ }
+
+ if (["tableCell", "tableHeader"].includes(node.type.name)) {
+ cellCount += 1
+ }
+ })
+
+ const allCellsSelected = cellCount === selection.ranges.length
+
+ if (!allCellsSelected) {
+ return false
+ }
+
+ editor.commands.deleteTable()
+
+ return true
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/utilities/getTableNodeTypes.ts b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/getTableNodeTypes.ts
new file mode 100644
index 000000000..293878cb0
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/getTableNodeTypes.ts
@@ -0,0 +1,21 @@
+import { NodeType, Schema } from "prosemirror-model"
+
+export function getTableNodeTypes(schema: Schema): { [key: string]: NodeType } {
+ if (schema.cached.tableNodeTypes) {
+ return schema.cached.tableNodeTypes
+ }
+
+ const roles: { [key: string]: NodeType } = {}
+
+ Object.keys(schema.nodes).forEach((type) => {
+ const nodeType = schema.nodes[type]
+
+ if (nodeType.spec.tableRole) {
+ roles[nodeType.spec.tableRole] = nodeType
+ }
+ })
+
+ schema.cached.tableNodeTypes = roles
+
+ return roles
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/Table/utilities/isCellSelection.ts b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/isCellSelection.ts
new file mode 100644
index 000000000..3c36bf055
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/Table/utilities/isCellSelection.ts
@@ -0,0 +1,5 @@
+import { CellSelection } from "@tiptap/prosemirror-tables"
+
+export function isCellSelection(value: unknown): value is CellSelection {
+ return value instanceof CellSelection
+}
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableCell/TableCell.ts b/packages/editor/core/src/ui/extensions/table-new/TableCell/TableCell.ts
new file mode 100644
index 000000000..dfa25c94d
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableCell/TableCell.ts
@@ -0,0 +1,55 @@
+import { mergeAttributes, Node } from "@tiptap/core"
+
+export interface TableCellOptions {
+ HTMLAttributes: Record
+}
+
+export default Node.create({
+ name: "tableCell",
+
+ addOptions() {
+ return {
+ HTMLAttributes: {}
+ }
+ },
+
+ content: "paragraph+",
+
+ addAttributes() {
+ return {
+ colspan: {
+ default: 1
+ },
+ rowspan: {
+ default: 1
+ },
+ colwidth: {
+ default: null,
+ parseHTML: (element) => {
+ const colwidth = element.getAttribute("colwidth")
+ const value = colwidth ? [parseInt(colwidth, 10)] : null
+
+ return value
+ }
+ }
+ }
+ },
+
+ tableRole: "cell",
+
+ isolating: true,
+
+ parseHTML() {
+ return [{ tag: "td" }]
+ },
+
+ renderHTML({ node, HTMLAttributes }) {
+ return [
+ "td",
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
+ style: `background-color: ${node.attrs.background}`
+ }),
+ 0
+ ]
+ }
+})
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableCell/index.ts b/packages/editor/core/src/ui/extensions/table-new/TableCell/index.ts
new file mode 100644
index 000000000..e4acbea9e
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableCell/index.ts
@@ -0,0 +1 @@
+export { default as default } from "./TableCell"
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableHeader/TableHeader.ts b/packages/editor/core/src/ui/extensions/table-new/TableHeader/TableHeader.ts
new file mode 100644
index 000000000..8e8d0f1d4
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableHeader/TableHeader.ts
@@ -0,0 +1,54 @@
+import { mergeAttributes, Node } from "@tiptap/core"
+
+export interface TableHeaderOptions {
+ HTMLAttributes: Record
+}
+export default Node.create({
+ name: "tableHeader",
+
+ addOptions() {
+ return {
+ HTMLAttributes: {}
+ }
+ },
+
+ content: "paragraph+",
+
+ addAttributes() {
+ return {
+ colspan: {
+ default: 1
+ },
+ rowspan: {
+ default: 1
+ },
+ colwidth: {
+ default: null,
+ parseHTML: (element) => {
+ const colwidth = element.getAttribute("colwidth")
+ const value = colwidth ? [parseInt(colwidth, 10)] : null
+
+ return value
+ }
+ }
+ }
+ },
+
+ tableRole: "header_cell",
+
+ isolating: true,
+
+ parseHTML() {
+ return [{ tag: "th" }]
+ },
+
+ renderHTML({ node, HTMLAttributes }) {
+ return [
+ "th",
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
+ style: `background-color: ${node.attrs.background}`
+ }),
+ 0
+ ]
+ }
+})
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableHeader/index.ts b/packages/editor/core/src/ui/extensions/table-new/TableHeader/index.ts
new file mode 100644
index 000000000..f260509cf
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableHeader/index.ts
@@ -0,0 +1 @@
+export { default as default } from "./TableHeader"
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableRow/TableRow.ts b/packages/editor/core/src/ui/extensions/table-new/TableRow/TableRow.ts
new file mode 100644
index 000000000..e922e7fa1
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableRow/TableRow.ts
@@ -0,0 +1,31 @@
+import { mergeAttributes, Node } from "@tiptap/core"
+
+export interface TableRowOptions {
+ HTMLAttributes: Record
+}
+
+export default Node.create({
+ name: "tableRow",
+
+ addOptions() {
+ return {
+ HTMLAttributes: {}
+ }
+ },
+
+ content: "(tableCell | tableHeader)*",
+
+ tableRole: "row",
+
+ parseHTML() {
+ return [{ tag: "tr" }]
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return [
+ "tr",
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
+ 0
+ ]
+ }
+})
diff --git a/packages/editor/core/src/ui/extensions/table-new/TableRow/index.ts b/packages/editor/core/src/ui/extensions/table-new/TableRow/index.ts
new file mode 100644
index 000000000..f01744bf7
--- /dev/null
+++ b/packages/editor/core/src/ui/extensions/table-new/TableRow/index.ts
@@ -0,0 +1 @@
+export { default as default } from "./TableRow"
diff --git a/packages/editor/core/src/ui/extensions/table/index.ts b/packages/editor/core/src/ui/extensions/table/index.ts
deleted file mode 100644
index 9b727bb51..000000000
--- a/packages/editor/core/src/ui/extensions/table/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Table as BaseTable } from "@tiptap/extension-table";
-
-const Table = BaseTable.configure({
- resizable: true,
- cellMinWidth: 100,
- allowTableNodeSelection: true,
-});
-
-export { Table };
diff --git a/packages/editor/core/src/ui/extensions/table/table-cell.ts b/packages/editor/core/src/ui/extensions/table/table-cell.ts
deleted file mode 100644
index 643cb8c64..000000000
--- a/packages/editor/core/src/ui/extensions/table/table-cell.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { TableCell } from "@tiptap/extension-table-cell";
-
-export const CustomTableCell = TableCell.extend({
- addAttributes() {
- return {
- ...this.parent?.(),
- isHeader: {
- default: false,
- parseHTML: (element) => {
- isHeader: element.tagName === "TD";
- },
- renderHTML: (attributes) => {
- tag: attributes.isHeader ? "th" : "td";
- },
- },
- };
- },
- renderHTML({ HTMLAttributes }) {
- if (HTMLAttributes.isHeader) {
- return [
- "th",
- {
- ...HTMLAttributes,
- class: `relative ${HTMLAttributes.class}`,
- },
- ["span", { class: "absolute top-0 right-0" }],
- 0,
- ];
- }
- return ["td", HTMLAttributes, 0];
- },
-});
diff --git a/packages/editor/core/src/ui/extensions/table/table-header.ts b/packages/editor/core/src/ui/extensions/table/table-header.ts
deleted file mode 100644
index f23aa93ef..000000000
--- a/packages/editor/core/src/ui/extensions/table/table-header.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { TableHeader as BaseTableHeader } from "@tiptap/extension-table-header";
-
-const TableHeader = BaseTableHeader.extend({
- content: "paragraph",
-});
-
-export { TableHeader };
diff --git a/packages/editor/core/src/ui/read-only/extensions.tsx b/packages/editor/core/src/ui/read-only/extensions.tsx
index 2246c64f9..6837a3d77 100644
--- a/packages/editor/core/src/ui/read-only/extensions.tsx
+++ b/packages/editor/core/src/ui/read-only/extensions.tsx
@@ -8,10 +8,10 @@ import TaskList from "@tiptap/extension-task-list";
import { Markdown } from "tiptap-markdown";
import Gapcursor from "@tiptap/extension-gapcursor";
-import { CustomTableCell } from "../extensions/table/table-cell";
-import { Table } from "../extensions/table";
-import { TableHeader } from "../extensions/table/table-header";
-import { TableRow } from "@tiptap/extension-table-row";
+// import { CustomTableCell } from "../extensions/table/table-cell";
+// import { Table } from "../extensions/table";
+// import { TableHeader } from "../extensions/table/table-header";
+// import { TableRow } from "@tiptap/extension-table-row";
import ReadOnlyImageExtension from "../extensions/image/read-only-image";
import { isValidHttpUrl } from "../../lib/utils";
@@ -85,8 +85,8 @@ export const CoreReadOnlyEditorExtensions = [
html: true,
transformCopiedText: true,
}),
- Table,
- TableHeader,
- CustomTableCell,
- TableRow,
+ // Table,
+ // TableHeader,
+ // CustomTableCell,
+ // TableRow,
];
diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx
index 29ba21cd2..d59dce7d3 100644
--- a/web/components/issues/comment/comment-card.tsx
+++ b/web/components/issues/comment/comment-card.tsx
@@ -14,8 +14,8 @@ import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-te
import { timeAgo } from "helpers/date-time.helper";
// types
import type { IIssueComment } from "types";
-import fileService from "services/file.service";
// services
+import fileService from "services/file.service";
type Props = {
comment: IIssueComment;
diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx
index b94e52d6b..53383fbe1 100644
--- a/web/pages/_app.tsx
+++ b/web/pages/_app.tsx
@@ -6,6 +6,7 @@ import NProgress from "nprogress";
// styles
import "styles/globals.css";
import "styles/editor.css";
+import "styles/tables.css";
import "styles/command-pallette.css";
import "styles/nprogress.css";
import "styles/react-datepicker.css";
diff --git a/web/styles/tables.css b/web/styles/tables.css
new file mode 100644
index 000000000..f43390120
--- /dev/null
+++ b/web/styles/tables.css
@@ -0,0 +1,200 @@
+.tableWrapper {
+ overflow-x: auto;
+ padding: 2px;
+ width: fit-content;
+ max-width: 100%;
+}
+
+.tableWrapper table {
+ border-collapse: collapse;
+ table-layout: fixed;
+ margin: 0;
+ border: 1px solid rgb(var(--color-border-200));
+ width: 100%;
+}
+
+.tableWrapper table td,
+.tableWrapper table th {
+ min-width: 1em;
+ border: 1px solid rgb(var(--color-border-200));
+ padding: 10px 15px;
+ vertical-align: top;
+ box-sizing: border-box;
+ position: relative;
+ transition: background-color 0.3s ease;
+
+ >* {
+ margin-bottom: 0;
+ }
+}
+
+.tableWrapper table td>*,
+.tableWrapper table th>* {
+ margin: 0 !important;
+ padding: 0.25rem 0 !important;
+}
+
+.tableWrapper table td.has-focus,
+.tableWrapper table th.has-focus {
+ box-shadow: rgba(var(--color-primary-300), 0.1) 0px 0px 0px 2px inset !important;
+}
+
+.tableWrapper table th {
+ font-weight: bold;
+ text-align: left;
+ background-color: rgb(var(--color-primary-100));
+}
+
+.tableWrapper table td:hover{
+ background-color: rgba(var(--color-primary-300), 0.1);
+}
+
+.tableWrapper table th * {
+ font-weight: 600;
+}
+
+.tableWrapper table .selectedCell:after {
+ z-index: 2;
+ position: absolute;
+ content: "";
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(var(--color-primary-300), 0.1);
+ pointer-events: none;
+}
+
+.tableWrapper table .column-resize-handle {
+ position: absolute;
+ right: -2px;
+ top: 0;
+ bottom: -2px;
+ width: 4px;
+ z-index: 99;
+ background-color: rgb(var(--color-primary-400));
+ pointer-events: none;
+}
+
+.tableWrapper .tableControls {
+ position: absolute;
+}
+
+.tableWrapper .tableControls .columnsControl,
+.tableWrapper .tableControls .rowsControl {
+ transition: opacity ease-in 100ms;
+ position: absolute;
+ z-index: 99;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.tableWrapper .tableControls .columnsControl {
+ height: 20px;
+ transform: translateY(-50%);
+}
+
+.tableWrapper .tableControls .columnsControl>button {
+ color: white;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M4.5 10.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S6 12.825 6 12s-.675-1.5-1.5-1.5zm15 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S21 12.825 21 12s-.675-1.5-1.5-1.5zm-7.5 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E");
+ width: 30px;
+ height: 15px;
+}
+
+.tableWrapper .tableControls .rowsControl {
+ width: 20px;
+ transform: translateX(-50%);
+}
+
+.tableWrapper .tableControls .rowsControl>button {
+ color: white;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M12 3c-.825 0-1.5.675-1.5 1.5S11.175 6 12 6s1.5-.675 1.5-1.5S12.825 3 12 3zm0 15c-.825 0-1.5.675-1.5 1.5S11.175 21 12 21s1.5-.675 1.5-1.5S12.825 18 12 18zm0-7.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E");
+ height: 30px;
+ width: 15px;
+}
+
+.tableWrapper .tableControls button {
+ background-color: rgb(var(--color-primary-100));
+ border: 1px solid rgb(var(--color-border-200));
+ border-radius: 2px;
+ background-size: 1.25rem;
+ background-repeat: no-repeat;
+ background-position: center;
+ transition: transform ease-out 100ms, background-color ease-out 100ms;
+ outline: none;
+ box-shadow: #000 0px 2px 4px;
+ cursor: pointer;
+}
+
+/* .tableWrapper .tableControls button:hover { */
+/* transform: scale(1.2, 1.2); */
+/* background-color: var(--color-n50); */
+/* } */
+
+.tableWrapper .tableControls .tableToolbox,
+.tableWrapper .tableControls .tableColorPickerToolbox {
+ padding: 0.25rem;
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ gap: 0.25rem;
+}
+
+.tableWrapper .tableControls .tableToolbox .toolboxItem,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem {
+ background-color: rgb(var(--color-background-100));
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ border: none;
+ padding: 0.1rem;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.tableWrapper .tableControls .tableToolbox .toolboxItem:hover,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem:hover {
+ background-color: var(--color-n100);
+}
+
+.tableWrapper .tableControls .tableToolbox .toolboxItem .iconContainer,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer,
+.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer {
+ border: 1px solid #e6e8f0;
+ border-radius: 3px;
+ padding: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.75rem;
+ height: 1.75rem;
+}
+
+.tableWrapper .tableControls .tableToolbox .toolboxItem .iconContainer svg,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer svg,
+.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer svg,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer svg {
+ width: 1rem;
+ height: 1rem;
+}
+
+.tableToolbox {
+ background-color: rgb(var(--color-background-100));
+}
+
+.tableWrapper .tableControls .tableToolbox .toolboxItem .label,
+.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .label {
+ font-size: 0.95rem;
+ color: var(--color-black);
+}
+
+.resize-cursor .tableWrapper .tableControls .rowsControl,
+.tableWrapper.controls--disabled .tableControls .rowsControl,
+.resize-cursor .tableWrapper .tableControls .columnsControl,
+.tableWrapper.controls--disabled .tableControls .columnsControl {
+ opacity: 0;
+ pointer-events: none;
+}
diff --git a/yarn.lock b/yarn.lock
index e3095db2c..8d340b3ea 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2476,26 +2476,6 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.1.10.tgz#ec311395d16af15345b63d2dac2d459b9ad5fa9e"
integrity sha512-KW63lZLPFIir5AIeh2I7UK6Tx1O3jetD7JIPUzEqp1I1BfJlHGHVQxV8VXAmJl0hTOzjQBsHW42PmBxSC97NUg==
-"@tiptap/extension-table-cell@^2.1.6":
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.1.10.tgz#e594b55622435c43a95edf6f2adfaca402f5cbed"
- integrity sha512-NQOTKjPOTJrkI7VaR9wFF3UKB9N2THD8zJZJDcECKQxLR740udF6/6jWm1uwkTwdkBekVKHBMQvKKK9W1bOBiw==
-
-"@tiptap/extension-table-header@^2.1.6":
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.1.10.tgz#6250676a26946e5b7186198a06990ea70f578a87"
- integrity sha512-NSC0Y10kXDvPGiJckJY/QU8VA7HHU0tI20Dj7/r1oD9itBWSnWP0zAOXzHVlQt9GpThhFNo2nu3fAaVQNfKoTg==
-
-"@tiptap/extension-table-row@^2.1.6":
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.1.10.tgz#e7a1ca8342b623a400848b437c82d57680e551e3"
- integrity sha512-yMOnAaXE7vK7MwULuVUO8v6AYZu6wxTfHAWQe/FqPeMf9tG0HL6+gyt1audremw0xBFMGPx6v4t8vlqPXW9p2g==
-
-"@tiptap/extension-table@^2.1.6":
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.1.10.tgz#5654426366b547631c647ffc5dacf040e65307e1"
- integrity sha512-fsf0c6qA+R6NzbFx+tm1l5POZsgadHjREsedvq5q1i8rCq1Gt1AK+lR7WQsaXlSeIRsWtg4RT0eUjAYNCmKkug==
-
"@tiptap/extension-task-item@^2.1.7":
version "2.1.10"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.1.10.tgz#8eb0d3e8b1234fa44205dd91619f3f1937ca3254"
@@ -2545,6 +2525,11 @@
prosemirror-transform "^1.7.0"
prosemirror-view "^1.28.2"
+"@tiptap/prosemirror-tables@^1.1.4":
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/@tiptap/prosemirror-tables/-/prosemirror-tables-1.1.4.tgz#e123978f13c9b5f980066ba660ec5df857755916"
+ integrity sha512-O2XnDhZV7xTHSFxMMl8Ei3UVeCxuMlbGYZ+J2QG8CzkK8mxDpBa66kFr5DdyAhvdi1ptpcH9u7/GMwItQpN4sA==
+
"@tiptap/react@^2.1.7":
version "2.1.10"
resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.10.tgz#51cd96462e61f6fffa0ca4eb359d8d7d15ebf422"
@@ -2805,7 +2790,7 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^18.0.17":
+"@types/react@*", "@types/react@18.0.15", "@types/react@18.0.28", "@types/react@18.2.0", "@types/react@^18.0.17", "@types/react@^18.2.5":
version "18.2.0"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21"
integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==
@@ -2814,33 +2799,6 @@
"@types/scheduler" "*"
csstype "^3.0.2"
-"@types/react@18.0.15":
- version "18.0.15"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe"
- integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
-"@types/react@18.0.28":
- version "18.0.28"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065"
- integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
-"@types/react@^18.2.5":
- version "18.2.24"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.24.tgz#3c7d68c02e0205a472f04abe4a0c1df35d995c05"
- integrity sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
"@types/reactcss@*":
version "1.2.6"
resolved "https://registry.yarnpkg.com/@types/reactcss/-/reactcss-1.2.6.tgz#133c1e7e896f2726370d1d5a26bf06a30a038bcc"
@@ -5761,6 +5719,13 @@ jsonpointer@^5.0.0:
object.assign "^4.1.4"
object.values "^1.1.6"
+jsx-dom-cjs@^8.0.3:
+ version "8.0.7"
+ resolved "https://registry.yarnpkg.com/jsx-dom-cjs/-/jsx-dom-cjs-8.0.7.tgz#098c54680ebf5bb6f6d12cdea5cde3799c172212"
+ integrity sha512-dQWnuQ+bTm7o72ZlJU4glzeMX8KLxx5U+ZwmEAzVP1+roL7BSM0MrkWdHjdsuNgmxobZCJ+qgiot9EgbJPOoEg==
+ dependencies:
+ csstype "^3.1.2"
+
keycode@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.1.tgz#09c23b2be0611d26117ea2501c2c391a01f39eff"
@@ -7035,7 +7000,14 @@ prosemirror-menu@^1.2.1:
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
-prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1:
+prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1:
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
+ integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
+ dependencies:
+ orderedmap "^2.0.0"
+
+prosemirror-model@^1.19.0:
version "1.19.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006"
integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==