Refactor renderer class
This commit is contained in:
parent
e2ec8312af
commit
6bd29f05dc
1 changed files with 52 additions and 40 deletions
92
render.js
92
render.js
|
@ -22,18 +22,18 @@ class MultiAbortController {
|
||||||
export const empty = Symbol("Explicit empty argument for Skooma")
|
export const empty = Symbol("Explicit empty argument for Skooma")
|
||||||
|
|
||||||
/** Converts a snake-case string to a CSS property name
|
/** Converts a snake-case string to a CSS property name
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
const snakeToCSS = key => key.replace(/^[A-Z]/, a => "-" + a).replace(/[A-Z]/g, a => '-' + a.toLowerCase())
|
const snakeToCSS = key => key.replace(/^[A-Z]/, a => "-" + a).replace(/[A-Z]/g, a => '-' + a.toLowerCase())
|
||||||
|
|
||||||
/** @typedef SpecialAttributeDescriptor
|
/** @typedef SpecialAttributeDescriptor
|
||||||
* @type {object}
|
* @type {object}
|
||||||
* @property {function(Node):void} [get]
|
* @property {function(Node):void} [get]
|
||||||
* @property {function(Node,any):void} [set]
|
* @property {function(Node,any):void} [set]
|
||||||
* @property {function(Node,function(any):void):void} [subscribe]
|
* @property {function(Node,function(any):void):void} [subscribe]
|
||||||
* @property {function(Node):boolean} [filter]
|
* @property {function(Node):boolean} [filter]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a fallback if value is fallback
|
* Returns a fallback if value is fallback
|
||||||
|
@ -42,18 +42,6 @@ const snakeToCSS = key => key.replace(/^[A-Z]/, a => "-" + a).replace(/[A-Z]/g,
|
||||||
*/
|
*/
|
||||||
const fallback = (value, whenUndefined) => typeof value != "undefined" ? value : whenUndefined
|
const fallback = (value, whenUndefined) => typeof value != "undefined" ? value : whenUndefined
|
||||||
|
|
||||||
/** Recursively finds the last 'is' attribute in a list nested array of objects
|
|
||||||
* @param {Array} args
|
|
||||||
*/
|
|
||||||
const getCustom = args => args.reduce(
|
|
||||||
(current, argument) => Array.isArray(argument)
|
|
||||||
? fallback(getCustom(argument), current)
|
|
||||||
: (argument && typeof argument == "object")
|
|
||||||
? fallback(argument.is, current)
|
|
||||||
: current
|
|
||||||
, undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
/** @typedef {EventTarget & {value: any}} Observable */
|
/** @typedef {EventTarget & {value: any}} Observable */
|
||||||
|
|
||||||
/** Cancelable event triggered when a reactive element gets replaced with something else */
|
/** Cancelable event triggered when a reactive element gets replaced with something else */
|
||||||
|
@ -86,9 +74,9 @@ export class ReplacedEvent extends Event {
|
||||||
// Other utility exports
|
// Other utility exports
|
||||||
|
|
||||||
/** Wraps an event handler in a function that calls preventDefault on the event
|
/** Wraps an event handler in a function that calls preventDefault on the event
|
||||||
* @param {function(event) : event} fn
|
* @param {function(event) : event} fn
|
||||||
* @return {function(event)}
|
* @return {function(event)}
|
||||||
*/
|
*/
|
||||||
export const handle = fn => event => { event.preventDefault(); return fn(event) }
|
export const handle = fn => event => { event.preventDefault(); return fn(event) }
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,10 +190,7 @@ export class DomRenderer extends Renderer {
|
||||||
* @param {Array} args
|
* @param {Array} args
|
||||||
*/
|
*/
|
||||||
node(name, args) {
|
node(name, args) {
|
||||||
const custom = getCustom(args)
|
const element = this.createElement(name)
|
||||||
const opts = custom && { is: String(custom) }
|
|
||||||
|
|
||||||
const element = this.createElement(name, opts)
|
|
||||||
this.constructor.apply(element, args)
|
this.constructor.apply(element, args)
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
|
@ -216,7 +201,7 @@ export class DomRenderer extends Renderer {
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @return {Node}
|
* @return {Node}
|
||||||
*/
|
*/
|
||||||
createElement(name, options) {
|
createElement(name, options={}) {
|
||||||
return document.createElement(name, options)
|
return document.createElement(name, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,9 +300,9 @@ export class DomRenderer extends Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CSSStyleDeclaration} style The style property of a node
|
* @param {CSSStyleDeclaration} style The style property of a node
|
||||||
* @param {object} rules A map of snake case property names to css values
|
* @param {object} rules A map of snake case property names to css values
|
||||||
*/
|
*/
|
||||||
static insertStyles(style, rules) {
|
static insertStyles(style, rules) {
|
||||||
for (const [key, value] of Object.entries(rules))
|
for (const [key, value] of Object.entries(rules))
|
||||||
if (typeof value == "undefined")
|
if (typeof value == "undefined")
|
||||||
|
@ -327,16 +312,16 @@ export class DomRenderer extends Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether an object is an observable according to skooma's contract
|
/** Returns whether an object is an observable according to skooma's contract
|
||||||
* @param {any} object
|
* @param {any} object
|
||||||
* @return {object is Observable}
|
* @return {object is Observable}
|
||||||
*/
|
*/
|
||||||
static isObservable(object) {
|
static isObservable(object) {
|
||||||
return object && object.observable
|
return object && object.observable
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wraps a list of elements in a document fragment
|
/** Wraps a list of elements in a document fragment
|
||||||
* @param {Array<Element|String>} elements
|
* @param {Array<Element|String>} elements
|
||||||
*/
|
*/
|
||||||
static documentFragment(...elements) {
|
static documentFragment(...elements) {
|
||||||
const fragment = new DocumentFragment()
|
const fragment = new DocumentFragment()
|
||||||
for (const element of elements)
|
for (const element of elements)
|
||||||
|
@ -359,9 +344,9 @@ export class DomRenderer extends Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String|Array<String>} data
|
* @param {String|Array<String>} data
|
||||||
* @param {Array<String|Element>} items
|
* @param {Array<String|Element>} items
|
||||||
*/
|
*/
|
||||||
static createTextOrFragment(data = "", ...items) {
|
static createTextOrFragment(data = "", ...items) {
|
||||||
return Array.isArray(data)
|
return Array.isArray(data)
|
||||||
? this.textFromTemplate(data, items)
|
? this.textFromTemplate(data, items)
|
||||||
|
@ -397,6 +382,33 @@ export class DomHtmlRenderer extends DomRenderer {
|
||||||
return document.createElement(name.replace(/([a-z])([A-Z])/g, "$1-$2"), options)
|
return document.createElement(name.replace(/([a-z])([A-Z])/g, "$1-$2"), options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a new node and make it a custom element if necessary
|
||||||
|
* @param {String} name
|
||||||
|
* @param {Array} args
|
||||||
|
*/
|
||||||
|
node(name, args) {
|
||||||
|
const custom = this.getCustom(args)
|
||||||
|
const opts = custom && { is: String(custom) }
|
||||||
|
|
||||||
|
const element = this.createElement(name, opts)
|
||||||
|
this.constructor.apply(element, args)
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Recursively finds the last 'is' attribute in a list nested array of objects
|
||||||
|
* @param {Array} args
|
||||||
|
*/
|
||||||
|
getCustom(args) {
|
||||||
|
return args.reduce(
|
||||||
|
(current, argument) => Array.isArray(argument)
|
||||||
|
? fallback(this.getCustom(argument), current)
|
||||||
|
: (argument && typeof argument == "object")
|
||||||
|
? fallback(argument.is, current)
|
||||||
|
: current
|
||||||
|
, undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {Object<string,SpecialAttributeDescriptor>} */
|
/** @type {Object<string,SpecialAttributeDescriptor>} */
|
||||||
static specialAttributes = {
|
static specialAttributes = {
|
||||||
value: {
|
value: {
|
||||||
|
|
Loading…
Reference in a new issue