mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-02-01 05:16:10 +01:00
add Decoration
and Selection
models (#2112)
#### Is this adding or improving a _feature_ or fixing a _bug_? Improvement. #### What's the new behavior? This introduces two new models: `Decoration` and `Selection`, which both implement the simpler `Range` interface. This way we can introduce properties to these concepts without having to have them live on all ranges, and we can start to introduce more helpful methods specific to each one's needs. It also means we don't need to move `isFocused` to value, which saves some complexity on the operations side, retaining `set_selection` as the only way selections are modified. In the process, it also cleans up a lot of the existing model logic for implementing the `Node` interface, and introduces another `Common` interface for shared properties of all Slate models. #### How does this change work? It introduces a new `interfaces/` directory where common sets of properties can be declared, and mixed in to the models with the new (simple) `mixin` utility. #### Have you checked that...? * [x] The new code matches the existing patterns and styles. * [x] The tests pass with `yarn test`. * [x] The linter passes with `yarn lint`. (Fix errors with `yarn prettier`.) * [x] The relevant examples still work. (Run examples with `yarn watch`.) #### Does this fix any issues or need any specific reviewers? Fixes: #1952 Fixes: #1807 Fixes: https://github.com/ianstormtaylor/slate/issues/2110
This commit is contained in:
parent
1f317cd9b4
commit
ecf48926cc
@ -225,7 +225,7 @@ class CodeHighlighting extends React.Component {
|
||||
}
|
||||
|
||||
if (typeof token != 'string') {
|
||||
const range = {
|
||||
const dec = {
|
||||
anchor: {
|
||||
key: startText.key,
|
||||
offset: startOffset,
|
||||
@ -234,10 +234,12 @@ class CodeHighlighting extends React.Component {
|
||||
key: endText.key,
|
||||
offset: endOffset,
|
||||
},
|
||||
marks: [{ type: token.type }],
|
||||
mark: {
|
||||
type: token.type,
|
||||
},
|
||||
}
|
||||
|
||||
decorations.push(range)
|
||||
decorations.push(dec)
|
||||
}
|
||||
|
||||
start = end
|
||||
|
@ -182,7 +182,7 @@ class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
if (typeof token != 'string') {
|
||||
const range = {
|
||||
const dec = {
|
||||
anchor: {
|
||||
key: startText.key,
|
||||
offset: startOffset,
|
||||
@ -191,10 +191,12 @@ class MarkdownPreview extends React.Component {
|
||||
key: endText.key,
|
||||
offset: endOffset,
|
||||
},
|
||||
marks: [{ type: token.type }],
|
||||
mark: {
|
||||
type: token.type,
|
||||
},
|
||||
}
|
||||
|
||||
decorations.push(range)
|
||||
decorations.push(dec)
|
||||
}
|
||||
|
||||
start = end
|
||||
|
@ -45,6 +45,20 @@ class SearchHighlighting extends React.Component {
|
||||
value: Value.fromJSON(initialValue),
|
||||
}
|
||||
|
||||
/**
|
||||
* The editor's schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
schema = {
|
||||
marks: {
|
||||
highlight: {
|
||||
isAtomic: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Render.
|
||||
*
|
||||
@ -67,6 +81,7 @@ class SearchHighlighting extends React.Component {
|
||||
<Editor
|
||||
placeholder="Enter some rich text..."
|
||||
value={this.state.value}
|
||||
schema={this.schema}
|
||||
onChange={this.onChange}
|
||||
renderMark={this.renderMark}
|
||||
spellCheck
|
||||
@ -127,8 +142,7 @@ class SearchHighlighting extends React.Component {
|
||||
decorations.push({
|
||||
anchor: { key, offset: offset - string.length },
|
||||
focus: { key, offset },
|
||||
marks: [{ type: 'highlight' }],
|
||||
isAtomic: true,
|
||||
mark: { type: 'highlight' },
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,20 @@ This document maintains a list of changes to the `slate-hyperscript` package wit
|
||||
|
||||
---
|
||||
|
||||
### `0.9.0` — August 22, 2018
|
||||
|
||||
###### NEW
|
||||
|
||||
**Introducing the `schema` option.** You can now pass in a `schema` option to the `createHyperscript` factory, which will ensure that schema rules are bound whenever you use the `<value>` tag. This is helpful for defining atomicity of decorations, or the voidness of nodes in the future.
|
||||
|
||||
###### BREAKING
|
||||
|
||||
**The `isFocused` prop of `<selection>` is now `focused`.** This is just to match the other boolean properties in this library which all omit the `is*` prefix to stay consistent with the DOM-style.
|
||||
|
||||
**The `atomic` prop of decorations is now controlled by the schema.** Previously each individual decoration could control whether it was atomic or not, but now this is controlled in the schema definition for the mark itself.
|
||||
|
||||
---
|
||||
|
||||
### `0.8.0` — August 15, 2018
|
||||
|
||||
###### BREAKING
|
||||
|
@ -1,13 +1,14 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
|
||||
import {
|
||||
Block,
|
||||
Decoration,
|
||||
Document,
|
||||
Inline,
|
||||
Mark,
|
||||
Node,
|
||||
Point,
|
||||
Range,
|
||||
Schema,
|
||||
Selection,
|
||||
Text,
|
||||
Value,
|
||||
} from 'slate'
|
||||
@ -45,19 +46,19 @@ class FocusPoint {
|
||||
|
||||
class DecorationPoint {
|
||||
constructor(attrs) {
|
||||
const { key = null, data = {}, marks } = attrs
|
||||
const { key = null, data = {}, type } = attrs
|
||||
this.id = key
|
||||
this.offset = 0
|
||||
this.marks = marks
|
||||
this.attribs = data || {}
|
||||
this.isAtomic = !!this.attribs.atomic
|
||||
delete this.attribs.atomic
|
||||
return this
|
||||
this.type = type
|
||||
this.data = data
|
||||
}
|
||||
|
||||
combine = focus => {
|
||||
if (!(focus instanceof DecorationPoint))
|
||||
if (!(focus instanceof DecorationPoint)) {
|
||||
throw new Error('misaligned decorations')
|
||||
return Range.create({
|
||||
}
|
||||
|
||||
return Decoration.create({
|
||||
anchor: {
|
||||
key: this.key,
|
||||
offset: this.offset,
|
||||
@ -66,9 +67,10 @@ class DecorationPoint {
|
||||
key: focus.key,
|
||||
offset: focus.offset,
|
||||
},
|
||||
marks: this.marks,
|
||||
isAtomic: this.isAtomic,
|
||||
...this.attribs,
|
||||
mark: {
|
||||
type: this.type,
|
||||
data: this.data,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -95,6 +97,29 @@ const CREATORS = {
|
||||
return new CursorPoint()
|
||||
},
|
||||
|
||||
decoration(tagName, attributes, children) {
|
||||
const { key, data } = attributes
|
||||
const type = tagName
|
||||
|
||||
if (key) {
|
||||
return new DecorationPoint({ key, type, data })
|
||||
}
|
||||
|
||||
const nodes = createChildren(children)
|
||||
const node = nodes[0]
|
||||
const { __decorations = [] } = node
|
||||
const __decoration = {
|
||||
anchorOffset: 0,
|
||||
focusOffset: nodes.reduce((len, n) => len + n.text.length, 0),
|
||||
type,
|
||||
data,
|
||||
}
|
||||
|
||||
__decorations.push(__decoration)
|
||||
node.__decorations = __decorations
|
||||
return nodes
|
||||
},
|
||||
|
||||
document(tagName, attributes, children) {
|
||||
return Document.create({
|
||||
...attributes,
|
||||
@ -119,34 +144,13 @@ const CREATORS = {
|
||||
return nodes
|
||||
},
|
||||
|
||||
decoration(tagName, attributes, children) {
|
||||
if (attributes.key) {
|
||||
return new DecorationPoint({
|
||||
...attributes,
|
||||
marks: [{ type: tagName }],
|
||||
})
|
||||
}
|
||||
|
||||
const nodes = createChildren(children)
|
||||
const node = nodes[0]
|
||||
const { __decorations = [] } = node
|
||||
const __decoration = {
|
||||
anchorOffset: 0,
|
||||
focusOffset: nodes.reduce((len, n) => len + n.text.length, 0),
|
||||
marks: [{ type: tagName }],
|
||||
isAtomic: !!attributes.data.atomic,
|
||||
}
|
||||
|
||||
__decorations.push(__decoration)
|
||||
node.__decorations = __decorations
|
||||
return nodes
|
||||
},
|
||||
|
||||
selection(tagName, attributes, children) {
|
||||
const anchor = children.find(c => c instanceof AnchorPoint)
|
||||
const focus = children.find(c => c instanceof FocusPoint)
|
||||
const selection = Range.create({
|
||||
...attributes,
|
||||
const { marks, focused } = attributes
|
||||
const selection = Selection.create({
|
||||
marks,
|
||||
isFocused: focused,
|
||||
anchor: anchor && {
|
||||
key: anchor.key,
|
||||
offset: anchor.offset,
|
||||
@ -162,10 +166,16 @@ const CREATORS = {
|
||||
return selection
|
||||
},
|
||||
|
||||
text(tagName, attributes, children) {
|
||||
const nodes = createChildren(children, { key: attributes.key })
|
||||
return nodes
|
||||
},
|
||||
|
||||
value(tagName, attributes, children) {
|
||||
const { data, normalize = true } = attributes
|
||||
const schema = Schema.create(attributes.schema || {})
|
||||
const document = children.find(Document.isDocument)
|
||||
let selection = children.find(Range.isRange) || Range.create()
|
||||
let selection = children.find(Selection.isSelection) || Selection.create()
|
||||
let anchor
|
||||
let focus
|
||||
let decorations = []
|
||||
@ -189,7 +199,7 @@ const CREATORS = {
|
||||
let range
|
||||
|
||||
if (!id) {
|
||||
range = Range.create({
|
||||
range = Decoration.create({
|
||||
anchor: {
|
||||
key: text.key,
|
||||
offset: dec.anchorOffset,
|
||||
@ -198,14 +208,16 @@ const CREATORS = {
|
||||
key: text.key,
|
||||
offset: dec.focusOffset,
|
||||
},
|
||||
marks: dec.marks,
|
||||
isAtomic: dec.isAtomic,
|
||||
mark: {
|
||||
type: dec.type,
|
||||
data: dec.data,
|
||||
},
|
||||
})
|
||||
} else if (partials[id]) {
|
||||
const partial = partials[id]
|
||||
delete partials[id]
|
||||
|
||||
range = Range.create({
|
||||
range = Decoration.create({
|
||||
anchor: {
|
||||
key: partial.key,
|
||||
offset: partial.offset,
|
||||
@ -214,8 +226,10 @@ const CREATORS = {
|
||||
key: text.key,
|
||||
offset: dec.offset,
|
||||
},
|
||||
marks: partial.marks,
|
||||
isAtomic: partial.isAtomic,
|
||||
mark: {
|
||||
type: dec.type,
|
||||
data: dec.data,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
dec.key = text.key
|
||||
@ -248,28 +262,26 @@ const CREATORS = {
|
||||
)
|
||||
}
|
||||
|
||||
let value = Value.fromJSON({ data, document, selection }, { normalize })
|
||||
let value = Value.fromJSON(
|
||||
{ data, document, selection, schema },
|
||||
{ normalize }
|
||||
)
|
||||
|
||||
if (anchor || focus) {
|
||||
selection = selection.setPoints([anchor, focus])
|
||||
selection = selection.merge({ isFocused: true })
|
||||
selection = selection.setIsFocused(true)
|
||||
selection = selection.normalize(value.document)
|
||||
value = value.set('selection', selection)
|
||||
}
|
||||
|
||||
if (decorations.length > 0) {
|
||||
decorations = decorations.map(d => d.normalize(value.document))
|
||||
decorations = Range.createList(decorations)
|
||||
decorations = Decoration.createList(decorations)
|
||||
value = value.set('decorations', decorations)
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
|
||||
text(tagName, attributes, children) {
|
||||
const nodes = createChildren(children, { key: attributes.key })
|
||||
return nodes
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,7 +457,13 @@ function createChildren(children, options = {}) {
|
||||
*/
|
||||
|
||||
function resolveCreators(options) {
|
||||
const { blocks = {}, inlines = {}, marks = {}, decorations = {} } = options
|
||||
const {
|
||||
blocks = {},
|
||||
inlines = {},
|
||||
marks = {},
|
||||
decorations = {},
|
||||
schema,
|
||||
} = options
|
||||
|
||||
const creators = {
|
||||
...CREATORS,
|
||||
@ -468,6 +486,11 @@ function resolveCreators(options) {
|
||||
creators[key] = normalizeNode(key, decorations[key], 'decoration')
|
||||
})
|
||||
|
||||
creators.value = (tagName, attributes = {}, children) => {
|
||||
const attrs = { schema, ...attributes }
|
||||
return CREATORS.value(tagName, attrs, children)
|
||||
}
|
||||
|
||||
return creators
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -61,7 +61,6 @@ export const output = {
|
||||
offset: 3,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -150,7 +150,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -84,7 +84,6 @@ export const output = {
|
||||
offset: 3,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -84,7 +84,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -84,7 +84,6 @@ export const output = {
|
||||
offset: 0,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -105,7 +105,6 @@ export const output = {
|
||||
offset: 5,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -105,7 +105,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -105,7 +105,6 @@ export const output = {
|
||||
offset: 0,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -61,7 +61,6 @@ export const output = {
|
||||
offset: 3,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -61,7 +61,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -61,7 +61,6 @@ export const output = {
|
||||
offset: 0,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -67,7 +67,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -96,7 +96,6 @@ export const output = {
|
||||
offset: 3,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -96,7 +96,6 @@ export const output = {
|
||||
offset: 0,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -96,7 +96,6 @@ export const output = {
|
||||
offset: 1,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -81,7 +81,6 @@ export const output = {
|
||||
offset: 6,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -81,7 +81,6 @@ export const output = {
|
||||
offset: 3,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -81,7 +81,6 @@ export const output = {
|
||||
offset: 4,
|
||||
},
|
||||
isFocused: true,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ export const output = {
|
||||
},
|
||||
decorations: [
|
||||
{
|
||||
object: 'range',
|
||||
object: 'decoration',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -93,15 +93,11 @@ export const output = {
|
||||
path: [1, 0],
|
||||
offset: 2,
|
||||
},
|
||||
isFocused: false,
|
||||
isAtomic: false,
|
||||
marks: [
|
||||
{
|
||||
object: 'mark',
|
||||
type: 'highlight',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
mark: {
|
||||
object: 'mark',
|
||||
type: 'highlight',
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export const output = {
|
||||
},
|
||||
decorations: [
|
||||
{
|
||||
object: 'range',
|
||||
object: 'decoration',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -70,15 +70,11 @@ export const output = {
|
||||
path: [0, 0],
|
||||
offset: 6,
|
||||
},
|
||||
isFocused: false,
|
||||
isAtomic: false,
|
||||
marks: [
|
||||
{
|
||||
object: 'mark',
|
||||
type: 'highlight',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
mark: {
|
||||
object: 'mark',
|
||||
type: 'highlight',
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: 'a',
|
||||
@ -65,7 +65,6 @@ export const output = {
|
||||
offset: 2,
|
||||
},
|
||||
isFocused: false,
|
||||
isAtomic: false,
|
||||
marks: null,
|
||||
},
|
||||
}
|
||||
|
@ -4,6 +4,14 @@ This document maintains a list of changes to the `slate-react` package with each
|
||||
|
||||
---
|
||||
|
||||
### `0.17.0` — August 22, 2018
|
||||
|
||||
###### NEW
|
||||
|
||||
**Updated to work with `slate@0.39.0` with the new `Decoration` and `Selection`.** This isn't a breaking change to any of the API's in `slate-react`, but it does update it to work with the newly introduced models and breaking changed in the newest version of Slate core.
|
||||
|
||||
---
|
||||
|
||||
### `0.16.0` — August 21, 2018
|
||||
|
||||
###### NEW
|
||||
|
@ -297,7 +297,7 @@ class Content extends React.Component {
|
||||
const native = window.getSelection()
|
||||
const range = findRange(native, value)
|
||||
|
||||
if (range && range.equals(selection)) {
|
||||
if (range && range.equals(selection.toRange())) {
|
||||
this.updateSelection()
|
||||
return
|
||||
}
|
||||
@ -380,7 +380,7 @@ class Content extends React.Component {
|
||||
const Container = tagName
|
||||
const { document, selection, decorations } = value
|
||||
const indexes = document.getSelectionIndexes(selection)
|
||||
const decs = document.getDecorations(stack).concat(decorations || [])
|
||||
const decs = document.getDecorations(stack).concat(decorations)
|
||||
const childrenDecorations = getChildrenDecorations(document, decs)
|
||||
|
||||
const children = document.nodes.toArray().map((child, i) => {
|
||||
|
@ -671,8 +671,9 @@ function AfterPlugin() {
|
||||
if (next) range = range.moveFocusTo(next.key, 0)
|
||||
}
|
||||
|
||||
range = document.resolveRange(range)
|
||||
change.select(range)
|
||||
let selection = document.createSelection(range)
|
||||
selection = selection.setIsFocused(true)
|
||||
change.select(selection)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,4 @@
|
||||
import getWindow from 'get-window'
|
||||
import isBackward from 'selection-is-backward'
|
||||
import { IS_IE, IS_EDGE } from 'slate-dev-environment'
|
||||
|
||||
import findPoint from './find-point'
|
||||
@ -63,8 +62,6 @@ function findRange(native, value) {
|
||||
const range = document.createRange({
|
||||
anchor,
|
||||
focus,
|
||||
isBackward: isCollapsed ? false : isBackward(native),
|
||||
isFocused: true,
|
||||
})
|
||||
|
||||
return range
|
||||
|
@ -1,12 +1,12 @@
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../../../helpers/h'
|
||||
import { Range } from 'slate'
|
||||
import { Selection } from 'slate'
|
||||
|
||||
export default function(simulator) {
|
||||
const { value } = simulator
|
||||
const text = value.document.getTexts().first()
|
||||
const selection = Range.create()
|
||||
const selection = Selection.create()
|
||||
.collapseToStartOf(text)
|
||||
.move(1)
|
||||
.focus()
|
||||
|
@ -15,7 +15,9 @@ function decorateNode(block) {
|
||||
key: text.key,
|
||||
offset: 2,
|
||||
},
|
||||
marks: [{ type: 'bold' }],
|
||||
mark: {
|
||||
type: 'bold',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -4,11 +4,98 @@ A list of changes to the `slate` package with each new version. Until `1.0.0` is
|
||||
|
||||
---
|
||||
|
||||
### `0.39.0` — August 22, 2018
|
||||
|
||||
###### NEW
|
||||
|
||||
**Introducing the `Range` model _and_ interface.** Previously the "range" concept was used in multiple different places, for the selection, for decorations, and for acting on ranges of the document. This worked okay, but it was hiding the underlying system which is that `Range` is really an interface that other models can choose to implement. Now, we still use the `Range` model for referencing parts of the document, but it can also be implemented by other models that need to attach more semantic meaning...
|
||||
|
||||
**Introducing the `Decoration` and `Selection` models.** These two new models both implement the new `Range` interface. Where previously they had to mis-use the `Range` model itself with added semantics. This just cleans up some of the confusion around overlapping properties, and allows us to add even more domain-specific methods and properties in the future without trouble.
|
||||
|
||||
###### BREAKING
|
||||
|
||||
**Decorations have changed!** Previously, decorations piggybacked on the `Range` model, using the existing `marks` property, and introducing their own `isAtomic` property. However, they have now been split out into their own `Decoration` model with a single `mark` and with the `isAtomic` property controlled by the schema. What previously would have looked like:
|
||||
|
||||
```js
|
||||
Range.create({
|
||||
anchor: { ... },
|
||||
focus: { ... },
|
||||
marks: [{ type: 'highlight' }],
|
||||
isAtomic: true,
|
||||
})
|
||||
```
|
||||
|
||||
Is now:
|
||||
|
||||
```js
|
||||
Decoration.create({
|
||||
anchor: { ... },
|
||||
focus: { ... },
|
||||
mark: { type: 'highlight' },
|
||||
})
|
||||
```
|
||||
|
||||
Each decoration maps to a single `mark` object. And the atomicity of the mark controlled in the schema instead, for example:
|
||||
|
||||
```js
|
||||
const schema = {
|
||||
marks: {
|
||||
highlight: {
|
||||
isAtomic: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**The `Range` model has reduced semantics.** Previously, since all decorations and selections were ranges, you could create ranges with an `isFocused`, `isAtomic` or `marks` properties. Now `Range` objects are much simpler, offering only an `anchor` and a `focus`, and can be extended by other models implementing the range interface. However, this means that using `Range.create` or `document.createRange` might not be what you want anymore. For example, for creating a new selection, you used to use:
|
||||
|
||||
```js
|
||||
const selection = document.createRange({
|
||||
isFocused: true,
|
||||
anchor: { ... },
|
||||
focus: { ... },
|
||||
})
|
||||
```
|
||||
|
||||
But now, you'll need to use `document.createSelection` instead:
|
||||
|
||||
```js
|
||||
const selection = document.createSelection({
|
||||
isFocused: true,
|
||||
anchor: { ... },
|
||||
focus: { ... },
|
||||
})
|
||||
```
|
||||
|
||||
**The `value.decorations` property is no longer nullable.** Previously when no decorations were applied to the value, the `decorations` property would be set to `null`. Now it will be an empty `List` object, so that the interface is more consistent.
|
||||
|
||||
###### DEPRECATED
|
||||
|
||||
**The `Node.createChildren` static method is deprecated.** This was just an alias for `Node.createList` and wasn't necessary. You can use `Node.createList` going forward for the same effect.
|
||||
|
||||
---
|
||||
|
||||
### `0.38.0` — August 21, 2018
|
||||
|
||||
###### DEPRECATED
|
||||
|
||||
**`Node.isVoid` access is deprecated.** Previously the "voidness" of a node was hardcoded in the data model. Soon it will be determined at runtime based on your editor's schema. This deprecation just ensures that you aren't using the `node.isVoid` property which will not work in future verisons.
|
||||
**`Node.isVoid` access is deprecated.** Previously the "voidness" of a node was hardcoded in the data model. Soon it will be determined at runtime based on your editor's schema. This deprecation just ensures that you aren't using the `node.isVoid` property which will not work in future verisons. What previously would have been:
|
||||
|
||||
```js
|
||||
if (node.isVoid) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Now becomes:
|
||||
|
||||
```js
|
||||
if (schema.isVoid(node)) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This requires you to have a reference to the `schema` object, which can be access as `value.schema`.
|
||||
|
||||
**`Value.isFocused/isBlurred` and `Value.hasUndos/hasRedos` are deprecated.** These properties are easily available via `value.selection` and `value.history` instead, and are now deprecated to reduce the complexity and number of different ways of doing things.
|
||||
|
||||
|
@ -376,7 +376,6 @@ Changes.replaceNodeByPath = (change, path, newNode, options) => {
|
||||
* @param {string} text
|
||||
* @param {Set<Mark>} marks (optional)
|
||||
* @param {Object} options
|
||||
*
|
||||
*/
|
||||
|
||||
Changes.replaceTextByPath = (
|
||||
|
@ -3,7 +3,7 @@ import isEmpty from 'is-empty'
|
||||
import logger from 'slate-dev-logger'
|
||||
import pick from 'lodash/pick'
|
||||
|
||||
import Range from '../models/range'
|
||||
import Selection from '../models/selection'
|
||||
|
||||
const Changes = {}
|
||||
|
||||
@ -12,7 +12,7 @@ Changes.blur = change => {
|
||||
}
|
||||
|
||||
Changes.deselect = change => {
|
||||
const range = Range.create()
|
||||
const range = Selection.create()
|
||||
change.select(range)
|
||||
}
|
||||
|
||||
@ -549,13 +549,13 @@ Changes.moveToStartOfText = change => {
|
||||
}
|
||||
|
||||
Changes.select = (change, properties, options = {}) => {
|
||||
properties = Range.createProperties(properties)
|
||||
properties = Selection.createProperties(properties)
|
||||
const { snapshot = false } = options
|
||||
const { value } = change
|
||||
const { document, selection } = value
|
||||
const props = {}
|
||||
let next = selection.setProperties(properties)
|
||||
next = document.resolveRange(next)
|
||||
next = document.resolveSelection(next)
|
||||
|
||||
// Re-compute the properties, to ensure that we get their normalized values.
|
||||
properties = pick(next, Object.keys(properties))
|
||||
|
@ -7,6 +7,7 @@
|
||||
const MODEL_TYPES = {
|
||||
BLOCK: '@@__SLATE_BLOCK__@@',
|
||||
CHANGE: '@@__SLATE_CHANGE__@@',
|
||||
DECORATION: '@@__SLATE_DECORATION__@@',
|
||||
DOCUMENT: '@@__SLATE_DOCUMENT__@@',
|
||||
HISTORY: '@@__SLATE_HISTORY__@@',
|
||||
INLINE: '@@__SLATE_INLINE__@@',
|
||||
@ -16,6 +17,7 @@ const MODEL_TYPES = {
|
||||
POINT: '@@__SLATE_POINT__@@',
|
||||
RANGE: '@@__SLATE_RANGE__@@',
|
||||
SCHEMA: '@@__SLATE_SCHEMA__@@',
|
||||
SELECTION: '@@__SLATE_SELECTION__@@',
|
||||
STACK: '@@__SLATE_STACK__@@',
|
||||
TEXT: '@@__SLATE_TEXT__@@',
|
||||
VALUE: '@@__SLATE_VALUE__@@',
|
||||
|
@ -1,7 +1,11 @@
|
||||
import './interfaces/common'
|
||||
import './interfaces/node'
|
||||
import './interfaces/range'
|
||||
import Block from './models/block'
|
||||
import Change from './models/change'
|
||||
import Changes from './changes'
|
||||
import Data from './models/data'
|
||||
import Decoration from './models/decoration'
|
||||
import Document from './models/document'
|
||||
import History from './models/history'
|
||||
import Inline from './models/inline'
|
||||
@ -15,6 +19,7 @@ import PathUtils from './utils/path-utils'
|
||||
import Point from './models/point'
|
||||
import Range from './models/range'
|
||||
import Schema from './models/schema'
|
||||
import Selection from './models/selection'
|
||||
import Stack from './models/stack'
|
||||
import Text from './models/text'
|
||||
import TextUtils from './utils/text-utils'
|
||||
@ -33,6 +38,7 @@ export {
|
||||
Change,
|
||||
Changes,
|
||||
Data,
|
||||
Decoration,
|
||||
Document,
|
||||
History,
|
||||
Inline,
|
||||
@ -48,6 +54,7 @@ export {
|
||||
resetKeyGenerator,
|
||||
resetMemoization,
|
||||
Schema,
|
||||
Selection,
|
||||
setKeyGenerator,
|
||||
Stack,
|
||||
Text,
|
||||
@ -60,6 +67,7 @@ export default {
|
||||
Block,
|
||||
Changes,
|
||||
Data,
|
||||
Decoration,
|
||||
Document,
|
||||
History,
|
||||
Inline,
|
||||
@ -75,6 +83,7 @@ export default {
|
||||
resetKeyGenerator,
|
||||
resetMemoization,
|
||||
Schema,
|
||||
Selection,
|
||||
setKeyGenerator,
|
||||
Stack,
|
||||
Text,
|
||||
|
83
packages/slate/src/interfaces/common.js
Normal file
83
packages/slate/src/interfaces/common.js
Normal file
@ -0,0 +1,83 @@
|
||||
import logger from 'slate-dev-logger'
|
||||
|
||||
import mixin from '../utils/mixin'
|
||||
import Block from '../models/block'
|
||||
import Change from '../models/change'
|
||||
import Decoration from '../models/decoration'
|
||||
import Document from '../models/document'
|
||||
import History from '../models/history'
|
||||
import Inline from '../models/inline'
|
||||
import Leaf from '../models/leaf'
|
||||
import Mark from '../models/mark'
|
||||
import Node from '../models/node'
|
||||
import Operation from '../models/operation'
|
||||
import Point from '../models/point'
|
||||
import Range from '../models/range'
|
||||
import Schema from '../models/schema'
|
||||
import Selection from '../models/selection'
|
||||
import Stack from '../models/stack'
|
||||
import Text from '../models/text'
|
||||
import Value from '../models/value'
|
||||
|
||||
/**
|
||||
* The interface that all Slate models implement.
|
||||
*
|
||||
* @type {Class}
|
||||
*/
|
||||
|
||||
class CommonInterface {
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS(...args) {
|
||||
return this.fromJSON(...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(...args) {
|
||||
return this.toJSON(...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
|
||||
return this.object
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mix in the common interface.
|
||||
*
|
||||
* @param {Record}
|
||||
*/
|
||||
|
||||
mixin(CommonInterface, [
|
||||
Block,
|
||||
Change,
|
||||
Decoration,
|
||||
Document,
|
||||
History,
|
||||
Inline,
|
||||
Leaf,
|
||||
Mark,
|
||||
Node,
|
||||
Operation,
|
||||
Point,
|
||||
Range,
|
||||
Schema,
|
||||
Selection,
|
||||
Stack,
|
||||
Text,
|
||||
Value,
|
||||
])
|
2199
packages/slate/src/interfaces/node.js
Normal file
2199
packages/slate/src/interfaces/node.js
Normal file
File diff suppressed because it is too large
Load Diff
1222
packages/slate/src/interfaces/range.js
Normal file
1222
packages/slate/src/interfaces/range.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,9 @@
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Map, Record } from 'immutable'
|
||||
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import KeyUtils from '../utils/key-utils'
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import Node from './node'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
@ -102,18 +98,12 @@ class Block extends Record(DEFAULTS) {
|
||||
type,
|
||||
isVoid: !!isVoid,
|
||||
data: Map(data),
|
||||
nodes: Block.createChildren(nodes),
|
||||
nodes: Node.createList(nodes),
|
||||
})
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Block.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Block`.
|
||||
*
|
||||
@ -144,47 +134,6 @@ class Block extends Record(DEFAULTS) {
|
||||
return 'block'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
get isVoid() {
|
||||
logger.deprecate(
|
||||
'0.38.0',
|
||||
'The `Node.isVoid` property is deprecated, please use the `Schema.isVoid()` checking method instead.'
|
||||
)
|
||||
|
||||
return this.get('isVoid')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the block is empty.
|
||||
* Returns true if block is not void and all it's children nodes are empty.
|
||||
* Void node is never empty, regardless of it's content.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
get isEmpty() {
|
||||
logger.deprecate('0.38.0', 'The `Node.isEmpty` property is deprecated.')
|
||||
|
||||
return !this.get('isVoid') && !this.nodes.some(child => !child.isEmpty)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the concatenated text of all the block's children.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
get text() {
|
||||
return this.getText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the block.
|
||||
*
|
||||
@ -207,14 +156,6 @@ class Block extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Debug from 'debug'
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import pick from 'lodash/pick'
|
||||
import { List } from 'immutable'
|
||||
|
||||
@ -61,14 +60,6 @@ class Change {
|
||||
return 'change'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an `operation` to the current value, saving the operation to the
|
||||
* history if needed.
|
||||
|
210
packages/slate/src/models/decoration.js
Normal file
210
packages/slate/src/models/decoration.js
Normal file
@ -0,0 +1,210 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Record } from 'immutable'
|
||||
|
||||
import Mark from './mark'
|
||||
import MODEL_TYPES from '../constants/model-types'
|
||||
import Point from './point'
|
||||
import Range from './range'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const DEFAULTS = {
|
||||
anchor: Point.create(),
|
||||
focus: Point.create(),
|
||||
mark: undefined,
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoration.
|
||||
*
|
||||
* @type {Decoration}
|
||||
*/
|
||||
|
||||
class Decoration extends Record(DEFAULTS) {
|
||||
/**
|
||||
* Create a new `Decoration` with `attrs`.
|
||||
*
|
||||
* @param {Object|Decoration} attrs
|
||||
* @return {Decoration}
|
||||
*/
|
||||
|
||||
static create(attrs = {}) {
|
||||
if (Decoration.isDecoration(attrs)) {
|
||||
return attrs
|
||||
}
|
||||
|
||||
if (Range.isRange(attrs)) {
|
||||
return Decoration.fromJSON(Range.createProperties(attrs))
|
||||
}
|
||||
|
||||
if (isPlainObject(attrs)) {
|
||||
return Decoration.fromJSON(attrs)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`\`Decoration.create\` only accepts objects or decorations, but you passed it: ${attrs}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of `Ranges` from `elements`.
|
||||
*
|
||||
* @param {Array<Decoration|Object>|List<Decoration|Object>} elements
|
||||
* @return {List<Decoration>}
|
||||
*/
|
||||
|
||||
static createList(elements = []) {
|
||||
if (List.isList(elements) || Array.isArray(elements)) {
|
||||
const list = new List(elements.map(Decoration.create))
|
||||
return list
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`\`Decoration.createList\` only accepts arrays or lists, but you passed it: ${elements}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dictionary of settable decoration properties from `attrs`.
|
||||
*
|
||||
* @param {Object|String|Decoration} attrs
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
static createProperties(a = {}) {
|
||||
if (Decoration.isDecoration(a)) {
|
||||
return {
|
||||
anchor: Point.createProperties(a.anchor),
|
||||
focus: Point.createProperties(a.focus),
|
||||
mark: Mark.create(a.mark),
|
||||
}
|
||||
}
|
||||
|
||||
if (isPlainObject(a)) {
|
||||
const p = {}
|
||||
if ('anchor' in a) p.anchor = Point.create(a.anchor)
|
||||
if ('focus' in a) p.focus = Point.create(a.focus)
|
||||
if ('mark' in a) p.mark = Mark.create(a.mark)
|
||||
return p
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`\`Decoration.createProperties\` only accepts objects or decorations, but you passed it: ${a}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `Decoration` from a JSON `object`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @return {Decoration}
|
||||
*/
|
||||
|
||||
static fromJSON(object) {
|
||||
const { anchor, focus } = object
|
||||
let { mark } = object
|
||||
|
||||
if (object.marks) {
|
||||
logger.deprecate(
|
||||
'0.39.0',
|
||||
'The `marks` property of decorations has been changed to a single `mark` property instead.'
|
||||
)
|
||||
|
||||
mark = object.marks[0]
|
||||
}
|
||||
|
||||
const decoration = new Decoration({
|
||||
anchor: Point.fromJSON(anchor || {}),
|
||||
focus: Point.fromJSON(focus || {}),
|
||||
mark: Mark.fromJSON(mark),
|
||||
})
|
||||
|
||||
return decoration
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an `obj` is a `Decoration`.
|
||||
*
|
||||
* @param {Any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
static isDecoration(obj) {
|
||||
return !!(obj && obj[MODEL_TYPES.DECORATION])
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
get object() {
|
||||
return 'decoration'
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new `properties` on the decoration.
|
||||
*
|
||||
* @param {Object|Range|Selection} properties
|
||||
* @return {Range}
|
||||
*/
|
||||
|
||||
setProperties(properties) {
|
||||
properties = Decoration.createProperties(properties)
|
||||
const { anchor, focus, mark } = properties
|
||||
const props = {}
|
||||
|
||||
if (anchor) {
|
||||
props.anchor = Point.create(anchor)
|
||||
}
|
||||
|
||||
if (focus) {
|
||||
props.focus = Point.create(focus)
|
||||
}
|
||||
|
||||
if (mark) {
|
||||
props.mark = Mark.create(mark)
|
||||
}
|
||||
|
||||
const decoration = this.merge(props)
|
||||
return decoration
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the decoration.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
toJSON(options = {}) {
|
||||
const object = {
|
||||
object: this.object,
|
||||
anchor: this.anchor.toJSON(options),
|
||||
focus: this.focus.toJSON(options),
|
||||
mark: this.mark.toJSON(options),
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a pseudo-symbol for type checking.
|
||||
*/
|
||||
|
||||
Decoration.prototype[MODEL_TYPES.DECORATION] = true
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Decoration}
|
||||
*/
|
||||
|
||||
export default Decoration
|
@ -1,9 +1,9 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Map, Record } from 'immutable'
|
||||
|
||||
import KeyUtils from '../utils/key-utils'
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import Node from './node'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
@ -66,18 +66,12 @@ class Document extends Record(DEFAULTS) {
|
||||
const document = new Document({
|
||||
key,
|
||||
data: new Map(data),
|
||||
nodes: Document.createChildren(nodes),
|
||||
nodes: Node.createList(nodes),
|
||||
})
|
||||
|
||||
return document
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Document.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Document`.
|
||||
*
|
||||
@ -97,45 +91,6 @@ class Document extends Record(DEFAULTS) {
|
||||
return 'document'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
get isVoid() {
|
||||
logger.deprecate(
|
||||
'0.38.0',
|
||||
'The `Node.isVoid` property is deprecated, please use the `Schema.isVoid()` checking method instead.'
|
||||
)
|
||||
|
||||
return this.get('isVoid')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the document is empty.
|
||||
* Returns true if all it's children nodes are empty.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
get isEmpty() {
|
||||
logger.deprecate('0.38.0', 'The `Node.isEmpty` property is deprecated.')
|
||||
return !this.nodes.some(child => !child.isEmpty)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the concatenated text of all the document's children.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
get text() {
|
||||
return this.getText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the document.
|
||||
*
|
||||
@ -156,14 +111,6 @@ class Document extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,6 @@
|
||||
import Debug from 'debug'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Record, Stack } from 'immutable'
|
||||
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
@ -92,12 +91,6 @@ class History extends Record(DEFAULTS) {
|
||||
return history
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = History.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `History`.
|
||||
*
|
||||
@ -117,14 +110,6 @@ class History extends Record(DEFAULTS) {
|
||||
return 'history'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an `operation` into the history.
|
||||
*
|
||||
@ -187,14 +172,6 @@ class History extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS() {
|
||||
return this.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,13 +1,9 @@
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Map, Record } from 'immutable'
|
||||
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import KeyUtils from '../utils/key-utils'
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import Node from './node'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
@ -102,18 +98,12 @@ class Inline extends Record(DEFAULTS) {
|
||||
type,
|
||||
isVoid: !!isVoid,
|
||||
data: new Map(data),
|
||||
nodes: Inline.createChildren(nodes),
|
||||
nodes: Node.createList(nodes),
|
||||
})
|
||||
|
||||
return inline
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Inline.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Inline`.
|
||||
*
|
||||
@ -144,46 +134,6 @@ class Inline extends Record(DEFAULTS) {
|
||||
return 'inline'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
get isVoid() {
|
||||
logger.deprecate(
|
||||
'0.38.0',
|
||||
'The `Node.isVoid` property is deprecated, please use the `Schema.isVoid()` checking method instead.'
|
||||
)
|
||||
|
||||
return this.get('isVoid')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the inline is empty.
|
||||
* Returns true if inline is not void and all it's children nodes are empty.
|
||||
* Void node is never empty, regardless of it's content.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
get isEmpty() {
|
||||
logger.deprecate('0.38.0', 'The `Node.isEmpty` property is deprecated.')
|
||||
return !this.get('isVoid') && !this.nodes.some(child => !child.isEmpty)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the concatenated text of all the inline's children.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
get text() {
|
||||
return this.getText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the inline.
|
||||
*
|
||||
@ -206,14 +156,6 @@ class Inline extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,4 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Record, Set } from 'immutable'
|
||||
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
@ -193,12 +192,6 @@ class Leaf extends Record(DEFAULTS) {
|
||||
return leaf
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Leaf.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Leaf`.
|
||||
*
|
||||
@ -229,14 +222,6 @@ class Leaf extends Record(DEFAULTS) {
|
||||
return 'leaf'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a `mark` at leaf, replace with newMark
|
||||
*
|
||||
@ -256,7 +241,19 @@ class Leaf extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a `set` of marks at `index` and `length`.
|
||||
* Add a `mark` to the leaf.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @returns {Text}
|
||||
*/
|
||||
|
||||
addMark(mark) {
|
||||
const { marks } = this
|
||||
return this.set('marks', marks.add(mark))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a `set` of marks to the leaf.
|
||||
*
|
||||
* @param {Set<Mark>} set
|
||||
* @returns {Text}
|
||||
@ -268,7 +265,7 @@ class Leaf extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a `mark` at `index` and `length`.
|
||||
* Remove a `mark` from the leaf.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @returns {Text}
|
||||
@ -294,14 +291,6 @@ class Leaf extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS() {
|
||||
return this.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,8 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { Map, Record, Set } from 'immutable'
|
||||
|
||||
import MODEL_TYPES, { isType } from '../constants/model-types'
|
||||
import Data from './data'
|
||||
import memoize from '../utils/memoize'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
@ -124,12 +122,6 @@ class Mark extends Record(DEFAULTS) {
|
||||
return mark
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Mark.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Mark`.
|
||||
*
|
||||
@ -158,25 +150,6 @@ class Mark extends Record(DEFAULTS) {
|
||||
return 'mark'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component for the node from a `schema`.
|
||||
*
|
||||
* @param {Schema} schema
|
||||
* @return {Component|Void}
|
||||
*/
|
||||
|
||||
getComponent(schema) {
|
||||
return schema.__getComponent(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the mark.
|
||||
*
|
||||
@ -192,14 +165,6 @@ class Mark extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS() {
|
||||
return this.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,12 +173,6 @@ class Mark extends Record(DEFAULTS) {
|
||||
|
||||
Mark.prototype[MODEL_TYPES.MARK] = true
|
||||
|
||||
/**
|
||||
* Memoize read methods.
|
||||
*/
|
||||
|
||||
memoize(Mark.prototype, ['getComponent'])
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,11 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { List, Record } from 'immutable'
|
||||
|
||||
import MODEL_TYPES from '../constants/model-types'
|
||||
import Mark from './mark'
|
||||
import Node from './node'
|
||||
import PathUtils from '../utils/path-utils'
|
||||
import Range from './range'
|
||||
import Selection from './selection'
|
||||
import Value from './value'
|
||||
|
||||
/**
|
||||
@ -155,7 +154,7 @@ class Operation extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
if (key === 'selection') {
|
||||
v = Range.create(v)
|
||||
v = Selection.create(v)
|
||||
}
|
||||
|
||||
if (key === 'value') {
|
||||
@ -175,7 +174,7 @@ class Operation extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
if (key === 'properties' && type === 'set_selection') {
|
||||
v = Range.createProperties(v)
|
||||
v = Selection.createProperties(v)
|
||||
}
|
||||
|
||||
if (key === 'properties' && type === 'set_value') {
|
||||
@ -193,12 +192,6 @@ class Operation extends Record(DEFAULTS) {
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Operation.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Operation`.
|
||||
*
|
||||
@ -231,14 +224,6 @@ class Operation extends Record(DEFAULTS) {
|
||||
return 'operation'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the operation.
|
||||
*
|
||||
@ -316,14 +301,6 @@ class Operation extends Record(DEFAULTS) {
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,12 +101,6 @@ class Point extends Record(DEFAULTS) {
|
||||
return point
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Point.fromJSON
|
||||
|
||||
/**
|
||||
* Check if an `obj` is a `Point`.
|
||||
*
|
||||
@ -403,11 +397,17 @@ class Point extends Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
* Unset the point.
|
||||
*
|
||||
* @return {Point}
|
||||
*/
|
||||
|
||||
toJS() {
|
||||
return this.toJSON()
|
||||
unset() {
|
||||
return this.merge({
|
||||
key: null,
|
||||
offset: null,
|
||||
path: null,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -218,7 +218,7 @@ class Schema extends Record(DEFAULTS) {
|
||||
|
||||
for (const plugin of plugins) {
|
||||
const { schema = {} } = plugin
|
||||
const { blocks = {}, inlines = {} } = schema
|
||||
const { blocks = {}, inlines = {}, marks = {} } = schema
|
||||
|
||||
if (schema.rules) {
|
||||
rules = rules.concat(schema.rules)
|
||||
@ -244,6 +244,13 @@ class Schema extends Record(DEFAULTS) {
|
||||
...inlines[key],
|
||||
})
|
||||
}
|
||||
|
||||
for (const key in marks) {
|
||||
rules.push({
|
||||
match: [{ object: 'mark', type: key }],
|
||||
...marks[key],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const stack = Stack.create({ plugins })
|
||||
@ -251,12 +258,6 @@ class Schema extends Record(DEFAULTS) {
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Schema.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Schema`.
|
||||
*
|
||||
@ -278,14 +279,6 @@ class Schema extends Record(DEFAULTS) {
|
||||
return 'schema'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a `node` with the schema, returning an error if it's invalid.
|
||||
*
|
||||
@ -359,6 +352,21 @@ class Schema extends Record(DEFAULTS) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a mark is void.
|
||||
*
|
||||
* @param {Mark}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
isAtomic(mark) {
|
||||
const rule = this.rules.find(
|
||||
r => 'isAtomic' in r && testRules(mark, r.match)
|
||||
)
|
||||
|
||||
return rule ? rule.isAtomic : false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is void.
|
||||
*
|
||||
@ -386,14 +394,6 @@ class Schema extends Record(DEFAULTS) {
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS() {
|
||||
return this.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -465,28 +465,28 @@ function defaultNormalize(change, error) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a `node` matches one of a set of `rules`.
|
||||
* Check that an `object` matches one of a set of `rules`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @param {Mixed} object
|
||||
* @param {Object|Array} rules
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
function testRules(node, rules) {
|
||||
const error = validateRules(node, rules)
|
||||
function testRules(object, rules) {
|
||||
const error = validateRules(object, rules)
|
||||
return !error
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a `node` matches a `rule` object or array.
|
||||
* Validate that a `object` matches a `rule` object or array.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @param {Mixed} object
|
||||
* @param {Object|Array} rule
|
||||
* @param {Array|Void} rules
|
||||
* @return {Error|Void}
|
||||
*/
|
||||
|
||||
function validateRules(node, rule, rules, options = {}) {
|
||||
function validateRules(object, rule, rules, options = {}) {
|
||||
const { every = false } = options
|
||||
|
||||
if (Array.isArray(rule)) {
|
||||
@ -494,7 +494,7 @@ function validateRules(node, rule, rules, options = {}) {
|
||||
let first
|
||||
|
||||
for (const r of array) {
|
||||
const error = validateRules(node, r, rules)
|
||||
const error = validateRules(object, r, rules)
|
||||
first = first || error
|
||||
if (every && error) return error
|
||||
if (!every && !error) return
|
||||
@ -504,15 +504,15 @@ function validateRules(node, rule, rules, options = {}) {
|
||||
}
|
||||
|
||||
const error =
|
||||
validateObject(node, rule) ||
|
||||
validateType(node, rule) ||
|
||||
validateIsVoid(node, rule) ||
|
||||
validateData(node, rule) ||
|
||||
validateMarks(node, rule) ||
|
||||
validateText(node, rule) ||
|
||||
validateFirst(node, rule) ||
|
||||
validateLast(node, rule) ||
|
||||
validateNodes(node, rule, rules)
|
||||
validateObject(object, rule) ||
|
||||
validateType(object, rule) ||
|
||||
validateIsVoid(object, rule) ||
|
||||
validateData(object, rule) ||
|
||||
validateMarks(object, rule) ||
|
||||
validateText(object, rule) ||
|
||||
validateFirst(object, rule) ||
|
||||
validateLast(object, rule) ||
|
||||
validateNodes(object, rule, rules)
|
||||
|
||||
return error
|
||||
}
|
||||
|
256
packages/slate/src/models/selection.js
Normal file
256
packages/slate/src/models/selection.js
Normal file
@ -0,0 +1,256 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import logger from 'slate-dev-logger'
|
||||
import { Record, Set } from 'immutable'
|
||||
|
||||
import MODEL_TYPES from '../constants/model-types'
|
||||
import Mark from './mark'
|
||||
import Point from './point'
|
||||
import Range from './range'
|
||||
|
||||
/**
|
||||
* Default properties.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const DEFAULTS = {
|
||||
anchor: Point.create(),
|
||||
focus: Point.create(),
|
||||
isFocused: false,
|
||||
marks: null,
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection.
|
||||
*
|
||||
* @type {Selection}
|
||||
*/
|
||||
|
||||
class Selection extends Record(DEFAULTS) {
|
||||
/**
|
||||
* Create a new `Selection` with `attrs`.
|
||||
*
|
||||
* @param {Object|Selection} attrs
|
||||
* @return {Selection}
|
||||
*/
|
||||
|
||||
static create(attrs = {}) {
|
||||
if (Selection.isSelection(attrs)) {
|
||||
return attrs
|
||||
}
|
||||
|
||||
if (Range.isRange(attrs)) {
|
||||
return Selection.fromJSON(Range.createProperties(attrs))
|
||||
}
|
||||
|
||||
if (isPlainObject(attrs)) {
|
||||
return Selection.fromJSON(attrs)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`\`Selection.create\` only accepts objects, ranges or selections, but you passed it: ${attrs}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dictionary of settable selection properties from `attrs`.
|
||||
*
|
||||
* @param {Object|String|Selection} attrs
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
static createProperties(a = {}) {
|
||||
if (Selection.isSelection(a)) {
|
||||
return {
|
||||
anchor: Point.createProperties(a.anchor),
|
||||
focus: Point.createProperties(a.focus),
|
||||
isFocused: a.isFocused,
|
||||
marks: a.marks,
|
||||
}
|
||||
}
|
||||
|
||||
if (Range.isRange(a)) {
|
||||
return {
|
||||
anchor: Point.createProperties(a.anchor),
|
||||
focus: Point.createProperties(a.focus),
|
||||
}
|
||||
}
|
||||
|
||||
if (isPlainObject(a)) {
|
||||
const p = {}
|
||||
if ('anchor' in a) p.anchor = Point.create(a.anchor)
|
||||
if ('focus' in a) p.focus = Point.create(a.focus)
|
||||
if ('isFocused' in a) p.isFocused = a.isFocused
|
||||
if ('marks' in a)
|
||||
p.marks = a.marks == null ? null : Mark.createSet(a.marks)
|
||||
return p
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`\`Selection.createProperties\` only accepts objects, ranges or selections, but you passed it: ${a}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `Selection` from a JSON `object`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @return {Selection}
|
||||
*/
|
||||
|
||||
static fromJSON(object) {
|
||||
let { anchor, focus, isFocused = false, marks = null } = object
|
||||
|
||||
if (
|
||||
!anchor &&
|
||||
(object.anchorKey || object.anchorOffset || object.anchorPath)
|
||||
) {
|
||||
logger.deprecate(
|
||||
'0.37.0',
|
||||
'`Selection` objects now take a `Point` object as an `anchor` instead of taking `anchorKey/Offset/Path` properties. But you passed:',
|
||||
object
|
||||
)
|
||||
|
||||
anchor = {
|
||||
key: object.anchorKey,
|
||||
offset: object.anchorOffset,
|
||||
path: object.anchorPath,
|
||||
}
|
||||
}
|
||||
|
||||
if (!focus && (object.focusKey || object.focusOffset || object.focusPath)) {
|
||||
logger.deprecate(
|
||||
'0.37.0',
|
||||
'`Selection` objects now take a `Point` object as a `focus` instead of taking `focusKey/Offset/Path` properties. But you passed:',
|
||||
object
|
||||
)
|
||||
|
||||
focus = {
|
||||
key: object.focusKey,
|
||||
offset: object.focusOffset,
|
||||
path: object.focusPath,
|
||||
}
|
||||
}
|
||||
|
||||
const selection = new Selection({
|
||||
anchor: Point.fromJSON(anchor || {}),
|
||||
focus: Point.fromJSON(focus || {}),
|
||||
isFocused,
|
||||
marks: marks == null ? null : new Set(marks.map(Mark.fromJSON)),
|
||||
})
|
||||
|
||||
return selection
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an `obj` is a `Selection`.
|
||||
*
|
||||
* @param {Any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
static isSelection(obj) {
|
||||
return !!(obj && obj[MODEL_TYPES.SELECTION])
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
get object() {
|
||||
return 'selection'
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the selection is blurred.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
get isBlurred() {
|
||||
return !this.isFocused
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the `isFocused` property to a new `value`.
|
||||
*
|
||||
* @param {Boolean} value
|
||||
* @return {Selection}
|
||||
*/
|
||||
|
||||
setIsFocused(value) {
|
||||
const selection = this.set('isFocused', value)
|
||||
return selection
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the `marks` property to a new set of `marks`.
|
||||
*
|
||||
* @param {Set} marks
|
||||
* @return {Selection}
|
||||
*/
|
||||
|
||||
setMarks(marks) {
|
||||
const selection = this.set('marks', marks)
|
||||
return selection
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new `properties` on the selection.
|
||||
*
|
||||
* @param {Object|Range|Selection} properties
|
||||
* @return {Range}
|
||||
*/
|
||||
|
||||
setProperties(properties) {
|
||||
properties = Selection.createProperties(properties)
|
||||
const { anchor, focus, ...props } = properties
|
||||
|
||||
if (anchor) {
|
||||
props.anchor = Point.create(anchor)
|
||||
}
|
||||
|
||||
if (focus) {
|
||||
props.focus = Point.create(focus)
|
||||
}
|
||||
|
||||
const selection = this.merge(props)
|
||||
return selection
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON representation of the selection.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
toJSON(options = {}) {
|
||||
const object = {
|
||||
object: this.object,
|
||||
anchor: this.anchor.toJSON(options),
|
||||
focus: this.focus.toJSON(options),
|
||||
isFocused: this.isFocused,
|
||||
marks:
|
||||
this.marks == null ? null : this.marks.toArray().map(m => m.toJSON()),
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a pseudo-symbol for type checking.
|
||||
*/
|
||||
|
||||
Selection.prototype[MODEL_TYPES.SELECTION] = true
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Selection}
|
||||
*/
|
||||
|
||||
export default Selection
|
@ -1,4 +1,3 @@
|
||||
import logger from 'slate-dev-logger'
|
||||
import { Record } from 'immutable'
|
||||
|
||||
import MODEL_TYPES from '../constants/model-types'
|
||||
@ -54,14 +53,6 @@ class Stack extends Record(DEFAULTS) {
|
||||
return 'stack'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all plugins with `property`.
|
||||
*
|
||||
|
@ -117,12 +117,6 @@ class Text extends Record(DEFAULTS) {
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Text.fromJSON
|
||||
|
||||
/**
|
||||
* Check if `any` is a `Text`.
|
||||
*
|
||||
@ -165,14 +159,6 @@ class Text extends Record(DEFAULTS) {
|
||||
return 'text'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the node empty?
|
||||
*
|
||||
@ -289,17 +275,6 @@ class Text extends Record(DEFAULTS) {
|
||||
return this.setLeaves(leaves)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the decorations for the node from a `schema`.
|
||||
*
|
||||
* @param {Schema} schema
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
getDecorations(schema) {
|
||||
return schema.__getDecorations(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the leaves for a list of `decorations`.
|
||||
*
|
||||
@ -314,8 +289,8 @@ class Text extends Record(DEFAULTS) {
|
||||
if (this.text.length === 0) return leaves
|
||||
const { key } = this
|
||||
|
||||
decorations.forEach(range => {
|
||||
const { start, end, marks } = range
|
||||
decorations.forEach(dec => {
|
||||
const { start, end, mark } = dec
|
||||
const hasStart = start.key == key
|
||||
const hasEnd = end.key == key
|
||||
|
||||
@ -329,12 +304,12 @@ class Text extends Record(DEFAULTS) {
|
||||
if (index !== 0 || length < this.text.length) {
|
||||
const [before, bundle] = Leaf.splitLeaves(leaves, index)
|
||||
const [middle, after] = Leaf.splitLeaves(bundle, length)
|
||||
leaves = before.concat(middle.map(x => x.addMarks(marks)), after)
|
||||
leaves = before.concat(middle.map(x => x.addMark(mark)), after)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
leaves = leaves.map(x => x.addMarks(marks))
|
||||
leaves = leaves.map(x => x.addMark(mark))
|
||||
})
|
||||
|
||||
if (leaves === this.leaves) return leaves
|
||||
@ -674,14 +649,6 @@ class Text extends Record(DEFAULTS) {
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a `mark` at `index` and `length` with `properties`.
|
||||
*
|
||||
@ -811,7 +778,6 @@ Text.prototype[MODEL_TYPES.TEXT] = true
|
||||
*/
|
||||
|
||||
memoize(Text.prototype, [
|
||||
'getDecorations',
|
||||
'getActiveMarks',
|
||||
'getMarks',
|
||||
'getMarksAsArray',
|
||||
|
@ -6,9 +6,10 @@ import MODEL_TYPES from '../constants/model-types'
|
||||
import PathUtils from '../utils/path-utils'
|
||||
import Change from './change'
|
||||
import Data from './data'
|
||||
import Decoration from './decoration'
|
||||
import Document from './document'
|
||||
import History from './history'
|
||||
import Range from './range'
|
||||
import Selection from './selection'
|
||||
import Schema from './schema'
|
||||
|
||||
/**
|
||||
@ -18,12 +19,12 @@ import Schema from './schema'
|
||||
*/
|
||||
|
||||
const DEFAULTS = {
|
||||
data: new Map(),
|
||||
decorations: null,
|
||||
data: Map(),
|
||||
decorations: List(),
|
||||
document: Document.create(),
|
||||
history: History.create(),
|
||||
schema: Schema.create(),
|
||||
selection: Range.create(),
|
||||
selection: Selection.create(),
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +75,8 @@ class Value extends Record(DEFAULTS) {
|
||||
if (isPlainObject(a)) {
|
||||
const p = {}
|
||||
if ('data' in a) p.data = Data.create(a.data)
|
||||
if ('decorations' in a) p.decorations = Range.createList(a.decorations)
|
||||
if ('decorations' in a)
|
||||
p.decorations = Decoration.createList(a.decorations)
|
||||
if ('schema' in a) p.schema = Schema.create(a.schema)
|
||||
return p
|
||||
}
|
||||
@ -98,7 +100,7 @@ class Value extends Record(DEFAULTS) {
|
||||
let { document = {}, selection = {}, schema = {}, history = {} } = object
|
||||
let data = new Map()
|
||||
document = Document.fromJSON(document)
|
||||
selection = Range.fromJSON(selection)
|
||||
selection = Selection.fromJSON(selection)
|
||||
schema = Schema.fromJSON(schema)
|
||||
history = History.fromJSON(history)
|
||||
|
||||
@ -114,15 +116,15 @@ class Value extends Record(DEFAULTS) {
|
||||
data = data.merge(object.data)
|
||||
}
|
||||
|
||||
selection = document.createRange(selection)
|
||||
selection = document.createSelection(selection)
|
||||
|
||||
if (selection.isUnset) {
|
||||
const text = document.getFirstText()
|
||||
if (text) selection = selection.moveToStartOfNode(text)
|
||||
selection = document.createRange(selection)
|
||||
selection = document.createSelection(selection)
|
||||
}
|
||||
|
||||
selection = document.createRange(selection)
|
||||
selection = document.createSelection(selection)
|
||||
|
||||
let value = new Value({
|
||||
data,
|
||||
@ -139,12 +141,6 @@ class Value extends Record(DEFAULTS) {
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `fromJS`.
|
||||
*/
|
||||
|
||||
static fromJS = Value.fromJSON
|
||||
|
||||
/**
|
||||
* Check if a `value` is a `Value`.
|
||||
*
|
||||
@ -166,14 +162,6 @@ class Value extends Record(DEFAULTS) {
|
||||
return 'value'
|
||||
}
|
||||
|
||||
get kind() {
|
||||
logger.deprecate(
|
||||
'slate@0.32.0',
|
||||
'The `kind` property of Slate objects has been renamed to `object`.'
|
||||
)
|
||||
return this.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current start text node's closest block parent.
|
||||
*
|
||||
@ -543,7 +531,7 @@ class Value extends Record(DEFAULTS) {
|
||||
|
||||
insertText(path, offset, text, marks) {
|
||||
let value = this
|
||||
let { document } = value
|
||||
let { document, schema } = value
|
||||
document = document.insertText(path, offset, text, marks)
|
||||
value = value.set('document', document)
|
||||
|
||||
@ -551,7 +539,9 @@ class Value extends Record(DEFAULTS) {
|
||||
const node = document.assertNode(path)
|
||||
|
||||
value = value.mapRanges(range => {
|
||||
const { anchor, focus, isBackward, isAtomic } = range
|
||||
const { anchor, focus, isBackward } = range
|
||||
const isAtomic =
|
||||
Decoration.isDecoration(range) && schema.isAtomic(range.mark)
|
||||
|
||||
if (
|
||||
anchor.key === node.key &&
|
||||
@ -682,13 +672,13 @@ class Value extends Record(DEFAULTS) {
|
||||
if (node.hasNode(start.key)) {
|
||||
range = prev
|
||||
? range.moveStartTo(prev.key, prev.text.length)
|
||||
: next ? range.moveStartTo(next.key, 0) : Range.create()
|
||||
: next ? range.moveStartTo(next.key, 0) : range.unset()
|
||||
}
|
||||
|
||||
if (node.hasNode(end.key)) {
|
||||
range = prev
|
||||
? range.moveEndTo(prev.key, prev.text.length)
|
||||
: next ? range.moveEndTo(next.key, 0) : Range.create()
|
||||
: next ? range.moveEndTo(next.key, 0) : range.unset()
|
||||
}
|
||||
|
||||
range = range.updatePoints(point => point.setPath(null))
|
||||
@ -717,6 +707,7 @@ class Value extends Record(DEFAULTS) {
|
||||
const node = document.assertNode(path)
|
||||
const { length } = text
|
||||
const rangeOffset = offset + length
|
||||
|
||||
value = value.clearAtomicRanges(node.key, offset, offset + length)
|
||||
|
||||
value = value.mapRanges(range => {
|
||||
@ -781,6 +772,41 @@ class Value extends Record(DEFAULTS) {
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on the value.
|
||||
*
|
||||
* @param {Object} properties
|
||||
* @return {Value}
|
||||
*/
|
||||
|
||||
setProperties(properties) {
|
||||
let value = this
|
||||
const { document } = value
|
||||
const { data, decorations, history, schema } = properties
|
||||
const props = {}
|
||||
|
||||
if (data) {
|
||||
props.data = data
|
||||
}
|
||||
|
||||
if (history) {
|
||||
props.history = history
|
||||
}
|
||||
|
||||
if (schema) {
|
||||
props.schema = schema
|
||||
}
|
||||
|
||||
if (decorations) {
|
||||
props.decorations = decorations.map(d => {
|
||||
return d.isSet ? d : document.resolveDecoration(d)
|
||||
})
|
||||
}
|
||||
|
||||
value = value.merge(props)
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `properties` on the selection.
|
||||
*
|
||||
@ -793,7 +819,7 @@ class Value extends Record(DEFAULTS) {
|
||||
let value = this
|
||||
let { document, selection } = value
|
||||
const next = selection.setProperties(properties)
|
||||
selection = document.resolveRange(next)
|
||||
selection = document.resolveSelection(next)
|
||||
value = value.set('selection', selection)
|
||||
return value
|
||||
}
|
||||
@ -848,25 +874,19 @@ class Value extends Record(DEFAULTS) {
|
||||
let value = this
|
||||
const { document, selection, decorations } = value
|
||||
|
||||
if (selection) {
|
||||
let next = selection.isSet ? iterator(selection) : selection
|
||||
if (!next) next = Range.create()
|
||||
if (next !== selection) next = document.createRange(next)
|
||||
value = value.set('selection', next)
|
||||
}
|
||||
let sel = selection.isSet ? iterator(selection) : selection
|
||||
if (!sel) sel = selection.unset()
|
||||
if (sel !== selection) sel = document.createSelection(sel)
|
||||
value = value.set('selection', sel)
|
||||
|
||||
if (decorations) {
|
||||
let next = decorations.map(decoration => {
|
||||
let n = decoration.isSet ? iterator(decoration) : decoration
|
||||
if (n && n !== decoration) n = document.createRange(n)
|
||||
return n
|
||||
})
|
||||
|
||||
next = next.filter(decoration => !!decoration)
|
||||
next = next.size ? next : null
|
||||
value = value.set('decorations', next)
|
||||
}
|
||||
let decs = decorations.map(decoration => {
|
||||
let n = decoration.isSet ? iterator(decoration) : decoration
|
||||
if (n && n !== decoration) n = document.createDecoration(n)
|
||||
return n
|
||||
})
|
||||
|
||||
decs = decs.filter(decoration => !!decoration)
|
||||
value = value.set('decorations', decs)
|
||||
return value
|
||||
}
|
||||
|
||||
@ -880,8 +900,13 @@ class Value extends Record(DEFAULTS) {
|
||||
*/
|
||||
|
||||
clearAtomicRanges(key, from, to = null) {
|
||||
return this.mapRanges(range => {
|
||||
const { isAtomic, start, end } = range
|
||||
let value = this
|
||||
const { schema } = value
|
||||
|
||||
value = this.mapRanges(range => {
|
||||
if (!Decoration.isDecoration(range)) return range
|
||||
const { start, end, mark } = range
|
||||
const isAtomic = schema.isAtomic(mark)
|
||||
if (!isAtomic) return range
|
||||
if (start.key !== key) return range
|
||||
|
||||
@ -899,6 +924,8 @@ class Value extends Record(DEFAULTS) {
|
||||
|
||||
return range
|
||||
})
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -920,8 +947,8 @@ class Value extends Record(DEFAULTS) {
|
||||
|
||||
if (options.preserveDecorations) {
|
||||
object.decorations = this.decorations
|
||||
? this.decorations.toArray().map(d => d.toJSON(options))
|
||||
: null
|
||||
.toArray()
|
||||
.map(d => d.toJSON(options))
|
||||
}
|
||||
|
||||
if (options.preserveHistory) {
|
||||
@ -939,14 +966,6 @@ class Value extends Record(DEFAULTS) {
|
||||
return object
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias `toJS`.
|
||||
*/
|
||||
|
||||
toJS(options) {
|
||||
return this.toJSON(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
|
@ -92,7 +92,7 @@ function applyOperation(value, op) {
|
||||
|
||||
case 'set_value': {
|
||||
const { properties } = op
|
||||
const next = value.merge(properties)
|
||||
const next = value.setProperties(properties)
|
||||
return next
|
||||
}
|
||||
|
||||
|
22
packages/slate/src/utils/mixin.js
Normal file
22
packages/slate/src/utils/mixin.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Mix in an `Interface` to a `Class`.
|
||||
*
|
||||
* @param {Class} Class
|
||||
* @param {Class} Interface
|
||||
*/
|
||||
|
||||
export default function mixin(Interface, Classes) {
|
||||
for (const Class of Classes) {
|
||||
for (const name of Object.getOwnPropertyNames(Interface)) {
|
||||
if (Class.hasOwnProperty(name)) continue
|
||||
const desc = Object.getOwnPropertyDescriptor(Interface, name)
|
||||
Object.defineProperty(Class, name, desc)
|
||||
}
|
||||
|
||||
for (const name of Object.getOwnPropertyNames(Interface.prototype)) {
|
||||
if (Class.prototype.hasOwnProperty(name)) continue
|
||||
const desc = Object.getOwnPropertyDescriptor(Interface.prototype, name)
|
||||
Object.defineProperty(Class.prototype, name, desc)
|
||||
}
|
||||
}
|
||||
}
|
@ -19,11 +19,6 @@ export const input = (
|
||||
export const output = (
|
||||
<value>
|
||||
<document />
|
||||
<selection
|
||||
anchorKey={null}
|
||||
anchorOffset={0}
|
||||
focusKey={null}
|
||||
focusOffset={0}
|
||||
/>
|
||||
<selection focused />
|
||||
</value>
|
||||
)
|
||||
|
@ -19,11 +19,6 @@ export const input = (
|
||||
export const output = (
|
||||
<value>
|
||||
<document />
|
||||
<selection
|
||||
anchorKey={null}
|
||||
anchorOffset={0}
|
||||
focusKey={null}
|
||||
focusOffset={0}
|
||||
/>
|
||||
<selection focused />
|
||||
</value>
|
||||
)
|
||||
|
@ -19,11 +19,6 @@ export const input = (
|
||||
export const output = (
|
||||
<value>
|
||||
<document />
|
||||
<selection
|
||||
anchorKey={null}
|
||||
anchorOffset={0}
|
||||
focusKey={null}
|
||||
focusOffset={0}
|
||||
/>
|
||||
<selection focused />
|
||||
</value>
|
||||
)
|
||||
|
@ -29,8 +29,16 @@ const h = createHyperscript({
|
||||
fontSize: 'font-size',
|
||||
},
|
||||
decorations: {
|
||||
result: 'result',
|
||||
highlight: 'highlight',
|
||||
},
|
||||
schema: {
|
||||
marks: {
|
||||
result: {
|
||||
isAtomic: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default h
|
||||
|
@ -16,7 +16,7 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
wor<highlight atomic>d</highlight>
|
||||
wor<result>d</result>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
@ -26,7 +26,7 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
w<highlight atomic>d</highlight>
|
||||
w<result>d</result>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@ -16,7 +16,7 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<highlight atomic>w</highlight>ord
|
||||
<result>w</result>ord
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
@ -26,7 +26,7 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<highlight atomic>w</highlight>d
|
||||
<result>w</result>d
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@ -16,7 +16,7 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
w<highlight atomic>or</highlight>d
|
||||
w<result>or</result>d
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@ -42,7 +42,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
key: '0',
|
||||
@ -57,7 +57,6 @@ export const output = {
|
||||
},
|
||||
isFocused: false,
|
||||
marks: null,
|
||||
isAtomic: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export const output = {
|
||||
],
|
||||
},
|
||||
selection: {
|
||||
object: 'range',
|
||||
object: 'selection',
|
||||
anchor: {
|
||||
object: 'point',
|
||||
path: [0, 0],
|
||||
@ -50,7 +50,6 @@ export const output = {
|
||||
},
|
||||
isFocused: false,
|
||||
marks: null,
|
||||
isAtomic: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user