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

Add data field to editor State. (#830)

* Add `data` field to editor `State`.
Plugins can use it to store their own internal state.

* Remove `serialize` and `deserialize` plugin methods.

* Add operation to set `data` on a state.

* Add `setDataOperation` tests.

* Remove the possibility to use keys different from strings.
Add `preserveData` option to `raw.serialize`.
Rewrite `set-data` test exploiting the new option.
This commit is contained in:
AlbertHilb
2017-07-11 22:41:13 +02:00
committed by Ian Storm Taylor
parent 74bf684ec9
commit 17cfde67ce
8 changed files with 145 additions and 14 deletions

View File

@@ -4,7 +4,7 @@ import Document from './document'
import SCHEMA from '../schemas/core'
import Selection from './selection'
import Transform from './transform'
import { Record, Set, Stack, List } from 'immutable'
import { Record, Set, Stack, List, Map } from 'immutable'
/**
* History.
@@ -27,6 +27,7 @@ const DEFAULTS = {
document: new Document(),
selection: new Selection(),
history: new History(),
data: new Map(),
isNative: false
}
@@ -52,13 +53,24 @@ class State extends new Record(DEFAULTS) {
const document = Document.create(properties.document)
let selection = Selection.create(properties.selection)
let data = new Map()
if (selection.isUnset) {
const text = document.getFirstText()
selection = selection.collapseToStartOf(text)
}
const state = new State({ document, selection })
// Set default value for `data`.
if (options.plugins) {
for (const plugin of options.plugins) {
if (plugin.data) data = data.merge(plugin.data)
}
}
// Then add data provided in `properties`.
if (properties.data) data = data.merge(properties.data)
const state = new State({ document, selection, data })
return options.normalize === false
? state

View File

@@ -184,7 +184,7 @@ const Raw = {
selection = Raw.deserializeSelection(object.selection, options)
}
return State.create({ document, selection }, options)
return State.create({ data: object.data, document, selection }, options)
},
/**
@@ -400,12 +400,15 @@ const Raw = {
serializeState(state, options = {}) {
const object = {
document: Raw.serializeDocument(state.document, options),
selection: Raw.serializeSelection(state.selection, options),
kind: state.kind
}
if (!options.preserveSelection) {
delete object.selection
if (options.preserveSelection) {
object.selection = Raw.serializeSelection(state.selection, options)
}
if (options.preserveStateData) {
object.data = state.data.toJSON()
}
const ret = options.terse
@@ -546,14 +549,17 @@ const Raw = {
*/
tersifyState(object) {
if (object.selection == null) {
return object.document
const { data, document, selection } = object
const emptyData = isEmpty(data)
if (!selection && emptyData) {
return document
}
return {
document: object.document,
selection: object.selection
}
const ret = { document }
if (!emptyData) ret.data = data
if (selection) ret.selection = selection
return ret
},
/**
@@ -673,9 +679,10 @@ const Raw = {
*/
untersifyState(object) {
if (object.selection || object.document) {
if (object.document) {
return {
kind: 'state',
data: object.data,
document: object.document,
selection: object.selection,
}

View File

@@ -40,7 +40,9 @@ const OPERATIONS = {
set_node: setNode,
split_node: splitNode,
// Selection operations.
set_selection: setSelection
set_selection: setSelection,
// State data operations.
set_data: setData
}
/**
@@ -334,6 +336,23 @@ function removeText(state, operation) {
return state
}
/**
* Set `data` on `state`.
*
* @param {State} state
* @param {Object} operation
* @return {State}
*/
function setData(state, operation) {
const { properties } = operation
let { data } = state
data = data.merge(properties)
state = state.set('data', data)
return state
}
/**
* Set `properties` on mark on text at `offset` and `length` in node by `path`.
*

View File

@@ -285,6 +285,36 @@ Transforms.removeTextOperation = (transform, path, offset, length) => {
transform.applyOperation(operation)
}
/**
* Merge `properties` into state `data`.
*
* @param {Transform} transform
* @param {Object} properties
*/
Transforms.setDataOperation = (transform, properties) => {
const { state } = transform
const { data } = state
const inverseProps = {}
for (const k in properties) {
inverseProps[k] = data[k]
}
const inverse = [{
type: 'set_data',
properties: inverseProps
}]
const operation = {
type: 'set_data',
properties,
inverse,
}
transform.applyOperation(operation)
}
/**
* Set `properties` on mark on text at `offset` and `length` in node by `path`.
*

View File

@@ -0,0 +1,14 @@
export default function (state) {
const data = {
key1: "value1",
key2: "value2"
}
const next = state
.transform()
.setDataOperation(data)
.apply()
return next
}

View File

@@ -0,0 +1,12 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word
- kind: block
type: paragraph
nodes:
- kind: text
text: another

View File

@@ -0,0 +1,16 @@
data:
key1: value1
key2: value2
document:
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word
- kind: block
type: paragraph
nodes:
- kind: text
text: another

View File

@@ -177,4 +177,25 @@ describe('transforms', async () => {
})
}
})
describe('state-data', () => {
const dir = resolve(__dirname, './fixtures/state-data')
const tests = fs.readdirSync(dir)
for (const test of tests) {
if (test[0] == '.') continue
it(test, async () => {
const testDir = resolve(dir, test)
const fn = require(testDir).default
const input = await readYaml(resolve(testDir, 'input.yaml'))
const expected = await readYaml(resolve(testDir, 'output.yaml'))
let state = Raw.deserialize(input, { terse: true })
state = fn(state)
const output = Raw.serialize(state, { terse: true, preserveStateData: true })
assert.deepEqual(strip(output), strip(expected))
})
}
})
})