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:
@@ -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:
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
91
docs/concepts/11-typescript.md
Normal file
91
docs/concepts/11-typescript.md
Normal 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.
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
|
@@ -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"
|
||||||
|
@@ -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",
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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",
|
||||||
|
@@ -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]
|
||||||
|
@@ -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
|
||||||
|
@@ -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',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
3568
packages/slate/tsconfig.tsbuildinfo
Normal file
3568
packages/slate/tsconfig.tsbuildinfo
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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: [
|
||||||
|
@@ -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>',
|
||||||
|
91
site/examples/custom-types.d.ts
vendored
91
site/examples/custom-types.d.ts
vendored
@@ -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
|
|
||||||
|
@@ -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: [
|
||||||
|
@@ -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:
|
||||||
|
@@ -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!' }],
|
||||||
|
@@ -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 },
|
||||||
|
@@ -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 (
|
||||||
|
@@ -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: [
|
||||||
|
@@ -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: [
|
||||||
|
@@ -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:
|
||||||
|
@@ -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!' }],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@@ -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: [
|
||||||
|
@@ -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 ' },
|
||||||
{
|
{
|
||||||
|
@@ -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:
|
||||||
|
@@ -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>!' },
|
||||||
],
|
],
|
||||||
|
@@ -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>!',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@@ -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: [
|
||||||
|
@@ -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!' },
|
||||||
],
|
],
|
||||||
|
@@ -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:
|
||||||
|
@@ -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==
|
||||||
|
Reference in New Issue
Block a user