Skip to content

Commit

Permalink
Merge pull request #15 from hunghg255/fix-crash
Browse files Browse the repository at this point in the history
  • Loading branch information
hunghg255 authored Aug 16, 2024
2 parents fcf196c + 6e99d75 commit a4beb82
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 121 deletions.
107 changes: 54 additions & 53 deletions playground/src/App.tsx
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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({
Expand All @@ -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 = `<h1 style="text-align: center">Rc Tiptap Editor</h1><p>A modern WYSIWYG rich text editor based on <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://github.com/scrumpy/tiptap">tiptap</a> and <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> for Reactjs</p><p></p><p style="text-align: center"></p><p style="text-align: center"><img height="auto" src="https://picsum.photos/1920/1080.webp?t=1" width="500"></p><p></p><div data-type="horizontalRule"><hr></div><h2>Demo</h2><p>👉<a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://reactjs-tiptap-editor.vercel.app/">Demo</a></p><h2>Features</h2><ul><li><p>Use <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> components</p></li><li><p>Markdown support</p></li><li><p>TypeScript support</p></li><li><p>I18n support</p></li><li><p>React support</p></li><li><p>Slash Commands</p></li><li><p>Multi Column</p></li><li><p>TailwindCss</p></li><li><p>Support emoji</p></li><li><p>Support iframe</p></li></ul><h2>Installation</h2><pre><code>pnpm add reactjs-tiptap-editor</code></pre><p></p>`;
const DEFAULT = `<h1 style="text-align: center">Rc Tiptap Editor</h1><p>A modern WYSIWYG rich text editor based on <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://github.com/scrumpy/tiptap">tiptap</a> and <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> for Reactjs</p><p></p><p style="text-align: center"></p><p style="text-align: center"><img height="auto" src="https://picsum.photos/1920/1080.webp?t=1" width="500"></p><p></p><div data-type="horizontalRule"><hr></div><h2>Demo</h2><p>👉<a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://reactjs-tiptap-editor.vercel.app/">Demo</a></p><h2>Features</h2><ul><li><p>Use <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://ui.shadcn.com/">shadcn ui</a> components</p></li><li><p>Markdown support</p></li><li><p>TypeScript support</p></li><li><p>I18n support</p></li><li><p>React support</p></li><li><p>Slash Commands</p></li><li><p>Multi Column</p></li><li><p>TailwindCss</p></li><li><p>Support emoji</p></li><li><p>Support iframe</p></li></ul><h2>Installation</h2><pre><code>pnpm add reactjs-tiptap-editor</code></pre><p></p>`

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 (
<div
className='p-[24px] flex flex-col w-full max-w-screen-lg gap-[24px] mx-[auto] my-0'
className="p-[24px] flex flex-col w-full max-w-screen-lg gap-[24px] mx-[auto] my-0"
style={{
maxWidth: 1024,
margin: '40px auto',
Expand All @@ -155,7 +156,7 @@ function App() {
</div>

<RcTiptapEditor
output='html'
output="html"
content={content as any}
onChangeContent={onValueChange}
extensions={extensions}
Expand All @@ -174,7 +175,7 @@ function App() {
/>
)}
</div>
);
)
}

export default App;
export default App
27 changes: 9 additions & 18 deletions src/components/menus/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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),
},
}))
}
Expand Down Expand Up @@ -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
}
70 changes: 33 additions & 37 deletions src/components/menus/components/BubbleMenuText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,11 @@ function ItemA({ item, disabled, editor }: any) {
}

return (
<Fragment>
<Comp
{...item.componentProps}
editor={editor}
disabled={disabled || item?.componentProps?.disabled}
/>
</Fragment>
<Comp
{...item.componentProps}
editor={editor}
disabled={disabled || item?.componentProps?.disabled}
/>
)
}

Expand Down Expand Up @@ -63,40 +61,38 @@ function BubbleMenuText(props: IPropsBubbleMenuText) {
}, [props.disabled, props.editor, lang, t])

return (
<>
<BubbleMenu shouldShow={shouldShow} editor={props?.editor} tippyOptions={tippyOptions as any}>
{items?.length
? (
<div className="border border-neutral-200 dark:border-neutral-800 px-3 py-2 transition-all select-none pointer-events-auto shadow-sm rounded-sm w-auto bg-background">
<div className="flex items-center gap-[4px] flex-nowrap whitespace-nowrap h-[26px] justify-start relative">
{items?.map((item: any, key: any) => {
if (item?.type === 'divider') {
return (
<Separator
key={`bubbleMenu-divider-${key}`}
orientation="vertical"
className="mx-1 me-2 h-[16px]"
/>
)
}

<BubbleMenu shouldShow={shouldShow} editor={props?.editor} tippyOptions={tippyOptions as any}>
{items?.length
? (
<div className="border border-neutral-200 dark:border-neutral-800 px-3 py-2 transition-all select-none pointer-events-auto shadow-sm rounded-sm w-auto bg-background">
<div className="flex items-center gap-[4px] flex-nowrap whitespace-nowrap h-[26px] justify-start relative">
{items?.map((item: any, key: any) => {
if (item?.type === 'divider') {
return (
<ItemA
key={`bubbleMenu-text-${key}`}
item={item}
disabled={props.disabled}
editor={props.editor}
<Separator
key={`bubbleMenu-divider-${key}`}
orientation="vertical"
className="mx-1 me-2 h-[16px]"
/>
)
})}
</div>
}

return (
<ItemA
key={`bubbleMenu-text-${key}`}
item={item}
disabled={props.disabled}
editor={props.editor}
/>
)
})}
</div>
)
: (
<></>
)}
</BubbleMenu>
</>
</div>
)
: (
<></>
)}
</BubbleMenu>
)
}

Expand Down
18 changes: 9 additions & 9 deletions src/components/menus/components/TableBubbleMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().addColumnBefore()}
disabled={!props?.editor?.can()?.addColumnBefore?.()}
/>
<ActionButton
icon="BetweenHorizonalStart"
Expand All @@ -109,7 +109,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().addColumnAfter()}
disabled={!props?.editor?.can()?.addColumnAfter?.()}
/>
<ActionButton
icon="DeleteColumn"
Expand All @@ -118,7 +118,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().deleteColumn()}
disabled={!props?.editor?.can().deleteColumn?.()}
/>
<Separator orientation="vertical" className="mx-1 me-2 h-[16px]" />

Expand All @@ -129,7 +129,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().addRowBefore()}
disabled={!props?.editor?.can().addRowBefore?.()}
/>

<ActionButton
Expand All @@ -139,7 +139,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().addRowAfter()}
disabled={!props?.editor?.can()?.addRowAfter?.()}
/>
<ActionButton
icon="DeleteRow"
Expand All @@ -148,7 +148,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().deleteRow()}
disabled={!props?.editor?.can()?.deleteRow?.()}
/>
<Separator orientation="vertical" className="mx-1 me-2 h-[16px]" />
<ActionButton
Expand All @@ -158,7 +158,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().mergeCells()}
disabled={!props?.editor?.can()?.mergeCells?.()}
/>
<ActionButton
icon="TableCellsSplit"
Expand All @@ -167,7 +167,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().splitCell()}
disabled={!props?.editor?.can()?.splitCell?.()}
/>
<Separator orientation="vertical" className="mx-1 me-2 h-[16px]" />

Expand All @@ -186,7 +186,7 @@ function TableBubbleMenu(props: any) {
tooltip-options={{
sideOffset: 15,
}}
disabled={!props?.editor?.can().deleteTable()}
disabled={!props?.editor?.can()?.deleteTable?.()}
/>
</div>
</BubbleMenu>
Expand Down
Loading

0 comments on commit a4beb82

Please sign in to comment.