1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-23 16:55:23 +01:00
slate/site/examples/iframe.tsx

159 lines
3.9 KiB
TypeScript

import React, { useCallback, useMemo, useState } from 'react'
import { createPortal } from 'react-dom'
import isHotkey from 'is-hotkey'
import { Editable, withReact, useSlate, Slate, ReactEditor } from 'slate-react'
import { Editor, createEditor, Descendant } from 'slate'
import { withHistory } from 'slate-history'
import { Button, Icon, Toolbar } from '../components'
const HOTKEYS = {
'mod+b': 'bold',
'mod+i': 'italic',
'mod+u': 'underline',
'mod+`': 'code',
}
const IFrameExample = () => {
const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(
({ attributes, children }) => <p {...attributes}>{children}</p>,
[]
)
const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
const handleBlur = useCallback(() => ReactEditor.deselect(editor), [editor])
return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Toolbar>
<MarkButton format="bold" icon="format_bold" />
<MarkButton format="italic" icon="format_italic" />
<MarkButton format="underline" icon="format_underlined" />
<MarkButton format="code" icon="code" />
</Toolbar>
<IFrame onBlur={handleBlur}>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
placeholder="Enter some rich text…"
spellCheck
autoFocus
onKeyDown={event => {
for (const hotkey in HOTKEYS) {
if (isHotkey(hotkey, event as any)) {
event.preventDefault()
const mark = HOTKEYS[hotkey]
toggleMark(editor, mark)
}
}
}}
/>
</IFrame>
</Slate>
)
}
const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format)
if (isActive) {
Editor.removeMark(editor, format)
} else {
Editor.addMark(editor, format, true)
}
}
const isMarkActive = (editor, format) => {
const marks = Editor.marks(editor)
return marks ? marks[format] === true : false
}
const Leaf = ({ attributes, children, leaf }) => {
if (leaf.bold) {
children = <strong>{children}</strong>
}
if (leaf.code) {
children = <code>{children}</code>
}
if (leaf.italic) {
children = <em>{children}</em>
}
if (leaf.underline) {
children = <u>{children}</u>
}
return <span {...attributes}>{children}</span>
}
const MarkButton = ({ format, icon }) => {
const editor = useSlate()
return (
<Button
active={isMarkActive(editor, format)}
onMouseDown={event => {
event.preventDefault()
toggleMark(editor, format)
}}
>
<Icon>{icon}</Icon>
</Button>
)
}
const IFrame = ({ children, ...props }) => {
const [contentRef, setContentRef] = useState(null)
const mountNode =
contentRef &&
contentRef.contentWindow &&
contentRef.contentWindow.document.body
return (
<iframe {...props} ref={setContentRef}>
{mountNode && createPortal(React.Children.only(children), mountNode)}
</iframe>
)
}
const initialValue: Descendant[] = [
{
type: 'paragraph',
children: [
{
text: 'In this example, the document gets rendered into a controlled ',
},
{ text: '<iframe>', code: true },
{
text: '. This is ',
},
{
text: 'particularly',
italic: true,
},
{
text:
' useful, when you need to separate the styles for your editor contents from the ones addressing your UI.',
},
],
},
{
type: 'paragraph',
children: [
{
text: 'This also the only reliable method to preview any ',
},
{
text: 'media queries',
bold: true,
},
{
text: ' in your CSS.',
},
],
},
]
export default IFrameExample