mi-element 0.9.5 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  > a lightweight alternative to write web components
7
7
 
8
- Only weights 2.5kB minified and gzipped.
8
+ Only weights 3.3kB minified and gzipped.
9
9
 
10
10
  mi-element provides features to build web applications through
11
11
  [Web Components][] like:
package/dist/bundle.js ADDED
@@ -0,0 +1,407 @@
1
+ /*!
2
+ * SPDX-License-Identifier: MIT
3
+ * mi-element v0.9.7
4
+ */
5
+ const context = [];
6
+
7
+ class State extends EventTarget {
8
+ #value;
9
+ #equals;
10
+ constructor(value, options) {
11
+ super();
12
+ const {equals: equals} = options || {};
13
+ this.#value = value, this.#equals = equals ?? ((value, nextValue) => value === nextValue);
14
+ }
15
+ get value() {
16
+ return this.get();
17
+ }
18
+ set value(nextValue) {
19
+ this.set(nextValue);
20
+ }
21
+ get() {
22
+ const running = context[context.length - 1];
23
+ return running && running.add(this), this.#value;
24
+ }
25
+ set(nextValue) {
26
+ this.#equals(this.#value, nextValue) || (this.#value = nextValue, this.dispatchEvent(new CustomEvent('signal')));
27
+ }
28
+ }
29
+
30
+ const createSignal = (initialValue, options) => initialValue instanceof State ? initialValue : new State(initialValue, options);
31
+
32
+ function effect(cb) {
33
+ const running = new Set;
34
+ context.push(running);
35
+ try {
36
+ cb();
37
+ } finally {
38
+ context.pop();
39
+ }
40
+ for (const dep of running) dep.addEventListener('signal', cb);
41
+ return () => {
42
+ for (const dep of running) dep.removeEventListener('signal', cb);
43
+ };
44
+ }
45
+
46
+ var index = {
47
+ State: State,
48
+ Computed: class {
49
+ #state;
50
+ #unsubscribe;
51
+ constructor(cb) {
52
+ this.#state = new State, this.#unsubscribe = effect(() => this.#state.set(cb()));
53
+ }
54
+ get() {
55
+ return this.#state.get();
56
+ }
57
+ unsubscribe() {
58
+ this.#unsubscribe();
59
+ }
60
+ },
61
+ createSignal: createSignal,
62
+ effect: effect
63
+ };
64
+
65
+ class ContextProvider {
66
+ constructor(host, context, initialValue) {
67
+ this.host = host, this.context = context, this.state = createSignal(initialValue),
68
+ this.host.addController?.(this);
69
+ }
70
+ hostConnected() {
71
+ this.host.addEventListener("context-request", this.onContextRequest);
72
+ }
73
+ hostDisconnected() {
74
+ this.host.removeEventListener("context-request", this.onContextRequest);
75
+ }
76
+ set(newValue) {
77
+ this.state.set(newValue);
78
+ }
79
+ get() {
80
+ return this.state.get();
81
+ }
82
+ set value(newValue) {
83
+ this.set(newValue);
84
+ }
85
+ get value() {
86
+ return this.get();
87
+ }
88
+ onContextRequest=ev => {
89
+ if (ev.context !== this.context) return;
90
+ let unsubscribe;
91
+ ev.stopPropagation(), ev.subscribe && (unsubscribe = effect(() => {
92
+ const value = this.get();
93
+ unsubscribe && ev.callback(value, unsubscribe);
94
+ })), ev.callback(this.get(), unsubscribe);
95
+ };
96
+ }
97
+
98
+ class ContextRequestEvent extends Event {
99
+ constructor(context, callback, subscribe) {
100
+ super("context-request", {
101
+ bubbles: !0,
102
+ composed: !0
103
+ }), this.context = context, this.callback = callback, this.subscribe = subscribe;
104
+ }
105
+ }
106
+
107
+ class ContextConsumer {
108
+ #value;
109
+ constructor(host, context, options) {
110
+ const {subscribe: subscribe = !1, validate: validate = () => !0} = options || {};
111
+ this.host = host, this.context = context, this.subscribe = !!subscribe, this.validate = validate,
112
+ this.unsubscribe = void 0, this.host.addController?.(this);
113
+ }
114
+ get() {
115
+ return this.#value;
116
+ }
117
+ get value() {
118
+ return this.#value;
119
+ }
120
+ hostConnected() {
121
+ this.dispatchRequest();
122
+ }
123
+ hostDisconnected() {
124
+ this.unsubscribe && (this.unsubscribe(), this.unsubscribe = void 0);
125
+ }
126
+ dispatchRequest() {
127
+ this.host.dispatchEvent(new ContextRequestEvent(this.context, this._callback.bind(this), this.subscribe));
128
+ }
129
+ _callback(value, unsubscribe) {
130
+ unsubscribe && (this.subscribe ? this.unsubscribe && (this.unsubscribe !== unsubscribe && this.unsubscribe(),
131
+ this.unsubscribe = unsubscribe) : unsubscribe()), this.validate(value) && (this.#value = value,
132
+ this.host.requestUpdate(value));
133
+ }
134
+ }
135
+
136
+ const camelToKebabCase = (str = "") => str.replace(/([A-Z])/g, (_, m) => `-${m.toLowerCase()}`), kebabToCamelCase = (str = "") => str.toLowerCase().replace(/[-_]\w/g, m => m[1].toUpperCase()), classNames = (...args) => {
137
+ const classList = [];
138
+ return args.forEach(arg => {
139
+ arg && ('string' == typeof arg ? classList.push(arg) : 'object' == typeof arg && Object.entries(arg).forEach(([key, value]) => {
140
+ value && classList.push(key);
141
+ }));
142
+ }), classList.join(' ');
143
+ }, styleMap = (map, options) => {
144
+ const {unit: unit = "px"} = options || {}, acc = [];
145
+ for (const [name, value] of Object.entries(map ?? {})) {
146
+ if (null == value) continue;
147
+ const _unit = Number.isFinite(value) ? unit : '';
148
+ acc.push(`${camelToKebabCase(name)}:${value}${_unit}`);
149
+ }
150
+ return acc.join(';');
151
+ };
152
+
153
+ let globalSheets = null;
154
+
155
+ function addGlobalStyles(renderRoot) {
156
+ renderRoot.adoptedStyleSheets.push(...(null === globalSheets && (globalSheets = Array.from(document.styleSheets).map(({cssRules: cssRules}) => {
157
+ const sheet = new CSSStyleSheet, css = Array.from(cssRules).map(rule => rule.cssText).join(' ');
158
+ return sheet.replaceSync(css), sheet;
159
+ })), globalSheets));
160
+ }
161
+
162
+ class UnsafeCss extends String {}
163
+
164
+ const unsafeCss = str => new UnsafeCss(str), escMap$1 = {
165
+ '&': '\\26 ',
166
+ '<': '\\3c ',
167
+ '>': '\\3e '
168
+ }, escCss = string => string instanceof UnsafeCss ? string : unsafeCss((string => string.replace(/[&<>]/g, tag => escMap$1[tag]))('' + string)), css = (strings, ...values) => String.raw({
169
+ raw: strings
170
+ }, ...values.map(escCss));
171
+
172
+ function refsBySelector(container, selectors) {
173
+ const found = {};
174
+ for (const [name, selector] of Object.entries(selectors)) found[name] = container.querySelector?.(selector);
175
+ return found;
176
+ }
177
+
178
+ const toJson = any => {
179
+ try {
180
+ return JSON.parse(any);
181
+ } catch {
182
+ return;
183
+ }
184
+ }, nameMap = {
185
+ class: 'className',
186
+ for: 'htmlFor'
187
+ };
188
+
189
+ class MiElement extends HTMLElement {
190
+ _props={};
191
+ #changedProps={};
192
+ #disposers=new Set;
193
+ #controllers=new Set;
194
+ #updateRequested=!1;
195
+ static get shadowRootInit() {
196
+ return {
197
+ mode: 'open'
198
+ };
199
+ }
200
+ static template;
201
+ static get properties() {}
202
+ static observedAttributes=[];
203
+ static styles='';
204
+ static get useGlobalStyles() {
205
+ return !1;
206
+ }
207
+ static createSignal=createSignal;
208
+ constructor() {
209
+ super();
210
+ const {createSignal: createSignal, properties: properties} = this.constructor;
211
+ for (const [name, {initial: initial}] of Object.entries(properties)) {
212
+ const descriptor = Object.getOwnPropertyDescriptor(this.constructor.prototype, name);
213
+ createSignal && (this._props[name] = createSignal()), Object.defineProperty(this, name, {
214
+ get() {
215
+ return descriptor?.get ? descriptor.get.call(this) : createSignal ? this._props[name].value : this._props[name];
216
+ },
217
+ set(value) {
218
+ const oldValue = this[name];
219
+ descriptor?.set ? descriptor.set.call(this, value) : createSignal ? this._props[name].value = value : this._props[name] = value,
220
+ oldValue !== this[name] && this.requestUpdate({
221
+ [name]: value
222
+ });
223
+ }
224
+ }), this[name] = initial;
225
+ }
226
+ }
227
+ connectedCallback() {
228
+ const {shadowRootInit: shadowRootInit, useGlobalStyles: useGlobalStyles, template: template} = this.constructor;
229
+ this.#controllers.forEach(controller => controller.hostConnected?.()), this.renderRoot = shadowRootInit ? this.shadowRoot ?? this.attachShadow(shadowRootInit) : this,
230
+ this.addTemplate(template), useGlobalStyles && addGlobalStyles(this.renderRoot),
231
+ this.render(), this.requestUpdate();
232
+ }
233
+ disconnectedCallback() {
234
+ this.#disposers.forEach(remover => remover()), this.#controllers.forEach(controller => controller.hostDisconnected?.());
235
+ }
236
+ attributeChangedCallback(name, _oldValue, newValue) {
237
+ const camelName = nameMap[name] ?? kebabToCamelCase(name), properties = this.constructor?.properties, {type: type} = properties?.[camelName] ?? {}, coercedValue = convertType(newValue, type);
238
+ if (name.startsWith('data-')) {
239
+ const datasetName = kebabToCamelCase(name.substring(5));
240
+ datasetName && (this.dataset[datasetName] = coercedValue);
241
+ }
242
+ this[camelName] = coercedValue;
243
+ }
244
+ requestUpdate(changedProps) {
245
+ this.#changedProps = {
246
+ ...this.#changedProps,
247
+ ...changedProps
248
+ }, !this.#updateRequested && this.renderRoot && (this.#updateRequested = !0, window.requestAnimationFrame(() => {
249
+ this.#updateRequested = !1;
250
+ const changedProps = this.#changedProps;
251
+ this.#changedProps = {}, this.update(changedProps);
252
+ }));
253
+ }
254
+ addTemplate(template) {
255
+ if (template) {
256
+ if (!(template instanceof HTMLTemplateElement)) throw new Error('template is not a HTMLTemplateElement');
257
+ this.renderRoot.append(template.content.cloneNode(!0));
258
+ }
259
+ }
260
+ render() {}
261
+ update(_changedProps) {}
262
+ on(eventName, listener, node = this) {
263
+ node.addEventListener(eventName, listener), this.#disposers.add(() => node.removeEventListener(eventName, listener));
264
+ }
265
+ once(eventName, listener, node = this) {
266
+ node.addEventListener(eventName, listener, {
267
+ once: !0
268
+ });
269
+ }
270
+ dispose(...listeners) {
271
+ for (const listener of listeners) {
272
+ if ('function' != typeof listener) throw new TypeError('listener must be a function');
273
+ this.#disposers.add(listener);
274
+ }
275
+ }
276
+ addController(controller) {
277
+ this.#controllers.add(controller), this.isConnected && controller.hostConnected?.();
278
+ }
279
+ removeController(controller) {
280
+ this.#controllers.delete(controller);
281
+ }
282
+ refsBySelector(selectors) {
283
+ return refsBySelector(this.renderRoot, selectors);
284
+ }
285
+ }
286
+
287
+ const define = (tagName, elementClass, options) => {
288
+ if (customElements.get(tagName)) return;
289
+ const {usedCssPrefix: usedCssPrefix = "", cssPrefix: cssPrefix = "", styles: styles} = options || {};
290
+ if (elementClass.properties) {
291
+ const observedAttrs = [];
292
+ for (const [name, {attribute: attribute = !0}] of Object.entries(elementClass.properties)) attribute && observedAttrs.push(camelToKebabCase(name));
293
+ Object.defineProperty(elementClass, 'observedAttributes', {
294
+ get: () => observedAttrs
295
+ });
296
+ } else if (elementClass.observedAttributes) {
297
+ const properties = elementClass.observedAttributes.reduce((acc, attr) => (acc[kebabToCamelCase(attr)] = {},
298
+ acc), {});
299
+ Object.defineProperty(elementClass, 'properties', {
300
+ get: () => properties
301
+ });
302
+ }
303
+ elementClass.styles && (elementClass.styles = styles || (usedCssPrefix === cssPrefix ? elementClass.styles : elementClass.styles.replaceAll(`--${usedCssPrefix}-`, cssPrefix))),
304
+ renderTemplate(elementClass), window.customElements.define(tagName, elementClass);
305
+ }, renderTemplate = element => {
306
+ if (!element.template || element.template instanceof HTMLTemplateElement) return;
307
+ const el = document.createElement('template');
308
+ el.innerHTML = element.template || '', element.template = el;
309
+ }, convertType = (value, type) => type === Boolean ? null !== value : type === Number ? (any => {
310
+ const n = Number(any);
311
+ return isNaN(n) ? 0 : n;
312
+ })(value) : type === Array ? toJson(value) ?? value.split(',').map(v => v.trim()) : type === Object ? toJson(value) : value, globalRenderCache = new class {
313
+ cnt=0;
314
+ cache=new Map;
315
+ last=0;
316
+ get size() {
317
+ return this.cache.size;
318
+ }
319
+ _inc() {
320
+ return this.cnt = 268435455 & ++this.cnt, this.cnt;
321
+ }
322
+ clear() {
323
+ this.cnt = 0, this.cache.clear();
324
+ }
325
+ set(value) {
326
+ const now = Date.now();
327
+ this.last < now && this.cache.clear(), this.last = now + 5e3;
328
+ const key = '__rc:' + this._inc().toString(36);
329
+ return this.cache.set(key, value), key;
330
+ }
331
+ get(key) {
332
+ const value = this.cache.get(key);
333
+ return this.cache.delete(key), value;
334
+ }
335
+ };
336
+
337
+ class UnsafeHtml extends String {}
338
+
339
+ const unsafeHtml = str => new UnsafeHtml(str), escMap = {
340
+ '&': '&amp;',
341
+ '<': '&lt;',
342
+ '>': '&gt;',
343
+ '"': '&quot;',
344
+ "'": '&#39;'
345
+ }, escRe = /[&<>"']/g, esc = string => string.replace(escRe, tag => escMap[tag]), escHtml = string => string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string)), escValue = any => {
346
+ if (any instanceof UnsafeHtml) return any;
347
+ const t = typeof any;
348
+ if ("object" === t || "function" === t) {
349
+ const key = globalRenderCache.set(any);
350
+ return unsafeHtml(key);
351
+ }
352
+ return unsafeHtml(esc('' + (any ?? '')));
353
+ }, html = (strings, ...values) => unsafeHtml(String.raw({
354
+ raw: strings
355
+ }, ...values.map(val => Array.isArray(val) ? val.map(escValue).join('') : escValue(val))));
356
+
357
+ function render(node, template, handlers = {}) {
358
+ const refs = {};
359
+ node.innerHTML = template.toString();
360
+ for (let i = 0, l = node.children.length; i < l; i++) renderAttrs(node.children[i], handlers, refs);
361
+ return refs;
362
+ }
363
+
364
+ const REF = 'ref', REF_Q = '[ref]';
365
+
366
+ function renderAttrs(node, handlers = {}, refs = {}) {
367
+ if (node.nodeType === Node.ELEMENT_NODE) {
368
+ const rmAttrs = [], attrs = node.attributes;
369
+ for (let i = 0, l = attrs.length; i < l; i++) {
370
+ const attr = attrs[i], attrName = attr.name, code = attrName.charCodeAt(0), name = attrName.slice(1);
371
+ let rm = 0;
372
+ if (63 === code) toJson(attr.value) ? node.setAttribute(name, '') : node.removeAttribute(name),
373
+ rm = 1; else if ('...' === attr.name) {
374
+ const obj = globalRenderCache.get(attr.value);
375
+ if (obj && "object" == typeof obj) for (const [k, v] of Object.entries(obj)) node[k] = v;
376
+ rm = 1;
377
+ } else if (46 === code) node[name] = globalRenderCache.get(attr.value) ?? attr.value,
378
+ rm = 1; else if (64 === code) {
379
+ const handlerName = attr.value, fn = globalRenderCache.get(handlerName);
380
+ fn ? node.addEventListener(name, fn) : "function" == typeof handlers[handlerName] && node.addEventListener(name, handlers[handlerName]),
381
+ rm = 1;
382
+ } else attr.name === REF && (refs[attr.value] = node, rm = 1);
383
+ rm && rmAttrs.push(attr.name);
384
+ }
385
+ for (let i = 0, l = rmAttrs.length; i < l; i++) node.removeAttribute(rmAttrs[i]);
386
+ }
387
+ if (customElements.get(node.localName)) {
388
+ const q = node.querySelectorAll(REF_Q);
389
+ for (let el of q) {
390
+ const refName = el.getAttribute(REF);
391
+ refName && !refs[refName] && (refs[refName] = el);
392
+ }
393
+ return refs;
394
+ }
395
+ if (!node.children?.length) return refs;
396
+ for (let child of Array.from(node.children)) renderAttrs(child, handlers, refs);
397
+ return refs;
398
+ }
399
+
400
+ class Store extends State {
401
+ constructor(actions, initialValue, options) {
402
+ super(initialValue, options);
403
+ for (const [action, dispatcher] of Object.entries(actions)) this[action] = data => this.set(dispatcher(data)(this.get()));
404
+ }
405
+ }
406
+
407
+ export { ContextConsumer, ContextProvider, ContextRequestEvent, MiElement, index as Signal, Store, addGlobalStyles, classNames, convertType, css, define, escCss, escHtml, html, refsBySelector, render, renderAttrs, styleMap, unsafeCss, unsafeHtml };
@@ -0,0 +1,5 @@
1
+ /*!
2
+ * SPDX-License-Identifier: MIT
3
+ * mi-element v0.9.7
4
+ */
5
+ const t=[];class e extends EventTarget{#t;#e;constructor(t,e){super();const{equals:s}=e||{};this.#t=t,this.#e=s??((t,e)=>t===e)}get value(){return this.get()}set value(t){this.set(t)}get(){const e=t[t.length-1];return e&&e.add(this),this.#t}set(t){this.#e(this.#t,t)||(this.#t=t,this.dispatchEvent(new CustomEvent("signal")))}}const s=(t,s)=>t instanceof e?t:new e(t,s);function n(e){const s=new Set;t.push(s);try{e()}finally{t.pop()}for(const t of s)t.addEventListener("signal",e);return()=>{for(const t of s)t.removeEventListener("signal",e)}}var r={State:e,Computed:class{#s;#n;constructor(t){this.#s=new e,this.#n=n(()=>this.#s.set(t()))}get(){return this.#s.get()}unsubscribe(){this.#n()}},createSignal:s,effect:n};const o="context-request";class i{constructor(t,e,n){this.host=t,this.context=e,this.state=s(n),this.host.addController?.(this)}hostConnected(){this.host.addEventListener(o,this.onContextRequest)}hostDisconnected(){this.host.removeEventListener(o,this.onContextRequest)}set(t){this.state.set(t)}get(){return this.state.get()}set value(t){this.set(t)}get value(){return this.get()}onContextRequest=t=>{if(t.context!==this.context)return;let e;t.stopPropagation(),t.subscribe&&(e=n(()=>{const s=this.get();e&&t.callback(s,e)})),t.callback(this.get(),e)}}class c extends Event{constructor(t,e,s){super(o,{bubbles:!0,composed:!0}),this.context=t,this.callback=e,this.subscribe=s}}class a{#t;constructor(t,e,s){const{subscribe:n=!1,validate:r=()=>!0}=s||{};this.host=t,this.context=e,this.subscribe=!!n,this.validate=r,this.unsubscribe=void 0,this.host.addController?.(this)}get(){return this.#t}get value(){return this.#t}hostConnected(){this.dispatchRequest()}hostDisconnected(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0)}dispatchRequest(){this.host.dispatchEvent(new c(this.context,this._callback.bind(this),this.subscribe))}_callback(t,e){e&&(this.subscribe?this.unsubscribe&&(this.unsubscribe!==e&&this.unsubscribe(),this.unsubscribe=e):e()),this.validate(t)&&(this.#t=t,this.host.requestUpdate(t))}}const h=(t="")=>t.replace(/([A-Z])/g,(t,e)=>`-${e.toLowerCase()}`),l=(t="")=>t.toLowerCase().replace(/[-_]\w/g,t=>t[1].toUpperCase()),u=(...t)=>{const e=[];return t.forEach(t=>{t&&("string"==typeof t?e.push(t):"object"==typeof t&&Object.entries(t).forEach(([t,s])=>{s&&e.push(t)}))}),e.join(" ")},d=(t,e)=>{const{unit:s="px"}=e||{},n=[];for(const[e,r]of Object.entries(t??{})){if(null==r)continue;const t=Number.isFinite(r)?s:"";n.push(`${h(e)}:${r}${t}`)}return n.join(";")};let p=null;function f(t){t.adoptedStyleSheets.push(...(null===p&&(p=Array.from(document.styleSheets).map(({cssRules:t})=>{const e=new CSSStyleSheet,s=Array.from(t).map(t=>t.cssText).join(" ");return e.replaceSync(s),e})),p))}class b extends String{}const g=t=>new b(t),m={"&":"\\26 ","<":"\\3c ",">":"\\3e "},v=t=>t instanceof b?t:g((t=>t.replace(/[&<>]/g,t=>m[t]))(""+t)),y=(t,...e)=>String.raw({raw:t},...e.map(v));function w(t,e){const s={};for(const[n,r]of Object.entries(e))s[n]=t.querySelector?.(r);return s}const E=t=>{try{return JSON.parse(t)}catch{return}},S={class:"className",for:"htmlFor"};class C extends HTMLElement{_props={};#r={};#o=new Set;#i=new Set;#c=!1;static get shadowRootInit(){return{mode:"open"}}static template;static get properties(){}static observedAttributes=[];static styles="";static get useGlobalStyles(){return!1}static createSignal=s;constructor(){super();const{createSignal:t,properties:e}=this.constructor;for(const[s,{initial:n}]of Object.entries(e)){const e=Object.getOwnPropertyDescriptor(this.constructor.prototype,s);t&&(this._props[s]=t()),Object.defineProperty(this,s,{get(){return e?.get?e.get.call(this):t?this._props[s].value:this._props[s]},set(n){const r=this[s];e?.set?e.set.call(this,n):t?this._props[s].value=n:this._props[s]=n,r!==this[s]&&this.requestUpdate({[s]:n})}}),this[s]=n}}connectedCallback(){const{shadowRootInit:t,useGlobalStyles:e,template:s}=this.constructor;this.#i.forEach(t=>t.hostConnected?.()),this.renderRoot=t?this.shadowRoot??this.attachShadow(t):this,this.addTemplate(s),e&&f(this.renderRoot),this.render(),this.requestUpdate()}disconnectedCallback(){this.#o.forEach(t=>t()),this.#i.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,s){const n=S[t]??l(t),r=this.constructor?.properties,{type:o}=r?.[n]??{},i=A(s,o);if(t.startsWith("data-")){const e=l(t.substring(5));e&&(this.dataset[e]=i)}this[n]=i}requestUpdate(t){this.#r={...this.#r,...t},!this.#c&&this.renderRoot&&(this.#c=!0,window.requestAnimationFrame(()=>{this.#c=!1;const t=this.#r;this.#r={},this.update(t)}))}addTemplate(t){if(t){if(!(t instanceof HTMLTemplateElement))throw new Error("template is not a HTMLTemplateElement");this.renderRoot.append(t.content.cloneNode(!0))}}render(){}update(t){}on(t,e,s=this){s.addEventListener(t,e),this.#o.add(()=>s.removeEventListener(t,e))}once(t,e,s=this){s.addEventListener(t,e,{once:!0})}dispose(...t){for(const e of t){if("function"!=typeof e)throw new TypeError("listener must be a function");this.#o.add(e)}}addController(t){this.#i.add(t),this.isConnected&&t.hostConnected?.()}removeController(t){this.#i.delete(t)}refsBySelector(t){return w(this.renderRoot,t)}}const q=(t,e,s)=>{if(customElements.get(t))return;const{usedCssPrefix:n="",cssPrefix:r="",styles:o}=s||{};if(e.properties){const t=[];for(const[s,{attribute:n=!0}]of Object.entries(e.properties))n&&t.push(h(s));Object.defineProperty(e,"observedAttributes",{get:()=>t})}else if(e.observedAttributes){const t=e.observedAttributes.reduce((t,e)=>(t[l(e)]={},t),{});Object.defineProperty(e,"properties",{get:()=>t})}e.styles&&(e.styles=o||(n===r?e.styles:e.styles.replaceAll(`--${n}-`,r))),x(e),window.customElements.define(t,e)},x=t=>{if(!t.template||t.template instanceof HTMLTemplateElement)return;const e=document.createElement("template");e.innerHTML=t.template||"",t.template=e},A=(t,e)=>e===Boolean?null!==t:e===Number?(t=>{const e=Number(t);return isNaN(e)?0:e})(t):e===Array?E(t)??t.split(",").map(t=>t.trim()):e===Object?E(t):t,j=new class{cnt=0;cache=new Map;last=0;get size(){return this.cache.size}_inc(){return this.cnt=268435455&++this.cnt,this.cnt}clear(){this.cnt=0,this.cache.clear()}set(t){const e=Date.now();this.last<e&&this.cache.clear(),this.last=e+5e3;const s="__rc:"+this._inc().toString(36);return this.cache.set(s,t),s}get(t){const e=this.cache.get(t);return this.cache.delete(t),e}};class L extends String{}const R=t=>new L(t),T={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},O=/[&<>"']/g,_=t=>t.replace(O,t=>T[t]),N=t=>t instanceof L?t:R(_(""+t)),P="object",k="function",M=t=>{if(t instanceof L)return t;const e=typeof t;if(e===P||e===k){const e=j.set(t);return R(e)}return R(_(""+(t??"")))},D=(t,...e)=>R(String.raw({raw:t},...e.map(t=>Array.isArray(t)?t.map(M).join(""):M(t))));function H(t,e,s={}){const n={};t.innerHTML=e.toString();for(let e=0,r=t.children.length;e<r;e++)F(t.children[e],s,n);return n}const U="ref",$="[ref]";function F(t,e={},s={}){if(t.nodeType===Node.ELEMENT_NODE){const n=[],r=t.attributes;for(let o=0,i=r.length;o<i;o++){const i=r[o],c=i.name,a=c.charCodeAt(0),h=c.slice(1);let l=0;if(63===a)E(i.value)?t.setAttribute(h,""):t.removeAttribute(h),l=1;else if("..."===i.name){const e=j.get(i.value);if(e&&typeof e===P)for(const[s,n]of Object.entries(e))t[s]=n;l=1}else if(46===a)t[h]=j.get(i.value)??i.value,l=1;else if(64===a){const s=i.value,n=j.get(s);n?t.addEventListener(h,n):typeof e[s]===k&&t.addEventListener(h,e[s]),l=1}else i.name===U&&(s[i.value]=t,l=1);l&&n.push(i.name)}for(let e=0,s=n.length;e<s;e++)t.removeAttribute(n[e])}if(customElements.get(t.localName)){const e=t.querySelectorAll($);for(let t of e){const e=t.getAttribute(U);e&&!s[e]&&(s[e]=t)}return s}if(!t.children?.length)return s;for(let n of Array.from(t.children))F(n,e,s);return s}class z extends e{constructor(t,e,s){super(e,s);for(const[e,s]of Object.entries(t))this[e]=t=>this.set(s(t)(this.get()))}}export{a as ContextConsumer,i as ContextProvider,c as ContextRequestEvent,C as MiElement,r as Signal,z as Store,f as addGlobalStyles,u as classNames,A as convertType,y as css,q as define,v as escCss,N as escHtml,D as html,w as refsBySelector,H as render,F as renderAttrs,d as styleMap,g as unsafeCss,R as unsafeHtml};
package/dist/html.js CHANGED
@@ -40,7 +40,7 @@ const unsafeHtml = str => new UnsafeHtml(str), escMap = {
40
40
  const key = globalRenderCache.set(any);
41
41
  return unsafeHtml(key);
42
42
  }
43
- return unsafeHtml(esc('' + any));
43
+ return unsafeHtml(esc('' + (any ?? '')));
44
44
  }, html = (strings, ...values) => unsafeHtml(String.raw({
45
45
  raw: strings
46
46
  }, ...values.map(val => Array.isArray(val) ? val.map(escValue).join('') : escValue(val))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mi-element",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "Build lightweight reactive micro web-components",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/commenthol/mi-element/tree/main/packages/mi-element#readme",
@@ -59,6 +59,14 @@
59
59
  "development": "./src/styling.js",
60
60
  "types": "./types/styling.d.ts",
61
61
  "default": "./dist/styling.js"
62
+ },
63
+ "./bundle": {
64
+ "types": "./types/index.d.ts",
65
+ "default": "./dist/bundle.js"
66
+ },
67
+ "./bundle.min": {
68
+ "types": "./types/index.d.ts",
69
+ "default": "./dist/bundle.min.js"
62
70
  }
63
71
  },
64
72
  "main": "src/index.js",
@@ -69,28 +77,28 @@
69
77
  "types"
70
78
  ],
71
79
  "dependencies": {
72
- "mi-signal": "0.9.0"
80
+ "mi-signal": "0.9.7"
73
81
  },
74
82
  "devDependencies": {
75
- "@eslint/js": "^9.39.2",
83
+ "@eslint/js": "^9.39.4",
76
84
  "@rollup/plugin-node-resolve": "^16.0.3",
77
85
  "@rollup/plugin-terser": "^0.4.4",
78
86
  "@testing-library/dom": "^10.4.1",
79
- "@types/node": "^24.10.4",
80
- "@vitest/browser": "^4.0.16",
81
- "@vitest/browser-playwright": "^4.0.16",
82
- "@vitest/coverage-istanbul": "^4.0.16",
83
- "eslint": "^9.39.2",
87
+ "@types/node": "^24.12.2",
88
+ "@vitest/browser": "^4.1.5",
89
+ "@vitest/browser-playwright": "^4.1.5",
90
+ "@vitest/coverage-istanbul": "^4.1.5",
91
+ "eslint": "^9.39.4",
84
92
  "globals": "^16.5.0",
85
93
  "npm-run-all2": "^8.0.4",
86
- "playwright": "^1.57.0",
87
- "prettier": "^3.7.4",
88
- "rimraf": "^6.1.2",
89
- "rollup": "^4.54.0",
94
+ "playwright": "^1.59.1",
95
+ "prettier": "^3.8.3",
96
+ "rimraf": "^6.1.3",
97
+ "rollup": "^4.60.2",
90
98
  "typescript": "^5.9.3",
91
- "vite": "^7.3.0",
92
- "vitest": "^4.0.16",
93
- "mi-html": "0.9.0"
99
+ "vite": "^7.3.2",
100
+ "vitest": "^4.1.5",
101
+ "mi-html": "0.9.7"
94
102
  },
95
103
  "scripts": {
96
104
  "all": "npm-run-all pretty lint test build types",
package/src/html.js CHANGED
@@ -65,6 +65,10 @@ const escMap = {
65
65
 
66
66
  const escRe = /[&<>"']/g
67
67
 
68
+ /**
69
+ * @param {string} string
70
+ * @returns {string}
71
+ */
68
72
  const esc = (string) => string.replace(escRe, (tag) => escMap[tag])
69
73
 
70
74
  /**
@@ -97,7 +101,7 @@ const escValue = (any) => {
97
101
  const key = globalRenderCache.set(any)
98
102
  return unsafeHtml(key)
99
103
  }
100
- return unsafeHtml(esc('' + any))
104
+ return unsafeHtml(esc('' + (any ?? '')))
101
105
  }
102
106
 
103
107
  /**
@@ -180,7 +184,7 @@ export function renderAttrs(node, handlers = {}, refs = {}) {
180
184
  }
181
185
  rm = 1
182
186
  } else if (attr.name === '...') {
183
- // spread attribute
187
+ // spread properties from object
184
188
  const obj = globalRenderCache.get(attr.value)
185
189
  if (obj && typeof obj === OBJECT) {
186
190
  for (const [k, v] of Object.entries(obj)) {