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.
A simple Typescript-forward utility that spits out Editor.js tools for React components. You may need to include relevant Context providers for your application as wrappers around whatever <Component> is rendered by the factory
// 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
}
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 },
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment