diff --git a/components.json b/components.json index c0a07e9..63fdcbe 100644 --- a/components.json +++ b/components.json @@ -8,7 +8,7 @@ "css": "src/styles/globals.css", "baseColor": "slate", "cssVariables": true, - "prefix": "" + "prefix": "richtext-" }, "aliases": { "components": "@/components", diff --git a/package.json b/package.json index 875a53b..c2f6b9f 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "git-scm-hooks": "^0.0.11", "globby": "^14.0.2", "md5": "^2.3.0", + "postcss-replace": "^2.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "sass": "^1.77.8", diff --git a/playground/src/App.tsx b/playground/src/App.tsx index 1cf870d..da4a068 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -112,7 +112,7 @@ const extensions = [ Katex, ] -const DEFAULT = `

Rich Text Editor

A modern WYSIWYG rich text editor based on tiptap and shadcn ui for Reactjs


Demo

👉Demo

Features

Installation

pnpm add reactjs-tiptap-editor

` +const DEFAULT = `

Rich Text Editor

A modern WYSIWYG rich text editor based on tiptap and shadcn ui for Reactjs


Demo

👉Demo

Features

Installation

pnpm add reactjs-tiptap-editor

` function debounce(func: any, wait: number) { let timeout: NodeJS.Timeout diff --git a/playground/tailwind.config.js b/playground/tailwind.config.js index 942d934..10c9924 100644 --- a/playground/tailwind.config.js +++ b/playground/tailwind.config.js @@ -1,5 +1,3 @@ -import animate from 'tailwindcss-animate'; - /** @type {import('tailwindcss').Config} */ export default { darkMode: ['class'], @@ -89,5 +87,4 @@ export default { }, }, }, - plugins: [animate], -}; +} diff --git a/playground/vite.config.ts b/playground/vite.config.ts index ecd1ad0..d029ee7 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -1,14 +1,14 @@ -import * as path from 'node:path'; +import * as path from 'node:path' -import react from '@vitejs/plugin-react'; -import { defineConfig } from 'vite'; -import checker from 'vite-plugin-checker'; -import EnvironmentPlugin from 'vite-plugin-environment'; +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' +import checker from 'vite-plugin-checker' +import EnvironmentPlugin from 'vite-plugin-environment' // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { - const isDev = mode !== 'production'; - const isAnalyze = mode === 'analyze'; + const isDev = mode !== 'production' + const isAnalyze = mode === 'analyze' return { plugins: [ @@ -45,5 +45,5 @@ export default defineConfig(({ mode }) => { esbuild: { sourcemap: isDev, }, - }; -}); + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64ca2e4..1fc9e2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -261,6 +261,9 @@ importers: md5: specifier: ^2.3.0 version: 2.3.0 + postcss-replace: + specifier: ^2.0.1 + version: 2.0.1(postcss@8.4.40) react: specifier: ^18.3.1 version: 18.3.1 @@ -4928,6 +4931,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -5390,6 +5397,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-path@0.11.8: + resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} + engines: {node: '>= 10.12.0'} + object.assign@4.1.5: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} @@ -5592,6 +5603,12 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-replace@2.0.1: + resolution: {integrity: sha512-T83GVovCkBQkFCTmuid0B2bWNu/O0Bh/HDMeEGFC62EwMvVBLZQFYM7iBbcGT48QDXSNSX6e/X1Q7/Syh5NFng==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + postcss-selector-parser@6.1.1: resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} engines: {node: '>=4'} @@ -12347,6 +12364,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + kleur@3.0.3: {} kolorist@1.8.0: {} @@ -12925,6 +12944,8 @@ snapshots: object-keys@1.1.1: {} + object-path@0.11.8: {} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 @@ -13116,6 +13137,12 @@ snapshots: postcss: 8.4.40 postcss-selector-parser: 6.1.1 + postcss-replace@2.0.1(postcss@8.4.40): + dependencies: + kind-of: 6.0.3 + object-path: 0.11.8 + postcss: 8.4.40 + postcss-selector-parser@6.1.1: dependencies: cssesc: 3.0.0 diff --git a/postcss.config.js b/postcss.config.js index f780382..1efa16b 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,7 +1,7 @@ export default { plugins: { - "tailwindcss/nesting": {}, - tailwindcss: {}, - autoprefixer: {}, + 'tailwindcss/nesting': {}, + 'tailwindcss': {}, + 'autoprefixer': {}, }, -}; +} diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx index eefa3ff..44962e9 100644 --- a/src/components/ActionButton.tsx +++ b/src/components/ActionButton.tsx @@ -8,6 +8,7 @@ import type { TooltipContentProps } from '@radix-ui/react-tooltip' import { Toggle, Tooltip, TooltipContent, TooltipTrigger, icons } from '@/components' import type { ButtonViewReturnComponentProps } from '@/types' import { getShortcutKeys } from '@/utils/plateform' +import { cn } from '@/lib/utils' export interface ActionButtonProps { /* Icon name to display */ @@ -69,20 +70,20 @@ const ActionButton = React.forwardRef - {Icon && } + {Icon && } {children} {tooltip && ( -
+
{tooltip}
{!!shortcutKeys?.length && {getShortcutKeys(shortcutKeys)}}
diff --git a/src/components/ActionMenuButton.tsx b/src/components/ActionMenuButton.tsx index 592a848..96ad185 100644 --- a/src/components/ActionMenuButton.tsx +++ b/src/components/ActionMenuButton.tsx @@ -37,23 +37,23 @@ const ActionMenuButton = React.forwardRef -
+
{props?.title && ( -
{props?.title}
+
{props?.title}
)} - {Icon && } + {Icon && }
-
+
{props?.tooltip &&
{props?.tooltip}
} -
+
{!!props?.shortcutKeys?.length && {getShortcutKeys(props?.shortcutKeys)}}
diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index f1101aa..b96c70a 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -76,15 +76,15 @@ function ColorPicker(props: ColorPickerProps) { {props?.children} - -
+ +
{highlight ? (
setColor(undefined)} > - + - {t('editor.nofill')} + {t('editor.nofill')}
) : ( - <> -
{ - setColor(undefined) - }} - > - - { + setColor(undefined) + }} + > + + + - - - - + + - {t('editor.default')} -
- + + {t('editor.default')} +
)} {chunkedColors.map((items: string[], index: number) => { return ( - + {items.map((item: string, idx) => { return ( setColor(item)} > @@ -149,16 +147,15 @@ function ColorPicker(props: ColorPickerProps) { style={{ backgroundColor: item, }} - className="relative w-[18px] h-[18px] block rounded-[2px] border-transparent" + className="richtext-relative richtext-w-[18px] richtext-h-[18px] richtext-block richtext-rounded-[2px] richtext-border-transparent" > {item === selectedColor ? ( @@ -184,17 +181,17 @@ function ColorPicker(props: ColorPickerProps) { })}
-
{t('editor.recent')}
- +
{t('editor.recent')}
+ {recentColorsStore?.map((item, index) => { return ( setColor(item)} > {t('editor.color.more')} ...
-
+
{ e.preventDefault() @@ -266,14 +263,14 @@ function AddMoreColor({ setColor }: AddMoreColorProps) { />
- + diff --git a/src/components/RichTextEditor.tsx b/src/components/RichTextEditor.tsx index 78039ff..e016729 100644 --- a/src/components/RichTextEditor.tsx +++ b/src/components/RichTextEditor.tsx @@ -102,7 +102,7 @@ function RichTextEditor(props: RichTextEditorProps, ref: React.ForwardedRef<{ ed }) useEffect(() => { - document.body.classList.toggle('dark', props.dark) + document.documentElement.classList.toggle('dark', props.dark) themeActions.setTheme(props.dark ? 'dark' : 'light') }, [props.dark]) @@ -150,18 +150,18 @@ function RichTextEditor(props: RichTextEditorProps, ref: React.ForwardedRef<{ ed return ( -
+
{!props?.hideBubble && } -
+
{!props?.hideToolbar && } - + -
+
{hasExtensionValue && ( -
-
+
+
{(editor as any).storage.characterCount.characters()} {' '} diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index 2424fba..3df1022 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -61,26 +61,26 @@ function Toolbar({ editor, disabled }: ToolbarProps) { return (
-
+
{items.map((item: ToolbarItemProps, key) => { const ButtonComponent = item.button.component return ( -
- {item?.spacer && } +
+ {item?.spacer && } - {item?.divider && } + {item?.divider && }
) })} diff --git a/src/components/icons/DeleteColumn.tsx b/src/components/icons/DeleteColumn.tsx index f64be13..c5cd890 100644 --- a/src/components/icons/DeleteColumn.tsx +++ b/src/components/icons/DeleteColumn.tsx @@ -5,7 +5,7 @@ function DeleteColumn() { xmlnsXlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" - className="w-4 h-4 iconify iconify--hugeicons" + className="richtext-w-4 richtext-h-4" width="1em" height="1em" viewBox="0 0 24 24" diff --git a/src/components/icons/DeleteRow.tsx b/src/components/icons/DeleteRow.tsx index 97ce62a..11c699d 100644 --- a/src/components/icons/DeleteRow.tsx +++ b/src/components/icons/DeleteRow.tsx @@ -5,7 +5,7 @@ function DeleteRow() { xmlnsXlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" - className="w-4 h-4 iconify iconify--hugeicons" + className="richtext-w-4 richtext-h-4" width="1em" height="1em" viewBox="0 0 24 24" diff --git a/src/components/icons/Icon.tsx b/src/components/icons/Icon.tsx index 704320b..393edc6 100644 --- a/src/components/icons/Icon.tsx +++ b/src/components/icons/Icon.tsx @@ -10,7 +10,7 @@ export interface IconComponentProps { function IconComponent(props: IconComponentProps) { const Icon = icons[props.name] - return Icon ? : null + return Icon ? : null } export { IconComponent } diff --git a/src/components/menus/components/BubbleMenuKatex.tsx b/src/components/menus/components/BubbleMenuKatex.tsx index cb97ea6..8e988f2 100644 --- a/src/components/menus/components/BubbleMenuKatex.tsx +++ b/src/components/menus/components/BubbleMenuKatex.tsx @@ -61,7 +61,7 @@ function BubbleMenuKatex({ editor, ...props }: any) { <> ) : ( -
+
{visible ? ( <> @@ -73,8 +73,8 @@ function BubbleMenuKatex({ editor, ...props }: any) { defaultValue={text} style={{ marginBottom: 8 }} /> -
- +
+ @@ -83,7 +83,7 @@ function BubbleMenuKatex({ editor, ...props }: any) { ) : ( -
+
toggleVisible(!visible)}> diff --git a/src/components/menus/components/BubbleMenuMedia.tsx b/src/components/menus/components/BubbleMenuMedia.tsx index ab8fc0f..c59e51f 100644 --- a/src/components/menus/components/BubbleMenuMedia.tsx +++ b/src/components/menus/components/BubbleMenuMedia.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/no-useless-fragment */ import { Fragment, useMemo } from 'react' import type { Editor } from '@tiptap/react' @@ -29,16 +30,14 @@ function ItemA({ item, disabled, editor }: any) { {item.type === 'divider' ? ( - + ) : ( - <> - - + )} ) @@ -87,8 +86,8 @@ function BubbleMenuImage(props: IPropsBubbleMenu) { > {items?.length ? ( -
-
+
+
{items?.map((item: any, key: any) => { return ( {items?.length ? ( -
-
+
+
{items?.map((item: any, key: any) => { return ( {items?.length ? ( -
-
+
+
{items?.map((item: any, key: any) => { if (item?.type === 'divider') { return ( ) } diff --git a/src/components/menus/components/ContentMenu.tsx b/src/components/menus/components/ContentMenu.tsx index 3d5867e..7c052a3 100644 --- a/src/components/menus/components/ContentMenu.tsx +++ b/src/components/menus/components/ContentMenu.tsx @@ -183,150 +183,148 @@ function ContentMenu(props: ContentMenuProps) { } return ( - <> -
-
- - -
- - - - - -

{t('editor.draghandle.tooltip')}

-
-
+
+
+ + +
+ + + + + +

{t('editor.draghandle.tooltip')}

+
+
- -
+ +
- - - - {t('editor.remove')} - + + + + {t('editor.remove')} + - {hasClearExtension - ? ( - - - {t('editor.clear.tooltip')} - - ) - : null} + {hasClearExtension + ? ( + + + {t('editor.clear.tooltip')} + + ) + : null} - - - {t('editor.copyToClipboard')} - - - - {t('editor.copy')} - + + + {t('editor.copyToClipboard')} + + + + {t('editor.copy')} + - {hasTextAlignExtension || hasIndentExtension - ? ( - - ) - : null} + {hasTextAlignExtension || hasIndentExtension + ? ( + + ) + : null} - {hasTextAlignExtension - ? ( - - - - {t('editor.textalign.tooltip')} - - - - setTextAlign('left')}> - - {t('editor.textalign.left.tooltip')} - - setTextAlign('center')}> - - {t('editor.textalign.center.tooltip')} - - setTextAlign('right')}> - - {t('editor.textalign.right.tooltip')} - - - - - ) - : null} + {hasTextAlignExtension + ? ( + + + + {t('editor.textalign.tooltip')} + + + + setTextAlign('left')}> + + {t('editor.textalign.left.tooltip')} + + setTextAlign('center')}> + + {t('editor.textalign.center.tooltip')} + + setTextAlign('right')}> + + {t('editor.textalign.right.tooltip')} + + + + + ) + : null} - {hasIndentExtension - ? ( - - - - {t('editor.indent')} - - - - = IndentProps.max} - > - - {t('editor.indent.tooltip')} - + {hasIndentExtension + ? ( + + + + {t('editor.indent')} + + + + = IndentProps.max} + > + + {t('editor.indent.tooltip')} + - - - {t('editor.outdent.tooltip')} - - - - - ) - : null} + + + {t('editor.outdent.tooltip')} + + + + + ) + : null} - - -
+ +
- +
) } diff --git a/src/components/menus/components/TableBubbleMenu.tsx b/src/components/menus/components/TableBubbleMenu.tsx index 9443d93..044c876 100644 --- a/src/components/menus/components/TableBubbleMenu.tsx +++ b/src/components/menus/components/TableBubbleMenu.tsx @@ -94,7 +94,7 @@ function TableBubbleMenu({ editor }: TableBubbleMenuProps) { sticky: 'popper', }} > -
+
- + - + - + {children} - + )) -DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName +DropdownMenuSubTrigger.displayName + = DropdownMenuPrimitive.SubTrigger.displayName const DropdownMenuSubContent = React.forwardRef< React.ElementRef, @@ -46,13 +47,14 @@ const DropdownMenuSubContent = React.forwardRef< )) -DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName +DropdownMenuSubContent.displayName + = DropdownMenuPrimitive.SubContent.displayName const DropdownMenuContent = React.forwardRef< React.ElementRef, @@ -63,7 +65,7 @@ const DropdownMenuContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', + 'richtext-z-50 richtext-min-w-[8rem] richtext-overflow-hidden richtext-rounded-md richtext-border richtext-bg-popover richtext-p-1 richtext-text-popover-foreground richtext-shadow-md data-[state=open]:richtext-animate-in data-[state=closed]:richtext-animate-out data-[state=closed]:richtext-fade-out-0 data-[state=open]:richtext-fade-in-0 data-[state=closed]:richtext-zoom-out-95 data-[state=open]:richtext-zoom-in-95 data-[side=bottom]:richtext-slide-in-from-top-2 data-[side=left]:richtext-slide-in-from-right-2 data-[side=right]:richtext-slide-in-from-left-2 data-[side=top]:richtext-slide-in-from-bottom-2', className, )} {...props} @@ -81,8 +83,8 @@ const DropdownMenuItem = React.forwardRef< - + - + {children} )) -DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName +DropdownMenuCheckboxItem.displayName + = DropdownMenuPrimitive.CheckboxItem.displayName const DropdownMenuRadioItem = React.forwardRef< React.ElementRef, @@ -120,14 +123,14 @@ const DropdownMenuRadioItem = React.forwardRef< - + - + {children} @@ -143,7 +146,11 @@ const DropdownMenuLabel = React.forwardRef< >(({ className, inset, ...props }, ref) => ( )) @@ -155,15 +162,21 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( )) DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName -function DropdownMenuShortcut({ className, ...props }: React.HTMLAttributes) { +function DropdownMenuShortcut({ + className, + ...props +}: React.HTMLAttributes) { return ( - + ) } DropdownMenuShortcut.displayName = 'DropdownMenuShortcut' diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index fc5146e..b62780e 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -11,7 +11,7 @@ const Input = React.forwardRef( (({ className, ...props }, ref) => ( diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 03caf14..2316d1d 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -14,7 +14,7 @@ const TabsList = React.forwardRef< ( return (