diff --git a/packages/client/src/tiptap/core/extensions/table-cell.tsx b/packages/client/src/tiptap/core/extensions/table-cell.tsx
index 21c08d0f..51966045 100644
--- a/packages/client/src/tiptap/core/extensions/table-cell.tsx
+++ b/packages/client/src/tiptap/core/extensions/table-cell.tsx
@@ -1,20 +1,7 @@
-import { mergeAttributes } from '@tiptap/core';
import { TableCell as BuiltInTableCell } from '@tiptap/extension-table-cell';
-import ReactDOM from 'react-dom';
-import { Button } from '@douyinfe/semi-ui';
-import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
-import { Tooltip } from 'components/tooltip';
-import { Plugin, PluginKey } from 'prosemirror-state';
+import { Plugin } from 'prosemirror-state';
import { Decoration, DecorationSet } from 'prosemirror-view';
-import {
- getCellsInRow,
- getCellsInColumn,
- isRowSelected,
- isTableSelected,
- selectRow,
- selectTable,
-} from 'tiptap/prose-utils';
-import { FloatMenuView } from 'tiptap/views/float-menu';
+import { getCellsInColumn, selectTable, isRowSelected, isTableSelected, selectRow } from 'tiptap/prose-utils';
export const TableCell = BuiltInTableCell.extend({
addAttributes() {
@@ -40,173 +27,68 @@ export const TableCell = BuiltInTableCell.extend({
};
},
- renderHTML({ HTMLAttributes }) {
- let totalWidth = 0;
- let fixedWidth = true;
+ addProseMirrorPlugins() {
+ return [
+ new Plugin({
+ props: {
+ decorations: (state) => {
+ const { doc, selection } = state;
+ const decorations: Decoration[] = [];
+ const cells = getCellsInColumn(0)(selection);
- if (HTMLAttributes.colwidth) {
- HTMLAttributes.colwidth.forEach((col) => {
- if (!col) {
- fixedWidth = false;
- } else {
- totalWidth += col;
- }
- });
- } else {
- fixedWidth = false;
- }
+ if (cells) {
+ cells.forEach(({ pos }, index) => {
+ if (index === 0) {
+ decorations.push(
+ Decoration.widget(pos + 1, () => {
+ let className = 'grip-table';
+ const selected = isTableSelected(selection);
+ if (selected) {
+ className += ' selected';
+ }
+ const grip = document.createElement('a');
+ grip.className = className;
+ grip.addEventListener('mousedown', (event) => {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ this.editor.view.dispatch(selectTable(this.editor.state.tr));
+ // this.options.onSelectTable(state);
+ });
+ return grip;
+ })
+ );
+ }
+ decorations.push(
+ Decoration.widget(pos + 1, () => {
+ const rowSelected = isRowSelected(index)(selection);
- if (fixedWidth && totalWidth > 0) {
- HTMLAttributes.style = `width: ${totalWidth}px;`;
- } else if (totalWidth && totalWidth > 0) {
- HTMLAttributes.style = `min-width: ${totalWidth}px`;
- } else {
- HTMLAttributes.style = null;
- }
+ let className = 'grip-row';
+ if (rowSelected) {
+ className += ' selected';
+ }
+ if (index === 0) {
+ className += ' first';
+ }
+ if (index === cells.length - 1) {
+ className += ' last';
+ }
+ const grip = document.createElement('a');
+ grip.className = className;
+ grip.addEventListener('mousedown', (event) => {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ this.editor.view.dispatch(selectRow(index)(this.editor.state.tr));
+ });
+ return grip;
+ })
+ );
+ });
+ }
- return ['td', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
+ return DecorationSet.create(doc, decorations);
+ },
+ },
+ }),
+ ];
},
-
- // addProseMirrorPlugins() {
- // const extensionThis = this;
- // let selectedRowIndex = -1;
-
- // return [
- // new Plugin({
- // key: new PluginKey(`${this.name}FloatMenu`),
- // view: () =>
- // new FloatMenuView({
- // editor: this.editor,
- // tippyOptions: {
- // zIndex: 100,
- // offset: [-28, 0],
- // },
- // shouldShow: ({ editor }, floatMenuView) => {
- // if (!editor.isEditable) {
- // return false;
- // }
- // if (isTableSelected(editor.state.selection)) {
- // return false;
- // }
- // const cells = getCellsInColumn(0)(editor.state.selection);
- // if (selectedRowIndex > -1) {
- // // 获取当前行的第一个单元格的位置
- // const rowCells = getCellsInRow(selectedRowIndex)(editor.state.selection);
- // if (rowCells && rowCells[0]) {
- // const node = editor.view.nodeDOM(rowCells[0].pos) as HTMLElement;
- // if (node) {
- // const el = node.querySelector('a.grip-row') as HTMLElement;
- // if (el) {
- // floatMenuView.parentNode = el;
- // }
- // }
- // }
- // }
- // return !!cells?.some((cell, index) => isRowSelected(index)(editor.state.selection));
- // },
- // init: (dom, editor) => {
- // dom.classList.add('bubble-memu-table-cell');
- // dom.classList.add('row');
- // ReactDOM.render(
- // <>
- //
- // }
- // onClick={() => {
- // editor.chain().addRowBefore().run();
- // }}
- // />
- //
- //
- // }
- // onClick={() => {
- // editor.chain().deleteRow().run();
- // }}
- // />
- //
- //
- // }
- // onClick={() => {
- // editor.chain().addRowAfter().run();
- // }}
- // />
- //
- // >,
- // dom
- // );
- // },
- // }),
- // props: {
- // decorations: (state) => {
- // if (!extensionThis.editor.isEditable) {
- // return;
- // }
-
- // const { doc, selection } = state;
- // const decorations: Decoration[] = [];
- // const cells = getCellsInColumn(0)(selection);
-
- // if (cells) {
- // cells.forEach(({ pos }, index) => {
- // if (index === 0) {
- // decorations.push(
- // Decoration.widget(pos + 1, () => {
- // const grip = document.createElement('a');
- // grip.classList.add('grip-table');
- // if (isTableSelected(selection)) {
- // grip.classList.add('selected');
- // }
- // grip.addEventListener('mousedown', (event) => {
- // event.preventDefault();
- // event.stopImmediatePropagation();
- // selectedRowIndex = -1;
- // this.editor.view.dispatch(selectTable(this.editor.state.tr));
- // });
- // return grip;
- // })
- // );
- // }
- // decorations.push(
- // Decoration.widget(pos + 1, () => {
- // const rowSelected = isRowSelected(index)(selection);
- // const grip = document.createElement('a');
- // grip.classList.add('grip-row');
- // if (rowSelected) {
- // grip.classList.add('selected');
- // }
- // if (index === 0) {
- // grip.classList.add('first');
- // }
- // if (index === cells.length - 1) {
- // grip.classList.add('last');
- // }
- // grip.addEventListener('mousedown', (event) => {
- // event.preventDefault();
- // event.stopImmediatePropagation();
- // selectedRowIndex = index;
- // this.editor.view.dispatch(selectRow(index)(this.editor.state.tr));
- // });
- // return grip;
- // })
- // );
- // });
- // }
-
- // return DecorationSet.create(doc, decorations);
- // },
- // },
- // }),
- // ];
- // },
});
diff --git a/packages/client/src/tiptap/core/extensions/table-header.tsx b/packages/client/src/tiptap/core/extensions/table-header.tsx
index 2f7e87da..4d6f52a5 100644
--- a/packages/client/src/tiptap/core/extensions/table-header.tsx
+++ b/packages/client/src/tiptap/core/extensions/table-header.tsx
@@ -1,13 +1,7 @@
-import { mergeAttributes } from '@tiptap/core';
import { TableHeader as BuiltInTableHeader } from '@tiptap/extension-table-header';
-import ReactDOM from 'react-dom';
-import { Button, Space } from '@douyinfe/semi-ui';
-import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
-import { Tooltip } from 'components/tooltip';
-import { Plugin, PluginKey } from 'prosemirror-state';
+import { Plugin } from 'prosemirror-state';
import { Decoration, DecorationSet } from 'prosemirror-view';
-import { getCellsInRow, isColumnSelected, isTableSelected, selectColumn } from 'tiptap/prose-utils';
-import { FloatMenuView } from '../views/float-menu';
+import { getCellsInRow, isColumnSelected, selectColumn } from 'tiptap/prose-utils';
export const TableHeader = BuiltInTableHeader.extend({
addAttributes() {
@@ -33,144 +27,46 @@ export const TableHeader = BuiltInTableHeader.extend({
};
},
- renderHTML({ HTMLAttributes }) {
- let totalWidth = 0;
- let fixedWidth = true;
+ addProseMirrorPlugins() {
+ return [
+ new Plugin({
+ props: {
+ decorations: (state) => {
+ const { doc, selection } = state;
+ const decorations: Decoration[] = [];
+ const cells = getCellsInRow(0)(selection);
- if (HTMLAttributes.colwidth) {
- HTMLAttributes.colwidth.forEach((col) => {
- if (!col) {
- fixedWidth = false;
- } else {
- totalWidth += col;
- }
- });
- } else {
- fixedWidth = false;
- }
+ if (cells) {
+ cells.forEach(({ pos }, index) => {
+ decorations.push(
+ Decoration.widget(pos + 1, () => {
+ const colSelected = isColumnSelected(index)(selection);
+ let className = 'grip-column';
+ if (colSelected) {
+ className += ' selected';
+ }
+ if (index === 0) {
+ className += ' first';
+ } else if (index === cells.length - 1) {
+ className += ' last';
+ }
+ const grip = document.createElement('a');
+ grip.className = className;
+ grip.addEventListener('mousedown', (event) => {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ this.editor.view.dispatch(selectColumn(index)(this.editor.state.tr));
+ });
+ return grip;
+ })
+ );
+ });
+ }
- if (fixedWidth && totalWidth > 0) {
- HTMLAttributes.style = `width: ${totalWidth}px;`;
- } else if (totalWidth && totalWidth > 0) {
- HTMLAttributes.style = `min-width: ${totalWidth}px`;
- } else {
- HTMLAttributes.style = null;
- }
-
- return ['th', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
+ return DecorationSet.create(doc, decorations);
+ },
+ },
+ }),
+ ];
},
-
- // addProseMirrorPlugins() {
- // const extensionThis = this;
-
- // return [
- // new Plugin({
- // key: new PluginKey(`${this.name}FloatMenu`),
- // view: () =>
- // new FloatMenuView({
- // editor: this.editor,
- // tippyOptions: {
- // zIndex: 100,
- // },
- // shouldShow: ({ editor }, floatMenuView) => {
- // if (!editor.isEditable) {
- // return false;
- // }
- // const selection = editor.state.selection;
- // if (isTableSelected(selection)) {
- // return false;
- // }
- // const cells = getCellsInRow(0)(selection);
-
- // if (cells && cells[0]) {
- // const node = editor.view.nodeDOM(cells[0].pos) as HTMLElement;
- // floatMenuView.setConatiner(node.parentElement.parentElement.parentElement.parentElement);
- // }
-
- // return !!cells?.some((cell, index) => isColumnSelected(index)(selection));
- // },
- // init: (dom, editor) => {
- // dom.classList.add('bubble-memu-table-cell');
- // ReactDOM.render(
- //
- //
- // }
- // onClick={() => {
- // editor.chain().addColumnBefore().run();
- // }}
- // />
- //
- //
- // }
- // onClick={() => {
- // editor.chain().deleteColumn().run();
- // }}
- // />
- //
- //
- // }
- // onClick={() => {
- // editor.chain().addColumnAfter().run();
- // }}
- // />
- //
- // ,
- // dom
- // );
- // },
- // }),
- // props: {
- // decorations: (state) => {
- // if (!extensionThis.editor.isEditable) {
- // return;
- // }
-
- // const { doc, selection } = state;
- // const decorations: Decoration[] = [];
- // const cells = getCellsInRow(0)(selection);
-
- // if (cells) {
- // cells.forEach(({ pos }, index) => {
- // decorations.push(
- // Decoration.widget(pos + 1, () => {
- // const colSelected = isColumnSelected(index)(selection);
- // const grip = document.createElement('a');
- // grip.classList.add('grip-column');
- // if (colSelected) {
- // grip.classList.add('selected');
- // }
- // if (index === 0) {
- // grip.classList.add('first');
- // } else if (index === cells.length - 1) {
- // grip.classList.add('last');
- // }
- // grip.addEventListener('mousedown', (event) => {
- // event.preventDefault();
- // event.stopImmediatePropagation();
- // this.editor.view.dispatch(selectColumn(index)(this.editor.state.tr));
- // });
- // return grip;
- // })
- // );
- // });
- // }
-
- // return DecorationSet.create(doc, decorations);
- // },
- // },
- // }),
- // ];
- // },
});
diff --git a/packages/client/src/tiptap/core/extensions/table.ts b/packages/client/src/tiptap/core/extensions/table.ts
index 72110624..2952326d 100644
--- a/packages/client/src/tiptap/core/extensions/table.ts
+++ b/packages/client/src/tiptap/core/extensions/table.ts
@@ -1,3 +1,53 @@
import BuiltInTable from '@tiptap/extension-table';
+import { Decoration, DecorationSet } from 'prosemirror-view';
+import { tableEditing } from 'prosemirror-tables';
+import { Plugin } from 'prosemirror-state';
-export const Table = BuiltInTable.configure({ resizable: true });
+export const Table = BuiltInTable.extend({
+ renderHTML() {
+ return [
+ 'div',
+ { class: 'scrollable-wrapper' },
+ ['div', { class: 'scrollable' }, ['table', { class: 'rme-table' }, ['tbody', 0]]],
+ ];
+ },
+
+ addProseMirrorPlugins() {
+ return [
+ tableEditing(),
+ new Plugin({
+ props: {
+ decorations: (state) => {
+ const { doc } = state;
+ const decorations: Decoration[] = [];
+ let index = 0;
+
+ doc.descendants((node, pos) => {
+ if (node.type.name !== this.name) return;
+
+ const elements = document.getElementsByClassName('rme-table');
+ const table = elements[index];
+ if (!table) return;
+
+ const element = table.parentElement;
+ const shadowRight = !!(element && element.scrollWidth > element.clientWidth);
+
+ if (shadowRight) {
+ decorations.push(
+ Decoration.widget(pos + 1, () => {
+ const shadow = document.createElement('div');
+ shadow.className = 'scrollable-shadow right';
+ return shadow;
+ })
+ );
+ }
+ index++;
+ });
+
+ return DecorationSet.create(doc, decorations);
+ },
+ },
+ }),
+ ];
+ },
+}).configure({ resizable: true });
diff --git a/packages/client/src/tiptap/core/styles/table.scss b/packages/client/src/tiptap/core/styles/table.scss
index 0b5fca3b..ccaa5d8a 100644
--- a/packages/client/src/tiptap/core/styles/table.scss
+++ b/packages/client/src/tiptap/core/styles/table.scss
@@ -1,30 +1,62 @@
.ProseMirror {
- .tableWrapper {
- max-width: 100%;
- padding: 0;
- margin: 0;
- overflow: auto;
+ .scrollable-wrapper {
+ position: relative;
+ margin: 0.5em 0;
+ scrollbar-width: thin;
+ scrollbar-color: transparent transparent;
+ }
+
+ .scrollable {
+ padding-left: 1em;
+ margin-left: -1em;
+ overflow: auto hidden;
+ border-left: 1px solid transparent;
+ border-right: 1px solid transparent;
+ transition: border 250ms ease-in-out 0s;
+ }
+
+ .scrollable-shadow {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: -1em;
+ width: 16px;
+ transition: box-shadow 250ms ease-in-out 0s;
+ border-width: 0 0 0 1em;
+ border-style: solid;
+ border-color: transparent;
+ border-image: initial;
+ pointer-events: none;
+
+ &.left {
+ box-shadow: 16px 0 16px -16px inset rgb(0 0 0 / 25%);
+ border-left: 1em solid red;
+ }
+
+ &.right {
+ right: 0;
+ left: auto;
+ box-shadow: rgb(0 0 0 / 25%) -16px 0 16px -16px inset;
+ }
}
table {
width: 100%;
- max-width: 100%;
- margin: 0.75em 0 0;
- overflow: hidden;
+ margin-top: 1em;
+ border-radius: 4px;
border-collapse: collapse;
- table-layout: fixed;
+ box-sizing: border-box;
+ border-color: var(--semi-color-fill-2);
td,
th {
position: relative;
- box-sizing: border-box;
- min-width: 1em;
- padding: 3px 5px;
- border-width: 1px;
- border-style: solid;
- border-color: var(--semi-color-fill-2);
- overflow: visible;
+ min-width: 100px;
+ padding: 4px 8px;
+ text-align: left;
vertical-align: top;
+ border: 1px solid rgb(232 235 237);
+ border-color: var(--semi-color-fill-2);
> * {
margin-bottom: 0;
@@ -42,6 +74,60 @@
background: var(--semi-color-info-light-hover);
}
+ .grip-column {
+ position: absolute;
+ top: -1em;
+ left: 0;
+ z-index: 10;
+ display: block;
+ width: 100%;
+ height: 0.7em;
+ margin-bottom: 3px;
+ cursor: pointer;
+ background: #ced4da;
+
+ &:hover,
+ &.selected {
+ background: var(--semi-color-info);
+ }
+ }
+
+ .grip-row {
+ position: absolute;
+ top: 0;
+ left: -1em;
+ z-index: 10;
+ display: block;
+ width: 0.7em;
+ height: 100%;
+ margin-right: 3px;
+ cursor: pointer;
+ background: #ced4da;
+
+ &:hover,
+ &.selected {
+ background: var(--semi-color-info);
+ }
+ }
+
+ .grip-table {
+ position: absolute;
+ top: -1em;
+ left: -1em;
+ z-index: 10;
+ display: block;
+ width: 0.8em;
+ height: 0.8em;
+ cursor: pointer;
+ background: #ced4da;
+ border-radius: 50%;
+
+ &:hover,
+ &.selected {
+ background: var(--semi-color-info);
+ }
+ }
+
.column-resize-handle {
position: absolute;
top: 0;
@@ -51,10 +137,6 @@
pointer-events: none;
background-color: #adf;
}
-
- p {
- margin: 0;
- }
}
}
diff --git a/packages/client/src/tiptap/editor/menus/table/bubble.tsx b/packages/client/src/tiptap/editor/menus/table/bubble.tsx
index cd76c3dd..ca07752d 100644
--- a/packages/client/src/tiptap/editor/menus/table/bubble.tsx
+++ b/packages/client/src/tiptap/editor/menus/table/bubble.tsx
@@ -43,11 +43,21 @@ export const TableBubbleMenu = ({ editor }) => {
className={'bubble-menu bubble-menu-table'}
editor={editor}
pluginKey="table-bubble-menu"
- shouldShow={() => editor.isActive(Table.name)}
tippyOptions={{
maxWidth: 'calc(100vw - 100px)',
+ placement: 'bottom',
+ offset: [0, 20],
+ }}
+ shouldShow={() => {
+ return editor.isActive(Table.name);
+ }}
+ getRenderContainer={(node) => {
+ let container = node;
+ while (container.tagName !== 'TABLE') {
+ container = container.parentElement;
+ }
+ return container;
}}
- matchRenderContainer={(node: HTMLElement) => node && node.tagName === 'TABLE'}
>
diff --git a/packages/client/src/tiptap/editor/menus/table/col-bubble.tsx b/packages/client/src/tiptap/editor/menus/table/col-bubble.tsx
new file mode 100644
index 00000000..c11fbe9d
--- /dev/null
+++ b/packages/client/src/tiptap/editor/menus/table/col-bubble.tsx
@@ -0,0 +1,57 @@
+import React, { useCallback } from 'react';
+import { Space, Button } from '@douyinfe/semi-ui';
+import { IconAddColumnBefore, IconAddColumnAfter, IconDeleteColumn } from 'components/icons';
+import { Tooltip } from 'components/tooltip';
+import { BubbleMenu } from 'tiptap/editor/views/bubble-menu';
+import { TableRow } from 'tiptap/core/extensions/table-row';
+import { isTableSelected } from 'tiptap/prose-utils';
+
+export const TableColBubbleMenu = ({ editor }) => {
+ const addColumnBefore = useCallback(() => editor.chain().focus().addColumnBefore().run(), [editor]);
+ const addColumnAfter = useCallback(() => editor.chain().focus().addColumnAfter().run(), [editor]);
+ const deleteColumn = useCallback(() => editor.chain().focus().deleteColumn().run(), [editor]);
+
+ return (
+ {
+ if (!node || isTableSelected(state.selection)) return false;
+ const gripColumn = node.querySelector('a.grip-column.selected');
+ return editor.isActive(TableRow.name) && !!gripColumn;
+ }}
+ getRenderContainer={(node) => {
+ return node;
+ }}
+ >
+
+
+ }
+ type="tertiary"
+ theme="borderless"
+ size="small"
+ />
+
+
+
+ }
+ type="tertiary"
+ theme="borderless"
+ size="small"
+ />
+
+
+ } type="tertiary" theme="borderless" size="small" />
+
+
+
+ );
+};
diff --git a/packages/client/src/tiptap/editor/menus/table/index.tsx b/packages/client/src/tiptap/editor/menus/table/index.tsx
index a2fb6dce..e4fc42a3 100644
--- a/packages/client/src/tiptap/editor/menus/table/index.tsx
+++ b/packages/client/src/tiptap/editor/menus/table/index.tsx
@@ -1,7 +1,15 @@
import React from 'react';
import { Editor } from 'tiptap/editor';
import { TableBubbleMenu } from './bubble';
+import { TableRowBubbleMenu } from './row-bubble';
+import { TableColBubbleMenu } from './col-bubble';
export const Table: React.FC<{ editor: Editor }> = ({ editor }) => {
- return ;
+ return (
+ <>
+
+
+
+ >
+ );
};
diff --git a/packages/client/src/tiptap/editor/menus/table/row-bubble.tsx b/packages/client/src/tiptap/editor/menus/table/row-bubble.tsx
new file mode 100644
index 00000000..6a310014
--- /dev/null
+++ b/packages/client/src/tiptap/editor/menus/table/row-bubble.tsx
@@ -0,0 +1,47 @@
+import React, { useCallback } from 'react';
+import { Space, Button } from '@douyinfe/semi-ui';
+import { IconAddRowBefore, IconAddRowAfter, IconDeleteRow } from 'components/icons';
+import { Tooltip } from 'components/tooltip';
+import { BubbleMenu } from 'tiptap/editor/views/bubble-menu';
+import { TableRow } from 'tiptap/core/extensions/table-row';
+import { isTableSelected } from 'tiptap/prose-utils';
+
+export const TableRowBubbleMenu = ({ editor }) => {
+ const addRowBefore = useCallback(() => editor.chain().focus().addRowBefore().run(), [editor]);
+ const addRowAfter = useCallback(() => editor.chain().focus().addRowAfter().run(), [editor]);
+ const deleteRow = useCallback(() => editor.chain().focus().deleteRow().run(), [editor]);
+
+ return (
+ {
+ if (!node || isTableSelected(state.selection)) return false;
+ const gripRow = node.querySelector('a.grip-row.selected');
+ return editor.isActive(TableRow.name) && !!gripRow;
+ }}
+ getRenderContainer={(node) => {
+ return node;
+ }}
+ >
+
+
+ } type="tertiary" theme="borderless" size="small" />
+
+
+
+ } type="tertiary" theme="borderless" size="small" />
+
+
+
+ } type="tertiary" theme="borderless" size="small" />
+
+
+
+ );
+};
diff --git a/packages/client/src/tiptap/editor/views/bubble-menu/bubble-menu-plugin.tsx b/packages/client/src/tiptap/editor/views/bubble-menu/bubble-menu-plugin.tsx
index 07fdacbe..5a11f589 100644
--- a/packages/client/src/tiptap/editor/views/bubble-menu/bubble-menu-plugin.tsx
+++ b/packages/client/src/tiptap/editor/views/bubble-menu/bubble-menu-plugin.tsx
@@ -11,6 +11,7 @@ export interface BubbleMenuPluginProps {
shouldShow?:
| ((props: {
editor: Editor;
+ node?: HTMLElement;
view?: EditorView;
state?: EditorState;
oldState?: EditorState;
@@ -20,6 +21,7 @@ export interface BubbleMenuPluginProps {
| null;
renderContainerSelector?: string;
matchRenderContainer?: (node: HTMLElement) => boolean;
+ getRenderContainer?: (node: HTMLElement) => HTMLElement;
}
export type BubbleMenuViewProps = BubbleMenuPluginProps & {
@@ -39,10 +41,9 @@ export class BubbleMenuView {
public tippyOptions?: Partial;
- public renderContainerSelector?: string;
-
- public matchRenderContainer?: BubbleMenuPluginProps['matchRenderContainer'];
-
+ // public renderContainerSelector?: string;
+ // public matchRenderContainer?: BubbleMenuPluginProps['matchRenderContainer'];
+ public getRenderContainer?: BubbleMenuPluginProps['getRenderContainer'];
public shouldShow: Exclude = ({ view, state, from, to }) => {
const { doc, selection } = state;
const { empty } = selection;
@@ -65,14 +66,17 @@ export class BubbleMenuView {
view,
tippyOptions = {},
shouldShow,
- renderContainerSelector,
- matchRenderContainer,
+ // renderContainerSelector,
+ // matchRenderContainer,
+ getRenderContainer,
}: BubbleMenuViewProps) {
this.editor = editor;
this.element = element;
this.view = view;
- this.renderContainerSelector = renderContainerSelector;
- this.matchRenderContainer = matchRenderContainer;
+ // this.renderContainerSelector = renderContainerSelector;
+ // this.matchRenderContainer = matchRenderContainer;
+
+ this.getRenderContainer = getRenderContainer;
if (shouldShow) {
this.shouldShow = shouldShow;
@@ -82,8 +86,8 @@ export class BubbleMenuView {
capture: true,
});
this.view.dom.addEventListener('dragstart', this.dragstartHandler);
- this.editor.on('focus', this.focusHandler);
- this.editor.on('blur', this.blurHandler);
+ // this.editor.on('focus', this.focusHandler);
+ // this.editor.on('blur', this.blurHandler);
this.tippyOptions = tippyOptions || {};
// Detaches menu content from its current parent
this.element.remove();
@@ -167,12 +171,14 @@ export class BubbleMenuView {
const { ranges } = selection;
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
+ const node = view.domAtPos(from).node as HTMLElement;
const shouldShow =
this.editor.isEditable &&
this.shouldShow?.({
editor: this.editor,
view,
+ node,
state,
oldState,
from,
@@ -187,35 +193,44 @@ export class BubbleMenuView {
this.tippy?.setProps({
getReferenceClientRect: () => {
if (isNodeSelection(state.selection)) {
- let node = view.nodeDOM(from) as HTMLElement;
+ const node = view.nodeDOM(from) as HTMLElement;
- if (this.matchRenderContainer) {
- while (node && !this.matchRenderContainer(node)) {
- node = node.firstElementChild as HTMLElement;
- }
-
- if (node) {
- return node.getBoundingClientRect();
- }
+ if (this.getRenderContainer) {
+ return this.getRenderContainer(node).getBoundingClientRect();
}
- if (node) {
- return node.getBoundingClientRect();
- }
+ // if (this.matchRenderContainer) {
+ // while (node && !this.matchRenderContainer(node)) {
+ // node = node.firstElementChild as HTMLElement;
+ // }
+
+ // if (node) {
+ // return node.getBoundingClientRect();
+ // }
+ // }
+
+ // if (node) {
+ // return node.getBoundingClientRect();
+ // }
}
- if (this.matchRenderContainer) {
- let node = view.domAtPos(from).node as HTMLElement;
-
- while (node && !this.matchRenderContainer(node)) {
- node = node.parentElement;
- }
-
- if (node) {
- return node.getBoundingClientRect();
- }
+ if (this.getRenderContainer) {
+ const node = view.domAtPos(from).node as HTMLElement;
+ return this.getRenderContainer(node).getBoundingClientRect();
}
+ // if (this.matchRenderContainer) {
+ // let node = view.domAtPos(from).node as HTMLElement;
+
+ // while (node && !this.matchRenderContainer(node)) {
+ // node = node.parentElement;
+ // }
+
+ // if (node) {
+ // return node.getBoundingClientRect();
+ // }
+ // }
+
return posToDOMRect(view, from, to);
},
});
diff --git a/packages/client/src/tiptap/editor/views/bubble-menu/index.tsx b/packages/client/src/tiptap/editor/views/bubble-menu/index.tsx
index 123ebd1d..a43e9d80 100644
--- a/packages/client/src/tiptap/editor/views/bubble-menu/index.tsx
+++ b/packages/client/src/tiptap/editor/views/bubble-menu/index.tsx
@@ -26,8 +26,9 @@ export const BubbleMenu: React.FC = (props) => {
editor,
tippyOptions = {},
shouldShow = null,
- renderContainerSelector,
- matchRenderContainer,
+ // renderContainerSelector,
+ // matchRenderContainer,
+ getRenderContainer,
} = props;
const plugin = BubbleMenuPlugin({
@@ -36,8 +37,9 @@ export const BubbleMenu: React.FC = (props) => {
element,
tippyOptions,
shouldShow,
- renderContainerSelector,
- matchRenderContainer,
+ // renderContainerSelector,
+ // matchRenderContainer,
+ getRenderContainer,
});
editor.registerPlugin(plugin);
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
index abae6d23..6bb2f094 100644
--- a/packages/client/tsconfig.json
+++ b/packages/client/tsconfig.json
@@ -27,7 +27,6 @@
"constants/*": ["constants/*"],
"helpers/*": ["helpers/*"],
"tiptap/*": ["tiptap/*"],
- "tiptap-v2/*": ["tiptap-v2/*"],
"event/*": ["event/*"]
}
},