Skip to content

Instantly share code, notes, and snippets.

@abc0990cba
Forked from mmmeff/00-ReactToolFactory.tsx
Created March 13, 2024 15:23
Show Gist options
  • Save abc0990cba/1d91c17c1ad444c4d7da08341b1641c9 to your computer and use it in GitHub Desktop.
Save abc0990cba/1d91c17c1ad444c4d7da08341b1641c9 to your computer and use it in GitHub Desktop.

Revisions

  1. @mmmeff mmmeff revised this gist Sep 27, 2023. No changes.
  2. @mmmeff mmmeff revised this gist Mar 29, 2023. 2 changed files with 0 additions and 0 deletions.
    File renamed without changes.
    File renamed without changes.
  3. @mmmeff mmmeff created this gist Mar 29, 2023.
    24 changes: 24 additions & 0 deletions ExampleUsageTool.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    import makeReactTool from './ReactToolFactory'
    import type { ReactEditorToolProps } from './ReactToolFactory'

    interface ToolData {
    clickCount: number
    }
    interface ToolConfig {}

    function Component({ onDataChange, data, readOnly }: ReactEditorToolProps<ToolData>) {
    return (
    <button onClick={() => onDataChange({ clickCount: data.clickCount + 1 })}>
    Clicked {props.data.clickCount} Time(s)
    </button>
    )
    }

    export default makeReactTool<ToolData, ToolConfig>(
    Component,
    {
    toolbox: { title: 'Tool!', icon: '<svg>...</svg>' },
    isReadOnlySupported: true,
    defaultData: { clickCount: 0 },
    }
    )
    86 changes: 86 additions & 0 deletions ReactToolFactory.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    // Based on https://walkthrough.so/pblc/QCawSCKwOQLn/creating-a-custom-editorjs-block-tool-with-react

    import type {
    API,
    BlockAPI,
    BlockTool,
    BlockToolConstructorOptions,
    } from '@editorjs/editorjs'
    import ReactDOM from 'react-dom'

    export interface ReactEditorToolProps<TData> {
    onDataChange: (data: TData) => unknown
    readOnly: boolean
    data: TData
    }

    interface ReactToolConfig<TData, TConfig> {
    toolbox: {
    icon: string
    title: string
    }
    isReadOnlySupported: boolean
    defaultData: TData
    }

    export default function makeReactTool<
    TData extends object,
    TConfig extends object
    >(
    Component: React.FC<ReactEditorToolProps<TData>>,
    configuration: ReactToolConfig<TData, TConfig>
    ) {
    class ReactTool implements BlockTool {
    static toolbox = configuration.toolbox
    static isReadOnlySupported = configuration.isReadOnlySupported

    private readOnly: boolean = false
    private data: TData
    private editorAPI: API
    private blockAPI: BlockAPI | undefined
    private config: TConfig | undefined
    private wrapperEl: HTMLDivElement | null = null

    constructor({
    data,
    config,
    api,
    block,
    readOnly,
    }: BlockToolConstructorOptions<TData, TConfig>) {
    this.config = config
    this.readOnly = readOnly
    this.editorAPI = api
    this.blockAPI = block
    this.data = data ?? configuration.defaultData
    }

    render() {
    const rootNode = document.createElement('div')
    this.wrapperEl = rootNode

    const onDataChange = (newData: TData) => {
    this.data = {
    ...newData,
    }
    }

    ReactDOM.render(
    <Component
    onDataChange={onDataChange}
    readOnly={this.readOnly}
    data={this.data}
    />,
    rootNode
    )

    return this.wrapperEl
    }

    save() {
    return this.data
    }
    }

    return ReactTool
    }