Support tables

This commit is contained in:
Oliver Walters 2026-05-26 10:40:35 +00:00
parent c37c1e67af
commit 5c4f082fd0
3 changed files with 139 additions and 2 deletions

View File

@ -83,6 +83,7 @@
"@tiptap/core": "^3.23.6",
"@tiptap/extension-image": "^3.23.6",
"@tiptap/extension-link": "^3.23.6",
"@tiptap/extension-table": "^3.23.6",
"@tiptap/pm": "^3.23.6",
"@tiptap/react": "^3.23.6",
"@tiptap/starter-kit": "^3.23.6",

View File

@ -4,7 +4,8 @@ import '@mantine/tiptap/styles.css';
import { useHotkeys } from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import { useQuery } from '@tanstack/react-query';
import { useEditor } from '@tiptap/react';
import { TableKit } from '@tiptap/extension-table';
import { useEditor, useEditorState } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import DOMPurify from 'dompurify';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@ -35,12 +36,22 @@ import {
Tooltip
} from '@mantine/core';
import {
IconArrowMerge,
IconCirclePlus,
IconColumnInsertLeft,
IconColumnInsertRight,
IconColumnRemove,
IconDeviceFloppy,
IconInfoCircle,
IconPhoto,
IconReload,
IconStar
IconRowInsertBottom,
IconRowInsertTop,
IconRowRemove,
IconStar,
IconTableOff,
IconTablePlus,
IconTableRow
} from '@tabler/icons-react';
import { useShallow } from 'zustand/react/shallow';
import { formatDate } from '../../defaults/formatters';
@ -150,6 +161,9 @@ export default function NotesEditor({
const src = await uploadFileRef.current(file);
return { src, 'data-keep-ratio': true };
}
}),
TableKit.configure({
table: { resizable: true, renderWrapper: true, cellMinWidth: 50 }
})
],
content: '',
@ -233,6 +247,11 @@ export default function NotesEditor({
[user, modelType, notesQuery]
);
const isInTable = useEditorState({
editor,
selector: ({ editor: e }) => e?.isActive('table') ?? false
});
// Sync editor editable state when permissions change
useEffect(() => {
editor?.setEditable(canEdit);
@ -466,6 +485,118 @@ export default function NotesEditor({
)}
</FileButton>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Control
onClick={() =>
editor
?.chain()
.focus()
.insertTable({
rows: 3,
cols: 3,
withHeaderRow: true
})
.run()
}
aria-label={t`Insert table`}
title={t`Insert table`}
>
<IconTablePlus size='0.9rem' />
</RichTextEditor.Control>
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
)}
{canEdit && isInTable && (
<RichTextEditor.Toolbar>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().addColumnBefore().run()
}
aria-label={t`Add column before`}
title={t`Add column before`}
>
<IconColumnInsertLeft size='0.9rem' />
</RichTextEditor.Control>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().addColumnAfter().run()
}
aria-label={t`Add column after`}
title={t`Add column after`}
>
<IconColumnInsertRight size='0.9rem' />
</RichTextEditor.Control>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().deleteColumn().run()
}
aria-label={t`Delete column`}
title={t`Delete column`}
>
<IconColumnRemove size='0.9rem' />
</RichTextEditor.Control>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().addRowBefore().run()
}
aria-label={t`Add row before`}
title={t`Add row before`}
>
<IconRowInsertTop size='0.9rem' />
</RichTextEditor.Control>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().addRowAfter().run()
}
aria-label={t`Add row after`}
title={t`Add row after`}
>
<IconRowInsertBottom size='0.9rem' />
</RichTextEditor.Control>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().deleteRow().run()
}
aria-label={t`Delete row`}
title={t`Delete row`}
>
<IconRowRemove size='0.9rem' />
</RichTextEditor.Control>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().mergeOrSplit().run()
}
aria-label={t`Merge or split cells`}
title={t`Merge or split cells`}
>
<IconArrowMerge size='0.9rem' />
</RichTextEditor.Control>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().toggleHeaderRow().run()
}
aria-label={t`Toggle header row`}
title={t`Toggle header row`}
>
<IconTableRow size='0.9rem' />
</RichTextEditor.Control>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Control
onClick={() =>
editor?.chain().focus().deleteTable().run()
}
aria-label={t`Delete table`}
title={t`Delete table`}
>
<IconTableOff size='0.9rem' />
</RichTextEditor.Control>
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
)}
<RichTextEditor.Content />

View File

@ -2110,6 +2110,11 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-3.23.6.tgz#b68e8df11c8c24c82d8c292ff94bc962f3cec871"
integrity sha512-oF7FEZ37f15aCe5kPgzGDYf/m+hr7VdQ/Ko/Hds/UM9pX7AG1fdtmRrl6wqkRqDM/incZaC/AQR2/Dpo2VCNGQ==
"@tiptap/extension-table@^3.23.6":
version "3.23.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-3.23.6.tgz#757a4d14f472fd4735ff5bab35da72402b0bced4"
integrity sha512-XbhZXjhsS6AP7ThoZxjAnNs+NiR81YRori25l6E+ORqB7quiPkIXOAi5h4AIpkn/CYIqze6ere11lWsYpDjtaQ==
"@tiptap/extension-text@^3.13.0":
version "3.23.4"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-3.23.4.tgz#1ab44d78d34c746f97100ff34cc28e0914231eaa"