Refactor renderer class

This commit is contained in:
Talia 2024-07-29 11:35:06 +02:00
parent e2ec8312af
commit 6bd29f05dc

View file

@ -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: {