@endorphinjs/template-runtime
Advanced tools
Comparing version 0.1.4 to 0.1.5
@@ -83,8 +83,10 @@ 'use strict'; | ||
function moveContents(from, to) { | ||
if (from.nodeType === from.DOCUMENT_FRAGMENT_NODE) { | ||
to.appendChild(from); | ||
} else { | ||
let node; | ||
while (node = from.firstChild) { | ||
to.appendChild(node); | ||
if (from !== to) { | ||
if (from.nodeType === from.DOCUMENT_FRAGMENT_NODE) { | ||
to.appendChild(from); | ||
} else { | ||
let node; | ||
while (node = from.firstChild) { | ||
to.appendChild(node); | ||
} | ||
} | ||
@@ -217,3 +219,3 @@ } | ||
function textValue(value) { | ||
return isDefined(value) ? value : ''; | ||
return value != null ? value : ''; | ||
} | ||
@@ -366,3 +368,4 @@ | ||
* @param {Injector} injector | ||
* @param {object} [data] Additional props (most likely static ones) | ||
* @param {Object} [data] Additional props (most likely static ones) | ||
* @return {Object} Updated props, if any | ||
*/ | ||
@@ -375,2 +378,3 @@ function finalizeProps(injector, data) { | ||
injector.parentNode.setProps(changes, true); | ||
return changes; | ||
} | ||
@@ -381,3 +385,3 @@ } | ||
* Normalizes attributes in given injector | ||
* @param {injector} injector | ||
* @param {Injector} injector | ||
*/ | ||
@@ -526,2 +530,63 @@ function normalize(injector) { | ||
/** | ||
* Registers given element as output slot for `host` component | ||
* @param {import('../types').Component} host | ||
* @param {string} name | ||
* @param {HTMLElement} elem | ||
* @param {Function} [defaultContent] Function for rendering default slot content | ||
*/ | ||
function mountSlot(host, name, elem, defaultContent) { | ||
const blockEntry = (host, injector) => { | ||
if (!renderSlot(host, injector)) { | ||
return defaultContent; | ||
} | ||
}; | ||
host.componentModel.slots[name] = mountBlock(host, createInjector(elem), blockEntry); | ||
} | ||
/** | ||
* Sync slot content if necessary | ||
* @param {import('../types').Component} host | ||
*/ | ||
function updateSlots(host) { | ||
const { slots } = host.componentModel; | ||
for (const name in slots) { | ||
updateBlock(slots[name]); | ||
} | ||
} | ||
/** | ||
* Renders incoming contents of given slot | ||
* @param {import('../types').Component} host | ||
* @param {import('../types').Injector} target | ||
* @returns {boolean} Returns `true` if slot content was filled with incoming data, | ||
* `false` otherwise | ||
*/ | ||
function renderSlot(host, target) { | ||
const { parentNode } = target; | ||
const name = parentNode.getAttribute('name') || ''; | ||
const slotted = parentNode.hasAttribute('slotted'); | ||
const { input } = host.componentModel; | ||
/** @type {Element | DocumentFragment} */ | ||
const source = input.slots[name]; | ||
if (source && source.childNodes.length) { | ||
// There’s incoming slot content | ||
if (!slotted) { | ||
parentNode.setAttribute('slotted', ''); | ||
input.slots[name] = moveContents(source, parentNode); | ||
} | ||
return true; | ||
} | ||
if (slotted) { | ||
// Parent renderer removed incoming data | ||
parentNode.removeAttribute('slotted'); | ||
input[name] = null; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Creates internal lightweight Endorphin component with given definition | ||
@@ -552,8 +617,32 @@ * @param {string} name | ||
element.state = collectData(definition, 'state'); | ||
element.setProps = (value, silent) => setProps(element, value, silent); | ||
element.setState = (value, silent) => setState(element, value, silent); | ||
element.setProps = function setProps(value, silent) { | ||
if (value != null) { | ||
assign(element.props, value); | ||
representProps(element, value); | ||
if (!silent && element.componentModel.mounted) { | ||
renderNext(element, value); | ||
} | ||
} | ||
}; | ||
element.setState = function setState(value, silent) { | ||
if (value != null && changed(value, element.state)) { | ||
const { componentModel } = element; | ||
assign(element.state, value); | ||
// If we’re in rendering state than current `setState()` is caused by | ||
// one of the `will*` hooks, which means applied changes will be automatically | ||
// applied during rendering stage. | ||
// If called outside oif rendering state we should schedule render | ||
// on next tick | ||
if (!silent && componentModel.mounted && !componentModel.rendering) { | ||
scheduleRender(element); | ||
} | ||
} | ||
}; | ||
forEachHook(definition, 'methods', methods => assign(element, methods)); | ||
// XXX Should point to Shadow Root in Web Components | ||
element.componentView = definition.componentView ? definition.componentView(host) : element; | ||
element.componentView = definition.componentView ? definition.componentView(element, host) : element; | ||
@@ -566,3 +655,3 @@ if (definition.store) { | ||
// Create slottted input | ||
// Create slotted input | ||
const input = createInjector(element.componentView); | ||
@@ -576,2 +665,3 @@ input.slots = obj(); | ||
refs: changeSet(), | ||
slots: obj(), | ||
mounted: false, | ||
@@ -581,3 +671,3 @@ rendering: false, | ||
queued: null, | ||
detachEvents: attachStaticEvents(element, definition) | ||
events: attachStaticEvents(element, definition) | ||
}; | ||
@@ -598,15 +688,16 @@ | ||
const { input, definition } = componentModel; | ||
elem$$1.slots = input.slots; | ||
finalizeEvents(input); | ||
finalizeProps(input, initialProps); | ||
const args = [initialProps || {}]; | ||
componentModel.rendering = true; | ||
runHook(elem$$1, 'willMount'); | ||
runHook(elem$$1, 'willMount', args); | ||
if (definition.default) { | ||
runHook(elem$$1, 'willRender'); | ||
runHook(elem$$1, 'willRender', args); | ||
componentModel.update = definition.default(elem$$1, getScope(elem$$1)); | ||
componentModel.rendering = false; | ||
runHook(elem$$1, 'didRender'); | ||
runHook(elem$$1, 'didRender', args); | ||
} | ||
runHook(elem$$1, 'didMount'); | ||
runHook(elem$$1, 'didMount', args); | ||
componentModel.mounted = true; | ||
@@ -618,3 +709,2 @@ } | ||
* @param {Component} elem | ||
* @param {object} initialProps | ||
*/ | ||
@@ -624,4 +714,7 @@ function updateComponent(elem$$1) { | ||
finalizeEvents(componentModel.input); | ||
finalizeProps(componentModel.input); | ||
renderComponent(elem$$1); | ||
updateSlots(elem$$1); | ||
const changes = finalizeProps(componentModel.input); | ||
if (changes) { | ||
renderComponent(elem$$1, changes); | ||
} | ||
} | ||
@@ -636,3 +729,3 @@ | ||
componentModel.mounted = false; | ||
componentModel.detachEvents(); | ||
detachStaticEvents(elem$$1, componentModel.events); | ||
@@ -670,8 +763,9 @@ if (elem$$1.store) { | ||
* @param {Component} elem | ||
* @param {Object} [changes] | ||
*/ | ||
function renderNext(elem$$1) { | ||
function renderNext(elem$$1, changes) { | ||
if (!elem$$1.componentModel.rendering) { | ||
renderComponent(elem$$1); | ||
renderComponent(elem$$1, changes); | ||
} else { | ||
scheduleRender(elem$$1); | ||
scheduleRender(elem$$1, changes); | ||
} | ||
@@ -683,4 +777,5 @@ } | ||
* @param {Component} elem | ||
* @param {Object} changes | ||
*/ | ||
function scheduleRender(elem$$1) { | ||
function scheduleRender(elem$$1, changes) { | ||
if (!elem$$1.componentModel.queued) { | ||
@@ -692,3 +787,3 @@ elem$$1.componentModel.queued = nextTick(() => { | ||
if (elem$$1.componentModel.queued) { | ||
renderComponent(elem$$1); | ||
renderComponent(elem$$1, changes); | ||
} | ||
@@ -702,5 +797,7 @@ }); | ||
* @param {Component} elem | ||
* @param {Object} changes | ||
*/ | ||
function renderComponent(elem$$1) { | ||
function renderComponent(elem$$1, changes) { | ||
const { componentModel } = elem$$1; | ||
const args = [changes || {}]; | ||
@@ -711,62 +808,16 @@ componentModel.queued = null; | ||
// TODO prepare data for hooks in `mountComponent`? | ||
runHook(elem$$1, 'willUpdate'); | ||
runHook(elem$$1, 'willUpdate', args); | ||
if (componentModel.update) { | ||
runHook(elem$$1, 'willRender'); | ||
runHook(elem$$1, 'willRender', args); | ||
componentModel.update(elem$$1, getScope(elem$$1)); | ||
componentModel.rendering = false; | ||
runHook(elem$$1, 'didRender'); | ||
runHook(elem$$1, 'didRender', args); | ||
} else { | ||
componentModel.rendering = false; | ||
} | ||
runHook(elem$$1, 'didUpdate'); | ||
} | ||
/** | ||
* Updates properties of context component | ||
* @param {Component} elem | ||
* @param {object} value | ||
* @param {boolean} [silent] | ||
* @this Component | ||
*/ | ||
function setProps(elem$$1, value, silent) { | ||
if (value != null) { | ||
assign(elem$$1.props, value); | ||
representProps(elem$$1, value); | ||
!silent && elem$$1.componentModel.mounted && renderNext(elem$$1); | ||
} | ||
runHook(elem$$1, 'didUpdate', args); | ||
} | ||
/** | ||
* Updates state of context component | ||
* @param {Component} elem | ||
* @param {object} value | ||
* @param {boolean} [silent] | ||
*/ | ||
function setState(elem$$1, value, silent) { | ||
const { componentModel } = elem$$1; | ||
if (value != null) { | ||
let updated = false; | ||
const prev = elem$$1.state; | ||
for (const p in value) { | ||
if (prev[p] !== value[p]) { | ||
updated = true; | ||
break; | ||
} | ||
} | ||
if (updated) { | ||
assign(prev, value); | ||
// If we’re in rendering state than current `setState()` is caused by | ||
// one of the `will*` hooks, which means applied changes will be automatically | ||
// applied during rendering stage. | ||
// If called outside oif rendering state we should schedule render | ||
// on next tick | ||
if (!silent && componentModel.mounted && !componentModel.rendering) { | ||
scheduleRender(elem$$1); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Collects data generated by `key` factory | ||
@@ -814,10 +865,10 @@ * @param {ComponentDefinition} definition | ||
* @param {ComponentDefinition} definition | ||
* @return {object} Map of attached event handlers | ||
* @return {import('../types').AttachedEventsMap} Map of attached event handlers | ||
*/ | ||
function attachStaticEvents(component, definition) { | ||
/** @type {{[name: string]: Array}} */ | ||
/** @type {import('../types').AttachedEventsMap} */ | ||
const eventMap = obj(); | ||
const handler = function(evt) { | ||
const listeners = eventMap[evt.type]; | ||
const listeners = eventMap[evt.type].listeners; | ||
for (let i = 0; i < listeners.length; i++) { | ||
@@ -834,6 +885,9 @@ listeners[i].call(this, evt, component); | ||
if (name in eventMap) { | ||
eventMap[name].push(events[name]); | ||
eventMap[name].listeners.push(events[name]); | ||
} else { | ||
component.addEventListener(name, handler); | ||
eventMap[name] = [events[name]]; | ||
eventMap[name] = { | ||
handler, | ||
listeners: [events[name]] | ||
}; | ||
} | ||
@@ -844,9 +898,16 @@ } | ||
return function detachStaticEvents() { | ||
for (let p in eventMap) { | ||
component.removeEventListener(p, handler); | ||
} | ||
}; | ||
return eventMap; | ||
} | ||
/** | ||
* Removes attached events from given map | ||
* @param {import('../types').Component} component | ||
* @param {import('../types').AttachedEventsMap} eventMap | ||
*/ | ||
function detachStaticEvents(component, eventMap) { | ||
for (let p in eventMap) { | ||
component.removeEventListener(p, eventMap[p].handler); | ||
} | ||
} | ||
const blockKey = '&block'; | ||
@@ -987,8 +1048,3 @@ | ||
// TODO seems like `markDisposed()` is not required for inner blocks | ||
// since we already disposed parent block. But unit-tests fail. Check | ||
// if it’s safe to remove `markDisposed()` in this case | ||
if (isBlock(item)) { | ||
markDisposed(item, false); | ||
} else { | ||
if (!isBlock(item)) { | ||
disposeElement(item); | ||
@@ -1135,5 +1191,6 @@ } | ||
* Initial block rendering | ||
* @param {Component} component | ||
* @param {Injector} injector | ||
* @param {function} get | ||
* @param {import('../types').Component} component | ||
* @param {import('../types').Injector} injector | ||
* @param {Function} get | ||
* @returns {import('../types').BlockContext} | ||
*/ | ||
@@ -1153,3 +1210,4 @@ function mountBlock(component, injector, get) { | ||
* Updated block, described in `ctx` object | ||
* @param {object} ctx | ||
* @param {import('../types').BlockContext} ctx | ||
* @returns {import('../types').BlockContext} | ||
*/ | ||
@@ -1329,34 +1387,2 @@ function updateBlock(ctx) { | ||
/** | ||
* Renders incoming contents of given slot | ||
* @param {Element} slot | ||
* @param {object} incoming | ||
* @returns {boolean} Returns `true` if slot content was filled with incoming data, | ||
* `false` otherwise | ||
*/ | ||
function renderSlot(slot, incoming) { | ||
const name = slot.getAttribute('name') || ''; | ||
const slotted = slot.hasAttribute('slotted'); | ||
/** @type {Element | DocumentFragment} */ | ||
const container = incoming && incoming[name]; | ||
if (container && container.childNodes.length) { | ||
// There’s incoming slot content | ||
if (!slotted) { | ||
slot.setAttribute('slotted', ''); | ||
incoming[name] = moveContents(container, slot); | ||
} | ||
return true; | ||
} | ||
if (slotted) { | ||
// Parent renderer removed incoming data | ||
slot.removeAttribute('slotted'); | ||
incoming[name] = null; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Sets runtime ref (e.g. ref which will be changed over time) to given host | ||
@@ -1702,3 +1728,4 @@ * @param {Component} host | ||
exports.getEventHandler = getEventHandler; | ||
exports.renderSlot = renderSlot; | ||
exports.mountSlot = mountSlot; | ||
exports.updateSlots = updateSlots; | ||
exports.setRef = setRef; | ||
@@ -1705,0 +1732,0 @@ exports.setStaticRef = setStaticRef; |
@@ -79,8 +79,10 @@ /** | ||
function moveContents(from, to) { | ||
if (from.nodeType === from.DOCUMENT_FRAGMENT_NODE) { | ||
to.appendChild(from); | ||
} else { | ||
let node; | ||
while (node = from.firstChild) { | ||
to.appendChild(node); | ||
if (from !== to) { | ||
if (from.nodeType === from.DOCUMENT_FRAGMENT_NODE) { | ||
to.appendChild(from); | ||
} else { | ||
let node; | ||
while (node = from.firstChild) { | ||
to.appendChild(node); | ||
} | ||
} | ||
@@ -213,3 +215,3 @@ } | ||
function textValue(value) { | ||
return isDefined(value) ? value : ''; | ||
return value != null ? value : ''; | ||
} | ||
@@ -362,3 +364,4 @@ | ||
* @param {Injector} injector | ||
* @param {object} [data] Additional props (most likely static ones) | ||
* @param {Object} [data] Additional props (most likely static ones) | ||
* @return {Object} Updated props, if any | ||
*/ | ||
@@ -371,2 +374,3 @@ function finalizeProps(injector, data) { | ||
injector.parentNode.setProps(changes, true); | ||
return changes; | ||
} | ||
@@ -377,3 +381,3 @@ } | ||
* Normalizes attributes in given injector | ||
* @param {injector} injector | ||
* @param {Injector} injector | ||
*/ | ||
@@ -522,2 +526,63 @@ function normalize(injector) { | ||
/** | ||
* Registers given element as output slot for `host` component | ||
* @param {import('../types').Component} host | ||
* @param {string} name | ||
* @param {HTMLElement} elem | ||
* @param {Function} [defaultContent] Function for rendering default slot content | ||
*/ | ||
function mountSlot(host, name, elem, defaultContent) { | ||
const blockEntry = (host, injector) => { | ||
if (!renderSlot(host, injector)) { | ||
return defaultContent; | ||
} | ||
}; | ||
host.componentModel.slots[name] = mountBlock(host, createInjector(elem), blockEntry); | ||
} | ||
/** | ||
* Sync slot content if necessary | ||
* @param {import('../types').Component} host | ||
*/ | ||
function updateSlots(host) { | ||
const { slots } = host.componentModel; | ||
for (const name in slots) { | ||
updateBlock(slots[name]); | ||
} | ||
} | ||
/** | ||
* Renders incoming contents of given slot | ||
* @param {import('../types').Component} host | ||
* @param {import('../types').Injector} target | ||
* @returns {boolean} Returns `true` if slot content was filled with incoming data, | ||
* `false` otherwise | ||
*/ | ||
function renderSlot(host, target) { | ||
const { parentNode } = target; | ||
const name = parentNode.getAttribute('name') || ''; | ||
const slotted = parentNode.hasAttribute('slotted'); | ||
const { input } = host.componentModel; | ||
/** @type {Element | DocumentFragment} */ | ||
const source = input.slots[name]; | ||
if (source && source.childNodes.length) { | ||
// There’s incoming slot content | ||
if (!slotted) { | ||
parentNode.setAttribute('slotted', ''); | ||
input.slots[name] = moveContents(source, parentNode); | ||
} | ||
return true; | ||
} | ||
if (slotted) { | ||
// Parent renderer removed incoming data | ||
parentNode.removeAttribute('slotted'); | ||
input[name] = null; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Creates internal lightweight Endorphin component with given definition | ||
@@ -548,8 +613,32 @@ * @param {string} name | ||
element.state = collectData(definition, 'state'); | ||
element.setProps = (value, silent) => setProps(element, value, silent); | ||
element.setState = (value, silent) => setState(element, value, silent); | ||
element.setProps = function setProps(value, silent) { | ||
if (value != null) { | ||
assign(element.props, value); | ||
representProps(element, value); | ||
if (!silent && element.componentModel.mounted) { | ||
renderNext(element, value); | ||
} | ||
} | ||
}; | ||
element.setState = function setState(value, silent) { | ||
if (value != null && changed(value, element.state)) { | ||
const { componentModel } = element; | ||
assign(element.state, value); | ||
// If we’re in rendering state than current `setState()` is caused by | ||
// one of the `will*` hooks, which means applied changes will be automatically | ||
// applied during rendering stage. | ||
// If called outside oif rendering state we should schedule render | ||
// on next tick | ||
if (!silent && componentModel.mounted && !componentModel.rendering) { | ||
scheduleRender(element); | ||
} | ||
} | ||
}; | ||
forEachHook(definition, 'methods', methods => assign(element, methods)); | ||
// XXX Should point to Shadow Root in Web Components | ||
element.componentView = definition.componentView ? definition.componentView(host) : element; | ||
element.componentView = definition.componentView ? definition.componentView(element, host) : element; | ||
@@ -562,3 +651,3 @@ if (definition.store) { | ||
// Create slottted input | ||
// Create slotted input | ||
const input = createInjector(element.componentView); | ||
@@ -572,2 +661,3 @@ input.slots = obj(); | ||
refs: changeSet(), | ||
slots: obj(), | ||
mounted: false, | ||
@@ -577,3 +667,3 @@ rendering: false, | ||
queued: null, | ||
detachEvents: attachStaticEvents(element, definition) | ||
events: attachStaticEvents(element, definition) | ||
}; | ||
@@ -594,15 +684,16 @@ | ||
const { input, definition } = componentModel; | ||
elem$$1.slots = input.slots; | ||
finalizeEvents(input); | ||
finalizeProps(input, initialProps); | ||
const args = [initialProps || {}]; | ||
componentModel.rendering = true; | ||
runHook(elem$$1, 'willMount'); | ||
runHook(elem$$1, 'willMount', args); | ||
if (definition.default) { | ||
runHook(elem$$1, 'willRender'); | ||
runHook(elem$$1, 'willRender', args); | ||
componentModel.update = definition.default(elem$$1, getScope(elem$$1)); | ||
componentModel.rendering = false; | ||
runHook(elem$$1, 'didRender'); | ||
runHook(elem$$1, 'didRender', args); | ||
} | ||
runHook(elem$$1, 'didMount'); | ||
runHook(elem$$1, 'didMount', args); | ||
componentModel.mounted = true; | ||
@@ -614,3 +705,2 @@ } | ||
* @param {Component} elem | ||
* @param {object} initialProps | ||
*/ | ||
@@ -620,4 +710,7 @@ function updateComponent(elem$$1) { | ||
finalizeEvents(componentModel.input); | ||
finalizeProps(componentModel.input); | ||
renderComponent(elem$$1); | ||
updateSlots(elem$$1); | ||
const changes = finalizeProps(componentModel.input); | ||
if (changes) { | ||
renderComponent(elem$$1, changes); | ||
} | ||
} | ||
@@ -632,3 +725,3 @@ | ||
componentModel.mounted = false; | ||
componentModel.detachEvents(); | ||
detachStaticEvents(elem$$1, componentModel.events); | ||
@@ -666,8 +759,9 @@ if (elem$$1.store) { | ||
* @param {Component} elem | ||
* @param {Object} [changes] | ||
*/ | ||
function renderNext(elem$$1) { | ||
function renderNext(elem$$1, changes) { | ||
if (!elem$$1.componentModel.rendering) { | ||
renderComponent(elem$$1); | ||
renderComponent(elem$$1, changes); | ||
} else { | ||
scheduleRender(elem$$1); | ||
scheduleRender(elem$$1, changes); | ||
} | ||
@@ -679,4 +773,5 @@ } | ||
* @param {Component} elem | ||
* @param {Object} changes | ||
*/ | ||
function scheduleRender(elem$$1) { | ||
function scheduleRender(elem$$1, changes) { | ||
if (!elem$$1.componentModel.queued) { | ||
@@ -688,3 +783,3 @@ elem$$1.componentModel.queued = nextTick(() => { | ||
if (elem$$1.componentModel.queued) { | ||
renderComponent(elem$$1); | ||
renderComponent(elem$$1, changes); | ||
} | ||
@@ -698,5 +793,7 @@ }); | ||
* @param {Component} elem | ||
* @param {Object} changes | ||
*/ | ||
function renderComponent(elem$$1) { | ||
function renderComponent(elem$$1, changes) { | ||
const { componentModel } = elem$$1; | ||
const args = [changes || {}]; | ||
@@ -707,62 +804,16 @@ componentModel.queued = null; | ||
// TODO prepare data for hooks in `mountComponent`? | ||
runHook(elem$$1, 'willUpdate'); | ||
runHook(elem$$1, 'willUpdate', args); | ||
if (componentModel.update) { | ||
runHook(elem$$1, 'willRender'); | ||
runHook(elem$$1, 'willRender', args); | ||
componentModel.update(elem$$1, getScope(elem$$1)); | ||
componentModel.rendering = false; | ||
runHook(elem$$1, 'didRender'); | ||
runHook(elem$$1, 'didRender', args); | ||
} else { | ||
componentModel.rendering = false; | ||
} | ||
runHook(elem$$1, 'didUpdate'); | ||
} | ||
/** | ||
* Updates properties of context component | ||
* @param {Component} elem | ||
* @param {object} value | ||
* @param {boolean} [silent] | ||
* @this Component | ||
*/ | ||
function setProps(elem$$1, value, silent) { | ||
if (value != null) { | ||
assign(elem$$1.props, value); | ||
representProps(elem$$1, value); | ||
!silent && elem$$1.componentModel.mounted && renderNext(elem$$1); | ||
} | ||
runHook(elem$$1, 'didUpdate', args); | ||
} | ||
/** | ||
* Updates state of context component | ||
* @param {Component} elem | ||
* @param {object} value | ||
* @param {boolean} [silent] | ||
*/ | ||
function setState(elem$$1, value, silent) { | ||
const { componentModel } = elem$$1; | ||
if (value != null) { | ||
let updated = false; | ||
const prev = elem$$1.state; | ||
for (const p in value) { | ||
if (prev[p] !== value[p]) { | ||
updated = true; | ||
break; | ||
} | ||
} | ||
if (updated) { | ||
assign(prev, value); | ||
// If we’re in rendering state than current `setState()` is caused by | ||
// one of the `will*` hooks, which means applied changes will be automatically | ||
// applied during rendering stage. | ||
// If called outside oif rendering state we should schedule render | ||
// on next tick | ||
if (!silent && componentModel.mounted && !componentModel.rendering) { | ||
scheduleRender(elem$$1); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Collects data generated by `key` factory | ||
@@ -810,10 +861,10 @@ * @param {ComponentDefinition} definition | ||
* @param {ComponentDefinition} definition | ||
* @return {object} Map of attached event handlers | ||
* @return {import('../types').AttachedEventsMap} Map of attached event handlers | ||
*/ | ||
function attachStaticEvents(component, definition) { | ||
/** @type {{[name: string]: Array}} */ | ||
/** @type {import('../types').AttachedEventsMap} */ | ||
const eventMap = obj(); | ||
const handler = function(evt) { | ||
const listeners = eventMap[evt.type]; | ||
const listeners = eventMap[evt.type].listeners; | ||
for (let i = 0; i < listeners.length; i++) { | ||
@@ -830,6 +881,9 @@ listeners[i].call(this, evt, component); | ||
if (name in eventMap) { | ||
eventMap[name].push(events[name]); | ||
eventMap[name].listeners.push(events[name]); | ||
} else { | ||
component.addEventListener(name, handler); | ||
eventMap[name] = [events[name]]; | ||
eventMap[name] = { | ||
handler, | ||
listeners: [events[name]] | ||
}; | ||
} | ||
@@ -840,9 +894,16 @@ } | ||
return function detachStaticEvents() { | ||
for (let p in eventMap) { | ||
component.removeEventListener(p, handler); | ||
} | ||
}; | ||
return eventMap; | ||
} | ||
/** | ||
* Removes attached events from given map | ||
* @param {import('../types').Component} component | ||
* @param {import('../types').AttachedEventsMap} eventMap | ||
*/ | ||
function detachStaticEvents(component, eventMap) { | ||
for (let p in eventMap) { | ||
component.removeEventListener(p, eventMap[p].handler); | ||
} | ||
} | ||
const blockKey = '&block'; | ||
@@ -983,8 +1044,3 @@ | ||
// TODO seems like `markDisposed()` is not required for inner blocks | ||
// since we already disposed parent block. But unit-tests fail. Check | ||
// if it’s safe to remove `markDisposed()` in this case | ||
if (isBlock(item)) { | ||
markDisposed(item, false); | ||
} else { | ||
if (!isBlock(item)) { | ||
disposeElement(item); | ||
@@ -1131,5 +1187,6 @@ } | ||
* Initial block rendering | ||
* @param {Component} component | ||
* @param {Injector} injector | ||
* @param {function} get | ||
* @param {import('../types').Component} component | ||
* @param {import('../types').Injector} injector | ||
* @param {Function} get | ||
* @returns {import('../types').BlockContext} | ||
*/ | ||
@@ -1149,3 +1206,4 @@ function mountBlock(component, injector, get) { | ||
* Updated block, described in `ctx` object | ||
* @param {object} ctx | ||
* @param {import('../types').BlockContext} ctx | ||
* @returns {import('../types').BlockContext} | ||
*/ | ||
@@ -1325,34 +1383,2 @@ function updateBlock(ctx) { | ||
/** | ||
* Renders incoming contents of given slot | ||
* @param {Element} slot | ||
* @param {object} incoming | ||
* @returns {boolean} Returns `true` if slot content was filled with incoming data, | ||
* `false` otherwise | ||
*/ | ||
function renderSlot(slot, incoming) { | ||
const name = slot.getAttribute('name') || ''; | ||
const slotted = slot.hasAttribute('slotted'); | ||
/** @type {Element | DocumentFragment} */ | ||
const container = incoming && incoming[name]; | ||
if (container && container.childNodes.length) { | ||
// There’s incoming slot content | ||
if (!slotted) { | ||
slot.setAttribute('slotted', ''); | ||
incoming[name] = moveContents(container, slot); | ||
} | ||
return true; | ||
} | ||
if (slotted) { | ||
// Parent renderer removed incoming data | ||
slot.removeAttribute('slotted'); | ||
incoming[name] = null; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Sets runtime ref (e.g. ref which will be changed over time) to given host | ||
@@ -1665,3 +1691,3 @@ * @param {Component} host | ||
export { get, filter, mountBlock, updateBlock, mountIterator, updateIterator, mountKeyIterator, updateKeyIterator, createInjector, block, run, insert, move, dispose, enterScope, exitScope, createScope, setScope, getScope, getProp, getState, getVar, setVar, setAttribute, updateAttribute, updateProps, addClass, finalizeAttributes, finalizeProps, addEvent, addStaticEvent, finalizeEvents, getEventHandler, renderSlot, setRef, setStaticRef, finalizeRefs, createComponent, mountComponent, updateComponent, unmountComponent, subscribeStore, scheduleRender, renderComponent, mountInnerHTML, updateInnerHTML, elem, elemWithText, text, updateText, mountPartial, updatePartial, Store }; | ||
export { get, filter, mountBlock, updateBlock, mountIterator, updateIterator, mountKeyIterator, updateKeyIterator, createInjector, block, run, insert, move, dispose, enterScope, exitScope, createScope, setScope, getScope, getProp, getState, getVar, setVar, setAttribute, updateAttribute, updateProps, addClass, finalizeAttributes, finalizeProps, addEvent, addStaticEvent, finalizeEvents, getEventHandler, mountSlot, updateSlots, setRef, setStaticRef, finalizeRefs, createComponent, mountComponent, updateComponent, unmountComponent, subscribeStore, scheduleRender, renderComponent, mountInnerHTML, updateInnerHTML, elem, elemWithText, text, updateText, mountPartial, updatePartial, Store }; | ||
//# sourceMappingURL=runtime.es.js.map |
{ | ||
"name": "@endorphinjs/template-runtime", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "EndorphinJS template runtime, embedded with template bundles", | ||
@@ -5,0 +5,0 @@ "main": "./dist/runtime.cjs.js", |
@@ -100,7 +100,11 @@ import { Store } from './lib/store'; | ||
/** | ||
* Detaches all static events bound to current component | ||
* @private | ||
* List of attached event handlers | ||
*/ | ||
detachEvents(): void; | ||
events: AttachedEventsMap; | ||
/** Slot output for component */ | ||
slots: { | ||
[name: string]: BlockContext | ||
} | ||
/** | ||
@@ -139,5 +143,4 @@ * Indicates that component was mounted | ||
* Returns pointer to element where contents of component should be rendered | ||
* @param parent | ||
*/ | ||
componentView?(parent?: Component | Element): Element; | ||
componentView?(component: Component, parentComponent?: Component | Element): Element; | ||
@@ -295,2 +298,9 @@ /** | ||
interface AttachedEventsMap { | ||
[event: string]: { | ||
listeners: Function[]; | ||
handler: Function; | ||
} | ||
} | ||
interface RefMap { | ||
@@ -308,1 +318,10 @@ [key: string]: HTMLElement; | ||
} | ||
interface BlockContext { | ||
component: Component; | ||
injector: Injector; | ||
block: Block, | ||
get: Function; | ||
fn?: Function, | ||
update?: Function, | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
253677
3263