diff --git a/docs/.vitepress/locale.ts b/docs/.vitepress/locale.ts index 09da974..3767ac7 100644 --- a/docs/.vitepress/locale.ts +++ b/docs/.vitepress/locale.ts @@ -54,6 +54,10 @@ export function getLocaleConfig(lang: string) { text: t('Bubble Menu'), link: `${urlPrefix}/guide/bubble-menu`, }, + { + text: t('Customize'), + link: `${urlPrefix}/guide/customize.md`, + }, { text: t('Internationalization'), link: `${urlPrefix}/guide/internationalization`, diff --git a/docs/guide/customize.md b/docs/guide/customize.md new file mode 100644 index 0000000..ce95124 --- /dev/null +++ b/docs/guide/customize.md @@ -0,0 +1,241 @@ +--- +description: How to customize the Reactjs-Tiptap-Editor. + +next: + text: Customize + link: /guide/customize.md +--- + +# Customizing Reactjs-Tiptap-Editor + +There are 3 main ways to customize the Reactjs-Tiptap-Editor: + +### 1. Extensions + +Add extensions to enhance editor functionality: + +```tsx +import React from 'react' +import RichTextEditor, { BaseKit, Bold, BulletList, Heading, Italic } from 'reactjs-tiptap-editor' +import 'reactjs-tiptap-editor/style.css' + +const extensions = [BaseKit, Heading, Italic, Bold, BulletList] + +export default function App() { + return ( + + ) +} +``` + +### 2. Editor Options + +The `useEditorOptions` property provides configuration options for customizing the editor's behavior with the `UseEditorOptions` interface. + +### Interface + +```tsx +interface UseEditorOptions { + /** Called when editor content is updated */ + onUpdate?: (props: { editor: Editor, transaction: Transaction }) => void + + /** Called when editor selection changes */ + onSelectionUpdate?: (props: { editor: Editor, transaction: Transaction }) => void + + /** Called when editor gains focus */ + onFocus?: (props: { editor: Editor, event: FocusEvent }) => void + + /** Called when editor loses focus */ + onBlur?: (props: { editor: Editor, event: FocusEvent }) => void + + /** Called when editor transaction is created */ + onTransaction?: (props: { editor: Editor, transaction: Transaction }) => void + + /** Called when editor is created */ + onCreate?: (props: { editor: Editor }) => void + + /** Called before editor is destroyed */ + onDestroy?: () => void + + /** Initial editor state */ + editorState?: string + + /** Enable or disable parsing content */ + enableInputRules?: boolean + enablePasteRules?: boolean + + /** Enable or disable content editing */ + editable?: boolean + + /** Custom autofocus behavior */ + autofocus?: boolean | 'start' | 'end' | number + + /** Editor view props */ + editorProps?: EditorProps +} +``` + +Example with editor options: + +```tsx +import React from 'react' +import RichTextEditor, { BaseKit, type UseEditorOptions } from 'reactjs-tiptap-editor' +import 'reactjs-tiptap-editor/style.css' + +const extensions = [BaseKit] + +const customOptions: UseEditorOptions = { + onUpdate: ({ editor }) => console.log('Content updated:', editor.getText()), + onSelectionUpdate: ({ editor }) => console.log('Selection updated:', editor.getText()), + onFocus: () => console.log('Editor focused'), + onBlur: () => console.log('Editor blurred'), + editable: true, + autofocus: 'start', +} + +export default function App() { + return ( + + ) +} +``` + +### 3. Accessing the Editor Instance + +There are two ways to access the editor instance: +Direct access to the editor instance using `useRef` or `useEditorState` + +#### Using useRef: + +```tsx +import React, { useRef } from 'react' +import RichTextEditor, { BaseKit, type Editor } from 'reactjs-tiptap-editor' +import 'reactjs-tiptap-editor/style.css' + +const extensions = [BaseKit] + +export default function App() { + const editorRef = useRef<{ editor: Editor | null }>(null) + + const handleCustomButton = () => { + if (editorRef.current?.editor) { + const text = editorRef.current.editor.getText() + console.log('Current selected text:', text) + } + } + + return ( +
+ + +
+ ) +} +``` + +#### Using useEditorState: + +```tsx +import RichTextEditor, { BaseKit, useEditorState } from 'reactjs-tiptap-editor' +import 'reactjs-tiptap-editor/style.css' + +const extensions = [BaseKit] + +export default function App() { + const { isReady, editor, editorRef } = useEditorState() + + const handleCustomButton = () => { + if (editor) { + const text = editor.getText() + console.log('Current text:', text) + } + } + + return ( +
+ + {isReady && ( + + )} +
+ ) +} +``` + +### Example: Custom Bubble Menu with Selection Text + +```tsx +import RichTextEditor, { BaseKit, BubbleMenu, useEditorState } from 'reactjs-tiptap-editor' +import type { Editor } from 'reactjs-tiptap-editor' +import 'reactjs-tiptap-editor/style.css' + +interface CustomBubbleMenuProps { + editor: Editor +} + +function CustomBubbleMenu({ editor }: CustomBubbleMenuProps) { + if (!editor) + return null + + const handlePrintSelection = () => { + const { from, to } = editor.state.selection + const text = editor.state.doc.textBetween(from, to) + console.log('Selected text:', text) + } + + return ( + + + + ) +} + +const extensions = [BaseKit] + +export default function App() { + const { isReady, editor, editorRef } = useEditorState() + + return ( +
+ + {isReady && editor && } +
+ ) +} +``` diff --git a/src/hooks/useEditorState.tsx b/src/hooks/useEditorState.tsx new file mode 100644 index 0000000..19a1edd --- /dev/null +++ b/src/hooks/useEditorState.tsx @@ -0,0 +1,23 @@ +import type { Editor } from '@tiptap/core' +import { useEffect, useRef, useState } from 'react' + +export interface UseEditorStateReturn { + isReady: boolean + editor: Editor | null + editorRef: React.MutableRefObject<{ editor: Editor | null }> +} + +export function useEditorState(): UseEditorStateReturn { + const editorRef = useRef<{ editor: Editor | null }>({ editor: null }) + const [isReady, setIsReady] = useState(false) + const [editor, setEditor] = useState(null) + + useEffect(() => { + if (editorRef.current?.editor) { + setIsReady(true) + setEditor(editorRef.current.editor) + } + }, [editorRef, editorRef.current?.editor]) + + return { isReady, editor, editorRef } +} diff --git a/src/index.ts b/src/index.ts index 0abff3c..8fe11bb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,5 +4,11 @@ export * from '@/extensions' export { default } from '@/components/RichTextEditor' import locale, { en, pt_BR, vi, zh_CN } from './locales' +import { useEditorState } from '@/hooks/useEditorState' export { locale, en, vi, zh_CN, pt_BR } + +export type { UseEditorStateReturn } from '@/hooks/useEditorState' +export { useEditorState } +export type { Editor, UseEditorOptions } from '@tiptap/react' +export { BubbleMenu } from '@tiptap/react'