mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-01-17 13:38:37 +01:00
01dc30b81d
* chore: moved all ts files for examples to examples/ts * add: tsc to eject js and jsx output * example: add js transpiled examples * example: update example site to show both js and ts code * chore: fix yarn lint * fix(example): getAllExamplesPath
173 lines
4.2 KiB
TypeScript
173 lines
4.2 KiB
TypeScript
import React, { useMemo, useRef, useEffect } from 'react'
|
|
import { Slate, Editable, withReact, useSlate, useFocused } from 'slate-react'
|
|
import {
|
|
Editor,
|
|
Transforms,
|
|
Text,
|
|
createEditor,
|
|
Descendant,
|
|
Range,
|
|
} from 'slate'
|
|
import { css } from '@emotion/css'
|
|
import { withHistory } from 'slate-history'
|
|
|
|
import { Button, Icon, Menu, Portal } from './components'
|
|
|
|
const HoveringMenuExample = () => {
|
|
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
|
|
|
|
return (
|
|
<Slate editor={editor} initialValue={initialValue}>
|
|
<HoveringToolbar />
|
|
<Editable
|
|
renderLeaf={props => <Leaf {...props} />}
|
|
placeholder="Enter some text..."
|
|
onDOMBeforeInput={(event: InputEvent) => {
|
|
switch (event.inputType) {
|
|
case 'formatBold':
|
|
event.preventDefault()
|
|
return toggleMark(editor, 'bold')
|
|
case 'formatItalic':
|
|
event.preventDefault()
|
|
return toggleMark(editor, 'italic')
|
|
case 'formatUnderline':
|
|
event.preventDefault()
|
|
return toggleMark(editor, 'underlined')
|
|
}
|
|
}}
|
|
/>
|
|
</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.italic) {
|
|
children = <em>{children}</em>
|
|
}
|
|
|
|
if (leaf.underlined) {
|
|
children = <u>{children}</u>
|
|
}
|
|
|
|
return <span {...attributes}>{children}</span>
|
|
}
|
|
|
|
const HoveringToolbar = () => {
|
|
const ref = useRef<HTMLDivElement | null>()
|
|
const editor = useSlate()
|
|
const inFocus = useFocused()
|
|
|
|
useEffect(() => {
|
|
const el = ref.current
|
|
const { selection } = editor
|
|
|
|
if (!el) {
|
|
return
|
|
}
|
|
|
|
if (
|
|
!selection ||
|
|
!inFocus ||
|
|
Range.isCollapsed(selection) ||
|
|
Editor.string(editor, selection) === ''
|
|
) {
|
|
el.removeAttribute('style')
|
|
return
|
|
}
|
|
|
|
const domSelection = window.getSelection()
|
|
const domRange = domSelection.getRangeAt(0)
|
|
const rect = domRange.getBoundingClientRect()
|
|
el.style.opacity = '1'
|
|
el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`
|
|
el.style.left = `${
|
|
rect.left + window.pageXOffset - el.offsetWidth / 2 + rect.width / 2
|
|
}px`
|
|
})
|
|
|
|
return (
|
|
<Portal>
|
|
<Menu
|
|
ref={ref}
|
|
className={css`
|
|
padding: 8px 7px 6px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
top: -10000px;
|
|
left: -10000px;
|
|
margin-top: -6px;
|
|
opacity: 0;
|
|
background-color: #222;
|
|
border-radius: 4px;
|
|
transition: opacity 0.75s;
|
|
`}
|
|
onMouseDown={e => {
|
|
// prevent toolbar from taking focus away from editor
|
|
e.preventDefault()
|
|
}}
|
|
>
|
|
<FormatButton format="bold" icon="format_bold" />
|
|
<FormatButton format="italic" icon="format_italic" />
|
|
<FormatButton format="underlined" icon="format_underlined" />
|
|
</Menu>
|
|
</Portal>
|
|
)
|
|
}
|
|
|
|
const FormatButton = ({ format, icon }) => {
|
|
const editor = useSlate()
|
|
return (
|
|
<Button
|
|
reversed
|
|
active={isMarkActive(editor, format)}
|
|
onClick={() => toggleMark(editor, format)}
|
|
>
|
|
<Icon>{icon}</Icon>
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
const initialValue: Descendant[] = [
|
|
{
|
|
type: 'paragraph',
|
|
children: [
|
|
{
|
|
text: 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ',
|
|
},
|
|
{ text: 'bold', bold: true },
|
|
{ text: ', ' },
|
|
{ text: 'italic', italic: true },
|
|
{ text: ', or anything else you might want to do!' },
|
|
],
|
|
},
|
|
{
|
|
type: 'paragraph',
|
|
children: [
|
|
{ text: 'Try it out yourself! Just ' },
|
|
{ text: 'select any piece of text and the menu will appear', bold: true },
|
|
{ text: '.' },
|
|
],
|
|
},
|
|
]
|
|
|
|
export default HoveringMenuExample
|