1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-01-17 13:38:37 +01:00
slate/site/examples/ts/hovering-toolbar.tsx
Ravi Lamkoti 01dc30b81d
Add Javascript Examples Support (#5722)
* 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
2024-09-26 00:24:11 -07:00

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