refactor: remove deprecated editor plugins and UI components for cleaner codebase

This commit is contained in:
Anish Sarkar 2026-02-16 01:15:21 +05:30
parent 1995fe9ec1
commit 73147c69a3
19 changed files with 0 additions and 981 deletions

View file

@ -1,13 +0,0 @@
import * as React from 'react';
import { type SlateElementProps, SlateElement } from 'platejs/static';
export function BlockquoteElementStatic(props: SlateElementProps) {
return (
<SlateElement
as="blockquote"
className="my-1 border-l-2 pl-6 italic"
{...props}
/>
);
}

View file

@ -1,161 +0,0 @@
import * as React from 'react';
import type { TCodeBlockElement } from 'platejs';
import {
type SlateElementProps,
type SlateLeafProps,
SlateElement,
SlateLeaf,
} from 'platejs/static';
export function CodeBlockElementStatic(
props: SlateElementProps<TCodeBlockElement>
) {
return (
<SlateElement
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] **:[.hljs-built\\\\_in,.hljs-symbol]:text-[#e36209] dark:**:[.hljs-built\\\\_in,.hljs-symbol]:text-[#c3854e] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#d73a49] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#ee6960] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] dark:**:[.hljs-section]:text-[#61a5f2] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#6f42c1] dark:**:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#a77bfa]"
{...props}
>
<div className="relative rounded-md bg-muted/50">
<pre className="overflow-x-auto p-8 pr-4 font-mono text-sm leading-[normal] [tab-size:2] print:break-inside-avoid">
<code>{props.children}</code>
</pre>
</div>
</SlateElement>
);
}
export function CodeLineElementStatic(props: SlateElementProps) {
return <SlateElement {...props} />;
}
export function CodeSyntaxLeafStatic(props: SlateLeafProps) {
const tokenClassName = props.leaf.className as string;
return <SlateLeaf className={tokenClassName} {...props} />;
}
/**
* DOCX-compatible code block components.
* Uses inline styles for proper rendering in Word documents.
*/
export function CodeBlockElementDocx(
props: SlateElementProps<TCodeBlockElement>
) {
return (
<SlateElement {...props}>
<div
style={{
backgroundColor: '#f5f5f5',
border: '1px solid #e0e0e0',
margin: '8pt 0',
padding: '12pt',
}}
>
{props.children}
</div>
</SlateElement>
);
}
export function CodeLineElementDocx(props: SlateElementProps) {
return (
<SlateElement
{...props}
as="p"
style={{
fontFamily: "'Courier New', Consolas, monospace",
fontSize: '10pt',
margin: 0,
padding: 0,
}}
/>
);
}
// Syntax highlighting color map for common token types
const syntaxColors: Record<string, string> = {
'hljs-addition': '#22863a',
'hljs-attr': '#005cc5',
'hljs-attribute': '#005cc5',
'hljs-built_in': '#e36209',
'hljs-bullet': '#735c0f',
'hljs-comment': '#6a737d',
'hljs-deletion': '#b31d28',
'hljs-doctag': '#d73a49',
'hljs-emphasis': '#24292e',
'hljs-formula': '#6a737d',
'hljs-keyword': '#d73a49',
'hljs-literal': '#005cc5',
'hljs-meta': '#005cc5',
'hljs-name': '#22863a',
'hljs-number': '#005cc5',
'hljs-operator': '#005cc5',
'hljs-quote': '#22863a',
'hljs-regexp': '#032f62',
'hljs-section': '#005cc5',
'hljs-selector-attr': '#005cc5',
'hljs-selector-class': '#005cc5',
'hljs-selector-id': '#005cc5',
'hljs-selector-pseudo': '#22863a',
'hljs-selector-tag': '#22863a',
'hljs-string': '#032f62',
'hljs-strong': '#24292e',
'hljs-symbol': '#e36209',
'hljs-template-tag': '#d73a49',
'hljs-template-variable': '#d73a49',
'hljs-title': '#6f42c1',
'hljs-type': '#d73a49',
'hljs-variable': '#005cc5',
};
// Convert regular spaces to non-breaking spaces to preserve indentation in Word
const preserveSpaces = (text: string): string => {
// Replace regular spaces with non-breaking spaces
return text.replace(/ /g, '\u00A0');
};
export function CodeSyntaxLeafDocx(props: SlateLeafProps) {
const tokenClassName = props.leaf.className as string;
// Extract color from className
let color: string | undefined;
let fontWeight: string | undefined;
let fontStyle: string | undefined;
if (tokenClassName) {
const classes = tokenClassName.split(' ');
for (const cls of classes) {
if (syntaxColors[cls]) {
color = syntaxColors[cls];
}
if (cls === 'hljs-strong' || cls === 'hljs-section') {
fontWeight = 'bold';
}
if (cls === 'hljs-emphasis') {
fontStyle = 'italic';
}
}
}
// Get the text content and preserve spaces
const text = props.leaf.text as string;
const preservedText = preserveSpaces(text);
return (
<span
data-slate-leaf="true"
style={{
color,
fontFamily: "'Courier New', Consolas, monospace",
fontSize: '10pt',
fontStyle,
fontWeight,
}}
>
{preservedText}
</span>
);
}

View file

@ -1,17 +0,0 @@
import * as React from 'react';
import type { SlateLeafProps } from 'platejs/static';
import { SlateLeaf } from 'platejs/static';
export function CodeLeafStatic(props: SlateLeafProps) {
return (
<SlateLeaf
{...props}
as="code"
className="whitespace-pre-wrap rounded-md bg-muted px-[0.3em] py-[0.2em] font-mono text-sm"
>
{props.children}
</SlateLeaf>
);
}

View file

@ -1,55 +0,0 @@
import * as React from 'react';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import { type PlateStaticProps, PlateStatic } from 'platejs/static';
import { cn } from '@/lib/utils';
export const editorVariants = cva(
cn(
'group/editor',
'relative w-full cursor-text select-text overflow-x-hidden whitespace-pre-wrap break-words',
'rounded-md ring-offset-background focus-visible:outline-none',
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:top-[auto_!important] **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
'[&_strong]:font-bold'
),
{
defaultVariants: {
variant: 'none',
},
variants: {
disabled: {
true: 'cursor-not-allowed opacity-50',
},
focused: {
true: 'ring-2 ring-ring ring-offset-2',
},
variant: {
ai: 'w-full px-0 text-base md:text-sm',
aiChat:
'max-h-[min(70vh,320px)] w-full overflow-y-auto px-5 py-3 text-base md:text-sm',
default:
'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
demo: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24',
none: '',
select: 'px-3 py-2 text-base data-readonly:w-fit',
},
},
}
);
export function EditorStatic({
className,
variant,
...props
}: PlateStaticProps & VariantProps<typeof editorVariants>) {
return (
<PlateStatic
className={cn(editorVariants({ variant }), className)}
{...props}
/>
);
}

View file

@ -1,27 +0,0 @@
'use client';
import * as React from 'react';
import { insertInlineEquation } from '@platejs/math';
import { RadicalIcon } from 'lucide-react';
import { useEditorRef } from 'platejs/react';
import { ToolbarButton } from './toolbar';
export function InlineEquationToolbarButton(
props: React.ComponentProps<typeof ToolbarButton>
) {
const editor = useEditorRef();
return (
<ToolbarButton
{...props}
onClick={() => {
insertInlineEquation(editor);
}}
tooltip="Mark as equation"
>
<RadicalIcon />
</ToolbarButton>
);
}

View file

@ -1,72 +0,0 @@
import * as React from 'react';
import type { SlateElementProps } from 'platejs/static';
import { type VariantProps, cva } from 'class-variance-authority';
import { SlateElement } from 'platejs/static';
const headingVariants = cva('relative mb-1', {
variants: {
variant: {
h1: 'mt-[1.6em] pb-1 font-bold font-heading text-4xl',
h2: 'mt-[1.4em] pb-px font-heading font-semibold text-2xl tracking-tight',
h3: 'mt-[1em] pb-px font-heading font-semibold text-xl tracking-tight',
h4: 'mt-[0.75em] font-heading font-semibold text-lg tracking-tight',
h5: 'mt-[0.75em] font-semibold text-lg tracking-tight',
h6: 'mt-[0.75em] font-semibold text-base tracking-tight',
},
},
});
export function HeadingElementStatic({
variant = 'h1',
...props
}: SlateElementProps & VariantProps<typeof headingVariants>) {
const id = props.element.id as string | undefined;
return (
<SlateElement
as={variant!}
className={headingVariants({ variant })}
{...props}
>
{/* Bookmark anchor for DOCX TOC internal links */}
{id && <span id={id} />}
{props.children}
</SlateElement>
);
}
export function H1ElementStatic(props: SlateElementProps) {
return <HeadingElementStatic variant="h1" {...props} />;
}
export function H2ElementStatic(
props: React.ComponentProps<typeof HeadingElementStatic>
) {
return <HeadingElementStatic variant="h2" {...props} />;
}
export function H3ElementStatic(
props: React.ComponentProps<typeof HeadingElementStatic>
) {
return <HeadingElementStatic variant="h3" {...props} />;
}
export function H4ElementStatic(
props: React.ComponentProps<typeof HeadingElementStatic>
) {
return <HeadingElementStatic variant="h4" {...props} />;
}
export function H5ElementStatic(
props: React.ComponentProps<typeof HeadingElementStatic>
) {
return <HeadingElementStatic variant="h5" {...props} />;
}
export function H6ElementStatic(
props: React.ComponentProps<typeof HeadingElementStatic>
) {
return <HeadingElementStatic variant="h6" {...props} />;
}

View file

@ -1,13 +0,0 @@
import * as React from 'react';
import type { SlateLeafProps } from 'platejs/static';
import { SlateLeaf } from 'platejs/static';
export function HighlightLeafStatic(props: SlateLeafProps) {
return (
<SlateLeaf {...props} as="mark" className="bg-highlight/30 text-inherit">
{props.children}
</SlateLeaf>
);
}

View file

@ -1,22 +0,0 @@
import * as React from 'react';
import type { SlateElementProps } from 'platejs/static';
import { SlateElement } from 'platejs/static';
import { cn } from '@/lib/utils';
export function HrElementStatic(props: SlateElementProps) {
return (
<SlateElement {...props}>
<div className="cursor-text py-6" contentEditable={false}>
<hr
className={cn(
'h-0.5 rounded-sm border-none bg-muted bg-clip-content'
)}
/>
</div>
{props.children}
</SlateElement>
);
}

View file

@ -1,17 +0,0 @@
import * as React from 'react';
import type { SlateLeafProps } from 'platejs/static';
import { SlateLeaf } from 'platejs/static';
export function KbdLeafStatic(props: SlateLeafProps) {
return (
<SlateLeaf
{...props}
as="kbd"
className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-sm shadow-[rgba(255,_255,_255,_0.1)_0px_0.5px_0px_0px_inset,_rgb(248,_249,_250)_0px_1px_5px_0px_inset,_rgb(193,_200,_205)_0px_0px_0px_0.5px,_rgb(193,_200,_205)_0px_2px_1px_-1px,_rgb(193,_200,_205)_0px_1px_0px_0px] dark:shadow-[rgba(255,_255,_255,_0.1)_0px_0.5px_0px_0px_inset,_rgb(26,_29,_30)_0px_1px_5px_0px_inset,_rgb(76,_81,_85)_0px_0px_0px_0.5px,_rgb(76,_81,_85)_0px_2px_1px_-1px,_rgb(76,_81,_85)_0px_1px_0px_0px]"
>
{props.children}
</SlateLeaf>
);
}

View file

@ -1,23 +0,0 @@
import * as React from 'react';
import type { TLinkElement } from 'platejs';
import type { SlateElementProps } from 'platejs/static';
import { getLinkAttributes } from '@platejs/link';
import { SlateElement } from 'platejs/static';
export function LinkElementStatic(props: SlateElementProps<TLinkElement>) {
return (
<SlateElement
{...props}
as="a"
className="font-medium text-primary underline decoration-primary underline-offset-4"
attributes={{
...props.attributes,
...getLinkAttributes(props.editor, props.element),
}}
>
{props.children}
</SlateElement>
);
}

View file

@ -1,69 +0,0 @@
'use client';
import * as React from 'react';
import { indentListItems, unindentListItems } from '@platejs/list-classic';
import {
useListToolbarButton,
useListToolbarButtonState,
} from '@platejs/list-classic/react';
import {
IndentIcon,
List,
ListOrdered,
ListTodo,
OutdentIcon,
} from 'lucide-react';
import { KEYS } from 'platejs';
import { useEditorRef } from 'platejs/react';
import { ToolbarButton } from './toolbar';
const nodeTypeMap: Record<string, { icon: React.JSX.Element; label: string }> =
{
[KEYS.olClassic]: { icon: <ListOrdered />, label: 'Numbered List' },
[KEYS.taskList]: { icon: <ListTodo />, label: 'Task List' },
[KEYS.ulClassic]: { icon: <List />, label: 'Bulleted List' },
};
export function ListToolbarButton({
nodeType = KEYS.ulClassic,
...props
}: React.ComponentProps<typeof ToolbarButton> & {
nodeType?: string;
}) {
const state = useListToolbarButtonState({ nodeType });
const { props: buttonProps } = useListToolbarButton(state);
const { icon, label } = nodeTypeMap[nodeType] ?? nodeTypeMap[KEYS.ulClassic];
return (
<ToolbarButton {...props} {...buttonProps} tooltip={label}>
{icon}
</ToolbarButton>
);
}
export function IndentToolbarButton({
reverse = false,
...props
}: React.ComponentProps<typeof ToolbarButton> & {
reverse?: boolean;
}) {
const editor = useEditorRef();
return (
<ToolbarButton
{...props}
onClick={() => {
if (reverse) {
unindentListItems(editor);
} else {
indentListItems(editor);
}
}}
tooltip={reverse ? 'Outdent' : 'Indent'}
>
{reverse ? <OutdentIcon /> : <IndentIcon />}
</ToolbarButton>
);
}

View file

@ -1,15 +0,0 @@
import * as React from 'react';
import type { SlateElementProps } from 'platejs/static';
import { SlateElement } from 'platejs/static';
import { cn } from '@/lib/utils';
export function ParagraphElementStatic(props: SlateElementProps) {
return (
<SlateElement {...props} className={cn('m-0 px-0 py-1')}>
{props.children}
</SlateElement>
);
}

View file

@ -1,101 +0,0 @@
import * as React from 'react';
import type { TTableCellElement, TTableElement } from 'platejs';
import type { SlateElementProps } from 'platejs/static';
import { BaseTablePlugin } from '@platejs/table';
import { SlateElement } from 'platejs/static';
import { cn } from '@/lib/utils';
export function TableElementStatic({
children,
...props
}: SlateElementProps<TTableElement>) {
const { disableMarginLeft } = props.editor.getOptions(BaseTablePlugin);
const marginLeft = disableMarginLeft ? 0 : props.element.marginLeft;
return (
<SlateElement
{...props}
className="overflow-x-auto py-5"
style={{ paddingLeft: marginLeft }}
>
<div className="group/table relative w-fit">
<table
className="mr-0 ml-px table h-px table-fixed border-collapse"
style={{ borderCollapse: 'collapse', width: '100%' }}
>
<tbody className="min-w-full">{children}</tbody>
</table>
</div>
</SlateElement>
);
}
export function TableRowElementStatic(props: SlateElementProps) {
return (
<SlateElement {...props} as="tr" className="h-full">
{props.children}
</SlateElement>
);
}
export function TableCellElementStatic({
isHeader,
...props
}: SlateElementProps<TTableCellElement> & {
isHeader?: boolean;
}) {
const { editor, element } = props;
const { api } = editor.getPlugin(BaseTablePlugin);
const { minHeight, width } = api.table.getCellSize({ element });
const borders = api.table.getCellBorders({ element });
return (
<SlateElement
{...props}
as={isHeader ? 'th' : 'td'}
className={cn(
'h-full overflow-visible border-none bg-background p-0',
element.background ? 'bg-(--cellBackground)' : 'bg-background',
isHeader && 'text-left font-normal *:m-0',
'before:size-full',
"before:absolute before:box-border before:select-none before:content-['']",
borders &&
cn(
borders.bottom?.size && 'before:border-b before:border-b-border',
borders.right?.size && 'before:border-r before:border-r-border',
borders.left?.size && 'before:border-l before:border-l-border',
borders.top?.size && 'before:border-t before:border-t-border'
)
)}
style={
{
'--cellBackground': element.background,
maxWidth: width || 240,
minWidth: width || 120,
} as React.CSSProperties
}
attributes={{
...props.attributes,
colSpan: api.table.getColSpan(element),
rowSpan: api.table.getRowSpan(element),
}}
>
<div
className="relative z-20 box-border h-full px-4 py-2"
style={{ minHeight }}
>
{props.children}
</div>
</SlateElement>
);
}
export function TableCellHeaderElementStatic(
props: SlateElementProps<TTableCellElement>
) {
return <TableCellElementStatic {...props} isHeader />;
}

View file

@ -1,266 +0,0 @@
'use client';
import * as React from 'react';
import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import { TablePlugin, useTableMergeState } from '@platejs/table/react';
import {
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
Combine,
Grid3x3Icon,
Table,
Trash2Icon,
Ungroup,
XIcon,
} from 'lucide-react';
import { KEYS } from 'platejs';
import { useEditorPlugin, useEditorSelector } from 'platejs/react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { cn } from '@/lib/utils';
import { ToolbarButton } from './toolbar';
export function TableToolbarButton(props: DropdownMenuProps) {
const tableSelected = useEditorSelector(
(editor) => editor.api.some({ match: { type: KEYS.table } }),
[]
);
const { editor, tf } = useEditorPlugin(TablePlugin);
const [open, setOpen] = React.useState(false);
const mergeState = useTableMergeState();
return (
<DropdownMenu open={open} onOpenChange={setOpen} modal={false} {...props}>
<DropdownMenuTrigger asChild>
<ToolbarButton pressed={open} tooltip="Table" isDropdown>
<Table />
</ToolbarButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="flex w-[180px] min-w-0 flex-col"
align="start"
>
<DropdownMenuGroup>
<DropdownMenuSub>
<DropdownMenuSubTrigger className="gap-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50">
<Grid3x3Icon className="size-4" />
<span>Table</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="m-0 p-0">
<TablePicker />
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSub>
<DropdownMenuSubTrigger
className="gap-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
disabled={!tableSelected}
>
<div className="size-4" />
<span>Cell</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!mergeState.canMerge}
onSelect={() => {
tf.table.merge();
editor.tf.focus();
}}
>
<Combine />
Merge cells
</DropdownMenuItem>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!mergeState.canSplit}
onSelect={() => {
tf.table.split();
editor.tf.focus();
}}
>
<Ungroup />
Split cell
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSub>
<DropdownMenuSubTrigger
className="gap-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
disabled={!tableSelected}
>
<div className="size-4" />
<span>Row</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.insert.tableRow({ before: true });
editor.tf.focus();
}}
>
<ArrowUp />
Insert row before
</DropdownMenuItem>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.insert.tableRow();
editor.tf.focus();
}}
>
<ArrowDown />
Insert row after
</DropdownMenuItem>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.remove.tableRow();
editor.tf.focus();
}}
>
<XIcon />
Delete row
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSub>
<DropdownMenuSubTrigger
className="gap-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
disabled={!tableSelected}
>
<div className="size-4" />
<span>Column</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.insert.tableColumn({ before: true });
editor.tf.focus();
}}
>
<ArrowLeft />
Insert column before
</DropdownMenuItem>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.insert.tableColumn();
editor.tf.focus();
}}
>
<ArrowRight />
Insert column after
</DropdownMenuItem>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.remove.tableColumn();
editor.tf.focus();
}}
>
<XIcon />
Delete column
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem
className="min-w-[180px]"
disabled={!tableSelected}
onSelect={() => {
tf.remove.table();
editor.tf.focus();
}}
>
<Trash2Icon />
Delete table
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}
function TablePicker() {
const { editor, tf } = useEditorPlugin(TablePlugin);
const [tablePicker, setTablePicker] = React.useState({
grid: Array.from({ length: 8 }, () => Array.from({ length: 8 }).fill(0)),
size: { colCount: 0, rowCount: 0 },
});
const onCellMove = (rowIndex: number, colIndex: number) => {
const newGrid = [...tablePicker.grid];
for (let i = 0; i < newGrid.length; i++) {
for (let j = 0; j < newGrid[i].length; j++) {
newGrid[i][j] =
i >= 0 && i <= rowIndex && j >= 0 && j <= colIndex ? 1 : 0;
}
}
setTablePicker({
grid: newGrid,
size: { colCount: colIndex + 1, rowCount: rowIndex + 1 },
});
};
return (
<div
className="flex! m-0 flex-col p-0"
onClick={() => {
tf.insert.table(tablePicker.size, { select: true });
editor.tf.focus();
}}
role="button"
>
<div className="grid size-[130px] grid-cols-8 gap-0.5 p-1">
{tablePicker.grid.map((rows, rowIndex) =>
rows.map((value, columIndex) => (
<div
key={`(${rowIndex},${columIndex})`}
className={cn(
'col-span-1 size-3 border border-solid bg-secondary',
!!value && 'border-current'
)}
onMouseMove={() => {
onCellMove(rowIndex, columIndex);
}}
/>
))
)}
</div>
<div className="text-center text-current text-xs">
{tablePicker.size.rowCount} x {tablePicker.size.colCount}
</div>
</div>
);
}