@taybart/corvid 0.1.31 → 0.2.2
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/dist/dom.d.ts +99 -14
- package/dist/dom.js +247 -36
- package/dist/index.js +263 -36
- package/dist/ls.js +227 -39
- package/dist/qr.js +227 -39
- package/dist/style.d.ts +36 -0
- package/dist/style.js +100 -1
- package/dist/style.test.d.ts +1 -0
- package/package.json +11 -9
package/dist/dom.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as style from './style';
|
|
1
2
|
import { logger } from './utils';
|
|
2
3
|
/****************
|
|
3
4
|
* DOM *
|
|
@@ -7,6 +8,8 @@ import { logger } from './utils';
|
|
|
7
8
|
* onDomContentLoaded callback
|
|
8
9
|
*/
|
|
9
10
|
export declare function ready(cb: () => void): void;
|
|
11
|
+
export declare function onFocus(cb: () => void): void;
|
|
12
|
+
export declare function onBlur(cb: () => void): void;
|
|
10
13
|
export declare function on(event: string, cb: (ev: Event) => void): () => void;
|
|
11
14
|
export declare function onKey(key: string, cb: (ev: {
|
|
12
15
|
ctrl: boolean;
|
|
@@ -15,50 +18,129 @@ export declare function onKey(key: string, cb: (ev: {
|
|
|
15
18
|
shift: boolean;
|
|
16
19
|
}) => void, verbose?: boolean): () => void;
|
|
17
20
|
export declare function els(query: string, verbose?: boolean): el[];
|
|
21
|
+
/**
|
|
22
|
+
* Define a custom element. Defining the same name with the same class again is a
|
|
23
|
+
* no-op rather than a `NotSupportedError`, so a double import or an HMR reload
|
|
24
|
+
* does not blow up; a *different* class on a taken name is still an error.
|
|
25
|
+
*/
|
|
26
|
+
export declare function registerElement(name: string, ctor: CustomElementConstructor): void;
|
|
18
27
|
/*** element ***/
|
|
19
|
-
type elOpts = {
|
|
28
|
+
export type elOpts = {
|
|
20
29
|
element?: HTMLElement;
|
|
21
30
|
query?: string;
|
|
22
|
-
|
|
31
|
+
tag?: string;
|
|
23
32
|
content?: any;
|
|
24
33
|
class?: string | string[];
|
|
25
34
|
style?: Object;
|
|
26
35
|
id?: string;
|
|
27
|
-
parent?: HTMLElement | el;
|
|
36
|
+
parent?: string | HTMLElement | el;
|
|
28
37
|
attrs?: Object;
|
|
29
38
|
};
|
|
30
39
|
export declare class el {
|
|
31
40
|
el: HTMLElement | null;
|
|
32
41
|
query: string;
|
|
33
42
|
log: logger;
|
|
34
|
-
listeners: Record<string, Array<
|
|
43
|
+
listeners: Record<string, Array<{
|
|
44
|
+
cb: (ev: Event) => void;
|
|
45
|
+
options?: AddEventListenerOptions | boolean;
|
|
46
|
+
}>>;
|
|
35
47
|
constructor(opts: HTMLElement | string | elOpts, verbose?: boolean);
|
|
36
48
|
static query(query: string, verbose?: boolean): el;
|
|
37
49
|
/*** dom manipulation ***/
|
|
38
|
-
value(update?: string): string |
|
|
50
|
+
value(update?: string): string | this;
|
|
39
51
|
parent(parent: HTMLElement | el): this;
|
|
52
|
+
append(ch: HTMLElement | el | string): this;
|
|
40
53
|
appendChild(ch: HTMLElement | el): this;
|
|
41
|
-
child(ch: HTMLElement | el): this;
|
|
42
|
-
|
|
54
|
+
child(ch: HTMLElement | el | string): this;
|
|
55
|
+
prepend(ch: HTMLElement | el | string): this;
|
|
56
|
+
prependChild(ch: HTMLElement | el | string): this;
|
|
43
57
|
empty(): this;
|
|
44
58
|
content(content: any, { text }?: {
|
|
45
59
|
text?: boolean;
|
|
46
60
|
}): this;
|
|
61
|
+
html(content: string): this;
|
|
47
62
|
src(url: string): this;
|
|
63
|
+
attrs(attrs: Object): this;
|
|
64
|
+
attr(key: string, val: string): this;
|
|
65
|
+
removeAttr(key: string): this;
|
|
48
66
|
/*** Style ***/
|
|
49
|
-
style(update: Object | string, stringify?: boolean): this
|
|
67
|
+
style(update: Object | string, stringify?: boolean): this;
|
|
50
68
|
hasClass(className: string): boolean;
|
|
51
69
|
addClass(className: string | string[]): this;
|
|
52
70
|
removeClass(className: string | string[]): this;
|
|
53
|
-
/*** Templates ***/
|
|
54
|
-
html(content: string): void;
|
|
55
|
-
render(vars?: {}): string;
|
|
56
|
-
appendTemplate(template: el, vars: any): void;
|
|
57
71
|
/*** Events ***/
|
|
58
|
-
on(event: string, cb: (ev: Event) => void): this;
|
|
59
|
-
listen(event: string, cb: (ev: Event) => void): this;
|
|
72
|
+
on(event: string, cb: (ev: Event) => void, options?: AddEventListenerOptions | boolean): this;
|
|
73
|
+
listen(event: string, cb: (ev: Event) => void, options?: AddEventListenerOptions | boolean): this;
|
|
60
74
|
removeListeners(event: string): this;
|
|
61
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Create an element and return the node itself, not an `el` wrapper. Takes the
|
|
78
|
+
* same options as `el`, so a custom element comes back ready to talk to:
|
|
79
|
+
*
|
|
80
|
+
* const form = create({ tag: 'x-search-form', parent: '#query-form' })
|
|
81
|
+
* form.onSubmit(...) // its own methods, no `.el` hop
|
|
82
|
+
*
|
|
83
|
+
* Wrap it later with `new el(node)` if you want the chainable helpers.
|
|
84
|
+
*/
|
|
85
|
+
export declare function create<T extends HTMLElement = HTMLElement>(opts: elOpts, verbose?: boolean): T;
|
|
86
|
+
/*** component ***/
|
|
87
|
+
/**
|
|
88
|
+
* Base class for custom elements. It handles registration, the component's
|
|
89
|
+
* stylesheet, and a mount hook
|
|
90
|
+
* class SearchForm extends component {
|
|
91
|
+
* static tag = 'x-search-form'
|
|
92
|
+
* static styles = { input: { border: 'none' } }
|
|
93
|
+
* input = create({ tag: 'input' })
|
|
94
|
+
* mount() {
|
|
95
|
+
* this.input.addEventListener('keydown', (e) => ...)
|
|
96
|
+
* this.append(this.input)
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* SearchForm.register()
|
|
100
|
+
*/
|
|
101
|
+
export declare class component extends HTMLElement {
|
|
102
|
+
#private;
|
|
103
|
+
/** the name to register as; must contain a hyphen */
|
|
104
|
+
static tag: string;
|
|
105
|
+
/**
|
|
106
|
+
* Injected once at `register()`, scoped to the tag. Written *relative* to the
|
|
107
|
+
* component — no tag selector of its own — so it always matches whatever name
|
|
108
|
+
* the class actually registered under.
|
|
109
|
+
*/
|
|
110
|
+
static styles: style.declarations | null;
|
|
111
|
+
/**
|
|
112
|
+
* Inject this component's styles and define it. Idempotent, so calling it
|
|
113
|
+
* twice (or after an HMR reload) is harmless.
|
|
114
|
+
* @param tag - overrides `static tag`
|
|
115
|
+
*/
|
|
116
|
+
static register(tag?: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Called once, the first time the element is connected. Build the subtree and
|
|
119
|
+
* wire listeners here — a custom element cannot give itself children before
|
|
120
|
+
* it is connected, and `connectedCallback` runs again on every re-insertion.
|
|
121
|
+
*/
|
|
122
|
+
mount(): void;
|
|
123
|
+
/**
|
|
124
|
+
* Called every time the element is disconnected. Listeners on this element
|
|
125
|
+
* and on children it owns go away with it; undo document/window listeners
|
|
126
|
+
* here, using the removers `on()` and `onKey()` hand back.
|
|
127
|
+
*/
|
|
128
|
+
unmount(): void;
|
|
129
|
+
/**
|
|
130
|
+
* Called for every change to an attribute named in `static observedAttributes`,
|
|
131
|
+
* and never before `mount()` — changes that arrive earlier (anything set at
|
|
132
|
+
* creation or parse time, which the platform reports *before*
|
|
133
|
+
* `connectedCallback`) are held and delivered in order right after mount.
|
|
134
|
+
*
|
|
135
|
+
* Attributes are strings. For structured data use a property with a setter
|
|
136
|
+
* that re-renders; an object put through `setAttribute` becomes
|
|
137
|
+
* "[object Object]".
|
|
138
|
+
*/
|
|
139
|
+
onAttr(_name: string, _value: string | null, _prev: string | null): void;
|
|
140
|
+
connectedCallback(): void;
|
|
141
|
+
disconnectedCallback(): void;
|
|
142
|
+
attributeChangedCallback(name: string, prev: string | null, value: string | null): void;
|
|
143
|
+
}
|
|
62
144
|
/**
|
|
63
145
|
* Get a template from a string
|
|
64
146
|
* https://stackoverflow.com/a/41015840
|
|
@@ -70,6 +152,9 @@ export declare function interpolate(str: string, params: Object): string;
|
|
|
70
152
|
declare const _default: {
|
|
71
153
|
el: typeof el;
|
|
72
154
|
els: typeof els;
|
|
155
|
+
create: typeof create;
|
|
156
|
+
component: typeof component;
|
|
157
|
+
registerElement: typeof registerElement;
|
|
73
158
|
ready: typeof ready;
|
|
74
159
|
on: typeof on;
|
|
75
160
|
onKey: typeof onKey;
|
package/dist/dom.js
CHANGED
|
@@ -6,6 +6,97 @@ function render(style) {
|
|
|
6
6
|
Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
|
|
7
7
|
return s;
|
|
8
8
|
}
|
|
9
|
+
function splitSelectors(selector) {
|
|
10
|
+
const out = [];
|
|
11
|
+
let depth = 0;
|
|
12
|
+
let quote = '';
|
|
13
|
+
let current = '';
|
|
14
|
+
for (const ch of selector){
|
|
15
|
+
if (quote) {
|
|
16
|
+
if (ch === quote) quote = '';
|
|
17
|
+
} else if ('"' === ch || "'" === ch) quote = ch;
|
|
18
|
+
else if ('(' === ch || '[' === ch) depth++;
|
|
19
|
+
else if (')' === ch || ']' === ch) depth--;
|
|
20
|
+
else if (',' === ch && 0 === depth) {
|
|
21
|
+
if (current.trim()) out.push(current.trim());
|
|
22
|
+
current = '';
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
current += ch;
|
|
26
|
+
}
|
|
27
|
+
if (current.trim()) out.push(current.trim());
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function resolve(parents, selector) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const parent of parents)for (const part of splitSelectors(selector))out.push(part.includes('&') ? part.replaceAll('&', parent) : `${parent} ${part}`);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function compile(parents, decls, indent = '') {
|
|
36
|
+
const blocks = [];
|
|
37
|
+
const props = [];
|
|
38
|
+
const nested = [];
|
|
39
|
+
for (const [k, v] of Object.entries(decls))if (null !== v && 'object' == typeof v) nested.push([
|
|
40
|
+
k,
|
|
41
|
+
v
|
|
42
|
+
]);
|
|
43
|
+
else props.push(`${indent} ${toKebab(k)}: ${v};`);
|
|
44
|
+
if (props.length) blocks.push(`${indent}${parents.join(', ')} {\n${props.join('\n')}\n${indent}}`);
|
|
45
|
+
for (const [selector, block] of nested){
|
|
46
|
+
if (selector.startsWith('@')) {
|
|
47
|
+
const inner = selector.startsWith('@keyframes') ? Object.entries(block).flatMap(([step, d])=>null !== d && 'object' == typeof d ? compile([
|
|
48
|
+
step
|
|
49
|
+
], d, `${indent} `) : []) : compile(parents, block, `${indent} `);
|
|
50
|
+
if (inner.length) blocks.push(`${indent}${selector} {\n${inner.join('\n')}\n${indent}}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
blocks.push(...compile(resolve(parents, selector), block, indent));
|
|
54
|
+
}
|
|
55
|
+
return blocks;
|
|
56
|
+
}
|
|
57
|
+
function css(selector, style) {
|
|
58
|
+
return compile(splitSelectors(selector), style).join('\n');
|
|
59
|
+
}
|
|
60
|
+
function registry() {
|
|
61
|
+
const doc = document;
|
|
62
|
+
if (!doc.__corvidSheets) doc.__corvidSheets = new Map();
|
|
63
|
+
return doc.__corvidSheets;
|
|
64
|
+
}
|
|
65
|
+
function adoptable() {
|
|
66
|
+
return 'undefined' != typeof CSSStyleSheet && 'replaceSync' in CSSStyleSheet.prototype && 'undefined' != typeof Document && 'adoptedStyleSheets' in Document.prototype;
|
|
67
|
+
}
|
|
68
|
+
function styleTag(key) {
|
|
69
|
+
const tagged = Array.from(document.head.querySelectorAll('style[data-corvid]'));
|
|
70
|
+
for (const node of tagged)if (node.dataset.corvid === key) return node;
|
|
71
|
+
const node = document.createElement('style');
|
|
72
|
+
node.dataset.corvid = key;
|
|
73
|
+
document.head.appendChild(node);
|
|
74
|
+
return node;
|
|
75
|
+
}
|
|
76
|
+
function inject(selector, style, { key = selector } = {}) {
|
|
77
|
+
const rules = css(selector, style);
|
|
78
|
+
const sheets = registry();
|
|
79
|
+
const existing = sheets.get(key);
|
|
80
|
+
if (existing) {
|
|
81
|
+
if ('replaceSync' in existing) existing.replaceSync(rules);
|
|
82
|
+
else existing.textContent = rules;
|
|
83
|
+
return rules;
|
|
84
|
+
}
|
|
85
|
+
if (adoptable()) {
|
|
86
|
+
const sheet = new CSSStyleSheet();
|
|
87
|
+
sheet.replaceSync(rules);
|
|
88
|
+
document.adoptedStyleSheets = [
|
|
89
|
+
...document.adoptedStyleSheets,
|
|
90
|
+
sheet
|
|
91
|
+
];
|
|
92
|
+
sheets.set(key, sheet);
|
|
93
|
+
return rules;
|
|
94
|
+
}
|
|
95
|
+
const node = styleTag(key);
|
|
96
|
+
node.textContent = rules;
|
|
97
|
+
sheets.set(key, node);
|
|
98
|
+
return rules;
|
|
99
|
+
}
|
|
9
100
|
function _define_property(obj, key, value) {
|
|
10
101
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
11
102
|
value: value,
|
|
@@ -61,6 +152,37 @@ class logger {
|
|
|
61
152
|
});
|
|
62
153
|
}
|
|
63
154
|
}
|
|
155
|
+
function _check_private_redeclaration(obj, privateCollection) {
|
|
156
|
+
if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
157
|
+
}
|
|
158
|
+
function _class_apply_descriptor_get(receiver, descriptor) {
|
|
159
|
+
if (descriptor.get) return descriptor.get.call(receiver);
|
|
160
|
+
return descriptor.value;
|
|
161
|
+
}
|
|
162
|
+
function _class_apply_descriptor_set(receiver, descriptor, value) {
|
|
163
|
+
if (descriptor.set) descriptor.set.call(receiver, value);
|
|
164
|
+
else {
|
|
165
|
+
if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
|
|
166
|
+
descriptor.value = value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function _class_extract_field_descriptor(receiver, privateMap, action) {
|
|
170
|
+
if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
|
|
171
|
+
return privateMap.get(receiver);
|
|
172
|
+
}
|
|
173
|
+
function _class_private_field_get(receiver, privateMap) {
|
|
174
|
+
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
|
|
175
|
+
return _class_apply_descriptor_get(receiver, descriptor);
|
|
176
|
+
}
|
|
177
|
+
function _class_private_field_init(obj, privateMap, value) {
|
|
178
|
+
_check_private_redeclaration(obj, privateMap);
|
|
179
|
+
privateMap.set(obj, value);
|
|
180
|
+
}
|
|
181
|
+
function _class_private_field_set(receiver, privateMap, value) {
|
|
182
|
+
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
|
|
183
|
+
_class_apply_descriptor_set(receiver, descriptor, value);
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
64
186
|
function dom_define_property(obj, key, value) {
|
|
65
187
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
66
188
|
value: value,
|
|
@@ -74,6 +196,16 @@ function dom_define_property(obj, key, value) {
|
|
|
74
196
|
function ready(cb) {
|
|
75
197
|
window.addEventListener('DOMContentLoaded', cb);
|
|
76
198
|
}
|
|
199
|
+
function onFocus(cb) {
|
|
200
|
+
window.addEventListener('focus', ()=>{
|
|
201
|
+
cb();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function onBlur(cb) {
|
|
205
|
+
window.addEventListener('blur', ()=>{
|
|
206
|
+
cb();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
77
209
|
function on(event, cb) {
|
|
78
210
|
document.addEventListener(event, cb);
|
|
79
211
|
return ()=>{
|
|
@@ -100,6 +232,14 @@ function onKey(key, cb, verbose = false) {
|
|
|
100
232
|
function els(query, verbose = false) {
|
|
101
233
|
return Array.from(document.querySelectorAll(query)).map((n)=>new dom_el(n, verbose));
|
|
102
234
|
}
|
|
235
|
+
function registerElement(name, ctor) {
|
|
236
|
+
const existing = customElements.get(name);
|
|
237
|
+
if (existing) {
|
|
238
|
+
if (existing !== ctor) throw new Error(`custom element ${name} is already registered by ${existing.name}`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
customElements.define(name, ctor);
|
|
242
|
+
}
|
|
103
243
|
class dom_el {
|
|
104
244
|
static query(query, verbose = false) {
|
|
105
245
|
return new dom_el(query, verbose);
|
|
@@ -122,21 +262,29 @@ class dom_el {
|
|
|
122
262
|
parent.appendChild(this.el);
|
|
123
263
|
return this;
|
|
124
264
|
}
|
|
265
|
+
append(ch) {
|
|
266
|
+
return this.child(ch);
|
|
267
|
+
}
|
|
125
268
|
appendChild(ch) {
|
|
126
269
|
return this.child(ch);
|
|
127
270
|
}
|
|
128
271
|
child(ch) {
|
|
129
272
|
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
130
|
-
if (
|
|
273
|
+
if ('string' == typeof ch) this.el.append(ch);
|
|
274
|
+
else if (ch instanceof dom_el) this.el.appendChild(ch.el);
|
|
131
275
|
else this.el.appendChild(ch);
|
|
132
276
|
return this;
|
|
133
277
|
}
|
|
134
|
-
|
|
278
|
+
prepend(ch) {
|
|
135
279
|
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
136
|
-
if (
|
|
280
|
+
if ('string' == typeof ch) this.el.prepend(ch);
|
|
281
|
+
else if (ch instanceof dom_el) this.el.prepend(ch.el);
|
|
137
282
|
else this.el.prepend(ch);
|
|
138
283
|
return this;
|
|
139
284
|
}
|
|
285
|
+
prependChild(ch) {
|
|
286
|
+
return this.prepend(ch);
|
|
287
|
+
}
|
|
140
288
|
empty() {
|
|
141
289
|
if (this.el) this.el.innerHTML = '';
|
|
142
290
|
return this;
|
|
@@ -147,17 +295,37 @@ class dom_el {
|
|
|
147
295
|
else this.el.innerHTML = content;
|
|
148
296
|
return this;
|
|
149
297
|
}
|
|
298
|
+
html(content) {
|
|
299
|
+
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
300
|
+
this.el.innerHTML = content;
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
150
303
|
src(url) {
|
|
151
304
|
if (this.el && 'src' in this.el) this.el.src = url;
|
|
152
305
|
return this;
|
|
153
306
|
}
|
|
307
|
+
attrs(attrs) {
|
|
308
|
+
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
309
|
+
for (const [k, v] of Object.entries(attrs))this.el.setAttribute(k, v);
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
attr(key, val) {
|
|
313
|
+
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
314
|
+
this.el.setAttribute(key, val);
|
|
315
|
+
return this;
|
|
316
|
+
}
|
|
317
|
+
removeAttr(key) {
|
|
318
|
+
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
319
|
+
this.el.removeAttribute(key);
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
154
322
|
style(update, stringify = false) {
|
|
155
323
|
if (this.el) {
|
|
156
324
|
if ('string' == typeof update) this.el.style = update;
|
|
157
325
|
else if ('object' == typeof update) {
|
|
158
326
|
if (!stringify) {
|
|
159
327
|
for (const [k, v] of Object.entries(update))this.el.style[k] = v;
|
|
160
|
-
return;
|
|
328
|
+
return this;
|
|
161
329
|
}
|
|
162
330
|
const s = render(update);
|
|
163
331
|
this.log.debug(`set style: ${this.el.style} -> ${s}`);
|
|
@@ -182,38 +350,23 @@ class dom_el {
|
|
|
182
350
|
else for (const sc of className)this.el.classList.remove(sc);
|
|
183
351
|
return this;
|
|
184
352
|
}
|
|
185
|
-
|
|
186
|
-
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
187
|
-
this.el.innerHTML = content;
|
|
188
|
-
}
|
|
189
|
-
render(vars = {}) {
|
|
190
|
-
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
191
|
-
try {
|
|
192
|
-
return interpolate(this.el.innerHTML, vars);
|
|
193
|
-
} catch (e) {
|
|
194
|
-
throw new Error(`could not render template ${this.query}: ${e}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
appendTemplate(template, vars) {
|
|
198
|
-
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
199
|
-
if (!template.el) throw new Error("template does not contain element");
|
|
200
|
-
const tmpl = template.render(vars);
|
|
201
|
-
this.el.insertAdjacentHTML('beforeend', tmpl);
|
|
202
|
-
}
|
|
203
|
-
on(event, cb) {
|
|
353
|
+
on(event, cb, options) {
|
|
204
354
|
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
205
355
|
if (!this.listeners[event]) this.listeners[event] = [];
|
|
206
|
-
this.listeners[event].push(
|
|
207
|
-
|
|
356
|
+
this.listeners[event].push({
|
|
357
|
+
cb,
|
|
358
|
+
options
|
|
359
|
+
});
|
|
360
|
+
this.el.addEventListener(event, cb, options);
|
|
208
361
|
return this;
|
|
209
362
|
}
|
|
210
|
-
listen(event, cb) {
|
|
211
|
-
return this.on(event, cb);
|
|
363
|
+
listen(event, cb, options) {
|
|
364
|
+
return this.on(event, cb, options);
|
|
212
365
|
}
|
|
213
366
|
removeListeners(event) {
|
|
214
367
|
if (!this.el) throw new Error(`no element from query: ${this.query}`);
|
|
215
368
|
if (!this.listeners[event]) return this;
|
|
216
|
-
for (const cb of this.listeners[event])this.el.removeEventListener(event, cb);
|
|
369
|
+
for (const { cb, options } of this.listeners[event])this.el.removeEventListener(event, cb, options);
|
|
217
370
|
this.listeners[event] = [];
|
|
218
371
|
return this;
|
|
219
372
|
}
|
|
@@ -233,7 +386,7 @@ class dom_el {
|
|
|
233
386
|
this.el = opts;
|
|
234
387
|
return;
|
|
235
388
|
}
|
|
236
|
-
const { query, element,
|
|
389
|
+
const { query, element, tag, class: styleClass, style, id, content, parent, attrs } = opts;
|
|
237
390
|
if (query) {
|
|
238
391
|
this.log.debug(`using query: ${query}`);
|
|
239
392
|
this.query = query;
|
|
@@ -242,11 +395,11 @@ class dom_el {
|
|
|
242
395
|
} else if (element) {
|
|
243
396
|
this.log.debug(`using existing element: ${element}`);
|
|
244
397
|
this.el = element;
|
|
245
|
-
} else if (
|
|
246
|
-
this.query =
|
|
247
|
-
this.log.debug(`creating element: ${
|
|
248
|
-
this.el = document.createElement(
|
|
249
|
-
} else throw new Error('no query or
|
|
398
|
+
} else if (tag) {
|
|
399
|
+
this.query = tag;
|
|
400
|
+
this.log.debug(`creating element: ${tag}`);
|
|
401
|
+
this.el = document.createElement(tag);
|
|
402
|
+
} else throw new Error('no query or tag provided');
|
|
250
403
|
if (this.el) {
|
|
251
404
|
if (id) {
|
|
252
405
|
this.log.debug(`setting id: ${id}`);
|
|
@@ -261,7 +414,12 @@ class dom_el {
|
|
|
261
414
|
}
|
|
262
415
|
if (parent) {
|
|
263
416
|
this.log.debug("adding to parent");
|
|
264
|
-
parent
|
|
417
|
+
let p = parent;
|
|
418
|
+
if ('string' == typeof parent) {
|
|
419
|
+
this.log.debug(`parent query: ${parent}`);
|
|
420
|
+
p = document.querySelector(parent);
|
|
421
|
+
}
|
|
422
|
+
p.appendChild(this.el);
|
|
265
423
|
}
|
|
266
424
|
}
|
|
267
425
|
if (attrs) for (const [key, val] of Object.entries(attrs)){
|
|
@@ -270,6 +428,56 @@ class dom_el {
|
|
|
270
428
|
}
|
|
271
429
|
}
|
|
272
430
|
}
|
|
431
|
+
function create(opts, verbose = false) {
|
|
432
|
+
const node = new dom_el(opts, verbose).el;
|
|
433
|
+
if (!node) throw new Error(`could not create element: ${opts.tag ?? opts.query ?? '?'}`);
|
|
434
|
+
return node;
|
|
435
|
+
}
|
|
436
|
+
var _mounted = /*#__PURE__*/ new WeakMap(), _pendingAttrs = /*#__PURE__*/ new WeakMap();
|
|
437
|
+
class component extends HTMLElement {
|
|
438
|
+
static register(tag) {
|
|
439
|
+
const name = tag ?? this.tag;
|
|
440
|
+
if (!name) throw new Error(`${this.name}: nothing to register as, set \`static tag = 'x-...'\``);
|
|
441
|
+
if (!name.includes('-')) throw new Error(`${this.name}: '${name}' is not a valid custom element name, it needs a hyphen`);
|
|
442
|
+
if (this.styles) inject(name, this.styles);
|
|
443
|
+
registerElement(name, this);
|
|
444
|
+
}
|
|
445
|
+
mount() {}
|
|
446
|
+
unmount() {}
|
|
447
|
+
onAttr(_name, _value, _prev) {}
|
|
448
|
+
connectedCallback() {
|
|
449
|
+
if (!_class_private_field_get(this, _mounted)) {
|
|
450
|
+
_class_private_field_set(this, _mounted, true);
|
|
451
|
+
this.mount();
|
|
452
|
+
const queued = _class_private_field_get(this, _pendingAttrs);
|
|
453
|
+
_class_private_field_set(this, _pendingAttrs, []);
|
|
454
|
+
for (const [name, value, prev] of queued)this.onAttr(name, value, prev);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
disconnectedCallback() {
|
|
458
|
+
this.unmount();
|
|
459
|
+
}
|
|
460
|
+
attributeChangedCallback(name, prev, value) {
|
|
461
|
+
if (prev === value) return;
|
|
462
|
+
if (!_class_private_field_get(this, _mounted)) return void _class_private_field_get(this, _pendingAttrs).push([
|
|
463
|
+
name,
|
|
464
|
+
value,
|
|
465
|
+
prev
|
|
466
|
+
]);
|
|
467
|
+
this.onAttr(name, value, prev);
|
|
468
|
+
}
|
|
469
|
+
constructor(...args){
|
|
470
|
+
super(...args), _class_private_field_init(this, _mounted, {
|
|
471
|
+
writable: true,
|
|
472
|
+
value: false
|
|
473
|
+
}), _class_private_field_init(this, _pendingAttrs, {
|
|
474
|
+
writable: true,
|
|
475
|
+
value: []
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
dom_define_property(component, "tag", '');
|
|
480
|
+
dom_define_property(component, "styles", null);
|
|
273
481
|
function interpolate(str, params) {
|
|
274
482
|
let names = Object.keys(params).map((k)=>`_${k}`);
|
|
275
483
|
let vals = Object.values(params);
|
|
@@ -278,8 +486,11 @@ function interpolate(str, params) {
|
|
|
278
486
|
const dom = {
|
|
279
487
|
el: dom_el,
|
|
280
488
|
els,
|
|
489
|
+
create,
|
|
490
|
+
component,
|
|
491
|
+
registerElement,
|
|
281
492
|
ready,
|
|
282
493
|
on,
|
|
283
494
|
onKey
|
|
284
495
|
};
|
|
285
|
-
export { dom as default, dom_el as el, els, interpolate, on, onKey, ready };
|
|
496
|
+
export { component, create, dom as default, dom_el as el, els, interpolate, on, onBlur, onFocus, onKey, ready, registerElement };
|