1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-24 01:02:31 +01:00
slate/site/examples/images.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

168 lines
3.8 KiB
TypeScript

import React, { useState, useMemo } from 'react'
import imageExtensions from 'image-extensions'
import isUrl from 'is-url'
import {
Node,
Transforms,
createEditor,
Element as SlateElement,
Descendant,
} from 'slate'
import {
Slate,
Editable,
useSlateStatic,
useSelected,
useFocused,
withReact,
} from 'slate-react'
import { withHistory } from 'slate-history'
import { css } from 'emotion'
import { Button, Icon, Toolbar } from '../components'
import { ImageElement } from './custom-types'
const ImagesExample = () => {
const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo(
() => withImages(withHistory(withReact(createEditor()))),
[]
)
return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
<Toolbar>
<InsertImageButton />
</Toolbar>
<Editable
renderElement={props => <Element {...props} />}
placeholder="Enter some text..."
/>
</Slate>
)
}
const withImages = editor => {
const { insertData, isVoid } = editor
editor.isVoid = element => {
return element.type === 'image' ? true : isVoid(element)
}
editor.insertData = data => {
const text = data.getData('text/plain')
const { files } = data
if (files && files.length > 0) {
for (const file of files) {
const reader = new FileReader()
const [mime] = file.type.split('/')
if (mime === 'image') {
reader.addEventListener('load', () => {
const url = reader.result
insertImage(editor, url)
})
reader.readAsDataURL(file)
}
}
} else if (isImageUrl(text)) {
insertImage(editor, text)
} else {
insertData(data)
}
}
return editor
}
const insertImage = (editor, url) => {
const text = { text: '' }
const image: ImageElement = { type: 'image', url, children: [text] }
Transforms.insertNodes(editor, image)
}
const Element = props => {
const { attributes, children, element } = props
switch (element.type) {
case 'image':
return <Image {...props} />
default:
return <p {...attributes}>{children}</p>
}
}
const Image = ({ attributes, children, element }) => {
const selected = useSelected()
const focused = useFocused()
return (
<div {...attributes}>
<div contentEditable={false}>
<img
src={element.url}
className={css`
display: block;
max-width: 100%;
max-height: 20em;
box-shadow: ${selected && focused ? '0 0 0 3px #B4D5FF' : 'none'};
`}
/>
</div>
{children}
</div>
)
}
const InsertImageButton = () => {
const editor = useSlateStatic()
return (
<Button
onMouseDown={event => {
event.preventDefault()
const url = window.prompt('Enter the URL of the image:')
if (!url) return
insertImage(editor, url)
}}
>
<Icon>image</Icon>
</Button>
)
}
const isImageUrl = url => {
if (!url) return false
if (!isUrl(url)) return false
const ext = new URL(url).pathname.split('.').pop()
return imageExtensions.includes(ext)
}
const initialValue: SlateElement[] = [
{
type: 'paragraph',
children: [
{
text:
'In addition to nodes that contain editable text, you can also create other types of nodes, like images or videos.',
},
],
},
{
type: 'image',
url: 'https://source.unsplash.com/kFrdX5IeQzI',
children: [{ text: '' }],
},
{
type: 'paragraph',
children: [
{
text:
'This example shows images in action. It features two ways to add images. You can either add an image via the toolbar icon above, or if you want in on a little secret, copy an image URL to your keyboard and paste it anywhere in the editor!',
},
],
},
]
export default ImagesExample