hooks port to domBinding
This commit is contained in:
parent
94933a704d
commit
1fe37c565e
@ -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',
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user