mi-element 0.9.4 → 0.9.6
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 +1 -1
- package/dist/bundle.js +407 -0
- package/dist/bundle.min.js +5 -0
- package/dist/html.js +16 -16
- package/package.json +9 -1
- package/src/html.js +26 -22
- package/types/html.d.ts +1 -1
package/README.md
CHANGED
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
* mi-element v0.9.6
|
|
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
|
+
'&': '&',
|
|
341
|
+
'<': '<',
|
|
342
|
+
'>': '>',
|
|
343
|
+
'"': '"',
|
|
344
|
+
"'": '''
|
|
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.6
|
|
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={"&":"&","<":"<",">":">",'"':""","'":"'"},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
|
@@ -33,9 +33,10 @@ const unsafeHtml = str => new UnsafeHtml(str), escMap = {
|
|
|
33
33
|
'>': '>',
|
|
34
34
|
'"': '"',
|
|
35
35
|
"'": '''
|
|
36
|
-
}, esc = string => string.replace(
|
|
36
|
+
}, escRe = /[&<>"']/g, esc = string => string.replace(escRe, tag => escMap[tag]), escHtml = string => string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string)), escValue = any => {
|
|
37
37
|
if (any instanceof UnsafeHtml) return any;
|
|
38
|
-
|
|
38
|
+
const t = typeof any;
|
|
39
|
+
if ("object" === t || "function" === t) {
|
|
39
40
|
const key = globalRenderCache.set(any);
|
|
40
41
|
return unsafeHtml(key);
|
|
41
42
|
}
|
|
@@ -45,10 +46,9 @@ const unsafeHtml = str => new UnsafeHtml(str), escMap = {
|
|
|
45
46
|
}, ...values.map(val => Array.isArray(val) ? val.map(escValue).join('') : escValue(val))));
|
|
46
47
|
|
|
47
48
|
function render(node, template, handlers = {}) {
|
|
48
|
-
const refs = {}
|
|
49
|
-
|
|
50
|
-
for (let
|
|
51
|
-
node.appendChild(child);
|
|
49
|
+
const refs = {};
|
|
50
|
+
node.innerHTML = template.toString();
|
|
51
|
+
for (let i = 0, l = node.children.length; i < l; i++) renderAttrs(node.children[i], handlers, refs);
|
|
52
52
|
return refs;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -56,24 +56,24 @@ const REF = 'ref', REF_Q = '[ref]';
|
|
|
56
56
|
|
|
57
57
|
function renderAttrs(node, handlers = {}, refs = {}) {
|
|
58
58
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
59
|
-
const
|
|
60
|
-
for (let
|
|
61
|
-
const
|
|
59
|
+
const rmAttrs = [], attrs = node.attributes;
|
|
60
|
+
for (let i = 0, l = attrs.length; i < l; i++) {
|
|
61
|
+
const attr = attrs[i], attrName = attr.name, code = attrName.charCodeAt(0), name = attrName.slice(1);
|
|
62
62
|
let rm = 0;
|
|
63
|
-
if (
|
|
63
|
+
if (63 === code) toJson(attr.value) ? node.setAttribute(name, '') : node.removeAttribute(name),
|
|
64
64
|
rm = 1; else if ('...' === attr.name) {
|
|
65
65
|
const obj = globalRenderCache.get(attr.value);
|
|
66
|
-
if (obj && typeof obj
|
|
66
|
+
if (obj && "object" == typeof obj) for (const [k, v] of Object.entries(obj)) node[k] = v;
|
|
67
67
|
rm = 1;
|
|
68
|
-
} else if (
|
|
69
|
-
rm = 1; else if (
|
|
68
|
+
} else if (46 === code) node[name] = globalRenderCache.get(attr.value) ?? attr.value,
|
|
69
|
+
rm = 1; else if (64 === code) {
|
|
70
70
|
const handlerName = attr.value, fn = globalRenderCache.get(handlerName);
|
|
71
|
-
fn ? node.addEventListener(name,
|
|
71
|
+
fn ? node.addEventListener(name, fn) : "function" == typeof handlers[handlerName] && node.addEventListener(name, handlers[handlerName]),
|
|
72
72
|
rm = 1;
|
|
73
73
|
} else attr.name === REF && (refs[attr.value] = node, rm = 1);
|
|
74
|
-
rm &&
|
|
74
|
+
rm && rmAttrs.push(attr.name);
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
for (let i = 0, l = rmAttrs.length; i < l; i++) node.removeAttribute(rmAttrs[i]);
|
|
77
77
|
}
|
|
78
78
|
if (customElements.get(node.localName)) {
|
|
79
79
|
const q = node.querySelectorAll(REF_Q);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mi-element",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
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",
|
package/src/html.js
CHANGED
|
@@ -63,7 +63,9 @@ const escMap = {
|
|
|
63
63
|
"'": '''
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const
|
|
66
|
+
const escRe = /[&<>"']/g
|
|
67
|
+
|
|
68
|
+
const esc = (string) => string.replace(escRe, (tag) => escMap[tag])
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* escape HTML and prevent double escaping of '&'
|
|
@@ -90,7 +92,8 @@ const escValue = (any) => {
|
|
|
90
92
|
// @ts-expect-error
|
|
91
93
|
return any
|
|
92
94
|
}
|
|
93
|
-
|
|
95
|
+
const t = typeof any
|
|
96
|
+
if (t === OBJECT || t === FUNCTION) {
|
|
94
97
|
const key = globalRenderCache.set(any)
|
|
95
98
|
return unsafeHtml(key)
|
|
96
99
|
}
|
|
@@ -120,20 +123,18 @@ export const html = (strings, ...values) =>
|
|
|
120
123
|
/**
|
|
121
124
|
* render HTML template into given node with support for special attributes
|
|
122
125
|
*
|
|
123
|
-
* @param {Element} node to
|
|
126
|
+
* @param {Element} node to render content
|
|
124
127
|
* @param {string|UnsafeHtml} template HTML template string
|
|
125
128
|
* @param {Record<string, Function>|HTMLElement} [handlers={}] event handlers or HTMLElement for method lookup
|
|
126
129
|
* @returns {Record<string, Element>} references collected
|
|
127
130
|
*/
|
|
128
131
|
export function render(node, template, handlers = {}) {
|
|
129
132
|
const refs = {}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
for (let child of Array.from(div.children)) {
|
|
133
|
+
node.innerHTML = template.toString()
|
|
134
|
+
for (let i = 0, l = node.children.length; i < l; i++) {
|
|
135
|
+
const child = node.children[i]
|
|
134
136
|
// @ts-expect-error
|
|
135
137
|
renderAttrs(child, handlers, refs)
|
|
136
|
-
node.appendChild(child)
|
|
137
138
|
}
|
|
138
139
|
// @ts-expect-error
|
|
139
140
|
return refs
|
|
@@ -162,12 +163,15 @@ const REF_Q = '[ref]'
|
|
|
162
163
|
*/
|
|
163
164
|
export function renderAttrs(node, handlers = {}, refs = {}) {
|
|
164
165
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
166
|
+
const rmAttrs = []
|
|
167
|
+
const attrs = node.attributes
|
|
168
|
+
for (let i = 0, l = attrs.length; i < l; i++) {
|
|
169
|
+
const attr = attrs[i]
|
|
170
|
+
const attrName = attr.name
|
|
171
|
+
const code = attrName.charCodeAt(0)
|
|
172
|
+
const name = attrName.slice(1)
|
|
169
173
|
let rm = 0
|
|
170
|
-
if (
|
|
174
|
+
if (code === 63 /* '?' */) {
|
|
171
175
|
// boolean attributes
|
|
172
176
|
if (toJson(attr.value)) {
|
|
173
177
|
node.setAttribute(name, '')
|
|
@@ -184,32 +188,32 @@ export function renderAttrs(node, handlers = {}, refs = {}) {
|
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
rm = 1
|
|
187
|
-
} else if (
|
|
191
|
+
} else if (code === 46 /* '.' */) {
|
|
188
192
|
// property binding
|
|
189
193
|
node[name] = globalRenderCache.get(attr.value) ?? attr.value
|
|
190
194
|
rm = 1
|
|
191
|
-
} else if (
|
|
195
|
+
} else if (code === 64 /* '@' */) {
|
|
192
196
|
// event listener
|
|
193
197
|
const handlerName = attr.value
|
|
194
198
|
const fn = globalRenderCache.get(handlerName)
|
|
195
199
|
if (fn) {
|
|
196
|
-
node.addEventListener(name,
|
|
200
|
+
node.addEventListener(name, fn)
|
|
197
201
|
} else if (typeof handlers[handlerName] === FUNCTION) {
|
|
198
|
-
node.addEventListener(name,
|
|
202
|
+
node.addEventListener(name, handlers[handlerName])
|
|
199
203
|
}
|
|
200
204
|
rm = 1
|
|
201
205
|
} else if (attr.name === REF) {
|
|
202
206
|
// element reference - remove as well to prevent collection by other processors
|
|
203
|
-
|
|
204
|
-
refs[refName] = node
|
|
207
|
+
refs[attr.value] = node
|
|
205
208
|
rm = 1
|
|
206
209
|
}
|
|
207
210
|
if (rm) {
|
|
208
|
-
|
|
211
|
+
rmAttrs.push(attr.name)
|
|
209
212
|
}
|
|
210
213
|
}
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
for (let i = 0, l = rmAttrs.length; i < l; i++) {
|
|
215
|
+
node.removeAttribute(rmAttrs[i])
|
|
216
|
+
}
|
|
213
217
|
}
|
|
214
218
|
// early abort if custom element but resolve slotted refs
|
|
215
219
|
if (customElements.get(node.localName)) {
|
package/types/html.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* render HTML template into given node with support for special attributes
|
|
3
3
|
*
|
|
4
|
-
* @param {Element} node to
|
|
4
|
+
* @param {Element} node to render content
|
|
5
5
|
* @param {string|UnsafeHtml} template HTML template string
|
|
6
6
|
* @param {Record<string, Function>|HTMLElement} [handlers={}] event handlers or HTMLElement for method lookup
|
|
7
7
|
* @returns {Record<string, Element>} references collected
|