mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-09 00:36:41 +02: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:
@@ -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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user