@xtia/jel 0.7.0 → 0.7.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/index.d.ts +1 -1
- package/index.js +1 -1
- package/internal/element.d.ts +3 -3
- package/internal/element.js +10 -10
- package/internal/emitter.d.ts +36 -8
- package/internal/emitter.js +91 -55
- package/internal/proxy.js +2 -9
- package/internal/types.d.ts +20 -18
- package/internal/util.d.ts +3 -2
- package/internal/util.js +4 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DomEntity, ElementClassDescriptor, ElementDescriptor, DOMContent, DomHelper, StyleAccessor, JelEntity } from "./internal/types";
|
|
2
2
|
import { $ } from "./internal/element";
|
|
3
3
|
export { createEntity } from "./internal/util";
|
|
4
|
-
export { createEventSource, interval } from "./internal/emitter";
|
|
4
|
+
export { createEventSource, interval, timeout, SubjectEmitter, toEventEmitter } from "./internal/emitter";
|
|
5
5
|
export { $ };
|
|
6
6
|
export declare const $body: import(".").DomEntity<HTMLElement>;
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $ } from "./internal/element";
|
|
2
2
|
export { createEntity } from "./internal/util";
|
|
3
|
-
export { createEventSource, interval } from "./internal/emitter";
|
|
3
|
+
export { createEventSource, interval, timeout, SubjectEmitter, toEventEmitter } from "./internal/emitter";
|
|
4
4
|
export { $ };
|
|
5
5
|
export const $body = $(document.body);
|
package/internal/element.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { DomHelper,
|
|
1
|
+
import { DomHelper, EmitterLike } from "./types";
|
|
2
2
|
export declare const $: DomHelper;
|
|
3
3
|
export declare class ClassAccessor {
|
|
4
4
|
private classList;
|
|
5
5
|
private listen;
|
|
6
6
|
private unlisten;
|
|
7
|
-
constructor(classList: DOMTokenList, listen: (className: string, stream:
|
|
7
|
+
constructor(classList: DOMTokenList, listen: (className: string, stream: EmitterLike<boolean>) => void, unlisten: (classNames: string[]) => void);
|
|
8
8
|
add(...className: string[]): void;
|
|
9
9
|
remove(...className: string[]): void;
|
|
10
10
|
toggle(className: string, value?: boolean): boolean;
|
|
11
|
-
toggle(className: string, value:
|
|
11
|
+
toggle(className: string, value: EmitterLike<boolean>): void;
|
|
12
12
|
contains(className: string): boolean;
|
|
13
13
|
get length(): number;
|
|
14
14
|
toString(): string;
|
package/internal/element.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { attribsProxy, eventsProxy, styleProxy } from "./proxy";
|
|
2
|
-
import { entityDataSymbol, isContent, isJelEntity } from "./util";
|
|
2
|
+
import { entityDataSymbol, isContent, isJelEntity, isReactiveSource } from "./util";
|
|
3
3
|
const elementWrapCache = new WeakMap();
|
|
4
4
|
const recursiveAppend = (parent, c) => {
|
|
5
5
|
if (c === null || c === undefined)
|
|
@@ -18,9 +18,9 @@ const recursiveAppend = (parent, c) => {
|
|
|
18
18
|
};
|
|
19
19
|
function createElement(tag, descriptor = {}) {
|
|
20
20
|
if (isContent(descriptor))
|
|
21
|
-
|
|
21
|
+
descriptor = {
|
|
22
22
|
content: descriptor,
|
|
23
|
-
}
|
|
23
|
+
};
|
|
24
24
|
const domElement = document.createElement(tag);
|
|
25
25
|
const ent = getWrappedElement(domElement);
|
|
26
26
|
const applyClasses = (classes) => {
|
|
@@ -68,6 +68,8 @@ function createElement(tag, descriptor = {}) {
|
|
|
68
68
|
if (descriptor.on) {
|
|
69
69
|
Object.entries(descriptor.on).forEach(([eventName, handler]) => ent.events[eventName].apply(handler));
|
|
70
70
|
}
|
|
71
|
+
if (descriptor.init)
|
|
72
|
+
descriptor.init(ent);
|
|
71
73
|
return ent;
|
|
72
74
|
}
|
|
73
75
|
;
|
|
@@ -132,10 +134,6 @@ function observeMutations() {
|
|
|
132
134
|
subtree: true
|
|
133
135
|
});
|
|
134
136
|
}
|
|
135
|
-
function isReactiveSource(value) {
|
|
136
|
-
return typeof value == "object" && value && (("listen" in value && typeof value.listen == "function")
|
|
137
|
-
|| ("subscribe" in value && typeof value.subscribe == "function"));
|
|
138
|
-
}
|
|
139
137
|
function getWrappedElement(element) {
|
|
140
138
|
if (!elementWrapCache.has(element)) {
|
|
141
139
|
const setCSSVariable = (k, v) => {
|
|
@@ -173,7 +171,7 @@ function getWrappedElement(element) {
|
|
|
173
171
|
elementMutationMap.set(element, {
|
|
174
172
|
add: () => {
|
|
175
173
|
Object.values(listeners).forEach(group => {
|
|
176
|
-
Object.values(group).forEach(l =>
|
|
174
|
+
Object.values(group).forEach(l => l.unsubscribe = l.subscribe());
|
|
177
175
|
});
|
|
178
176
|
},
|
|
179
177
|
remove: () => {
|
|
@@ -221,9 +219,11 @@ function getWrappedElement(element) {
|
|
|
221
219
|
},
|
|
222
220
|
get element() { return element; },
|
|
223
221
|
on(eventId, handler) {
|
|
224
|
-
|
|
222
|
+
const fn = (eventData) => {
|
|
225
223
|
handler.call(domEntity, eventData);
|
|
226
|
-
}
|
|
224
|
+
};
|
|
225
|
+
element.addEventListener(eventId, fn);
|
|
226
|
+
return () => element.removeEventListener(eventId, fn);
|
|
227
227
|
},
|
|
228
228
|
append(...content) {
|
|
229
229
|
var _a;
|
package/internal/emitter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EmitterLike } from "./types";
|
|
2
2
|
type Handler<T> = (value: T) => void;
|
|
3
3
|
export type ListenFunc<T> = (handler: Handler<T>) => UnsubscribeFunc;
|
|
4
4
|
export type UnsubscribeFunc = () => void;
|
|
@@ -95,7 +95,7 @@ export declare class EventEmitter<T> {
|
|
|
95
95
|
* @param notifier
|
|
96
96
|
* @returns
|
|
97
97
|
*/
|
|
98
|
-
takeUntil(notifier:
|
|
98
|
+
takeUntil(notifier: EmitterLike<any>): EventEmitter<T>;
|
|
99
99
|
/**
|
|
100
100
|
* Creates a chainable emitter that forwards its parent's emissions while the predicate returns true
|
|
101
101
|
* Disconnects from the parent and becomes inert when the predicate returns false
|
|
@@ -115,9 +115,15 @@ export declare class EventEmitter<T> {
|
|
|
115
115
|
* @returns
|
|
116
116
|
*/
|
|
117
117
|
cached(): EventEmitter<T>;
|
|
118
|
+
/**
|
|
119
|
+
* Creates a chainable emitter that forwards emissions from the parent and any of the provided emitters
|
|
120
|
+
* @param emitters
|
|
121
|
+
*/
|
|
122
|
+
or(...emitters: EmitterLike<T>[]): EventEmitter<T>;
|
|
123
|
+
or<U>(...emitters: EmitterLike<U>[]): EventEmitter<T | U>;
|
|
118
124
|
}
|
|
119
125
|
/**
|
|
120
|
-
* Creates a linked
|
|
126
|
+
* Creates a linked EventEmitter and emit() pair
|
|
121
127
|
* @example
|
|
122
128
|
* ```ts
|
|
123
129
|
* function createForm(options?: { onsubmit?: (data: FormData) => void }) {
|
|
@@ -151,17 +157,39 @@ export declare function createEventSource<T>(initialHandler?: Handler<T>): {
|
|
|
151
157
|
emit: (value: T) => void;
|
|
152
158
|
emitter: EventEmitter<T>;
|
|
153
159
|
};
|
|
154
|
-
export declare function createListenable<T>(
|
|
160
|
+
export declare function createListenable<T>(sourceListen?: () => UnsubscribeFunc | undefined): {
|
|
155
161
|
listen: (fn: (v: T) => void) => UnsubscribeFunc;
|
|
156
162
|
emit: (value: T) => void;
|
|
157
163
|
};
|
|
158
|
-
export declare function interval(
|
|
164
|
+
export declare function interval(ms: number | {
|
|
159
165
|
asMilliseconds: number;
|
|
160
166
|
}): EventEmitter<number>;
|
|
161
|
-
export declare function timeoutx(t: number | {
|
|
162
|
-
asMilliseconds: number;
|
|
163
|
-
}): EventEmitter<void>;
|
|
164
167
|
export declare function timeout(t: number | {
|
|
165
168
|
asMilliseconds: number;
|
|
166
169
|
}): EventEmitter<void>;
|
|
170
|
+
export declare class SubjectEmitter<T> extends EventEmitter<T> {
|
|
171
|
+
private emit;
|
|
172
|
+
private _value;
|
|
173
|
+
constructor(initial: T);
|
|
174
|
+
get value(): T;
|
|
175
|
+
next(value: T): void;
|
|
176
|
+
}
|
|
177
|
+
type EventSource<T, E extends string> = {
|
|
178
|
+
on: (eventName: E, handler: (value: T) => void) => UnsubscribeFunc;
|
|
179
|
+
} | {
|
|
180
|
+
on: (eventName: E, handler: (value: T) => void) => void | UnsubscribeFunc;
|
|
181
|
+
off: (eventName: E, handler: (value: T) => void) => void;
|
|
182
|
+
} | {
|
|
183
|
+
addEventListener: (eventName: E, handler: (value: T) => void) => UnsubscribeFunc;
|
|
184
|
+
} | {
|
|
185
|
+
addEventListener: (eventName: E, handler: (value: T) => void) => void | UnsubscribeFunc;
|
|
186
|
+
removeEventListener: (eventName: E, handler: (value: T) => void) => void;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Create an EventEmitter from an event source. Event sources can be RxJS observables, existing EventEmitters, or objects that
|
|
190
|
+
* provide a subscribe()/listen() => UnsubscribeFunc method.
|
|
191
|
+
* @param source
|
|
192
|
+
*/
|
|
193
|
+
export declare function toEventEmitter<T>(source: EmitterLike<T>): EventEmitter<T>;
|
|
194
|
+
export declare function toEventEmitter<T, E extends string>(source: EventSource<T, E>, eventName: E): EventEmitter<T>;
|
|
167
195
|
export {};
|
package/internal/emitter.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
+
import { isReactiveSource } from "./util";
|
|
1
2
|
export class EventEmitter {
|
|
2
3
|
constructor(onListen) {
|
|
3
4
|
this.onListen = onListen;
|
|
4
5
|
}
|
|
5
6
|
transform(handler) {
|
|
6
|
-
|
|
7
|
-
const parentListen = this.onListen;
|
|
8
|
-
const { emit, listen } = createListenable(() => parentUnsubscribe = parentListen(value => {
|
|
7
|
+
const { emit, listen } = createListenable(() => this.onListen(value => {
|
|
9
8
|
handler(value, emit);
|
|
10
|
-
})
|
|
11
|
-
parentUnsubscribe();
|
|
12
|
-
parentUnsubscribe = null;
|
|
13
|
-
});
|
|
9
|
+
}));
|
|
14
10
|
return listen;
|
|
15
11
|
}
|
|
16
12
|
/**
|
|
@@ -171,7 +167,8 @@ export class EventEmitter {
|
|
|
171
167
|
clear();
|
|
172
168
|
emit(v);
|
|
173
169
|
});
|
|
174
|
-
|
|
170
|
+
return clear;
|
|
171
|
+
});
|
|
175
172
|
return new EventEmitter(listen);
|
|
176
173
|
}
|
|
177
174
|
delay(ms) {
|
|
@@ -226,11 +223,7 @@ export class EventEmitter {
|
|
|
226
223
|
}
|
|
227
224
|
});
|
|
228
225
|
}
|
|
229
|
-
|
|
230
|
-
if (sourceUnsub) {
|
|
231
|
-
sourceUnsub();
|
|
232
|
-
sourceUnsub = null;
|
|
233
|
-
}
|
|
226
|
+
return sourceUnsub;
|
|
234
227
|
});
|
|
235
228
|
return new EventEmitter(listen);
|
|
236
229
|
}
|
|
@@ -245,14 +238,8 @@ export class EventEmitter {
|
|
|
245
238
|
let notifierUnsub = null;
|
|
246
239
|
let completed = false;
|
|
247
240
|
const clear = () => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
parentUnsubscribe = null;
|
|
251
|
-
}
|
|
252
|
-
if (notifierUnsub) {
|
|
253
|
-
notifierUnsub();
|
|
254
|
-
notifierUnsub = null;
|
|
255
|
-
}
|
|
241
|
+
parentUnsubscribe === null || parentUnsubscribe === void 0 ? void 0 : parentUnsubscribe();
|
|
242
|
+
notifierUnsub === null || notifierUnsub === void 0 ? void 0 : notifierUnsub();
|
|
256
243
|
};
|
|
257
244
|
const { emit, listen } = createListenable(() => {
|
|
258
245
|
if (completed)
|
|
@@ -262,10 +249,9 @@ export class EventEmitter {
|
|
|
262
249
|
completed = true;
|
|
263
250
|
clear();
|
|
264
251
|
};
|
|
265
|
-
notifierUnsub =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}, clear);
|
|
252
|
+
notifierUnsub = toEventEmitter(notifier).listen(handler);
|
|
253
|
+
return clear;
|
|
254
|
+
});
|
|
269
255
|
return new EventEmitter(listen);
|
|
270
256
|
}
|
|
271
257
|
/**
|
|
@@ -274,14 +260,8 @@ export class EventEmitter {
|
|
|
274
260
|
* @param predicate Callback to determine whether to keep forwarding
|
|
275
261
|
*/
|
|
276
262
|
takeWhile(predicate) {
|
|
277
|
-
let parentUnsubscribe
|
|
263
|
+
let parentUnsubscribe;
|
|
278
264
|
let completed = false;
|
|
279
|
-
const clear = () => {
|
|
280
|
-
if (parentUnsubscribe) {
|
|
281
|
-
parentUnsubscribe();
|
|
282
|
-
parentUnsubscribe = null;
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
265
|
const { emit, listen } = createListenable(() => {
|
|
286
266
|
if (completed)
|
|
287
267
|
return;
|
|
@@ -291,10 +271,12 @@ export class EventEmitter {
|
|
|
291
271
|
}
|
|
292
272
|
else {
|
|
293
273
|
completed = true;
|
|
294
|
-
|
|
274
|
+
parentUnsubscribe();
|
|
275
|
+
parentUnsubscribe = undefined;
|
|
295
276
|
}
|
|
296
277
|
});
|
|
297
|
-
|
|
278
|
+
return () => parentUnsubscribe === null || parentUnsubscribe === void 0 ? void 0 : parentUnsubscribe();
|
|
279
|
+
});
|
|
298
280
|
return new EventEmitter(listen);
|
|
299
281
|
}
|
|
300
282
|
/**
|
|
@@ -316,24 +298,25 @@ export class EventEmitter {
|
|
|
316
298
|
*/
|
|
317
299
|
cached() {
|
|
318
300
|
let cache = null;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
emit(value);
|
|
324
|
-
}));
|
|
325
|
-
}, () => {
|
|
326
|
-
unsub();
|
|
327
|
-
});
|
|
301
|
+
const { listen, emit } = createListenable(() => this.onListen((value => {
|
|
302
|
+
cache = { value };
|
|
303
|
+
emit(value);
|
|
304
|
+
})));
|
|
328
305
|
return new EventEmitter(handler => {
|
|
329
306
|
if (cache)
|
|
330
307
|
handler(cache.value);
|
|
331
308
|
return listen(handler);
|
|
332
309
|
});
|
|
333
310
|
}
|
|
311
|
+
or(...emitters) {
|
|
312
|
+
return new EventEmitter(handler => {
|
|
313
|
+
const unsubs = [this, ...emitters].map(e => toEventEmitter(e).listen(handler));
|
|
314
|
+
return () => unsubs.forEach(unsub => unsub());
|
|
315
|
+
});
|
|
316
|
+
}
|
|
334
317
|
}
|
|
335
318
|
/**
|
|
336
|
-
* Creates a linked
|
|
319
|
+
* Creates a linked EventEmitter and emit() pair
|
|
337
320
|
* @example
|
|
338
321
|
* ```ts
|
|
339
322
|
* function createForm(options?: { onsubmit?: (data: FormData) => void }) {
|
|
@@ -372,13 +355,14 @@ export function createEventSource(initialHandler) {
|
|
|
372
355
|
emitter: new EventEmitter(listen)
|
|
373
356
|
};
|
|
374
357
|
}
|
|
375
|
-
export function createListenable(
|
|
358
|
+
export function createListenable(sourceListen) {
|
|
376
359
|
const handlers = [];
|
|
360
|
+
let onRemoveLast;
|
|
377
361
|
const addListener = (fn) => {
|
|
378
362
|
const unique = { fn };
|
|
379
363
|
handlers.push(unique);
|
|
380
|
-
if (
|
|
381
|
-
|
|
364
|
+
if (sourceListen && handlers.length == 1)
|
|
365
|
+
onRemoveLast = sourceListen();
|
|
382
366
|
return () => {
|
|
383
367
|
const idx = handlers.indexOf(unique);
|
|
384
368
|
if (idx === -1)
|
|
@@ -393,19 +377,17 @@ export function createListenable(onAddFirst, onRemoveLast) {
|
|
|
393
377
|
emit: (value) => handlers.forEach(h => h.fn(value)),
|
|
394
378
|
};
|
|
395
379
|
}
|
|
396
|
-
export function interval(
|
|
380
|
+
export function interval(ms) {
|
|
397
381
|
let intervalId = null;
|
|
398
382
|
let idx = 0;
|
|
399
383
|
const { emit, listen } = createListenable(() => {
|
|
400
384
|
intervalId = setInterval(() => {
|
|
401
385
|
emit(idx++);
|
|
402
|
-
}, typeof
|
|
403
|
-
|
|
386
|
+
}, typeof ms == "number" ? ms : ms.asMilliseconds);
|
|
387
|
+
return () => clearInterval(intervalId);
|
|
388
|
+
});
|
|
404
389
|
return new EventEmitter(listen);
|
|
405
390
|
}
|
|
406
|
-
export function timeoutx(t) {
|
|
407
|
-
return interval(t).once().map(() => { });
|
|
408
|
-
}
|
|
409
391
|
export function timeout(t) {
|
|
410
392
|
const ms = typeof t === "number" ? t : t.asMilliseconds;
|
|
411
393
|
const targetTime = Date.now() + ms;
|
|
@@ -415,8 +397,62 @@ export function timeout(t) {
|
|
|
415
397
|
if (reminaingMs < 0)
|
|
416
398
|
return;
|
|
417
399
|
timeoutId = setTimeout(emit, reminaingMs);
|
|
418
|
-
|
|
419
|
-
clearTimeout(timeoutId);
|
|
400
|
+
return () => clearTimeout(timeoutId);
|
|
420
401
|
});
|
|
421
402
|
return new EventEmitter(listen);
|
|
422
403
|
}
|
|
404
|
+
export class SubjectEmitter extends EventEmitter {
|
|
405
|
+
constructor(initial) {
|
|
406
|
+
const { emit, listen } = createListenable();
|
|
407
|
+
super(h => {
|
|
408
|
+
h(this._value);
|
|
409
|
+
return listen(h);
|
|
410
|
+
});
|
|
411
|
+
this.emit = emit;
|
|
412
|
+
this._value = initial;
|
|
413
|
+
}
|
|
414
|
+
get value() {
|
|
415
|
+
return this._value;
|
|
416
|
+
}
|
|
417
|
+
next(value) {
|
|
418
|
+
this._value = value;
|
|
419
|
+
this.emit(value);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
export function toEventEmitter(source, eventName) {
|
|
423
|
+
if (source instanceof EventEmitter)
|
|
424
|
+
return source;
|
|
425
|
+
if (eventName !== undefined) {
|
|
426
|
+
// addEL()
|
|
427
|
+
if ("addEventListener" in source) {
|
|
428
|
+
if ("removeEventListener" in source && typeof source.removeEventListener == "function") {
|
|
429
|
+
return new EventEmitter(h => {
|
|
430
|
+
return source.addEventListener(eventName, h)
|
|
431
|
+
|| (() => source.removeEventListener(eventName, h));
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return new EventEmitter(h => {
|
|
435
|
+
return source.addEventListener(eventName, h);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
// on()
|
|
439
|
+
if ("on" in source) {
|
|
440
|
+
if ("off" in source && typeof source.off == "function") {
|
|
441
|
+
return new EventEmitter(h => {
|
|
442
|
+
return source.on(eventName, h)
|
|
443
|
+
|| (() => source.off(eventName, h));
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return new EventEmitter(h => {
|
|
447
|
+
return source.on(eventName, h);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (isReactiveSource(source)) {
|
|
452
|
+
const subscribe = "subscribe" in source
|
|
453
|
+
? (h) => source.subscribe(h)
|
|
454
|
+
: (h) => source.listen(h);
|
|
455
|
+
return new EventEmitter(subscribe);
|
|
456
|
+
}
|
|
457
|
+
throw new Error("Invalid event source");
|
|
458
|
+
}
|
package/internal/proxy.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toEventEmitter } from "./emitter";
|
|
2
2
|
export const styleProxy = {
|
|
3
3
|
get(style, prop) {
|
|
4
4
|
return style(prop);
|
|
@@ -42,13 +42,6 @@ export const eventsProxy = {
|
|
|
42
42
|
if (key == "removeEventListener") {
|
|
43
43
|
return (name, handler) => element.removeEventListener(name, handler);
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
const wrappedHandler = (event) => handler(event);
|
|
47
|
-
element.addEventListener(key, wrappedHandler);
|
|
48
|
-
return () => {
|
|
49
|
-
element.removeEventListener(key, wrappedHandler);
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
return new EventEmitter(listen);
|
|
45
|
+
return toEventEmitter(element, key);
|
|
53
46
|
}
|
|
54
47
|
};
|
package/internal/types.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ClassAccessor } from "./element";
|
|
2
2
|
import { EventEmitter, UnsubscribeFunc } from "./emitter";
|
|
3
3
|
import { entityDataSymbol } from "./util";
|
|
4
|
-
export type ElementClassDescriptor = string | Record<string, boolean |
|
|
4
|
+
export type ElementClassDescriptor = string | Record<string, boolean | EmitterLike<boolean> | undefined> | undefined | ElementClassDescriptor[];
|
|
5
5
|
export type DOMContent = number | null | string | Element | JelEntity<object> | Text | DOMContent[];
|
|
6
6
|
export type DomEntity<T extends HTMLElement> = JelEntity<ElementAPI<T>>;
|
|
7
|
-
export type
|
|
7
|
+
export type HTMLTag = keyof HTMLElementTagNameMap;
|
|
8
|
+
export type EmitterLike<T> = {
|
|
8
9
|
subscribe: (callback: (value: T) => void) => UnsubscribeFunc;
|
|
9
10
|
} | {
|
|
10
11
|
listen: (callback: (value: T) => void) => UnsubscribeFunc;
|
|
@@ -19,10 +20,10 @@ export type StylesDescriptor = {
|
|
|
19
20
|
[K in keyof CSSStyleDeclaration as [
|
|
20
21
|
K,
|
|
21
22
|
CSSStyleDeclaration[K]
|
|
22
|
-
] extends [string, string] ? K : never]+?: CSSValue |
|
|
23
|
+
] extends [string, string] ? K : never]+?: CSSValue | EmitterLike<CSSValue>;
|
|
23
24
|
};
|
|
24
|
-
export type SetStyleFunc = ((property: CSSProperty, value: CSSValue |
|
|
25
|
-
export type SetGetStyleFunc = SetStyleFunc & ((property: CSSProperty) => string |
|
|
25
|
+
export type SetStyleFunc = ((property: CSSProperty, value: CSSValue | EmitterLike<CSSValue>) => void);
|
|
26
|
+
export type SetGetStyleFunc = SetStyleFunc & ((property: CSSProperty) => string | EmitterLike<CSSValue>);
|
|
26
27
|
export type StyleAccessor = ((styles: StylesDescriptor) => void) & StylesDescriptor & SetStyleFunc;
|
|
27
28
|
type ContentlessTag = "area" | "br" | "hr" | "iframe" | "input" | "textarea" | "img" | "canvas" | "link" | "meta" | "source" | "embed" | "track" | "base";
|
|
28
29
|
type TagWithHref = "a" | "link" | "base";
|
|
@@ -32,18 +33,19 @@ type TagWithWidthHeight = "canvas" | "img" | "embed" | "iframe" | "video";
|
|
|
32
33
|
type TagWithType = "input" | "source" | "button";
|
|
33
34
|
type TagWithName = 'input' | 'textarea' | 'select' | 'form';
|
|
34
35
|
type ContentlessElement = HTMLElementTagNameMap[ContentlessTag];
|
|
35
|
-
export type ElementDescriptor<Tag extends
|
|
36
|
+
export type ElementDescriptor<Tag extends HTMLTag> = {
|
|
36
37
|
classes?: ElementClassDescriptor;
|
|
37
38
|
attribs?: Record<string, string | number | boolean>;
|
|
38
39
|
on?: {
|
|
39
40
|
[E in keyof HTMLElementEventMap]+?: (event: HTMLElementEventMap[E]) => void;
|
|
40
41
|
};
|
|
41
42
|
style?: StylesDescriptor;
|
|
42
|
-
cssVariables?: Record<string, CSSValue |
|
|
43
|
+
cssVariables?: Record<string, CSSValue | EmitterLike<CSSValue>>;
|
|
44
|
+
init?: (entity: DomEntity<HTMLElementTagNameMap[Tag]>) => void;
|
|
43
45
|
} & (Tag extends TagWithValue ? {
|
|
44
46
|
value?: string | number;
|
|
45
47
|
} : {}) & (Tag extends ContentlessTag ? {} : {
|
|
46
|
-
content?: DOMContent |
|
|
48
|
+
content?: DOMContent | EmitterLike<DOMContent>;
|
|
47
49
|
}) & (Tag extends TagWithSrc ? {
|
|
48
50
|
src?: string;
|
|
49
51
|
} : {}) & (Tag extends TagWithHref ? {
|
|
@@ -64,18 +66,18 @@ type ElementAPI<T extends HTMLElement> = {
|
|
|
64
66
|
};
|
|
65
67
|
readonly events: EventsAccessor;
|
|
66
68
|
readonly style: StyleAccessor;
|
|
67
|
-
setCSSVariable(variableName: string, value: CSSValue |
|
|
68
|
-
setCSSVariable(table: Record<string, CSSValue |
|
|
69
|
+
setCSSVariable(variableName: string, value: CSSValue | EmitterLike<CSSValue>): void;
|
|
70
|
+
setCSSVariable(table: Record<string, CSSValue | EmitterLike<CSSValue>>): void;
|
|
69
71
|
qsa(selector: string): (Element | DomEntity<HTMLElement>)[];
|
|
70
72
|
remove(): void;
|
|
71
73
|
getRect(): DOMRect;
|
|
72
74
|
focus(): void;
|
|
73
75
|
blur(): void;
|
|
74
|
-
on<E extends keyof HTMLElementEventMap>(eventId: E, handler: (this: ElementAPI<T>, data: HTMLElementEventMap[E]) => void):
|
|
76
|
+
on<E extends keyof HTMLElementEventMap>(eventId: E, handler: (this: ElementAPI<T>, data: HTMLElementEventMap[E]) => void): UnsubscribeFunc;
|
|
75
77
|
} & (T extends ContentlessElement ? {} : {
|
|
76
78
|
append(...content: DOMContent[]): void;
|
|
77
79
|
innerHTML: string;
|
|
78
|
-
content: DOMContent |
|
|
80
|
+
content: DOMContent | EmitterLike<DOMContent>;
|
|
79
81
|
}) & (T extends HTMLElementTagNameMap[TagWithValue] ? {
|
|
80
82
|
value: string;
|
|
81
83
|
select(): void;
|
|
@@ -99,26 +101,26 @@ export type DomHelper = ((
|
|
|
99
101
|
/**
|
|
100
102
|
* Creates an element of the specified tag
|
|
101
103
|
*/
|
|
102
|
-
<T extends
|
|
104
|
+
<T extends HTMLTag>(tagName: T, descriptor: ElementDescriptor<T>) => DomEntity<HTMLElementTagNameMap[T]>) & (
|
|
103
105
|
/**
|
|
104
106
|
* Creates an element of the specified tag
|
|
105
107
|
*/
|
|
106
|
-
<T extends
|
|
108
|
+
<T extends HTMLTag>(tagName: T) => DomEntity<HTMLElementTagNameMap[T]>) & (
|
|
107
109
|
/**
|
|
108
110
|
* Creates an element with ID and classes as specified by a selector-like string
|
|
109
111
|
*/
|
|
110
|
-
<T extends
|
|
112
|
+
<T extends HTMLTag>(selector: `${T}#${string}`, content?: T extends ContentlessTag ? void : DOMContent) => DomEntity<HTMLElementTagNameMap[T]>) & (
|
|
111
113
|
/**
|
|
112
114
|
* Creates an element with ID and classes as specified by a selector-like string
|
|
113
115
|
*/
|
|
114
|
-
<T extends
|
|
116
|
+
<T extends HTMLTag>(selector: `${T}.${string}`, content?: T extends ContentlessTag ? void : DOMContent) => DomEntity<HTMLElementTagNameMap[T]>) & (
|
|
115
117
|
/**
|
|
116
118
|
* Wraps an existing element as a DomEntity
|
|
117
119
|
*/
|
|
118
120
|
<T extends HTMLElement>(element: T) => DomEntity<T>) & {
|
|
119
|
-
[T in
|
|
121
|
+
[T in HTMLTag]: (descriptor: ElementDescriptor<T>) => DomEntity<HTMLElementTagNameMap[T]>;
|
|
120
122
|
} & {
|
|
121
|
-
[T in
|
|
123
|
+
[T in HTMLTag]: T extends ContentlessTag ? () => DomEntity<HTMLElementTagNameMap[T]> : (content?: DOMContent) => DomEntity<HTMLElementTagNameMap[T]>;
|
|
122
124
|
});
|
|
123
125
|
type JelEntityData = {
|
|
124
126
|
dom: DOMContent;
|
package/internal/util.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DOMContent, ElementDescriptor, JelEntity } from "./types";
|
|
1
|
+
import { DOMContent, ElementDescriptor, EmitterLike, HTMLTag, JelEntity } from "./types";
|
|
2
2
|
export declare const entityDataSymbol: unique symbol;
|
|
3
|
-
export declare const isContent: (value: DOMContent | ElementDescriptor<
|
|
3
|
+
export declare const isContent: <T extends HTMLTag>(value: DOMContent | ElementDescriptor<T> | undefined) => value is DOMContent;
|
|
4
4
|
export declare function isJelEntity(content: DOMContent): content is JelEntity<object>;
|
|
5
5
|
/**
|
|
6
6
|
* Wraps an object such that it can be appended as DOM content while retaining its original API
|
|
@@ -9,3 +9,4 @@ export declare function isJelEntity(content: DOMContent): content is JelEntity<o
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function createEntity<API extends object>(content: DOMContent, api: API extends DOMContent ? never : API): JelEntity<API>;
|
|
11
11
|
export declare function createEntity(content: DOMContent): JelEntity<void>;
|
|
12
|
+
export declare function isReactiveSource(value: any): value is EmitterLike<any>;
|
package/internal/util.js
CHANGED
|
@@ -26,3 +26,7 @@ export function createEntity(content, api) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
;
|
|
29
|
+
export function isReactiveSource(value) {
|
|
30
|
+
return typeof value == "object" && value && (("listen" in value && typeof value.listen == "function")
|
|
31
|
+
|| ("subscribe" in value && typeof value.subscribe == "function"));
|
|
32
|
+
}
|