diff --git a/playground/src/App.tsx b/playground/src/App.tsx
index b333ed2..8409808 100644
--- a/playground/src/App.tsx
+++ b/playground/src/App.tsx
@@ -1,46 +1,44 @@
-/* eslint-disable unicorn/no-null */
-/* eslint-disable quotes */
-import { useCallback, useState } from 'react';
+import { useCallback, useState } from 'react'
-import { createLowlight, common } from 'lowlight';
+import { common, createLowlight } from 'lowlight'
import RcTiptapEditor, {
BaseKit,
- History,
- FormatPainter,
- Clear,
- Heading,
- FontSize,
+ Blockquote,
Bold,
- Italic,
- Underline,
- Strike,
- MoreMark,
+ BulletList,
+ Clear,
+ Code,
+ CodeBlock,
Color,
+ ColumnToolbar,
+ FontFamily,
+ FontSize,
+ FormatPainter,
+ Heading,
Highlight,
- BulletList,
- OrderedList,
- TextAlign,
+ History,
+ HorizontalRule,
+ Iframe,
+ Image,
+ ImageUpload,
Indent,
+ Italic,
LineHeight,
- TaskList,
Link,
- Image,
- ImageUpload,
- Video,
- VideoUpload,
- Blockquote,
+ MoreMark,
+ OrderedList,
SlashCommand,
- HorizontalRule,
- ColumnToolbar,
- FontFamily,
- CodeBlock,
+ Strike,
Table,
- Code,
+ TaskList,
+ TextAlign,
+ Underline,
+ Video,
+ VideoUpload,
locale,
- Iframe,
-} from 'reactjs-tiptap-editor';
+} from 'reactjs-tiptap-editor'
-import 'reactjs-tiptap-editor/style.css';
+import 'reactjs-tiptap-editor/style.css'
const extensions = [
BaseKit.configure({
@@ -67,7 +65,7 @@ const extensions = [
Highlight,
BulletList,
OrderedList,
- TextAlign.configure({ types: ['heading', 'paragraph'], spacer: true }),
+ TextAlign.configure({ types: ['heading', 'paragraph'], spacer: true, bubble: true }),
Indent,
LineHeight,
TaskList.configure({
@@ -82,56 +80,59 @@ const extensions = [
upload: (files: File) => {
return new Promise((resolve) => {
setTimeout(() => {
- resolve(URL.createObjectURL(files));
- }, 500);
- });
+ resolve(URL.createObjectURL(files))
+ }, 500)
+ })
},
}),
Video,
VideoUpload.configure({
upload: (files: File[]) => {
- const f = files.map((file) => ({
+ const f = files.map(file => ({
src: URL.createObjectURL(file),
alt: file.name,
- }));
- return Promise.resolve(f);
+ }))
+ return Promise.resolve(f)
},
}),
Blockquote,
SlashCommand,
HorizontalRule,
- Code,
+ Code.configure({
+ toolbar: false,
+ bubble: true,
+ }),
CodeBlock.configure({ lowlight: createLowlight(common) }),
ColumnToolbar,
Table,
Iframe.configure({ spacer: true }),
-];
+]
-const DEFAULT = `
Rc Tiptap Editor
A modern WYSIWYG rich text editor based on tiptap and shadcn ui for Reactjs
Demo
👉Demo
Features
Use shadcn ui components
Markdown support
TypeScript support
I18n support
React support
Slash Commands
Multi Column
TailwindCss
Support emoji
Support iframe
Installation
pnpm add reactjs-tiptap-editor
`;
+const DEFAULT = `Rc Tiptap Editor
A modern WYSIWYG rich text editor based on tiptap and shadcn ui for Reactjs
Demo
👉Demo
Features
Use shadcn ui components
Markdown support
TypeScript support
I18n support
React support
Slash Commands
Multi Column
TailwindCss
Support emoji
Support iframe
Installation
pnpm add reactjs-tiptap-editor
`
function debounce(func: any, wait: number) {
- let timeout: NodeJS.Timeout;
+ let timeout: NodeJS.Timeout
return function (...args: any[]) {
- clearTimeout(timeout);
+ clearTimeout(timeout)
// @ts-ignore
- timeout = setTimeout(() => func.apply(this, args), wait);
- };
+ timeout = setTimeout(() => func.apply(this, args), wait)
+ }
}
function App() {
- const [content, setContent] = useState(DEFAULT);
- const [theme, setTheme] = useState('light');
- const [disable, setDisable] = useState(false);
+ const [content, setContent] = useState(DEFAULT)
+ const [theme, setTheme] = useState('light')
+ const [disable, setDisable] = useState(false)
const onValueChange = useCallback(
debounce((value: any) => {
- setContent(value);
+ setContent(value)
}, 300),
[],
- );
+ )
return (
)}
- );
+ )
}
-export default App;
+export default App
diff --git a/src/components/menus/bubble.ts b/src/components/menus/bubble.ts
index f9fb777..0be322c 100644
--- a/src/components/menus/bubble.ts
+++ b/src/components/menus/bubble.ts
@@ -5,7 +5,6 @@ import { IMAGE_SIZE, VIDEO_SIZE } from '@/constants'
import type { ButtonViewParams, ButtonViewReturn, ExtensionNameKeys } from '@/types'
import { localeActions } from '@/locales'
import ActionButton from '@/components/ActionButton'
-import { Bold, Code, Color, Highlight, Italic, Link, Strike, Underline } from '@/extensions'
/** Represents the size types for bubble images or videos */
type BubbleImageOrVideoSizeType = 'size-small' | 'size-medium' | 'size-large'
@@ -106,9 +105,9 @@ function imageAlignMenus(editor: Editor): BubbleMenuItem[] {
componentProps: {
tooltip: localeActions.t(`editor.textalign.${k}.tooltip`),
icon: iconMap[k],
- action: () => editor.commands.setTextAlign(k),
+ action: () => editor.commands?.setTextAlign?.(k),
isActive: () => editor.isActive({ textAlign: k }) || false,
- disabled: !editor.can().setTextAlign(k),
+ disabled: !editor.can()?.setTextAlign?.(k),
},
}))
}
@@ -172,19 +171,11 @@ export function getBubbleVideo(editor: Editor): BubbleMenuItem[] {
}
export function getBubbleText(editor: Editor, t: any) {
- return [
- Bold.configure().options.button({ editor, t } as any),
- Italic.configure().options.button({ editor, t } as any),
- Underline.configure().options.button({ editor, t } as any),
- Strike.configure().options.button({ editor, t } as any),
- Code.configure().options.button({ editor, t } as any),
- Link.configure().options.button({ editor, t } as any),
- {
- type: 'divider',
- component: undefined,
- componentProps: {},
- },
- Color.configure().options.button({ editor, t } as any),
- Highlight.configure().options.button({ editor, t } as any),
- ]
+ const bubbleMenu = editor.extensionManager.extensions.filter((ext) => {
+ return ext.options.bubble
+ }).sort((a, b) => a.options?.sort - b.options?.sort).map((ext) => {
+ return ext.configure().options.button({ editor, t, extension: ext } as any)
+ })
+
+ return bubbleMenu
}
diff --git a/src/components/menus/components/BubbleMenuText.tsx b/src/components/menus/components/BubbleMenuText.tsx
index 582f32b..290a2d4 100644
--- a/src/components/menus/components/BubbleMenuText.tsx
+++ b/src/components/menus/components/BubbleMenuText.tsx
@@ -28,13 +28,11 @@ function ItemA({ item, disabled, editor }: any) {
}
return (
-
-
-
+
)
}
@@ -63,40 +61,38 @@ function BubbleMenuText(props: IPropsBubbleMenuText) {
}, [props.disabled, props.editor, lang, t])
return (
- <>
-
- {items?.length
- ? (
-
-
- {items?.map((item: any, key: any) => {
- if (item?.type === 'divider') {
- return (
-
- )
- }
-
+
+ {items?.length
+ ? (
+
+
+ {items?.map((item: any, key: any) => {
+ if (item?.type === 'divider') {
return (
-
)
- })}
-
+ }
+
+ return (
+
+ )
+ })}
- )
- : (
- <>>
- )}
-
- >
+
+ )
+ : (
+ <>>
+ )}
+
)
}
diff --git a/src/components/menus/components/TableBubbleMenu.tsx b/src/components/menus/components/TableBubbleMenu.tsx
index ecc5797..0c2413d 100644
--- a/src/components/menus/components/TableBubbleMenu.tsx
+++ b/src/components/menus/components/TableBubbleMenu.tsx
@@ -100,7 +100,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
- disabled={!props?.editor?.can().addColumnBefore()}
+ disabled={!props?.editor?.can()?.addColumnBefore?.()}
/>
@@ -129,7 +129,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
- disabled={!props?.editor?.can().addRowBefore()}
+ disabled={!props?.editor?.can().addRowBefore?.()}
/>
@@ -186,7 +186,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
- disabled={!props?.editor?.can().deleteTable()}
+ disabled={!props?.editor?.can()?.deleteTable?.()}
/>
diff --git a/src/extensions/Bold/Bold.ts b/src/extensions/Bold/Bold.ts
index 5b9950b..fbf429e 100644
--- a/src/extensions/Bold/Bold.ts
+++ b/src/extensions/Bold/Bold.ts
@@ -10,6 +10,7 @@ export const Bold = TiptapBold.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
button: ({ editor, t }: any) => ({
component: ActionButton,
componentProps: {
diff --git a/src/extensions/Code/Code.ts b/src/extensions/Code/Code.ts
index 8fe0039..aa744c8 100644
--- a/src/extensions/Code/Code.ts
+++ b/src/extensions/Code/Code.ts
@@ -10,6 +10,8 @@ export const Code = TiptapCode.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
+
button: ({ editor, t }) => ({
component: ActionButton,
componentProps: {
diff --git a/src/extensions/Color/Color.ts b/src/extensions/Color/Color.ts
index 985422e..a2921c9 100644
--- a/src/extensions/Color/Color.ts
+++ b/src/extensions/Color/Color.ts
@@ -10,6 +10,7 @@ export const Color = TiptapColor.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
button({ editor, t }) {
return {
component: ColorActionButton,
diff --git a/src/extensions/FontFamily/FontFamily.ts b/src/extensions/FontFamily/FontFamily.ts
index c263bec..fb5ce49 100644
--- a/src/extensions/FontFamily/FontFamily.ts
+++ b/src/extensions/FontFamily/FontFamily.ts
@@ -1,21 +1,24 @@
import type { Extension } from '@tiptap/core'
+import type { FontFamilyOptions as TiptapFontFamilyOptions } from '@tiptap/extension-font-family'
import FontFamilyTiptap from '@tiptap/extension-font-family'
// import type { GeneralOptions } from '@/types';
import type { BaseKitOptions } from '../BaseKit'
import FontFamilyButton from '@/extensions/FontFamily/components/FontFamilyButton'
+import type { GeneralOptions } from '@/types'
// export interface FontFamilyOptions extends , GeneralOptions {}
+export interface FontFamilyOptions extends TiptapFontFamilyOptions, GeneralOptions {}
-export const FontFamily = FontFamilyTiptap.extend({
+export const FontFamily = FontFamilyTiptap.extend({
addOptions() {
return {
...this.parent?.(),
fonts: ['Inter', 'Comic Sans MS, Comic Sans', 'serif', 'monospace', 'cursive'],
button({ editor, extension, t }: any) {
const { extensions = [] } = editor.extensionManager ?? []
- const fonts = extension.options?.fonts || []
+ const fonts = extension?.options?.fonts || []
const baseKitExt = extensions.find(
(k: any) => k.name === 'base-kit',
) as Extension
diff --git a/src/extensions/Highlight/Highlight.ts b/src/extensions/Highlight/Highlight.ts
index 7a8f46e..79afba0 100644
--- a/src/extensions/Highlight/Highlight.ts
+++ b/src/extensions/Highlight/Highlight.ts
@@ -12,6 +12,7 @@ export const Highlight = TiptapHighlight.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
multicolor: true,
button: ({ editor, t }) => ({
component: HighlightActionButton,
diff --git a/src/extensions/Italic/Italic.ts b/src/extensions/Italic/Italic.ts
index ba4a6f6..200b53d 100644
--- a/src/extensions/Italic/Italic.ts
+++ b/src/extensions/Italic/Italic.ts
@@ -11,6 +11,7 @@ export const Italic = TiptapItalic.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
button({ editor, t }: { editor: Editor, t: (...args: any[]) => string }) {
return {
component: ActionButton,
diff --git a/src/extensions/Link/Link.ts b/src/extensions/Link/Link.ts
index 8ef8f5d..8869cbf 100644
--- a/src/extensions/Link/Link.ts
+++ b/src/extensions/Link/Link.ts
@@ -32,6 +32,7 @@ export const Link = TiptapLink.extend({
return {
...this.parent?.(),
openOnClick: true,
+ bubble: true,
button: ({ editor, t }) => {
return {
component: LinkEditPopover,
diff --git a/src/extensions/Strike/Strike.ts b/src/extensions/Strike/Strike.ts
index f85f756..28c4a4c 100644
--- a/src/extensions/Strike/Strike.ts
+++ b/src/extensions/Strike/Strike.ts
@@ -10,6 +10,7 @@ export const Strike = TiptapStrike.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
button: ({ editor, t }: any) => ({
component: ActionButton,
componentProps: {
diff --git a/src/extensions/TextAlign/TextAlign.ts b/src/extensions/TextAlign/TextAlign.ts
index 21b25e6..d8f0d40 100644
--- a/src/extensions/TextAlign/TextAlign.ts
+++ b/src/extensions/TextAlign/TextAlign.ts
@@ -49,8 +49,8 @@ export const TextAlign = TiptapTextAlign.extend({
icon: iconMap[k],
shortcutKeys: shortcutKeysMap[k],
isActive: () => editor.isActive({ textAlign: k }) || false,
- action: () => editor.commands.setTextAlign(k),
- disabled: !editor.can().setTextAlign(k),
+ action: () => editor.commands?.setTextAlign?.(k),
+ disabled: !editor?.can?.()?.setTextAlign?.(k),
}))
const disabled = items.filter(k => k.disabled).length === items.length
return {
diff --git a/src/extensions/UnderLine/Underline.ts b/src/extensions/UnderLine/Underline.ts
index 691ec62..8f7433e 100644
--- a/src/extensions/UnderLine/Underline.ts
+++ b/src/extensions/UnderLine/Underline.ts
@@ -12,6 +12,7 @@ export const Underline = TiptapUnderline.extend({
addOptions() {
return {
...this.parent?.(),
+ bubble: true,
button({ editor, t }: any) {
return {
component: ActionButton,
diff --git a/src/styles/global.scss b/src/styles/global.scss
index 415e410..621c073 100644
--- a/src/styles/global.scss
+++ b/src/styles/global.scss
@@ -76,3 +76,10 @@
@apply bg-background text-foreground;
}
}
+
+// https://github.com/radix-ui/primitives/issues/1496
+html body[data-scroll-locked] {
+ --removed-body-scroll-bar-size: 0 !important;
+ // margin-right: 0 !important;
+ position: initial !important;
+}
diff --git a/src/types.ts b/src/types.ts
index 6e1a2a6..38853ec 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -52,6 +52,8 @@ export interface GeneralOptions {
button: ButtonView
/** Show on Toolbar */
toolbar?: boolean
+ /** Show on Bubble menu */
+ bubble?: boolean
}
/**