mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-02 19:55:18 +02:00
refactor: remove deprecated editor plugins and UI components for cleaner codebase
This commit is contained in:
parent
1995fe9ec1
commit
73147c69a3
19 changed files with 0 additions and 981 deletions
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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} />;
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 />;
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue