diff --git a/apps/www/src/registry/default/components/editor/use-create-editor.ts b/apps/www/src/registry/default/components/editor/use-create-editor.ts index f3820bcff5..27b4008000 100644 --- a/apps/www/src/registry/default/components/editor/use-create-editor.ts +++ b/apps/www/src/registry/default/components/editor/use-create-editor.ts @@ -35,6 +35,10 @@ import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react'; import { KbdPlugin } from '@udecode/plate-kbd/react'; import { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react'; import { LinkPlugin } from '@udecode/plate-link/react'; +import { + EquationPlugin, + InlineEquationPlugin, +} from '@udecode/plate-math/react'; import { AudioPlugin, FilePlugin, @@ -92,6 +96,8 @@ import { TableRowElement } from '@/registry/default/plate-ui/table-row-element'; import { TocElement } from '@/registry/default/plate-ui/toc-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; +import { EquationElement } from '../../plate-ui/equation-element'; +import { InlineEquationElement } from '../../plate-ui/inline-equation-element'; import { editorPlugins, viewPlugins } from './plugins/editor-plugins'; export const viewComponents = { @@ -106,6 +112,7 @@ export const viewComponents = { [ColumnPlugin.key]: ColumnGroupElement, [CommentsPlugin.key]: CommentLeaf, [DatePlugin.key]: DateElement, + [EquationPlugin.key]: EquationElement, [FilePlugin.key]: MediaFileElement, [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), @@ -116,6 +123,7 @@ export const viewComponents = { [HighlightPlugin.key]: HighlightLeaf, [HorizontalRulePlugin.key]: HrElement, [ImagePlugin.key]: ImageElement, + [InlineEquationPlugin.key]: InlineEquationElement, [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }), [KbdPlugin.key]: KbdLeaf, [LinkPlugin.key]: LinkElement, diff --git a/apps/www/src/registry/default/plate-ui/equation-element.tsx b/apps/www/src/registry/default/plate-ui/equation-element.tsx new file mode 100644 index 0000000000..6e63a82cf3 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/equation-element.tsx @@ -0,0 +1,79 @@ +'use client'; + +import React, { useRef, useState } from 'react'; + +import type { TEquationElement } from '@udecode/plate-math'; + +import { cn, withRef } from '@udecode/cn'; +import { useElement } from '@udecode/plate-common/react'; +import { useEquationElement } from '@udecode/plate-math/react'; +import { RadicalIcon } from 'lucide-react'; + +import { EquationPopoverContent } from './equation-popover'; +import { PlateElement } from './plate-element'; +import { Popover, PopoverTrigger } from './popover'; + +export const EquationElement = withRef( + ({ children, className, ...props }, ref) => { + const element = useElement(); + + const [open, setOpen] = useState(false); + const katexRef = useRef(null); + + useEquationElement({ + element, + katexRef: katexRef, + options: { + displayMode: true, + errorColor: '#cc0000', + fleqn: false, + leqno: false, + macros: { '\\f': '#1f(#2)' }, + output: 'htmlAndMathml', + strict: 'warn', + throwOnError: false, + trust: false, + }, + }); + + return ( + + + +
+ {element.texExpression.length > 0 ? ( + + ) : ( +
+ +
Add a Tex equation
+
+ )} +
+
+ + 0 \\\\\n 0, &\\quad x = 0 \\\\\n -x^2, &\\quad x < 0\n\\end{cases}`} + isInline={false} + setOpen={setOpen} + /> +
+ + {children} +
+ ); + } +); diff --git a/apps/www/src/registry/default/plate-ui/equation-popover.tsx b/apps/www/src/registry/default/plate-ui/equation-popover.tsx new file mode 100644 index 0000000000..bfeed147b6 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/equation-popover.tsx @@ -0,0 +1,83 @@ +'use client'; + +import React, { useEffect } from 'react'; +import TextareaAutosize, { + type TextareaAutosizeProps, +} from 'react-textarea-autosize'; + +import type { TEquationElement } from '@udecode/plate-math'; + +import { cn } from '@udecode/cn'; +import { + createPrimitiveComponent, + selectSiblingNodePoint, + useEditorRef, + useElement, +} from '@udecode/plate-common/react'; +import { useEquationInput } from '@udecode/plate-math/react'; +import { BlockSelectionPlugin } from '@udecode/plate-selection/react'; +import { CornerDownLeftIcon } from 'lucide-react'; +import { useReadOnly, useSelected } from 'slate-react'; + +import { Button } from './button'; +import { PopoverContent } from './popover'; + +const EquationInput = createPrimitiveComponent(TextareaAutosize)({ + propsHook: useEquationInput, +}); + +const EquationPopoverContent = ({ + className, + isInline, + setOpen, + ...props +}: { + isInline: boolean; + setOpen: (open: boolean) => void; +} & TextareaAutosizeProps) => { + const editor = useEditorRef(); + const readOnly = useReadOnly(); + const element = useElement(); + const selected = useSelected(); + + useEffect(() => { + setOpen(selected); + }, [selected, setOpen]); + + if (readOnly) return null; + + const onClose = () => { + setOpen(false); + + if (isInline) { + selectSiblingNodePoint(editor, { node: element }); + } else { + editor + .getApi(BlockSelectionPlugin) + .blockSelection.addSelectedRow(element.id as string); + } + }; + + return ( + { + e.preventDefault(); + }} + contentEditable={false} + > + + + + + ); +}; + +export { EquationPopoverContent }; diff --git a/apps/www/src/registry/default/plate-ui/inline-equation-element.tsx b/apps/www/src/registry/default/plate-ui/inline-equation-element.tsx new file mode 100644 index 0000000000..8f5b4b4f13 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/inline-equation-element.tsx @@ -0,0 +1,88 @@ +'use client'; + +import { useRef, useState } from 'react'; + +import type { TEquationElement } from '@udecode/plate-math'; + +import { cn, withRef } from '@udecode/cn'; +import { useElement } from '@udecode/plate-common/react'; +import { useEquationElement } from '@udecode/plate-math/react'; +import { RadicalIcon } from 'lucide-react'; + +import { EquationPopoverContent } from './equation-popover'; +import { PlateElement } from './plate-element'; +import { Popover, PopoverTrigger } from './popover'; + +export const InlineEquationElement = withRef( + ({ children, className, ...props }, ref) => { + const element = useElement(); + const katexRef = useRef(null); + const [open, setOpen] = useState(false); + + useEquationElement({ + element, + katexRef: katexRef, + options: { + displayMode: true, + errorColor: '#cc0000', + fleqn: false, + leqno: false, + macros: { '\\f': '#1f(#2)' }, + output: 'htmlAndMathml', + strict: 'warn', + throwOnError: false, + trust: false, + }, + }); + + return ( + + + +
0 && open && 'after:bg-brand/15', + element.texExpression.length === 0 && + 'text-muted-foreground after:bg-neutral-500/10', + className + )} + contentEditable={false} + > + + {element.texExpression.length === 0 && ( + + + New equation + + )} +
+
+ + +
+ + {children} +
+ ); + } +); diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 343f04799f..48118d65dc 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -11,6 +11,10 @@ import { DatePlugin } from '@udecode/plate-date/react'; import { HEADING_KEYS } from '@udecode/plate-heading'; import { TocPlugin } from '@udecode/plate-heading/react'; import { INDENT_LIST_KEYS, ListStyleType } from '@udecode/plate-indent-list'; +import { + EquationPlugin, + InlineEquationPlugin, +} from '@udecode/plate-math/react'; import { TablePlugin } from '@udecode/plate-table/react'; import { TogglePlugin } from '@udecode/plate-toggle/react'; import { @@ -25,6 +29,7 @@ import { ListOrdered, PilcrowIcon, Quote, + RadicalIcon, SparklesIcon, Square, Table, @@ -167,6 +172,11 @@ const groups: Group[] = [ label: '3 columns', value: 'action_three_columns', }, + { + icon: , + label: 'Equation', + value: EquationPlugin.key, + }, ].map((item) => ({ ...item, onSelect: (editor, value) => { @@ -184,6 +194,11 @@ const groups: Group[] = [ label: 'Date', value: DatePlugin.key, }, + { + icon: , + label: 'Equation', + value: InlineEquationPlugin.key, + }, ].map((item) => ({ ...item, onSelect: (editor, value) => { diff --git a/packages/math/src/react/hooks/useEquationInput.ts b/packages/math/src/react/hooks/useEquationInput.ts index eb74bbb670..6e85fc80dd 100644 --- a/packages/math/src/react/hooks/useEquationInput.ts +++ b/packages/math/src/react/hooks/useEquationInput.ts @@ -22,7 +22,6 @@ export const useEquationInput = ({ const editor = useEditorRef(); const element = useElement(); const inputRef = useRef(null); - const [expressionInput, setExpressionInput] = React.useState( element.texExpression ); @@ -79,9 +78,6 @@ export const useEquationInput = ({ } else if (isHotkey('escape')(e)) { e.preventDefault(); onDismiss(); - } else if (isHotkey('meta+z')(e)) { - e.preventDefault(); - editor.undo(); } else if (isHotkey('meta+y')(e) || isHotkey('meta+shift+z')(e)) { e.preventDefault(); editor.redo();