1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-30 02:19:52 +02:00

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
This commit is contained in:
Sunny Hirai
2021-03-11 11:48:31 -08:00
committed by GitHub
parent 5267f07175
commit a72958e6c3
37 changed files with 4006 additions and 116 deletions

View File

@@ -80,6 +80,10 @@ If you only want to run a specific test or tests, you can run `yarn test --fgrep
## Publishing Releases ## Publishing Releases
**Important**: When creating releases using Lerna with the instructions below, you will be given choices around how to increase version numbers. You should always use a `major`, `minor` or `patch` release and must never use a `prerelease`. If a prerelease is used, the root package will not link to the packages in the `packages` directory creating hard to diagnose issues.
### Publishing Normal `@latest` Release
Since we use [Lerna](https://lerna.js.org) to manage the Slate packages this is fairly easy, just run: Since we use [Lerna](https://lerna.js.org) to manage the Slate packages this is fairly easy, just run:
```shell ```shell
@@ -90,7 +94,7 @@ And follow the prompts Lerna gives you.
Note that this will automatically run the prelease script first that will build, test and lint before attempting to publish. Note that this will automatically run the prelease script first that will build, test and lint before attempting to publish.
## Publishing `@next` Releases ### Publishing `@next` Release
If we are unsure as to the stability of a release because there are significant changes and/or particularly complex changes, release with the `@next` tag. If we are unsure as to the stability of a release because there are significant changes and/or particularly complex changes, release with the `@next` tag.
@@ -100,7 +104,15 @@ yarn release:next
And follow the prompts Lerna gives you. And follow the prompts Lerna gives you.
## Running Prerelease Script ### Publishing `@experimental` Release
If you need to create an experimental release to see how a published package will behave during an actual publish, release with the `@experimental` tag. End users should have no expectation that an `@experimental` release will be usable.
```shell
yarn release:experimental
```
### Running Prerelease Script
If we want to make sure that Slate code follows the preparations for a release but without actually publishing, run: If we want to make sure that Slate code follows the preparations for a release but without actually publishing, run:

View File

@@ -22,6 +22,7 @@
- [Rendering](./concepts/08-rendering.md) - [Rendering](./concepts/08-rendering.md)
- [Serializing](./concepts/09-serializing.md) - [Serializing](./concepts/09-serializing.md)
- [Normalizing](./concepts/10-normalizing.md) - [Normalizing](./concepts/10-normalizing.md)
- [TypeScript](./concepts/11-typescript.md)
- [Migrating](./concepts/XX-migrating.md) - [Migrating](./concepts/XX-migrating.md)
## API ## API

View File

@@ -0,0 +1,91 @@
# TypeScript
Slate supports typing of one Slate document model (eg. one set of custom `Element` and `Text` types).
If you need to support more than one document model, see the section Multiple Document Models.
## Defining Custom Element and Text Types
To define a custom `Element` or `Text` type, extend the `CustomTypes` interface in the `slate` module like this.
```ts
declare module 'slate' {
interface CustomTypes {
Element: { type: 'paragraph'; children: Descendant[] }
Text: { text: string; bold: boolean; italic: boolean }
}
}
```
## Recommended Custom Element and Text
While you can define types directly in the `CustomTypes` interface, best practice is to:
- define and export each custom `Element`/`Text` type
- merge these into and export `CustomElement`/`CustomText` type
- Use the `CustomElement`/`CustomText` in the `CustomTypes` definition
These are best practices because elsewhere in your code, you may want to directly reference a specific `Element` type like a bullet or image.
Using best practices, the custom types will look something like:
```ts
export type ParagraphElement = {
type: 'paragraph'
children: Descendant
}
export type HeadingElement = {
type: 'paragraph'
level: number
children: Descendant
}
export type CustomElement = ParagraphElement | HeadingElement
export type FormattedText = { text: 'string'; bold: boolean; italic: boolean }
export type CustomText = FormattedText
declare module 'slate' {
interface CustomTypes {
Element: CustomElement
Text: CustomText
}
}
```
In this example, `CustomText` is equal to `FormattedText` but in a real editor, there can be more types of text like text in a code block which may not allow formatting for example.
## Why Is The Type Definition Unusual
Because it gets asked often, this section explains why Slate's type definition is atypical.
Slate needs to support a feature called type discrimination which is available when using union types (e.g. `ParagraphElement | HeadingElement`). This allows a user to narrow a type. If presented with code like `if (node.type === 'paragraph') { ... }` the inside of the block, will narrow the type of node to `ParagraphElement`.
Slate also needs a way to allow developers to get their custom types into Slate. This is done through declaration merging which is a feature of an `interface`.
Slate combines a union type and an interface to deliver this feature.
For more information see [Proposal: Add Custom TypeScript Types to Slate](https://github.com/ianstormtaylor/slate/issues/3725)
## Multiple Document Models
At the moment, Slate only supports types for a single document model at a time. For example, it cannot support a full Rich Text Editor for editing documents while also having a less featured Editor for editing comments.
Slate's TypeScript support was designed this way because some improved typing support was better than none. The goal is to also support typing for multiple editor definitions but this will depend on community support.
One workaround for support multiple document models is to create each editor in a separate package and then import them. This hasn't been tested but should work.
## Extending Other Types
Currently there is also support for extending:
- `Editor`
- `Selection`
- `Range`
- `Point`
Feel free to extend these types but extended these types should be considered experimental. We are actively looking for better ways to implement this.
For some examples of how to extend these types, see `packages/slate-react/src/custom-types.ts` in the slate repository.

View File

@@ -1,6 +1,6 @@
{ {
"lerna": "2.7.1", "lerna": "2.7.1",
"version": "0.60.4", "version": "0.60.7",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true "useWorkspaces": true
} }

View File

@@ -19,8 +19,10 @@
"prerelease": "cross-env NODE_ENV=production yarn build:rollup && yarn test && yarn lint", "prerelease": "cross-env NODE_ENV=production yarn build:rollup && yarn test && yarn lint",
"release:publish:latext": "lerna publish", "release:publish:latext": "lerna publish",
"release:publish:next": "lerna publish --dist-tag next", "release:publish:next": "lerna publish --dist-tag next",
"release:publish:experimental": "lerna publish --dist-tag experimental",
"release:latest": "yarn prerelease && lerna publish", "release:latest": "yarn prerelease && lerna publish",
"release:next": "yarn prerelease && lerna publish --dist-tag next", "release:next": "yarn prerelease && lerna publish --dist-tag next",
"release:experimental": "yarn prerelease && lerna publish --dist-tag experimental",
"serve": "cd ./site && next", "serve": "cd ./site && next",
"start": "npm-run-all --parallel --print-label watch serve", "start": "npm-run-all --parallel --print-label watch serve",
"test": "mocha --require ./config/babel/register.cjs ./packages/*/test/index.js", "test": "mocha --require ./config/babel/register.cjs ./packages/*/test/index.js",
@@ -91,6 +93,6 @@
"slate-hyperscript": "*", "slate-hyperscript": "*",
"slate-react": "*", "slate-react": "*",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"typescript": "^3.7.2" "typescript": "3.9.7"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "slate-history", "name": "slate-history",
"description": "An operation-based history implementation for Slate editors.", "description": "An operation-based history implementation for Slate editors.",
"version": "0.60.4", "version": "0.60.7",
"license": "MIT", "license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git", "repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js", "main": "dist/index.js",
@@ -18,8 +18,8 @@
"is-plain-object": "^3.0.0" "is-plain-object": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"slate": "^0.60.4", "slate": "^0.60.7",
"slate-hyperscript": "^0.60.4" "slate-hyperscript": "^0.60.7"
}, },
"peerDependencies": { "peerDependencies": {
"slate": ">=0.55.0" "slate": ">=0.55.0"

View File

@@ -1,7 +1,7 @@
{ {
"name": "slate-hyperscript", "name": "slate-hyperscript",
"description": "A hyperscript helper for creating Slate documents.", "description": "A hyperscript helper for creating Slate documents.",
"version": "0.60.4", "version": "0.60.7",
"license": "MIT", "license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git", "repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js", "main": "dist/index.js",
@@ -17,7 +17,7 @@
"is-plain-object": "^3.0.0" "is-plain-object": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"slate": "^0.60.4" "slate": "^0.60.7"
}, },
"peerDependencies": { "peerDependencies": {
"slate": ">=0.55.0" "slate": ">=0.55.0"

View File

@@ -1,7 +1,7 @@
{ {
"name": "slate-react", "name": "slate-react",
"description": "Tools for building completely customizable richtext editors with React.", "description": "Tools for building completely customizable richtext editors with React.",
"version": "0.60.4", "version": "0.60.7",
"license": "MIT", "license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git", "repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js", "main": "dist/index.js",
@@ -23,9 +23,9 @@
"scroll-into-view-if-needed": "^2.2.20" "scroll-into-view-if-needed": "^2.2.20"
}, },
"devDependencies": { "devDependencies": {
"slate": "^0.60.4", "slate": "^0.60.7",
"slate-history": "^0.60.4", "slate-history": "^0.60.7",
"slate-hyperscript": "^0.60.4" "slate-hyperscript": "^0.60.7"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0", "react": ">=16.8.0",

View File

@@ -1,12 +1,12 @@
import { CustomTypes } from 'slate' import { BaseRange, BaseText } from 'slate'
declare module 'slate' { declare module 'slate' {
interface CustomTypes { interface CustomTypes {
Text: { Text: {
placeholder: string placeholder: string
} } & BaseText
Range: { Range: {
placeholder?: string placeholder?: string
} } & BaseRange
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "slate", "name": "slate",
"description": "A completely customizable framework for building rich text editors.", "description": "A completely customizable framework for building rich text editors.",
"version": "0.60.4", "version": "0.60.7",
"license": "MIT", "license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git", "repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -8,4 +8,4 @@ export interface CustomTypes {
export type ExtendedType<K extends string, B> = unknown extends CustomTypes[K] export type ExtendedType<K extends string, B> = unknown extends CustomTypes[K]
? B ? B
: B & CustomTypes[K] : CustomTypes[K]

View File

@@ -9,7 +9,7 @@ import { ExtendedType } from './custom-types'
*/ */
export type BaseNode = Editor | Element | Text export type BaseNode = Editor | Element | Text
export type Node = ExtendedType<'Node', BaseNode> export type Node = Editor | Element | Text
export interface NodeInterface { export interface NodeInterface {
ancestor: (root: Node, path: Path) => Ancestor ancestor: (root: Node, path: Path) => Ancestor
@@ -317,7 +317,9 @@ export const Node: NodeInterface = {
} }
} }
if (Editor.isEditor(r)) delete r.selection if (Editor.isEditor(r)) {
r.selection = null
}
}) })
return newRoot.children return newRoot.children

View File

@@ -3,6 +3,8 @@ import { isBoldText } from './type-guards'
export const input: Text = { export const input: Text = {
placeholder: 'heading', placeholder: 'heading',
bold: false,
italic: false,
text: 'mytext', text: 'mytext',
} }

View File

@@ -1,30 +1,72 @@
import { Descendant, Element, Text, CustomTypes } from 'slate' // import { Descendant, Element, Text, CustomTypes, BaseText } from 'slate'
export interface HeadingElement { // export type HeadingElement = {
// type: 'heading'
// level: number
// children: Descendant[]
// }
// export type ListItemElement = {
// type: 'list-item'
// depth: number
// children: Descendant[]
// }
// export type CustomText = {
// placeholder: string
// bold: boolean
// italic: boolean
// text: string
// }
// export type BoldCustomText = {
// bold: boolean
// text: string
// }
// declare module 'slate' {
// interface CustomTypes {
// Element: HeadingElement | ListItemElement
// Text: CustomText
// }
// }
import {
BaseText,
BaseEditor,
BaseSelection,
BasePoint,
BaseRange,
BaseElement,
} from 'slate'
// import { Prettify } from './prettify'
export type HeadingElement = {
type: 'heading' type: 'heading'
level: number level: number
children: Descendant[] } & BaseElement
}
export interface ListItemElement { export type ListItemElement = {
type: 'list-item' type: 'list-item'
depth: number depth: number
children: Descendant[] } & BaseElement
}
export interface CustomText { export type CustomText = {
placeholder: string placeholder: string
text: string
}
export interface BoldCustomText {
bold: boolean bold: boolean
text: string italic: boolean
} } & BaseText
export type CustomElement = HeadingElement | ListItemElement
declare module 'slate' { declare module 'slate' {
interface CustomTypes { interface CustomTypes {
Element: HeadingElement | ListItemElement Editor: BaseEditor
Text: CustomText | BoldCustomText Element: CustomElement
Text: CustomText
Node: CustomElement | CustomText
Point: BasePoint
Range: BaseRange
Selection: BaseSelection
} }
} }

View File

@@ -1,8 +1,8 @@
import { Element, Text } from 'slate' import { Element, Text } from 'slate'
import { BoldCustomText, CustomText, HeadingElement } from './custom-types' import { CustomText, HeadingElement } from './custom-types'
export const isBoldText = (text: Text): text is BoldCustomText => export const isBoldText = (text: Text): text is CustomText =>
!!(text as BoldCustomText).bold !!(text as CustomText).bold
export const isCustomText = (text: Text): text is CustomText => export const isCustomText = (text: Text): text is CustomText =>
!!(text as CustomText).placeholder !!(text as CustomText).placeholder

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,7 @@ import {
import { css } from 'emotion' import { css } from 'emotion'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
const initialValue: Descendant[] = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -17,7 +17,7 @@ import { withHistory } from 'slate-history'
import { css } from 'emotion' import { css } from 'emotion'
const CodeHighlightingExample = () => { const CodeHighlightingExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const [language, setLanguage] = useState('html') const [language, setLanguage] = useState('html')
const renderLeaf = useCallback(props => <Leaf {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
@@ -149,8 +149,9 @@ const Leaf = ({ attributes, children, leaf }) => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: '<h1>Hi!</h1>', text: '<h1>Hi!</h1>',

View File

@@ -1,18 +1,87 @@
import { Text, createEditor, Node, Element, Editor, Descendant } from 'slate' import { Text, createEditor, Node, Element, Editor, Descendant } from 'slate'
export type BlockQuoteElement = { type: 'block-quote'; children: Descendant[] }
export type BulletedListElement = {
type: 'bulleted-list'
children: Descendant[]
}
export type CheckListItemElement = {
type: 'check-list-item'
checked: boolean
children: Descendant[]
}
export type EditableVoidElement = {
type: 'editable-void'
children: EmptyText[]
}
export type HeadingElement = { type: 'heading'; children: Descendant[] }
export type HeadingTwoElement = { type: 'heading-two'; children: Descendant[] }
export type ImageElement = {
type: 'image'
url: string
children: EmptyText[]
}
export type LinkElement = { type: 'link'; url: string; children: Descendant[] }
export type ListItemElement = { type: 'list-item'; children: Descendant[] }
export type MentionElement = {
type: 'mention'
character: string
children: CustomText[]
}
export type ParagraphElement = { type: 'paragraph'; children: Descendant[] }
export type TableElement = { type: 'table'; children: TableRow[] }
export type TableCellElement = { type: 'table-cell'; children: CustomText[] }
export type TableRowElement = { type: 'table-row'; children: TableCell[] }
export type TitleElement = { type: 'title'; children: Descendant[] }
export type VideoElement = { type: 'video'; url: string; children: EmptyText[] }
type CustomElement =
| BlockQuoteElement
| BulletedListElement
| CheckListItemElement
| EditableVoidElement
| HeadingElement
| HeadingTwoElement
| ImageElement
| LinkElement
| ListItemElement
| MentionElement
| ParagraphElement
| TableElement
| TableRowElement
| TableCellElement
| TitleElement
| VideoElement
export type CustomText = {
bold?: boolean
italic?: boolean
code?: boolean
text: string
}
export type EmptyText = {
text: string
}
declare module 'slate' { declare module 'slate' {
interface CustomTypes { interface CustomTypes {
Element: CustomElement Element: CustomElement
Text: CustomText | EmptyText
Node: CustomNode
} }
} }
interface CustomElement {
type?: string
checked?: boolean
url?: string
children: Descendant[]
}
type CustomNode = Editor | CustomElement | Text

View File

@@ -1,14 +1,21 @@
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import { Transforms, createEditor, Node } from 'slate' import {
Transforms,
createEditor,
Node,
Element as SlateElement,
Descendant,
} from 'slate'
import { Slate, Editable, useSlateStatic, withReact } from 'slate-react' import { Slate, Editable, useSlateStatic, withReact } from 'slate-react'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { css } from 'emotion' import { css } from 'emotion'
import RichTextEditor from './richtext' import RichTextEditor from './richtext'
import { Button, Icon, Toolbar } from '../components' import { Button, Icon, Toolbar } from '../components'
import { EditableVoidElement } from './custom-types'
const EditableVoidsExample = () => { const EditableVoidsExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo( const editor = useMemo(
() => withEditableVoids(withHistory(withReact(createEditor()))), () => withEditableVoids(withHistory(withReact(createEditor()))),
[] []
@@ -40,7 +47,10 @@ const withEditableVoids = editor => {
const insertEditableVoid = editor => { const insertEditableVoid = editor => {
const text = { text: '' } const text = { text: '' }
const voidNode = { type: 'editable-void', children: [text] } const voidNode: EditableVoidElement = {
type: 'editable-void',
children: [text],
}
Transforms.insertNodes(editor, voidNode) Transforms.insertNodes(editor, voidNode)
} }
@@ -49,7 +59,7 @@ const Element = props => {
switch (element.type) { switch (element.type) {
case 'editable-void': case 'editable-void':
return <EditableVoidElement {...props} /> return <EditableVoid {...props} />
default: default:
return <p {...attributes}>{children}</p> return <p {...attributes}>{children}</p>
} }
@@ -59,7 +69,7 @@ const unsetWidthStyle = css`
width: unset; width: unset;
` `
const EditableVoidElement = ({ attributes, children, element }) => { const EditableVoid = ({ attributes, children, element }) => {
const [inputValue, setInputValue] = useState('') const [inputValue, setInputValue] = useState('')
return ( return (
@@ -127,7 +137,7 @@ const InsertEditableVoidButton = () => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -1,5 +1,11 @@
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import { Transforms, createEditor, Node, Element as SlateElement } from 'slate' import {
Transforms,
createEditor,
Node,
Element as SlateElement,
Descendant,
} from 'slate'
import { import {
Slate, Slate,
Editable, Editable,
@@ -11,7 +17,7 @@ import {
} from 'slate-react' } from 'slate-react'
const EmbedsExample = () => { const EmbedsExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo(() => withEmbeds(withReact(createEditor())), []) const editor = useMemo(() => withEmbeds(withReact(createEditor())), [])
return ( return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}> <Slate editor={editor} value={value} onChange={value => setValue(value)}>
@@ -98,8 +104,9 @@ const UrlInput = ({ url, onChange }) => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -113,6 +120,7 @@ const initialValue = [
children: [{ text: '' }], children: [{ text: '' }],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:

View File

@@ -1,7 +1,14 @@
import React, { useState, useCallback, useMemo } from 'react' import React, { useState, useCallback, useMemo } from 'react'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
import { Transforms, createEditor, Node, Element as SlateElement } from 'slate' import {
Transforms,
createEditor,
Node,
Element as SlateElement,
Descendant,
} from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { ParagraphElement, TitleElement } from './custom-types'
const withLayout = editor => { const withLayout = editor => {
const { normalizeNode } = editor const { normalizeNode } = editor
@@ -9,12 +16,18 @@ const withLayout = editor => {
editor.normalizeNode = ([node, path]) => { editor.normalizeNode = ([node, path]) => {
if (path.length === 0) { if (path.length === 0) {
if (editor.children.length < 1) { if (editor.children.length < 1) {
const title = { type: 'title', children: [{ text: 'Untitled' }] } const title: TitleElement = {
type: 'title',
children: [{ text: 'Untitled' }],
}
Transforms.insertNodes(editor, title, { at: path.concat(0) }) Transforms.insertNodes(editor, title, { at: path.concat(0) })
} }
if (editor.children.length < 2) { if (editor.children.length < 2) {
const paragraph = { type: 'paragraph', children: [{ text: '' }] } const paragraph: ParagraphElement = {
type: 'paragraph',
children: [{ text: '' }],
}
Transforms.insertNodes(editor, paragraph, { at: path.concat(1) }) Transforms.insertNodes(editor, paragraph, { at: path.concat(1) })
} }
@@ -35,7 +48,7 @@ const withLayout = editor => {
} }
const ForcedLayoutExample = () => { const ForcedLayoutExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const editor = useMemo( const editor = useMemo(
() => withLayout(withHistory(withReact(createEditor()))), () => withLayout(withHistory(withReact(createEditor()))),
@@ -62,7 +75,7 @@ const Element = ({ attributes, children, element }) => {
} }
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'title', type: 'title',
children: [{ text: 'Enforce Your Layout!' }], children: [{ text: 'Enforce Your Layout!' }],

View File

@@ -1,6 +1,14 @@
import React, { useState, useMemo, useRef, useEffect } from 'react' import React, { useState, useMemo, useRef, useEffect } from 'react'
import { Slate, Editable, ReactEditor, withReact, useSlate } from 'slate-react' import { Slate, Editable, ReactEditor, withReact, useSlate } from 'slate-react'
import { Editor, Transforms, Text, createEditor, Node } from 'slate' import {
Editor,
Transforms,
Text,
createEditor,
Node,
Element,
Descendant,
} from 'slate'
import { css } from 'emotion' import { css } from 'emotion'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
@@ -8,7 +16,7 @@ import { Button, Icon, Menu, Portal } from '../components'
import { Range } from 'slate' import { Range } from 'slate'
const HoveringMenuExample = () => { const HoveringMenuExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
return ( return (
@@ -140,8 +148,9 @@ const FormatButton = ({ format, icon }) => {
) )
} }
const initialValue = [ const initialValue: Element[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -154,6 +163,7 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ text: 'Try it out yourself! Just ' }, { text: 'Try it out yourself! Just ' },
{ text: 'select any piece of text and the menu will appear', bold: true }, { text: 'select any piece of text and the menu will appear', bold: true },

View File

@@ -1,11 +1,11 @@
import React, { useState, useMemo, useCallback } from 'react' import React, { useState, useMemo, useCallback } from 'react'
import faker from 'faker' import faker from 'faker'
import { createEditor } from 'slate' import { Element as SlateElement, Node, createEditor, Descendant } from 'slate'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
const HEADINGS = 100 const HEADINGS = 100
const PARAGRAPHS = 7 const PARAGRAPHS = 7
const initialValue = [] const initialValue: SlateElement[] = []
for (let h = 0; h < HEADINGS; h++) { for (let h = 0; h < HEADINGS; h++) {
initialValue.push({ initialValue.push({
@@ -15,13 +15,14 @@ for (let h = 0; h < HEADINGS; h++) {
for (let p = 0; p < PARAGRAPHS; p++) { for (let p = 0; p < PARAGRAPHS; p++) {
initialValue.push({ initialValue.push({
type: 'paragraph',
children: [{ text: faker.lorem.paragraph() }], children: [{ text: faker.lorem.paragraph() }],
}) })
} }
} }
const HugeDocumentExample = () => { const HugeDocumentExample = () => {
const [value, setValue] = useState(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const editor = useMemo(() => withReact(createEditor()), []) const editor = useMemo(() => withReact(createEditor()), [])
return ( return (

View File

@@ -2,7 +2,13 @@ import React, { useCallback, useMemo, useState } from 'react'
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import isHotkey from 'is-hotkey' import isHotkey from 'is-hotkey'
import { Editable, withReact, useSlate, Slate, ReactEditor } from 'slate-react' import { Editable, withReact, useSlate, Slate, ReactEditor } from 'slate-react'
import { Editor, createEditor, Node } from 'slate' import {
Editor,
Element as SlateElement,
createEditor,
Node,
Descendant,
} from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { Button, Icon, Toolbar } from '../components' import { Button, Icon, Toolbar } from '../components'
@@ -15,7 +21,7 @@ const HOTKEYS = {
} }
const IFrameExample = () => { const IFrameExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback( const renderElement = useCallback(
({ attributes, children }) => <p {...attributes}>{children}</p>, ({ attributes, children }) => <p {...attributes}>{children}</p>,
[] []
@@ -117,7 +123,7 @@ const IFrame = ({ children, ...props }) => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -1,7 +1,13 @@
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import imageExtensions from 'image-extensions' import imageExtensions from 'image-extensions'
import isUrl from 'is-url' import isUrl from 'is-url'
import { Node, Transforms, createEditor } from 'slate' import {
Node,
Transforms,
createEditor,
Element as SlateElement,
Descendant,
} from 'slate'
import { import {
Slate, Slate,
Editable, Editable,
@@ -14,9 +20,10 @@ import { withHistory } from 'slate-history'
import { css } from 'emotion' import { css } from 'emotion'
import { Button, Icon, Toolbar } from '../components' import { Button, Icon, Toolbar } from '../components'
import { ImageElement } from './custom-types'
const ImagesExample = () => { const ImagesExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo( const editor = useMemo(
() => withImages(withHistory(withReact(createEditor()))), () => withImages(withHistory(withReact(createEditor()))),
[] []
@@ -72,7 +79,7 @@ const withImages = editor => {
const insertImage = (editor, url) => { const insertImage = (editor, url) => {
const text = { text: '' } const text = { text: '' }
const image = { type: 'image', url, children: [text] } const image: ImageElement = { type: 'image', url, children: [text] }
Transforms.insertNodes(editor, image) Transforms.insertNodes(editor, image)
} }
@@ -81,13 +88,13 @@ const Element = props => {
switch (element.type) { switch (element.type) {
case 'image': case 'image':
return <ImageElement {...props} /> return <Image {...props} />
default: default:
return <p {...attributes}>{children}</p> return <p {...attributes}>{children}</p>
} }
} }
const ImageElement = ({ attributes, children, element }) => { const Image = ({ attributes, children, element }) => {
const selected = useSelected() const selected = useSelected()
const focused = useFocused() const focused = useFocused()
return ( return (
@@ -131,7 +138,7 @@ const isImageUrl = url => {
return imageExtensions.includes(ext) return imageExtensions.includes(ext)
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -8,13 +8,15 @@ import {
Range, Range,
createEditor, createEditor,
Element as SlateElement, Element as SlateElement,
Descendant,
} from 'slate' } from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { LinkElement } from './custom-types'
import { Button, Icon, Toolbar } from '../components' import { Button, Icon, Toolbar } from '../components'
const LinkExample = () => { const LinkExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo( const editor = useMemo(
() => withLinks(withHistory(withReact(createEditor()))), () => withLinks(withHistory(withReact(createEditor()))),
[] []
@@ -89,7 +91,7 @@ const wrapLink = (editor, url) => {
const { selection } = editor const { selection } = editor
const isCollapsed = selection && Range.isCollapsed(selection) const isCollapsed = selection && Range.isCollapsed(selection)
const link = { const link: LinkElement = {
type: 'link', type: 'link',
url, url,
children: isCollapsed ? [{ text: url }] : [], children: isCollapsed ? [{ text: url }] : [],
@@ -133,8 +135,9 @@ const LinkButton = () => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: 'In addition to block nodes, you can create inline nodes, like ', text: 'In addition to block nodes, you can create inline nodes, like ',
@@ -150,6 +153,7 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:

View File

@@ -1,7 +1,7 @@
import Prism from 'prismjs' import Prism from 'prismjs'
import React, { useState, useCallback, useMemo } from 'react' import React, { useState, useCallback, useMemo } from 'react'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
import { Node, Text, createEditor } from 'slate' import { Node, Text, createEditor, Element, Descendant } from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { css } from 'emotion' import { css } from 'emotion'
@@ -9,7 +9,7 @@ import { css } from 'emotion'
;Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore ;Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore
const MarkdownPreviewExample = () => { const MarkdownPreviewExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderLeaf = useCallback(props => <Leaf {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
const decorate = useCallback(([node, path]) => { const decorate = useCallback(([node, path]) => {
@@ -109,8 +109,9 @@ const Leaf = ({ attributes, children, leaf }) => {
) )
} }
const initialValue = [ const initialValue: Element[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -119,9 +120,11 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [{ text: '## Try it out!' }], children: [{ text: '## Try it out!' }],
}, },
{ {
type: 'paragraph',
children: [{ text: 'Try it out for yourself!' }], children: [{ text: 'Try it out for yourself!' }],
}, },
] ]

View File

@@ -8,8 +8,10 @@ import {
Point, Point,
createEditor, createEditor,
Element as SlateElement, Element as SlateElement,
Descendant,
} from 'slate' } from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { BulletedListElement } from './custom-types'
const SHORTCUTS = { const SHORTCUTS = {
'*': 'list-item', '*': 'list-item',
@@ -25,7 +27,7 @@ const SHORTCUTS = {
} }
const MarkdownShortcutsExample = () => { const MarkdownShortcutsExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const editor = useMemo( const editor = useMemo(
() => withShortcuts(withReact(withHistory(createEditor()))), () => withShortcuts(withReact(withHistory(createEditor()))),
@@ -71,7 +73,10 @@ const withShortcuts = editor => {
}) })
if (type === 'list-item') { if (type === 'list-item') {
const list = { type: 'bulleted-list', children: [] } const list: BulletedListElement = {
type: 'bulleted-list',
children: [],
}
Transforms.wrapNodes(editor, list, { Transforms.wrapNodes(editor, list, {
match: n => match: n =>
!Editor.isEditor(n) && !Editor.isEditor(n) &&
@@ -156,7 +161,7 @@ const Element = ({ attributes, children, element }) => {
} }
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -1,5 +1,13 @@
import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react' import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react'
import { Node, Editor, Transforms, Range, createEditor } from 'slate' import {
Node,
Editor,
Transforms,
Range,
createEditor,
Element as SlateElement,
Descendant,
} from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { import {
Slate, Slate,
@@ -11,10 +19,11 @@ import {
} from 'slate-react' } from 'slate-react'
import { Portal } from '../components' import { Portal } from '../components'
import { MentionElement } from './custom-types'
const MentionExample = () => { const MentionExample = () => {
const ref = useRef<HTMLDivElement | null>() const ref = useRef<HTMLDivElement | null>()
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const [target, setTarget] = useState<Range | undefined>() const [target, setTarget] = useState<Range | undefined>()
const [index, setIndex] = useState(0) const [index, setIndex] = useState(0)
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
@@ -154,7 +163,11 @@ const withMentions = editor => {
} }
const insertMention = (editor, character) => { const insertMention = (editor, character) => {
const mention = { type: 'mention', character, children: [{ text: '' }] } const mention: MentionElement = {
type: 'mention',
character,
children: [{ text: '' }],
}
Transforms.insertNodes(editor, mention) Transforms.insertNodes(editor, mention)
Transforms.move(editor) Transforms.move(editor)
} }
@@ -163,13 +176,13 @@ const Element = props => {
const { attributes, children, element } = props const { attributes, children, element } = props
switch (element.type) { switch (element.type) {
case 'mention': case 'mention':
return <MentionElement {...props} /> return <Mention {...props} />
default: default:
return <p {...attributes}>{children}</p> return <p {...attributes}>{children}</p>
} }
} }
const MentionElement = ({ attributes, children, element }) => { const Mention = ({ attributes, children, element }) => {
const selected = useSelected() const selected = useSelected()
const focused = useFocused() const focused = useFocused()
return ( return (
@@ -193,8 +206,9 @@ const MentionElement = ({ attributes, children, element }) => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -203,6 +217,7 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ text: 'Try mentioning characters, like ' }, { text: 'Try mentioning characters, like ' },
{ {

View File

@@ -1,6 +1,12 @@
import React, { useState, useCallback, useMemo } from 'react' import React, { useState, useCallback, useMemo } from 'react'
import { jsx } from 'slate-hyperscript' import { jsx } from 'slate-hyperscript'
import { Node, Transforms, createEditor } from 'slate' import {
Node,
Transforms,
createEditor,
Element as SlateElement,
Descendant,
} from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { css } from 'emotion' import { css } from 'emotion'
import { import {
@@ -80,7 +86,7 @@ export const deserialize = el => {
} }
const PasteHtmlExample = () => { const PasteHtmlExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const renderLeaf = useCallback(props => <Leaf {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo( const editor = useMemo(
@@ -211,8 +217,9 @@ const Leaf = ({ attributes, children, leaf }) => {
return <span {...attributes}>{children}</span> return <span {...attributes}>{children}</span>
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -228,9 +235,11 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [{ text: 'This is an example of doing exactly that!' }], children: [{ text: 'This is an example of doing exactly that!' }],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:

View File

@@ -1,10 +1,10 @@
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import { Node, createEditor } from 'slate' import { createEditor, Element, Descendant } from 'slate'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
const PlainTextExample = () => { const PlainTextExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
return ( return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}> <Slate editor={editor} value={value} onChange={value => setValue(value)}>
@@ -13,8 +13,9 @@ const PlainTextExample = () => {
) )
} }
const initialValue = [ const initialValue: Element[] = [
{ {
type: 'paragraph',
children: [ children: [
{ text: 'This is editable plain text, just like a <textarea>!' }, { text: 'This is editable plain text, just like a <textarea>!' },
], ],

View File

@@ -1,9 +1,9 @@
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import { createEditor, Node } from 'slate' import { createEditor, Descendant, Element, Node } from 'slate'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
const ReadOnlyExample = () => { const ReadOnlyExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const editor = useMemo(() => withReact(createEditor()), []) const editor = useMemo(() => withReact(createEditor()), [])
return ( return (
<Slate editor={editor} value={value} onChange={value => setValue(value)}> <Slate editor={editor} value={value} onChange={value => setValue(value)}>
@@ -12,10 +12,13 @@ const ReadOnlyExample = () => {
) )
} }
const initialValue = [ const initialValue: Element[] = [
{ {
type: 'paragraph',
children: [ children: [
{ text: 'This is editable plain text, just like a <textarea>!' }, {
text: 'This is editable plain text, just like a <textarea>!',
},
], ],
}, },
] ]

View File

@@ -5,6 +5,7 @@ import {
Editor, Editor,
Transforms, Transforms,
createEditor, createEditor,
Descendant,
Node, Node,
Element as SlateElement, Element as SlateElement,
} from 'slate' } from 'slate'
@@ -22,7 +23,7 @@ const HOTKEYS = {
const LIST_TYPES = ['numbered-list', 'bulleted-list'] const LIST_TYPES = ['numbered-list', 'bulleted-list']
const RichTextExample = () => { const RichTextExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const renderLeaf = useCallback(props => <Leaf {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
@@ -175,7 +176,7 @@ const MarkButton = ({ format, icon }) => {
) )
} }
const initialValue = [ const initialValue: SlateElement[] = [
{ {
type: 'paragraph', type: 'paragraph',
children: [ children: [

View File

@@ -1,13 +1,13 @@
import React, { useState, useCallback, useMemo } from 'react' import React, { useState, useCallback, useMemo } from 'react'
import { Slate, Editable, withReact } from 'slate-react' import { Slate, Editable, withReact } from 'slate-react'
import { Text, Node, createEditor } from 'slate' import { Text, Node, Descendant, createEditor } from 'slate'
import { css } from 'emotion' import { css } from 'emotion'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
import { Icon, Toolbar } from '../components' import { Icon, Toolbar } from '../components'
const SearchHighlightingExample = () => { const SearchHighlightingExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const [search, setSearch] = useState<string | undefined>() const [search, setSearch] = useState<string | undefined>()
const editor = useMemo(() => withHistory(withReact(createEditor())), []) const editor = useMemo(() => withHistory(withReact(createEditor())), [])
const decorate = useCallback( const decorate = useCallback(
@@ -85,8 +85,9 @@ const Leaf = ({ attributes, children, leaf }) => {
) )
} }
const initialValue = [ const initialValue: Descendant[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -97,6 +98,7 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ text: 'Try it out for yourself by typing in the search box above!' }, { text: 'Try it out for yourself by typing in the search box above!' },
], ],

View File

@@ -4,14 +4,14 @@ import {
Editor, Editor,
Range, Range,
Point, Point,
Node, Descendant,
createEditor, createEditor,
Element as SlateElement, Element as SlateElement,
} from 'slate' } from 'slate'
import { withHistory } from 'slate-history' import { withHistory } from 'slate-history'
const TablesExample = () => { const TablesExample = () => {
const [value, setValue] = useState<Node[]>(initialValue) const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback(props => <Element {...props} />, []) const renderElement = useCallback(props => <Element {...props} />, [])
const renderLeaf = useCallback(props => <Leaf {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo( const editor = useMemo(
@@ -123,8 +123,9 @@ const Leaf = ({ attributes, children, leaf }) => {
return <span {...attributes}>{children}</span> return <span {...attributes}>{children}</span>
} }
const initialValue = [ const initialValue: Descendant[] = [
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:
@@ -201,6 +202,7 @@ const initialValue = [
], ],
}, },
{ {
type: 'paragraph',
children: [ children: [
{ {
text: text:

View File

@@ -10109,7 +10109,7 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.7.2: typescript@3.9.7:
version "3.9.7" version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==