1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-24 01:02:31 +01:00
slate/site/examples/check-lists.tsx
Sunny Hirai a72958e6c3
Improved Types (#4119)
* Experimental release to see if CustomTypes holds up through a publish

* Add experimental release script

* Fix lint

* v0.60.5-alpha.0

* Allow null properties in setNodes

* v0.60.6-alpha.0

* Revert null properties on Transforms.setNodes

* v0.60.7-alpha.0

* Update examples to use custom Element and Text with discriminated unions

* Add documentation for using TypeScript improvements

* Be explicit about typescript version in package.json

* Force lerna bootstrap to fix build issues on CI and fix a few type examples

* Add slate devDependencies with * back

* v0.60.7

* Switch to a non prerelease version to fix lerna not linking in root

* Add documentation for not using prerelease versions and on how to create experimental releases

* Try removing lerna bootstrap and see if it works
2021-03-11 11:48:31 -08:00

194 lines
4.3 KiB
TypeScript

import React, { useState, useMemo, useCallback } from 'react'
import {
Slate,
Editable,
withReact,
useSlateStatic,
useReadOnly,
ReactEditor,
} from 'slate-react'
import {
Node,
Editor,
Transforms,
Range,
Point,
createEditor,
Descendant,
Element as SlateElement,
} from 'slate'
import { css } from 'emotion'
import { withHistory } from 'slate-history'
const initialValue: SlateElement[] = [
{
type: 'paragraph',
children: [
{
text:
'With Slate you can build complex block types that have their own embedded content and behaviors, like rendering checkboxes inside check list items!',
},
],
},
{
type: 'check-list-item',
checked: true,
children: [{ text: 'Slide to the left.' }],
},
{
type: 'check-list-item',
checked: true,
children: [{ text: 'Slide to the right.' }],
},
{
type: 'check-list-item',
checked: false,
children: [{ text: 'Criss-cross.' }],
},
{
type: 'check-list-item',
checked: true,
children: [{ text: 'Criss-cross!' }],
},
{
type: 'check-list-item',
checked: false,
children: [{ text: 'Cha cha real smooth…' }],
},
{
type: 'check-list-item',
checked: false,
children: [{ text: "Let's go to work!" }],
},
{
type: 'paragraph',
children: [{ text: 'Try it out for yourself!' }],
},
]
const CheckListsExample = () => {
const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, [])
const editor = useMemo(
() => withChecklists(withHistory(withReact(createEditor()))),
[]
)
return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Editable
renderElement={renderElement}
placeholder="Get to work…"
spellCheck
autoFocus
/>
</Slate>
)
}
const withChecklists = editor => {
const { deleteBackward } = editor
editor.deleteBackward = (...args) => {
const { selection } = editor
if (selection && Range.isCollapsed(selection)) {
const [match] = Editor.nodes(editor, {
match: n =>
!Editor.isEditor(n) &&
SlateElement.isElement(n) &&
n.type === 'check-list-item',
})
if (match) {
const [, path] = match
const start = Editor.start(editor, path)
if (Point.equals(selection.anchor, start)) {
const newProperties: Partial<SlateElement> = {
type: 'paragraph',
}
Transforms.setNodes(editor, newProperties, {
match: n =>
!Editor.isEditor(n) &&
SlateElement.isElement(n) &&
n.type === 'check-list-item',
})
return
}
}
}
deleteBackward(...args)
}
return editor
}
const Element = props => {
const { attributes, children, element } = props
switch (element.type) {
case 'check-list-item':
return <CheckListItemElement {...props} />
default:
return <p {...attributes}>{children}</p>
}
}
const CheckListItemElement = ({ attributes, children, element }) => {
const editor = useSlateStatic()
const readOnly = useReadOnly()
const { checked } = element
return (
<div
{...attributes}
className={css`
display: flex;
flex-direction: row;
align-items: center;
& + & {
margin-top: 0;
}
`}
>
<span
contentEditable={false}
className={css`
margin-right: 0.75em;
`}
>
<input
type="checkbox"
checked={checked}
onChange={event => {
const path = ReactEditor.findPath(editor, element)
const newProperties: Partial<SlateElement> = {
checked: event.target.checked,
}
Transforms.setNodes(editor, newProperties, { at: path })
}}
/>
</span>
<span
contentEditable={!readOnly}
suppressContentEditableWarning
className={css`
flex: 1;
opacity: ${checked ? 0.666 : 1};
text-decoration: ${checked ? 'none' : 'line-through'};
&:focus {
outline: none;
}
`}
>
{children}
</span>
</div>
)
}
export default CheckListsExample