Skip to content

Instantly share code, notes, and snippets.

@intrnl
Created January 26, 2023 00:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save intrnl/2d8d2914d90ac6cfba78ddf4a59ee841 to your computer and use it in GitHub Desktop.
Save intrnl/2d8d2914d90ac6cfba78ddf4a59ee841 to your computer and use it in GitHub Desktop.
Material UI v4 Rich Text Editor component
import React, {type ReactNode} from 'react'
import {type CreateEditorStateProps} from 'remirror'
import {
BoldExtension,
ItalicExtension,
UnderlineExtension,
BulletListExtension,
OrderedListExtension,
HardBreakExtension,
} from 'remirror/extensions'
import {
useActive,
useCommands,
useRemirror,
EditorComponent,
PlaceholderExtension,
Remirror,
type RemirrorProps,
} from '@remirror/react'
import {Button, Tooltip, type ButtonProps} from '@material-ui/core'
import {type SvgIconComponent} from '@material-ui/icons'
import FormatBoldIcon from '@material-ui/icons/FormatBold'
import FormatItalicIcon from '@material-ui/icons/FormatItalic'
import FormatUnderlinedIcon from '@material-ui/icons/FormatUnderlined'
import FormatListBulletedIcon from '@material-ui/icons/FormatListBulleted'
import FormatListNumberedIcon from '@material-ui/icons/FormatListNumbered'
import {clsx} from '~/utils/helpers'
import './rte.css'
export interface RichTextEditorProps
extends Pick<CreateEditorStateProps, 'stringHandler'>,
Pick<RemirrorProps, 'initialContent' | 'editable' | 'autoFocus' | 'hooks'> {
placeholder?: string
children?: ReactNode
}
const RichTextEditor = (props: RichTextEditorProps) => {
const {placeholder, stringHandler, children, ...rest} = props
const {manager} = useRemirror({
stringHandler,
extensions: React.useCallback(
() => [
new PlaceholderExtension({placeholder}),
new HardBreakExtension(),
new BoldExtension(),
new ItalicExtension(),
new UnderlineExtension(),
new BulletListExtension(),
new OrderedListExtension(),
],
[placeholder]
),
})
return (
<div className="mui-rte">
<Remirror manager={manager} {...rest}>
<div className="mui-rte-toolbar">
<ToggleBoldButton />
<ToggleItalicButton />
<ToggleUnderlineButton />
<div className="mx-1 border-r border-black/10" />
<ToggleBulletedListButton />
<ToggleOrderedListButton />
</div>
<EditorComponent />
{children}
</Remirror>
</div>
)
}
export default RichTextEditor
const ToggleBoldButton = () => {
const {toggleBold} = useCommands()
const active = useActive().bold()
const enabled = toggleBold.enabled()
return (
<ToggleButton
label="Bold"
icon={FormatBoldIcon}
selected={active}
disabled={!enabled}
onClick={() => toggleBold()}
/>
)
}
const ToggleItalicButton = () => {
const {toggleItalic} = useCommands()
const active = useActive().italic()
const enabled = toggleItalic.enabled()
return (
<ToggleButton
label="Italic"
icon={FormatItalicIcon}
selected={active}
disabled={!enabled}
onClick={() => toggleItalic()}
/>
)
}
const ToggleUnderlineButton = () => {
const {toggleUnderline} = useCommands()
const active = useActive().underline()
const enabled = toggleUnderline.enabled()
return (
<ToggleButton
label="Underline"
icon={FormatUnderlinedIcon}
selected={active}
disabled={!enabled}
onClick={() => toggleUnderline()}
/>
)
}
const ToggleBulletedListButton = () => {
const {toggleBulletList} = useCommands()
const active = useActive().bulletList()
const enabled = toggleBulletList.enabled()
return (
<ToggleButton
label="Bulleted list"
icon={FormatListBulletedIcon}
selected={active}
disabled={!enabled}
onClick={() => toggleBulletList()}
/>
)
}
const ToggleOrderedListButton = () => {
const {toggleOrderedList} = useCommands()
const active = useActive().orderedList()
const enabled = toggleOrderedList.enabled()
return (
<ToggleButton
label="Numbered list"
icon={FormatListNumberedIcon}
selected={active}
disabled={!enabled}
onClick={() => toggleOrderedList()}
/>
)
}
interface ToggleButtonProps extends Pick<ButtonProps, 'disabled' | 'onClick'> {
label: string
icon: SvgIconComponent
selected?: boolean
}
const ToggleButton = (props: ToggleButtonProps) => {
const {label, icon: Icon, selected, disabled, onClick} = props
return (
<Tooltip title={label}>
<Button
disabled={disabled}
onClick={onClick}
className={clsx(['h-6 w-6 min-w-0 transition-none', selected && 'bg-blue-50'])}
>
<Icon fontSize="small" className={clsx([selected && 'text-blue-800'])} />
</Button>
</Tooltip>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment