hooks port to domBinding

This commit is contained in:
Kevin Jahns 2018-04-26 13:26:21 +02:00
parent 94933a704d
commit 1fe37c565e
9 changed files with 68 additions and 51 deletions

View File

@ -1,7 +1,20 @@
/* global Y, d3 */ /* global Y, d3 */
const hooks = {
"magic-drawing": {
fillType: function (dom, type) {
initDrawingBindings(type, dom)
},
createDom: function (type) {
const dom = document.createElement('magic-drawing')
initDrawingBindings(type, dom)
return dom
}
}
}
window.onload = function () { window.onload = function () {
window.yXmlType.bindToDom(document.body) window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { hooks })
} }
window.addMagicDrawing = function addMagicDrawing () { window.addMagicDrawing = function addMagicDrawing () {
@ -96,17 +109,6 @@ function initDrawingBindings (type, dom) {
} }
} }
Y.XmlHook.addHook('magic-drawing', {
fillType: function (dom, type) {
initDrawingBindings(type, dom)
},
createDom: function (type) {
const dom = document.createElement('magic-drawing')
initDrawingBindings(type, dom)
return dom
}
})
let y = new Y('html-editor-drawing-hook-example', { let y = new Y('html-editor-drawing-hook-example', {
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',

View File

@ -30,6 +30,9 @@ export default class DomBinding extends Binding {
constructor (type, target, opts = {}) { constructor (type, target, opts = {}) {
// Binding handles textType as this.type and domTextarea as this.target // Binding handles textType as this.type and domTextarea as this.target
super(type, target) super(type, target)
this.opts = opts
opts.document = opts.document || document
opts.hooks = opts.hooks || {}
/** /**
* Maps each DOM element to the type that it is associated with. * Maps each DOM element to the type that it is associated with.
* @type {Map} * @type {Map}
@ -49,11 +52,11 @@ export default class DomBinding extends Binding {
// set initial value // set initial value
target.innerHTML = '' target.innerHTML = ''
for (let child of type) { for (let child of type) {
target.insertBefore(child.toDom(this.domToType, this.typeToDom), null) target.insertBefore(child.toDom(opts.document, opts.hooks, this), null)
} }
this._typeObserver = typeObserver.bind(this) this._typeObserver = typeObserver.bind(this)
this._domObserver = (mutations) => { this._domObserver = (mutations) => {
domObserver.call(this, mutations, opts._document) domObserver.call(this, mutations, opts.document)
} }
type.observeDeep(this._typeObserver) type.observeDeep(this._typeObserver)
this._mutationObserver = new MutationObserver(this._domObserver) this._mutationObserver = new MutationObserver(this._domObserver)

View File

@ -1,5 +1,5 @@
import { YXmlText, YXmlElement } from '../../Types/YXml/YXml.js' import { YXmlText, YXmlElement, YXmlHook } from '../../Types/YXml/YXml.js'
import { createAssociation } from './util.js' import { createAssociation } from './util.js'
/** /**
@ -15,14 +15,29 @@ export default function domToType (element, _document = document, binding) {
let type let type
switch (element.nodeType) { switch (element.nodeType) {
case _document.ELEMENT_NODE: case _document.ELEMENT_NODE:
type = new YXmlElement(element.nodeName) let hookName = element.dataset.yjsHook
const attrs = element.attributes let hook
for (let i = attrs.length - 1; i >= 0; i--) { if (hookName !== undefined) {
const attr = attrs[i] hook = binding.opts.hooks[hookName]
type.setAttribute(attr.name, attr.value) if (hook === undefined) {
console.error(`Unknown hook "${hookName}". Deleting yjsHook dataset property.`)
delete element.dataset.yjsHook
hookName = undefined
}
}
if (hookName === undefined) {
type = new YXmlElement(element.nodeName)
const attrs = element.attributes
for (let i = attrs.length - 1; i >= 0; i--) {
const attr = attrs[i]
type.setAttribute(attr.name, attr.value)
}
const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
type.insert(0, children)
} else {
type = new YXmlHook(hookName)
hook.fillType(element, type)
} }
const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
type.insert(0, children)
break break
case _document.TEXT_NODE: case _document.TEXT_NODE:
type = new YXmlText() type = new YXmlText()

View File

@ -6,7 +6,7 @@ import { removeDomChildrenUntilElementFound } from './util.js'
/** /**
* @private * @private
*/ */
export default function typeObserver (events, _document) { export default function typeObserver (events) {
this._mutualExclude(() => { this._mutualExclude(() => {
events.forEach(event => { events.forEach(event => {
const yxml = event.target const yxml = event.target
@ -37,11 +37,10 @@ export default function typeObserver (events, _document) {
let currentChild = dom.firstChild let currentChild = dom.firstChild
yxml.forEach(childType => { yxml.forEach(childType => {
const childNode = this.typeToDom.get(childType) const childNode = this.typeToDom.get(childType)
const binding = this
switch (childNode) { switch (childNode) {
case undefined: case undefined:
// Does not exist. Create it. // Does not exist. Create it.
const node = childType.toDom(_document, binding) const node = childType.toDom(this.opts.document, this.opts.hooks, this)
dom.insertBefore(node, currentChild) dom.insertBefore(node, currentChild)
break break
case false: case false:

View File

@ -164,6 +164,8 @@ export default class YXmlElement extends YXmlFragment {
* @param {Document} [_document=document] The document object (you must define * @param {Document} [_document=document] The document object (you must define
* this when calling this method in * this when calling this method in
* nodejs) * nodejs)
* @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is * @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a * used if DomBinding wants to create a
* association to the created DOM type. * association to the created DOM type.
@ -171,14 +173,14 @@ export default class YXmlElement extends YXmlFragment {
* *
* @public * @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, hooks = {}, binding) {
const dom = _document.createElement(this.nodeName) const dom = _document.createElement(this.nodeName)
let attrs = this.getAttributes() let attrs = this.getAttributes()
for (let key in attrs) { for (let key in attrs) {
dom.setAttribute(key, attrs[key]) dom.setAttribute(key, attrs[key])
} }
this.forEach(yxml => { this.forEach(yxml => {
dom.appendChild(yxml.toDom(_document, binding)) dom.appendChild(yxml.toDom(_document, hooks, binding))
}) })
createAssociation(binding, dom, this) createAssociation(binding, dom, this)
return dom return dom

View File

@ -136,18 +136,20 @@ export default class YXmlFragment extends YArray {
* @param {Document} [_document=document] The document object (you must define * @param {Document} [_document=document] The document object (you must define
* this when calling this method in * this when calling this method in
* nodejs) * nodejs)
* @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is * @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a * used if DomBinding wants to create a
* association to the created DOM type. * association to the created DOM type
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
* *
* @public * @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, hooks = {}, binding) {
const fragment = _document.createDocumentFragment() const fragment = _document.createDocumentFragment()
createAssociation(binding, fragment, this) createAssociation(binding, fragment, this)
this.forEach(xmlType => { this.forEach(xmlType => {
fragment.insertBefore(xmlType.toDom(_document, binding), null) fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
}) })
return fragment return fragment
} }

View File

@ -1,5 +1,4 @@
import YMap from '../YMap/YMap.js' import YMap from '../YMap/YMap.js'
import { getHook, addHook } from './hooks.js'
/** /**
* You can manage binding to a custom type with YXmlHook. * You can manage binding to a custom type with YXmlHook.
@ -35,16 +34,24 @@ export default class YXmlHook extends YMap {
* @param {Document} [_document=document] The document object (you must define * @param {Document} [_document=document] The document object (you must define
* this when calling this method in * this when calling this method in
* nodejs) * nodejs)
* @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is * @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a * used if DomBinding wants to create a
* association to the created DOM type. * association to the created DOM type
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
* *
* @public * @public
*/ */
toDom (_document = document) { toDom (_document = document, hooks = {}, binding) {
const dom = getHook(this.hookName).createDom(this) const hook = hooks[this.hookName]
dom._yjsHook = this.hookName let dom
if (hook !== undefined) {
dom = hook.createDom(this)
} else {
dom = document.createElement(this.hookName)
}
dom.dataset.yjsHook = this.hookName
return dom return dom
} }
@ -97,4 +104,3 @@ export default class YXmlHook extends YMap {
super._integrate(y) super._integrate(y)
} }
} }
YXmlHook.addHook = addHook

View File

@ -14,6 +14,8 @@ export default class YXmlText extends YText {
* @param {Document} [_document=document] The document object (you must define * @param {Document} [_document=document] The document object (you must define
* this when calling this method in * this when calling this method in
* nodejs) * nodejs)
* @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is * @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a * used if DomBinding wants to create a
* association to the created DOM type. * association to the created DOM type.
@ -21,7 +23,7 @@ export default class YXmlText extends YText {
* *
* @public * @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, hooks, binding) {
const dom = _document.createTextNode(this.toString()) const dom = _document.createTextNode(this.toString())
createAssociation(binding, dom, this) createAssociation(binding, dom, this)
return dom return dom

View File

@ -1,14 +0,0 @@
const xmlHooks = {}
export function addHook (name, hook) {
xmlHooks[name] = hook
}
export function getHook (name) {
const hook = xmlHooks[name]
if (hook === undefined) {
throw new Error(`The hook "${name}" is not specified! You must not access this hook!`)
}
return hook
}