mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-07-31 20:40:19 +02:00
Consistent insert* methods (#5415)
* feat: add reviewpad.yml file * Update reviewpad.yml * ✨ options to `Editor.insert*` * 📝 options to `Editor.insert*` * ✨ getDefaultInsertLocation * ✨ getDefaultInsertLocation * ✨ getDefaultInsertLocation * 📝 consistent selection * Create tasty-lizards-remain.md * Update tasty-lizards-remain.md --------- Co-authored-by: reviewpad[bot] <104832597+reviewpad[bot]@users.noreply.github.com> Co-authored-by: Dylan Schiemann <dylan@dojotoolkit.org>
This commit is contained in:
6
.changeset/tasty-lizards-remain.md
Normal file
6
.changeset/tasty-lizards-remain.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
'slate': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
`Editor.insertFragment`, `Editor.insertNode`, `Editor.insertText` now accept `options`.
|
||||||
|
For all insert methods, the default location is now the editor selection if `at` is not defined, or the end of document if `editor.selection` is not defined.
|
@@ -246,23 +246,23 @@ Delete the content in the current selection.
|
|||||||
|
|
||||||
Insert a block break at the current selection.
|
Insert a block break at the current selection.
|
||||||
|
|
||||||
#### `Editor.insertFragment(editor: Editor, fragment: Node[]) => void`
|
#### `Editor.insertFragment(editor: Editor, fragment: Node[], options?) => void`
|
||||||
|
|
||||||
Inserts a fragment _at the current selection_.
|
Inserts a fragment at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
If the selection is currently expanded, it will be deleted first. To atomically insert nodes (including at the very beginning or end), use [Transforms.insertNodes](../transforms.md#transformsinsertnodeseditor-editor-nodes-node--node-options).
|
Options: `{at?: Location, hanging?: boolean, voids?: boolean}`
|
||||||
|
|
||||||
#### `Editor.insertNode(editor: Editor, node: Node) => void`
|
#### `Editor.insertNode(editor: Editor, node: Node, options?) => void`
|
||||||
|
|
||||||
Inserts a node _at the current selection_.
|
Atomically insert `node` at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
If the selection is currently expanded, it will be deleted first. To atomically insert a node (including at the very beginning or end), use [Transforms.insertNodes](../transforms.md#transformsinsertnodeseditor-editor-nodes-node--node-options).
|
Options supported: `NodeOptions & {hanging?: boolean, select?: boolean}`.
|
||||||
|
|
||||||
#### `Editor.insertText(editor: Editor, text: string) => void`
|
#### `Editor.insertText(editor: Editor, text: string, options?) => void`
|
||||||
|
|
||||||
Inserts text _at the current selection_.
|
Insert a string of text at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
If the selection is currently expanded, it will be deleted first.
|
Options: `{at?: Location, voids?: boolean}`
|
||||||
|
|
||||||
#### `Editor.removeMark(editor: Editor, key: string) => void`
|
#### `Editor.removeMark(editor: Editor, key: string) => void`
|
||||||
|
|
||||||
|
@@ -38,13 +38,13 @@ Transforms that operate on nodes.
|
|||||||
|
|
||||||
#### `Transforms.insertFragment(editor: Editor, fragment: Node[], options?)`
|
#### `Transforms.insertFragment(editor: Editor, fragment: Node[], options?)`
|
||||||
|
|
||||||
Insert of fragment of nodes at the specified location in the document. If no location is specified, insert at the current selection.
|
Insert of fragment of nodes at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
Options: `{at?: Location, hanging?: boolean, voids?: boolean}`
|
Options: `{at?: Location, hanging?: boolean, voids?: boolean}`
|
||||||
|
|
||||||
#### `Transforms.insertNodes(editor: Editor, nodes: Node | Node[], options?)`
|
#### `Transforms.insertNodes(editor: Editor, nodes: Node | Node[], options?)`
|
||||||
|
|
||||||
Atomically inserts `nodes` at the specified location in the document. If no location is specified, inserts at the current selection. If there is no selection, inserts at the end of the document.
|
Atomically inserts `nodes` at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
Options supported: `NodeOptions & {hanging?: boolean, select?: boolean}`.
|
Options supported: `NodeOptions & {hanging?: boolean, select?: boolean}`.
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ Options: `{at?: Location, distance?: number, unit?: 'character' | 'word' | 'line
|
|||||||
|
|
||||||
#### `Transforms.insertText(editor: Editor, text: string, options?)`
|
#### `Transforms.insertText(editor: Editor, text: string, options?)`
|
||||||
|
|
||||||
Insert a string of text at the specified location in the document. If no location is specified, insert at the current selection.
|
Insert a string of text at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
|
|
||||||
Options: `{at?: Location, voids?: boolean}`
|
Options: `{at?: Location, voids?: boolean}`
|
||||||
|
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
import { Transforms } from '../interfaces/transforms'
|
import { Transforms } from '../interfaces/transforms'
|
||||||
import { EditorInterface } from '../interfaces/editor'
|
import { EditorInterface } from '../interfaces/editor'
|
||||||
|
|
||||||
export const insertNode: EditorInterface['insertNode'] = (editor, node) => {
|
export const insertNode: EditorInterface['insertNode'] = (
|
||||||
Transforms.insertNodes(editor, node)
|
editor,
|
||||||
|
node,
|
||||||
|
options
|
||||||
|
) => {
|
||||||
|
Transforms.insertNodes(editor, node, options)
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,10 @@ export const insertText: EditorInterface['insertText'] = (
|
|||||||
if (selection) {
|
if (selection) {
|
||||||
if (marks) {
|
if (marks) {
|
||||||
const node = { text, ...marks }
|
const node = { text, ...marks }
|
||||||
Transforms.insertNodes(editor, node)
|
Transforms.insertNodes(editor, node, {
|
||||||
|
at: options.at,
|
||||||
|
voids: options.voids,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Transforms.insertText(editor, text, options)
|
Transforms.insertText(editor, text, options)
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,11 @@ import {
|
|||||||
} from '../types/types'
|
} from '../types/types'
|
||||||
import { OmitFirstArg } from '../utils/types'
|
import { OmitFirstArg } from '../utils/types'
|
||||||
import { isEditor } from '../editor/is-editor'
|
import { isEditor } from '../editor/is-editor'
|
||||||
import { TextInsertTextOptions } from './transforms/text'
|
import {
|
||||||
|
TextInsertFragmentOptions,
|
||||||
|
TextInsertTextOptions,
|
||||||
|
} from './transforms/text'
|
||||||
|
import { NodeInsertNodesOptions } from './transforms/node'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Editor` interface stores all the state of a Slate editor. It is extended
|
* The `Editor` interface stores all the state of a Slate editor. It is extended
|
||||||
@@ -412,18 +416,24 @@ export interface EditorInterface {
|
|||||||
insertBreak: (editor: Editor) => void
|
insertBreak: (editor: Editor) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a fragment at the current selection.
|
* Inserts a fragment
|
||||||
*
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
* If the selection is currently expanded, it will be deleted first.
|
|
||||||
*/
|
*/
|
||||||
insertFragment: (editor: Editor, fragment: Node[]) => void
|
insertFragment: (
|
||||||
|
editor: Editor,
|
||||||
|
fragment: Node[],
|
||||||
|
options?: TextInsertFragmentOptions
|
||||||
|
) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a node at the current selection.
|
* Atomically inserts `nodes`
|
||||||
*
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
* If the selection is currently expanded, it will be deleted first.
|
|
||||||
*/
|
*/
|
||||||
insertNode: (editor: Editor, node: Node) => void
|
insertNode: <T extends Node>(
|
||||||
|
editor: Editor,
|
||||||
|
node: Node,
|
||||||
|
options?: NodeInsertNodesOptions<T>
|
||||||
|
) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a soft break at the current selection.
|
* Insert a soft break at the current selection.
|
||||||
@@ -433,9 +443,8 @@ export interface EditorInterface {
|
|||||||
insertSoftBreak: (editor: Editor) => void
|
insertSoftBreak: (editor: Editor) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert text at the current selection.
|
* Insert a string of text
|
||||||
*
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
* If the selection is currently expanded, it will be deleted first.
|
|
||||||
*/
|
*/
|
||||||
insertText: (
|
insertText: (
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
@@ -774,8 +783,8 @@ export const Editor: EditorInterface = {
|
|||||||
editor.insertBreak()
|
editor.insertBreak()
|
||||||
},
|
},
|
||||||
|
|
||||||
insertFragment(editor, fragment) {
|
insertFragment(editor, fragment, options) {
|
||||||
editor.insertFragment(fragment)
|
editor.insertFragment(fragment, options)
|
||||||
},
|
},
|
||||||
|
|
||||||
insertNode(editor, node) {
|
insertNode(editor, node) {
|
||||||
|
@@ -2,21 +2,24 @@ import { Editor, Element, Location, Node, Path } from '../../index'
|
|||||||
import { NodeMatch, PropsCompare, PropsMerge } from '../editor'
|
import { NodeMatch, PropsCompare, PropsMerge } from '../editor'
|
||||||
import { MaximizeMode, RangeMode } from '../../types/types'
|
import { MaximizeMode, RangeMode } from '../../types/types'
|
||||||
|
|
||||||
|
export interface NodeInsertNodesOptions<T extends Node> {
|
||||||
|
at?: Location
|
||||||
|
match?: NodeMatch<T>
|
||||||
|
mode?: RangeMode
|
||||||
|
hanging?: boolean
|
||||||
|
select?: boolean
|
||||||
|
voids?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface NodeTransforms {
|
export interface NodeTransforms {
|
||||||
/**
|
/**
|
||||||
* Insert nodes at a specific location in the Editor.
|
* Insert nodes in the editor
|
||||||
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
*/
|
*/
|
||||||
insertNodes: <T extends Node>(
|
insertNodes: <T extends Node>(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
nodes: Node | Node[],
|
nodes: Node | Node[],
|
||||||
options?: {
|
options?: NodeInsertNodesOptions<T>
|
||||||
at?: Location
|
|
||||||
match?: NodeMatch<T>
|
|
||||||
mode?: RangeMode
|
|
||||||
hanging?: boolean
|
|
||||||
select?: boolean
|
|
||||||
voids?: boolean
|
|
||||||
}
|
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Editor, Location, Node, Path, Range, Transforms } from '../../index'
|
import { Editor, Location, Node, Path, Range, Transforms } from '../../index'
|
||||||
import { TextUnit } from '../../types/types'
|
import { TextUnit } from '../../types/types'
|
||||||
|
import { getDefaultInsertLocation } from '../../utils'
|
||||||
|
|
||||||
export interface TextDeleteOptions {
|
export interface TextDeleteOptions {
|
||||||
at?: Location
|
at?: Location
|
||||||
@@ -28,7 +29,8 @@ export interface TextTransforms {
|
|||||||
delete: (editor: Editor, options?: TextDeleteOptions) => void
|
delete: (editor: Editor, options?: TextDeleteOptions) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a fragment at a specific location in the editor.
|
* Insert a fragment in the editor
|
||||||
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
*/
|
*/
|
||||||
insertFragment: (
|
insertFragment: (
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
@@ -37,7 +39,8 @@ export interface TextTransforms {
|
|||||||
) => void
|
) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a string of text in the Editor.
|
* Insert a string of text in the editor
|
||||||
|
* at the specified location or (if not defined) the current selection or (if not defined) the end of the document.
|
||||||
*/
|
*/
|
||||||
insertText: (
|
insertText: (
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
@@ -61,11 +64,7 @@ export const TextTransforms: TextTransforms = {
|
|||||||
): void {
|
): void {
|
||||||
Editor.withoutNormalizing(editor, () => {
|
Editor.withoutNormalizing(editor, () => {
|
||||||
const { voids = false } = options
|
const { voids = false } = options
|
||||||
let { at = editor.selection } = options
|
let { at = getDefaultInsertLocation(editor) } = options
|
||||||
|
|
||||||
if (!at) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Path.isPath(at)) {
|
if (Path.isPath(at)) {
|
||||||
at = Editor.range(editor, at)
|
at = Editor.range(editor, at)
|
||||||
|
@@ -7,6 +7,7 @@ import { Point } from '../interfaces/point'
|
|||||||
import { Text } from '../interfaces/text'
|
import { Text } from '../interfaces/text'
|
||||||
import { Element } from '../interfaces/element'
|
import { Element } from '../interfaces/element'
|
||||||
import { Path } from '../interfaces/path'
|
import { Path } from '../interfaces/path'
|
||||||
|
import { getDefaultInsertLocation } from '../utils'
|
||||||
|
|
||||||
export const insertNodes: NodeTransforms['insertNodes'] = (
|
export const insertNodes: NodeTransforms['insertNodes'] = (
|
||||||
editor,
|
editor,
|
||||||
@@ -27,18 +28,8 @@ export const insertNodes: NodeTransforms['insertNodes'] = (
|
|||||||
|
|
||||||
const [node] = nodes
|
const [node] = nodes
|
||||||
|
|
||||||
// By default, use the selection as the target location. But if there is
|
|
||||||
// no selection, insert at the end of the document since that is such a
|
|
||||||
// common use case when inserting from a non-selected state.
|
|
||||||
if (!at) {
|
if (!at) {
|
||||||
if (editor.selection) {
|
at = getDefaultInsertLocation(editor)
|
||||||
at = editor.selection
|
|
||||||
} else if (editor.children.length > 0) {
|
|
||||||
at = Editor.end(editor, [])
|
|
||||||
} else {
|
|
||||||
at = [0]
|
|
||||||
}
|
|
||||||
|
|
||||||
select = true
|
select = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import { Element } from '../interfaces/element'
|
|||||||
import { Node, NodeEntry } from '../interfaces/node'
|
import { Node, NodeEntry } from '../interfaces/node'
|
||||||
import { Text } from '../interfaces/text'
|
import { Text } from '../interfaces/text'
|
||||||
import { TextTransforms } from '../interfaces/transforms/text'
|
import { TextTransforms } from '../interfaces/transforms/text'
|
||||||
|
import { getDefaultInsertLocation } from '../utils'
|
||||||
|
|
||||||
export const insertFragment: TextTransforms['insertFragment'] = (
|
export const insertFragment: TextTransforms['insertFragment'] = (
|
||||||
editor,
|
editor,
|
||||||
@@ -14,15 +15,13 @@ export const insertFragment: TextTransforms['insertFragment'] = (
|
|||||||
) => {
|
) => {
|
||||||
Editor.withoutNormalizing(editor, () => {
|
Editor.withoutNormalizing(editor, () => {
|
||||||
const { hanging = false, voids = false } = options
|
const { hanging = false, voids = false } = options
|
||||||
let { at = editor.selection } = options
|
let { at = getDefaultInsertLocation(editor) } = options
|
||||||
|
|
||||||
if (!fragment.length) {
|
if (!fragment.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!at) {
|
if (Range.isRange(at)) {
|
||||||
return
|
|
||||||
} else if (Range.isRange(at)) {
|
|
||||||
if (!hanging) {
|
if (!hanging) {
|
||||||
at = Editor.unhangRange(editor, at, { voids })
|
at = Editor.unhangRange(editor, at, { voids })
|
||||||
}
|
}
|
||||||
|
17
packages/slate/src/utils/get-default-insert-location.ts
Normal file
17
packages/slate/src/utils/get-default-insert-location.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Editor, Location } from '../interfaces'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default location to insert content into the editor.
|
||||||
|
* By default, use the selection as the target location. But if there is
|
||||||
|
* no selection, insert at the end of the document since that is such a
|
||||||
|
* common use case when inserting from a non-selected state.
|
||||||
|
*/
|
||||||
|
export const getDefaultInsertLocation = (editor: Editor): Location => {
|
||||||
|
if (editor.selection) {
|
||||||
|
return editor.selection
|
||||||
|
} else if (editor.children.length > 0) {
|
||||||
|
return Editor.end(editor, [])
|
||||||
|
} else {
|
||||||
|
return [0]
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
export * from './deep-equal'
|
export * from './deep-equal'
|
||||||
|
export * from './get-default-insert-location'
|
||||||
export * from './match-path'
|
export * from './match-path'
|
||||||
export * from './string'
|
export * from './string'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
Reference in New Issue
Block a user