mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +02:00
Data property for Operation model (#2373)
* Add possibility to add arbitrary data to operations Using a data property similar to the node models (block, inline, etc) * Fix broken example 'Syncing Operations' Using data property on operations that are applied
This commit is contained in:
committed by
Ian Storm Taylor
parent
220dd476e3
commit
b773d44ae9
@@ -239,35 +239,43 @@ class SyncingOperationsExample extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* When editor one changes, send document-alterting operations to edtior two.
|
||||
* When editor one changes, send document-altering operations to editor two.
|
||||
*
|
||||
* @param {Array} operations
|
||||
*/
|
||||
|
||||
onOneChange = change => {
|
||||
const ops = change.operations
|
||||
.filter(o => o.type != 'set_selection' && o.type != 'set_value')
|
||||
.filter(
|
||||
o =>
|
||||
o.type != 'set_selection' &&
|
||||
o.type != 'set_value' &&
|
||||
(!o.data || !o.data.has('source'))
|
||||
)
|
||||
.toJS()
|
||||
.map(o => ({ ...o, data: { source: 'one' } }))
|
||||
|
||||
setTimeout(() => {
|
||||
ops.forEach(o => this.two.applyOperation(o))
|
||||
})
|
||||
setTimeout(() => this.two.applyOperations(ops))
|
||||
}
|
||||
|
||||
/**
|
||||
* When editor two changes, send document-alterting operations to edtior one.
|
||||
* When editor two changes, send document-altering operations to editor one.
|
||||
*
|
||||
* @param {Array} operations
|
||||
*/
|
||||
|
||||
onTwoChange = change => {
|
||||
const ops = change.operations
|
||||
.filter(o => o.type != 'set_selection' && o.type != 'set_value')
|
||||
.filter(
|
||||
o =>
|
||||
o.type != 'set_selection' &&
|
||||
o.type != 'set_value' &&
|
||||
(!o.data || !o.data.has('source'))
|
||||
)
|
||||
.toJS()
|
||||
.map(o => ({ ...o, data: { source: 'two' } }))
|
||||
|
||||
setTimeout(() => {
|
||||
ops.forEach(o => this.one.applyOperation(o))
|
||||
})
|
||||
setTimeout(() => this.one.applyOperations(ops))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import { List, Record } from 'immutable'
|
||||
import { List, Record, Map } from 'immutable'
|
||||
|
||||
import Mark from './mark'
|
||||
import Node from './node'
|
||||
@@ -16,19 +16,19 @@ import invert from '../operations/invert'
|
||||
*/
|
||||
|
||||
const OPERATION_ATTRIBUTES = {
|
||||
add_mark: ['value', 'path', 'offset', 'length', 'mark'],
|
||||
insert_node: ['value', 'path', 'node'],
|
||||
insert_text: ['value', 'path', 'offset', 'text', 'marks'],
|
||||
merge_node: ['value', 'path', 'position', 'properties', 'target'],
|
||||
move_node: ['value', 'path', 'newPath'],
|
||||
remove_mark: ['value', 'path', 'offset', 'length', 'mark'],
|
||||
remove_node: ['value', 'path', 'node'],
|
||||
remove_text: ['value', 'path', 'offset', 'text', 'marks'],
|
||||
set_mark: ['value', 'path', 'offset', 'length', 'mark', 'properties'],
|
||||
set_node: ['value', 'path', 'node', 'properties'],
|
||||
set_selection: ['value', 'selection', 'properties'],
|
||||
set_value: ['value', 'properties'],
|
||||
split_node: ['value', 'path', 'position', 'properties', 'target'],
|
||||
add_mark: ['value', 'path', 'offset', 'length', 'mark', 'data'],
|
||||
insert_node: ['value', 'path', 'node', 'data'],
|
||||
insert_text: ['value', 'path', 'offset', 'text', 'marks', 'data'],
|
||||
merge_node: ['value', 'path', 'position', 'properties', 'target', 'data'],
|
||||
move_node: ['value', 'path', 'newPath', 'data'],
|
||||
remove_mark: ['value', 'path', 'offset', 'length', 'mark', 'data'],
|
||||
remove_node: ['value', 'path', 'node', 'data'],
|
||||
remove_text: ['value', 'path', 'offset', 'text', 'marks', 'data'],
|
||||
set_mark: ['value', 'path', 'offset', 'length', 'mark', 'properties', 'data'],
|
||||
set_node: ['value', 'path', 'node', 'properties', 'data'],
|
||||
set_selection: ['value', 'selection', 'properties', 'data'],
|
||||
set_value: ['value', 'properties', 'data'],
|
||||
split_node: ['value', 'path', 'position', 'properties', 'target', 'data'],
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +52,7 @@ const DEFAULTS = {
|
||||
text: undefined,
|
||||
type: undefined,
|
||||
value: undefined,
|
||||
data: undefined,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,6 +134,9 @@ class Operation extends Record(DEFAULTS) {
|
||||
if (key == 'value') continue
|
||||
if (key == 'node' && type != 'insert_node') continue
|
||||
|
||||
// Skip optional user defined data
|
||||
if (key == 'data') continue
|
||||
|
||||
throw new Error(
|
||||
`\`Operation.fromJSON\` was passed a "${type}" operation without the required "${key}" attribute.`
|
||||
)
|
||||
@@ -186,6 +190,10 @@ class Operation extends Record(DEFAULTS) {
|
||||
v = Node.createProperties(v)
|
||||
}
|
||||
|
||||
if (key === 'data') {
|
||||
v = Map(v)
|
||||
}
|
||||
|
||||
attrs[key] = v
|
||||
}
|
||||
|
||||
@@ -303,6 +311,10 @@ class Operation extends Record(DEFAULTS) {
|
||||
value = v
|
||||
}
|
||||
|
||||
if (key === 'data' && value) {
|
||||
value = value.toJSON()
|
||||
}
|
||||
|
||||
json[key] = value
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,14 @@ describe('slate', () => {
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
|
||||
fixtures(__dirname, 'models/operation', ({ module }) => {
|
||||
const { input, output } = module
|
||||
const fn = module.default
|
||||
const actual = fn(input).toJSON()
|
||||
const expected = output
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
|
||||
fixtures(__dirname, 'models/point', ({ module }) => {
|
||||
const { input, output } = module
|
||||
const fn = module.default
|
||||
|
@@ -0,0 +1,28 @@
|
||||
import Operation from '../../../../src/models/operation'
|
||||
|
||||
export const input = {
|
||||
type: 'add_mark',
|
||||
path: [2, 1],
|
||||
offset: 3,
|
||||
length: 5,
|
||||
mark: 'b',
|
||||
data: { info: 'user supplied text', flag: true },
|
||||
}
|
||||
|
||||
export default function(op) {
|
||||
return Operation.create(op)
|
||||
}
|
||||
|
||||
export const output = {
|
||||
object: 'operation',
|
||||
type: 'add_mark',
|
||||
path: [2, 1],
|
||||
offset: 3,
|
||||
length: 5,
|
||||
mark: {
|
||||
data: {},
|
||||
object: 'mark',
|
||||
type: 'b',
|
||||
},
|
||||
data: { info: 'user supplied text', flag: true },
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
import Operation from '../../../../src/models/operation'
|
||||
|
||||
export const input = {
|
||||
type: 'add_mark',
|
||||
path: [2, 1],
|
||||
offset: 3,
|
||||
length: 5,
|
||||
mark: 'b',
|
||||
}
|
||||
|
||||
export default function(op) {
|
||||
return Operation.create(op)
|
||||
}
|
||||
|
||||
export const output = {
|
||||
object: 'operation',
|
||||
type: 'add_mark',
|
||||
path: [2, 1],
|
||||
offset: 3,
|
||||
length: 5,
|
||||
mark: {
|
||||
data: {},
|
||||
object: 'mark',
|
||||
type: 'b',
|
||||
},
|
||||
data: undefined,
|
||||
}
|
Reference in New Issue
Block a user