Skip to content

Commit

Permalink
Merge pull request #96 from javier1234559/main
Browse files Browse the repository at this point in the history
  • Loading branch information
hunghg255 authored Nov 3, 2024
2 parents da82fe5 + a221881 commit 53c4eca
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/.vitepress/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
241 changes: 241 additions & 0 deletions docs/guide/customize.md
Original file line number Diff line number Diff line change
@@ -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 (
<RichTextEditor
content=""
output="html"
extensions={extensions}
/>
)
}
```

### 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 (
<RichTextEditor
content=""
output="html"
useEditorOptions={customOptions}
extensions={extensions}
/>
)
}
```

### 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 (
<div>
<RichTextEditor
content=""
output="html"
ref={editorRef}
extensions={extensions}
/>
<button type="button" onClick={handleCustomButton}>
Custom Action
</button>
</div>
)
}
```

#### 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 (
<div>
<RichTextEditor
content=""
output="html"
ref={editorRef}
extensions={extensions}
/>
{isReady && (
<button type="button" onClick={handleCustomButton}>
Custom Action
</button>
)}
</div>
)
}
```

### 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 (
<BubbleMenu
editor={editor}
>
<button
type="button"
onClick={handlePrintSelection}
>
Print Selection
</button>
</BubbleMenu>
)
}

const extensions = [BaseKit]

export default function App() {
const { isReady, editor, editorRef } = useEditorState()

return (
<div>
<RichTextEditor
ref={editorRef}
content=""
output="html"
extensions={extensions}
hideBubble
/>
{isReady && editor && <CustomBubbleMenu editor={editor} />}
</div>
)
}
```
23 changes: 23 additions & 0 deletions src/hooks/useEditorState.tsx
Original file line number Diff line number Diff line change
@@ -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<Editor | null>(null)

useEffect(() => {
if (editorRef.current?.editor) {
setIsReady(true)
setEditor(editorRef.current.editor)
}
}, [editorRef, editorRef.current?.editor])

return { isReady, editor, editorRef }
}
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

0 comments on commit 53c4eca

Please sign in to comment.