Comparing version 8.1.15 to 8.2.0
@@ -48,3 +48,3 @@ import { existsSync, readFileSync, writeFileSync } from "fs"; | ||
const getDesc = (text, path) => { | ||
const getDesc = (text = "", path) => { | ||
return `${options.includePath && path ? `${path}:\n` : ""}${text}`; | ||
@@ -57,3 +57,5 @@ }; | ||
const c = acc[m.key]; | ||
let description = m.description && getDesc(m.description, m.path); | ||
let description = | ||
(m.description || options.includePath) && | ||
getDesc(m.description, m.path); | ||
@@ -81,11 +83,12 @@ if (m.description) { | ||
const targetContent = JSON.stringify( | ||
Object.fromEntries( | ||
Object.entries(body).sort(([a], [b]) => | ||
a.localeCompare(b, "en", { sensitivity: "base" }), | ||
const targetContent = | ||
JSON.stringify( | ||
Object.fromEntries( | ||
Object.entries(body).sort(([a], [b]) => | ||
a.localeCompare(b, "en", { sensitivity: "base" }), | ||
), | ||
), | ||
), | ||
null, | ||
2, | ||
); | ||
null, | ||
2, | ||
) + "\n"; | ||
@@ -92,0 +95,0 @@ if (targetPath) { |
@@ -31,3 +31,12 @@ import { | ||
export function resolveFileOrDir(path, cb) { | ||
function commonPart(str1, str2) { | ||
let i = 0; | ||
while (str1[i] === str2[i]) { | ||
i++; | ||
} | ||
return str1.slice(0, i); | ||
} | ||
export function resolveFileOrDir(path, cb, dir) { | ||
if (!existsSync(path)) { | ||
@@ -43,6 +52,6 @@ console.error(`'${path}' path does not exist`); | ||
readdirSync(path).forEach((file) => | ||
resolveFileOrDir(resolve(path, file), cb), | ||
resolveFileOrDir(resolve(path, file), cb, dir || path), | ||
); | ||
} else if (path.endsWith(".js") || path.endsWith(".ts")) { | ||
cb(readFileSync(path, "utf8"), path.replace(cwd, "")); | ||
cb(readFileSync(path, "utf8"), path.replace(commonPart(dir, cwd), "")); | ||
} | ||
@@ -49,0 +58,0 @@ } |
{ | ||
"name": "hybrids", | ||
"version": "8.1.15", | ||
"version": "8.2.0", | ||
"description": "A JavaScript framework for creating fully-featured web applications, components libraries, and single web components with unique declarative and functional architecture", | ||
@@ -43,3 +43,3 @@ "type": "module", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"jasmine-core": "^4.0.0", | ||
"jasmine-core": "^5.0.0", | ||
"karma": "^6.3.11", | ||
@@ -50,3 +50,3 @@ "karma-chrome-launcher": "^3.1.0", | ||
"karma-jasmine": "^5.1.0", | ||
"karma-webkit-launcher": "^1.1.0", | ||
"karma-webkit-launcher": "^2.1.0", | ||
"playwright": "^1.27.1", | ||
@@ -53,0 +53,0 @@ "prettier": "^2.5.1", |
@@ -12,3 +12,3 @@ | ||
**Hybrids** provides a complete set of tools for the web - everything without external dependencies: | ||
**Hybrids** provides a complete set of features for building modern web applications: | ||
@@ -20,3 +20,3 @@ * **Component Model** based on plain objects and pure functions | ||
* **Layout Engine** making UI layouts development much faster | ||
* **Hot Module Replacement** support and other DX features | ||
* **Hot Module Replacement** out of the box support and other DX features | ||
@@ -189,4 +189,4 @@ ### Documentation | ||
**hybrids** is released under the [MIT License](LICENSE). | ||
**Hybrids** is released under the [MIT License](LICENSE). | ||
[^1]: Pure functions only apply to the component definition. Side effects attached to event listeners might mutate the host element. |
@@ -135,3 +135,7 @@ import * as emitter from "./emitter.js"; | ||
emitter.add(entry.observe); | ||
try { | ||
entry.observe(); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
@@ -138,0 +142,0 @@ return () => { |
import global from "./global.js"; | ||
import { constructors } from "./define.js"; | ||
function walk(node, fn, options, items = [], host = node) { | ||
for (const child of Array.from(node.children)) { | ||
const hybrids = child.constructor.hybrids; | ||
const hybrids = constructors.get(child.constructor); | ||
if (hybrids && fn(hybrids, host)) { | ||
@@ -7,0 +8,0 @@ items.push(child); |
@@ -9,8 +9,11 @@ import global from "./global.js"; | ||
export const constructors = new WeakMap(); | ||
const disconnects = new WeakMap(); | ||
function compile(hybrids, HybridsElement) { | ||
if (HybridsElement) { | ||
if (hybrids === HybridsElement.hybrids) return HybridsElement; | ||
const prevHybrids = constructors.get(HybridsElement); | ||
if (hybrids === prevHybrids) return HybridsElement; | ||
for (const key of Object.keys(HybridsElement.hybrids)) { | ||
for (const key of Object.keys(prevHybrids)) { | ||
delete HybridsElement.prototype[key]; | ||
@@ -21,3 +24,5 @@ } | ||
connectedCallback() { | ||
for (const key of Object.keys(this)) { | ||
for (const key of HybridsElement.settable) { | ||
if (!hasOwnProperty.call(this, key)) continue; | ||
const value = this[key]; | ||
@@ -33,4 +38,4 @@ delete this[key]; | ||
if (set === disconnects.get(this)) { | ||
for (const fn of this.constructor.connects) set.add(fn(this)); | ||
for (const fn of this.constructor.observers) set.add(fn(this)); | ||
for (const fn of HybridsElement.connects) set.add(fn(this)); | ||
for (const fn of HybridsElement.observers) set.add(fn(this)); | ||
} | ||
@@ -53,6 +58,7 @@ }); | ||
HybridsElement.hybrids = hybrids; | ||
constructors.set(HybridsElement, Object.freeze(hybrids)); | ||
const connects = new Set(); | ||
const observers = new Set(); | ||
const settable = new Set(); | ||
@@ -100,2 +106,4 @@ for (const key of Object.keys(hybrids)) { | ||
if (desc.set) settable.add(key); | ||
Object.defineProperty(HybridsElement.prototype, key, { | ||
@@ -129,2 +137,3 @@ get: function get() { | ||
HybridsElement.observers = observers; | ||
HybridsElement.settable = settable; | ||
@@ -141,3 +150,3 @@ return HybridsElement; | ||
const prevHybrids = updateQueue.get(node.constructor); | ||
const hybrids = node.constructor.hybrids; | ||
const hybrids = constructors.get(node.constructor); | ||
node.disconnectedCallback(); | ||
@@ -151,2 +160,4 @@ | ||
hybrids[key] !== prevHybrids[key]; | ||
if (clearValue) node.removeAttribute(camelToDash(key)); | ||
cache.invalidate(node, key, { clearValue }); | ||
@@ -161,3 +172,3 @@ } | ||
} | ||
updateQueue.set(HybridsElement, HybridsElement.hybrids); | ||
updateQueue.set(HybridsElement, constructors.get(HybridsElement)); | ||
} | ||
@@ -175,7 +186,7 @@ | ||
if (HybridsElement) { | ||
if (HybridsElement.hybrids) { | ||
if (constructors.get(HybridsElement)) { | ||
update(HybridsElement); | ||
compile(hybrids, HybridsElement); | ||
return Object.freeze(hybrids); | ||
return hybrids; | ||
} | ||
@@ -189,3 +200,3 @@ | ||
global.customElements.define(hybrids.tag, compile(hybrids)); | ||
return Object.freeze(hybrids); | ||
return hybrids; | ||
} | ||
@@ -192,0 +203,0 @@ |
export { default as define } from "./define.js"; | ||
export { default as mount } from "./mount.js"; | ||
@@ -3,0 +4,0 @@ export { default as parent } from "./parent.js"; |
@@ -1,8 +0,10 @@ | ||
function walk(node, fn) { | ||
let parentElement = node.parentElement || node.parentNode.host; | ||
import { constructors } from "./define.js"; | ||
function walk(host, fn) { | ||
let parentElement = host.parentElement || host.parentNode.host; | ||
while (parentElement) { | ||
const hybrids = parentElement.constructor.hybrids; | ||
const hybrids = constructors.get(parentElement.constructor); | ||
if (hybrids && fn(hybrids, node)) { | ||
if (hybrids && fn(hybrids, host)) { | ||
return parentElement; | ||
@@ -9,0 +11,0 @@ } |
import global from "./global.js"; | ||
import * as cache from "./cache.js"; | ||
import { deferred, dispatch, walkInShadow } from "./utils.js"; | ||
import { constructors } from "./define.js"; | ||
@@ -262,3 +263,3 @@ const connect = Symbol("router.connect"); | ||
if (!Constructor || Constructor.hybrids !== hybrids) { | ||
if (!Constructor || constructors.get(Constructor) !== hybrids) { | ||
throw Error( | ||
@@ -843,5 +844,29 @@ `<${id}> view must be defined by 'define()' function before it can be used in router factory`, | ||
function setTransition(stack, prevStack) { | ||
const el = global.document.documentElement; | ||
if (!stack) { | ||
el.removeAttribute("router-transition"); | ||
return; | ||
} | ||
const { dialog } = configs.get(stack[0].constructor); | ||
const transition = | ||
(dialog && "dialog") || | ||
(prevStack.length && | ||
((stack.length > prevStack.length && "forward") || | ||
(stack.length < prevStack.length && "backward") || | ||
(stack[0] !== prevStack[0] && "replace"))) || | ||
""; | ||
el.setAttribute("router-transition", transition); | ||
} | ||
function connectRootRouter(host, invalidate, options) { | ||
function flush() { | ||
const prevStack = stacks.get(host); | ||
const stack = resolveStack(host, global.history.state, options); | ||
if (options.transition) setTransition(stack, prevStack); | ||
invalidate(); | ||
@@ -994,2 +1019,4 @@ | ||
setTransition(null); | ||
entryPoints.clear(); | ||
@@ -996,0 +1023,0 @@ rootRouter = null; |
@@ -102,75 +102,90 @@ import global from "../global.js"; | ||
const prevStyleSheetsMap = new WeakMap(); | ||
function setupStyleUpdater(target) { | ||
if (target.adoptedStyleSheets) { | ||
return (styles) => { | ||
const prevStyleSheets = prevStyleSheetsMap.get(target); | ||
const styleSheets = | ||
styles && | ||
styles.map((style) => { | ||
let styleSheet = style; | ||
if (!(styleSheet instanceof global.CSSStyleSheet)) { | ||
styleSheet = styleSheetsMap.get(style); | ||
if (!styleSheet) { | ||
styleSheet = new global.CSSStyleSheet(); | ||
styleSheet.replaceSync(style); | ||
styleSheetsMap.set(style, styleSheet); | ||
} | ||
} | ||
function updateAdoptedStylesheets(target, styles) { | ||
const prevStyleSheets = prevStyleSheetsMap.get(target); | ||
if (!prevStyleSheets && !styles) return; | ||
return styleSheet; | ||
}); | ||
let adoptedStyleSheets; | ||
if (prevStyleSheets) { | ||
if ( | ||
styleSheets && | ||
styleSheets.length === prevStyleSheets.length && | ||
styleSheets.every((s, i) => s === prevStyleSheets[i]) | ||
) { | ||
return; | ||
const styleSheets = | ||
styles && | ||
styles.map((style) => { | ||
let styleSheet = style; | ||
if (!(styleSheet instanceof global.CSSStyleSheet)) { | ||
styleSheet = styleSheetsMap.get(style); | ||
if (!styleSheet) { | ||
styleSheet = new global.CSSStyleSheet(); | ||
styleSheet.replaceSync(style); | ||
styleSheetsMap.set(style, styleSheet); | ||
} | ||
adoptedStyleSheets = target.adoptedStyleSheets.filter( | ||
(s) => !prevStyleSheets.includes(s), | ||
); | ||
} | ||
if (styleSheets) { | ||
adoptedStyleSheets = ( | ||
adoptedStyleSheets || target.adoptedStyleSheets | ||
).concat(styleSheets); | ||
} | ||
return styleSheet; | ||
}); | ||
if (adoptedStyleSheets) { | ||
target.adoptedStyleSheets = adoptedStyleSheets; | ||
} | ||
let adoptedStyleSheets; | ||
prevStyleSheetsMap.set(target, styleSheets); | ||
}; | ||
if (prevStyleSheets) { | ||
if ( | ||
styleSheets && | ||
styleSheets.length === prevStyleSheets.length && | ||
styleSheets.every((s, i) => s === prevStyleSheets[i]) | ||
) { | ||
return; | ||
} | ||
adoptedStyleSheets = target.adoptedStyleSheets.filter( | ||
(s) => !prevStyleSheets.includes(s), | ||
); | ||
} | ||
let styleEl; | ||
return (styleSheets) => { | ||
if (styleSheets) { | ||
if (!styleEl) { | ||
styleEl = global.document.createElement("style"); | ||
target = getTemplateEnd(target); | ||
if (target.nodeType === global.Node.TEXT_NODE) { | ||
target.parentNode.insertBefore(styleEl, target.nextSibling); | ||
} else { | ||
target.appendChild(styleEl); | ||
} | ||
} | ||
const result = [...styleSheets].join("\n/*------*/\n"); | ||
if (styleSheets) { | ||
adoptedStyleSheets = ( | ||
adoptedStyleSheets || target.adoptedStyleSheets | ||
).concat(styleSheets); | ||
} | ||
if (styleEl.textContent !== result) { | ||
styleEl.textContent = result; | ||
target.adoptedStyleSheets = adoptedStyleSheets; | ||
prevStyleSheetsMap.set(target, styleSheets); | ||
} | ||
const styleElementMap = new WeakMap(); | ||
function updateStyleElement(target, styles) { | ||
let styleEl = styleElementMap.get(target); | ||
if (styles) { | ||
if (!styleEl || styleEl.parentNode !== target) { | ||
styleEl = global.document.createElement("style"); | ||
styleElementMap.set(target, styleEl); | ||
target = getTemplateEnd(target); | ||
if (target.nodeType === global.Node.TEXT_NODE) { | ||
target.parentNode.insertBefore(styleEl, target.nextSibling); | ||
} else { | ||
target.appendChild(styleEl); | ||
} | ||
} else if (styleEl) { | ||
styleEl.parentNode.removeChild(styleEl); | ||
styleEl = null; | ||
} | ||
}; | ||
const result = [...styles].join("\n/*------*/\n"); | ||
if (styleEl.textContent !== result) { | ||
styleEl.textContent = result; | ||
} | ||
} else if (styleEl) { | ||
styleEl.parentNode.removeChild(styleEl); | ||
styleElementMap.set(target, null); | ||
} | ||
} | ||
const updateStyleFns = new WeakMap(); | ||
function updateStyles(target, styles) { | ||
let fn = updateStyleFns.get(target); | ||
if (!fn) { | ||
fn = target.adoptedStyleSheets | ||
? updateAdoptedStylesheets | ||
: updateStyleElement; | ||
updateStyleFns.set(target, fn); | ||
} | ||
fn(target, styles); | ||
} | ||
export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { | ||
@@ -197,3 +212,3 @@ let template = global.document.createElement("template"); | ||
const value = attr.value.trim(); | ||
if (attr.name.startsWith("layout") && value) { | ||
if (value && attr.name.startsWith("layout")) { | ||
if (value.match(PLACEHOLDER_REGEXP_ALL)) { | ||
@@ -218,3 +233,3 @@ throw Error("Layout attribute cannot contain expressions"); | ||
useLayout = true; | ||
useLayout = hostLayout || layoutTemplate.hasAttribute("layout"); | ||
template = layoutTemplate; | ||
@@ -437,3 +452,3 @@ } | ||
const partsKeys = Object.keys(parts); | ||
return function updateTemplateInstance(host, target, args, styles) { | ||
return function updateTemplateInstance(host, target, args, { styleSheets }) { | ||
let meta = getMeta(target); | ||
@@ -476,5 +491,6 @@ | ||
meta.markers = markers; | ||
meta.styles = setupStyleUpdater(target); | ||
if (target.nodeType === global.Node.TEXT_NODE) { | ||
updateStyleElement(target); | ||
meta.startNode = fragment.childNodes[0]; | ||
@@ -504,3 +520,3 @@ meta.endNode = fragment.childNodes[fragment.childNodes.length - 1]; | ||
meta.styles(styles); | ||
updateStyles(target, styleSheets); | ||
@@ -517,5 +533,5 @@ for (const marker of meta.markers) { | ||
console.error( | ||
`Following error was thrown when updating a template expression in ${stringifyElement( | ||
`Error while updating template expression in ${stringifyElement( | ||
host, | ||
)}\n${beautifyTemplateLog(signature, marker.index)}`, | ||
)}:\n${beautifyTemplateLog(signature, marker.index)}`, | ||
); | ||
@@ -522,0 +538,0 @@ |
import { compileTemplate } from "./core.js"; | ||
import { getPlaceholder } from "./utils.js"; | ||
import * as helpers from "./helpers.js"; | ||
import * as helpers from "./helpers/index.js"; | ||
import * as methods from "./methods.js"; | ||
const PLACEHOLDER = getPlaceholder(); | ||
@@ -10,28 +12,2 @@ const PLACEHOLDER_SVG = getPlaceholder("svg"); | ||
const methods = { | ||
key(id) { | ||
this.id = id; | ||
return this; | ||
}, | ||
style(...styles) { | ||
this.styleSheets = this.styleSheets || []; | ||
this.styleSheets.push(...styles); | ||
return this; | ||
}, | ||
css(parts, ...args) { | ||
this.styleSheets = this.styleSheets || []; | ||
let result = parts[0]; | ||
for (let index = 1; index < parts.length; index++) { | ||
result += | ||
(args[index - 1] !== undefined ? args[index - 1] : "") + parts[index]; | ||
} | ||
this.styleSheets.push(result); | ||
return this; | ||
}, | ||
}; | ||
const templates = new Map(); | ||
@@ -51,3 +27,10 @@ export function compile(parts, args, isSVG, isMsg) { | ||
render(host, target, args, template.styleSheets); | ||
if (template.plugins) { | ||
template.plugins.reduce( | ||
(acc, plugin) => plugin(acc), | ||
() => render(host, target, args, template), | ||
)(host, target); | ||
} else { | ||
render(host, target, args, template); | ||
} | ||
} | ||
@@ -54,0 +37,0 @@ |
import global from "../global.js"; | ||
const hasAdoptedStylesheets = !!global.document.adoptedStyleSheets; | ||
const hasAdoptedStylesheets = !!( | ||
global.document && global.document.adoptedStyleSheets | ||
); | ||
const NUMBER_REGEXP = /^\d+$/; | ||
@@ -150,2 +152,15 @@ const rules = { | ||
layer: (props, value = 1) => ({ "z-index": value }), | ||
"": (props, _, ...args) => { | ||
if (args.length < 2) { | ||
throw new Error( | ||
"Generic rule '::' requires at least two arguments, eg.: ::[property]:[name]", | ||
); | ||
} | ||
return { | ||
[args[args.length - 2]]: `var(--${args.join("-")})`, | ||
}; | ||
}, | ||
view: (props, value) => ({ "view-transition-name": value }), | ||
}; | ||
@@ -152,0 +167,0 @@ |
@@ -7,9 +7,7 @@ const targets = new WeakMap(); | ||
const eventMap = targets.get(target); | ||
if (eventMap) { | ||
target.removeEventListener( | ||
eventType, | ||
eventMap.get(lastValue), | ||
lastValue.options !== undefined ? lastValue.options : false, | ||
); | ||
} | ||
target.removeEventListener( | ||
eventType, | ||
eventMap.get(lastValue), | ||
lastValue.options !== undefined ? lastValue.options : false, | ||
); | ||
} | ||
@@ -16,0 +14,0 @@ |
@@ -23,8 +23,6 @@ import global from "../global.js"; | ||
export function removeTemplate(target) { | ||
const data = getMeta(target); | ||
if (target.nodeType === global.Node.TEXT_NODE) { | ||
const data = metaMap.get(target); | ||
if (data.styles) data.styles(); | ||
if (target.nodeType === global.Node.TEXT_NODE) { | ||
if (data.startNode) { | ||
if (data && data.startNode) { | ||
const endNode = getTemplateEnd(data.endNode); | ||
@@ -40,2 +38,3 @@ | ||
} | ||
metaMap.set(target, {}); | ||
} | ||
@@ -48,5 +47,5 @@ } else { | ||
} | ||
metaMap.set(target, {}); | ||
} | ||
metaMap.delete(target); | ||
} | ||
@@ -53,0 +52,0 @@ |
156
src/value.js
import { camelToDash } from "./utils.js"; | ||
function string(desc, attrName) { | ||
const defaultValue = desc.value; | ||
const setters = { | ||
string: (host, value, attrName) => { | ||
const nextValue = value ? String(value) : ""; | ||
if (nextValue) { | ||
host.setAttribute(attrName, nextValue); | ||
} else { | ||
host.removeAttribute(attrName); | ||
} | ||
return { | ||
get: (host, value) => | ||
value === undefined ? host.getAttribute(attrName) || defaultValue : value, | ||
set: (host, value) => { | ||
value = String(value); | ||
return nextValue; | ||
}, | ||
number: (host, value, attrName) => { | ||
const nextValue = Number(value); | ||
host.setAttribute(attrName, nextValue); | ||
return nextValue; | ||
}, | ||
boolean: (host, value, attrName) => { | ||
const nextValue = Boolean(value); | ||
if (nextValue) { | ||
host.setAttribute(attrName, ""); | ||
} else { | ||
host.removeAttribute(attrName); | ||
} | ||
return nextValue; | ||
}, | ||
undefined: (host, value, attrName) => { | ||
const type = typeof value; | ||
const set = type !== "undefined" && setters[type]; | ||
if (set) { | ||
return set(host, value, attrName); | ||
} else if (host.hasAttribute(attrName)) { | ||
host.removeAttribute(attrName); | ||
} | ||
if (value) { | ||
host.setAttribute(attrName, value); | ||
} else { | ||
host.removeAttribute(attrName); | ||
} | ||
return value; | ||
}, | ||
}; | ||
return value; | ||
}, | ||
connect: | ||
defaultValue !== "" | ||
? (host, key, invalidate) => { | ||
if (!host.hasAttribute(attrName) && host[key] === defaultValue) { | ||
host.setAttribute(attrName, defaultValue); | ||
} | ||
// istanbul ignore next | ||
return desc.connect && desc.connect(host, key, invalidate); | ||
} | ||
: desc.connect, | ||
observe: desc.observe, | ||
}; | ||
} | ||
const getters = { | ||
string: (host, attrName) => host.getAttribute(attrName), | ||
number: (host, attrName) => Number(host.getAttribute(attrName)) || 0, | ||
boolean: (host, attrName) => host.hasAttribute(attrName), | ||
undefined: (host, attrName) => host.getAttribute(attrName), | ||
}; | ||
function number(desc, attrName) { | ||
const defaultValue = desc.value; | ||
export default function value(key, desc) { | ||
const type = typeof desc.value; | ||
const set = setters[type]; | ||
const get = getters[type]; | ||
return { | ||
get: (host, value) => | ||
value === undefined | ||
? Number(host.getAttribute(attrName) || defaultValue) | ||
: value, | ||
set: (host, value) => { | ||
value = Number(value); | ||
host.setAttribute(attrName, value); | ||
return value; | ||
}, | ||
connect: (host, key, invalidate) => { | ||
if (!host.hasAttribute(attrName) && host[key] === defaultValue) { | ||
host.setAttribute(attrName, defaultValue); | ||
} | ||
// istanbul ignore next | ||
return desc.connect && desc.connect(host, key, invalidate); | ||
}, | ||
observe: desc.observe, | ||
}; | ||
} | ||
if (!set) { | ||
throw TypeError( | ||
`Invalid default value for '${key}' property - it must be a string, number, boolean or undefined: ${type}`, | ||
); | ||
} | ||
function boolean(desc, attrName) { | ||
const defaultValue = desc.value; | ||
const attrName = camelToDash(key); | ||
return { | ||
get: (host, value) => | ||
value === undefined ? host.hasAttribute(attrName) || defaultValue : value, | ||
set: (host, value) => { | ||
value = Boolean(value); | ||
if (value) { | ||
host.setAttribute(attrName, ""); | ||
} else { | ||
host.removeAttribute(attrName); | ||
} | ||
return value; | ||
}, | ||
value === undefined ? get(host, attrName) || desc.value : value, | ||
set: (host, value) => set(host, value, attrName), | ||
connect: | ||
defaultValue === true | ||
type !== "undefined" | ||
? (host, key, invalidate) => { | ||
if (!host.hasAttribute(attrName) && host[key] === defaultValue) { | ||
host.setAttribute(attrName, ""); | ||
if (!host.hasAttribute(attrName) && host[key] === desc.value) { | ||
host[key] = set(host, desc.value, attrName); | ||
} | ||
// istanbul ignore next | ||
return desc.connect && desc.connect(host, key, invalidate); | ||
@@ -88,33 +78,1 @@ } | ||
} | ||
function undef(desc, attrName) { | ||
const defaultValue = desc.value; | ||
return { | ||
get: (host, value) => | ||
value === undefined ? host.getAttribute(attrName) || defaultValue : value, | ||
set: (host, value) => value, | ||
connect: desc.connect, | ||
observe: desc.observe, | ||
}; | ||
} | ||
export default function value(key, desc) { | ||
const type = typeof desc.value; | ||
const attrName = camelToDash(key); | ||
switch (type) { | ||
case "string": | ||
return string(desc, attrName); | ||
case "number": | ||
return number(desc, attrName); | ||
case "boolean": | ||
return boolean(desc, attrName); | ||
case "undefined": | ||
return undef(desc, attrName); | ||
default: | ||
throw TypeError( | ||
`Invalid default value for '${key}' property - it must be a string, number, boolean or undefined: ${type}`, | ||
); | ||
} | ||
} |
@@ -65,2 +65,5 @@ declare module "hybrids" { | ||
/* Mount */ | ||
function mount<E>(target: HTMLElement, component: Component<E>): () => void; | ||
/* Factories */ | ||
@@ -226,2 +229,3 @@ | ||
params?: Array<keyof E>; | ||
transition?: boolean; | ||
}, | ||
@@ -320,2 +324,3 @@ ): Descriptor<E, HTMLElement[]>; | ||
css: (parts: TemplateStringsArray, ...args: unknown[]) => this; | ||
use: (fn: (template: UpdateFunction<E>) => UpdateFunction<E>) => this; | ||
} | ||
@@ -345,2 +350,4 @@ | ||
function transition<E>(template: UpdateFunction<E>): UpdateFunction<E>; | ||
function msg(parts: TemplateStringsArray, ...args: unknown[]): string; | ||
@@ -347,0 +354,0 @@ } |
200960
39
6392