@xtia/jel 0.7.2 → 0.9.1

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  export { DomEntity, ElementClassDescriptor, ElementDescriptor, DOMContent, DomHelper, StyleAccessor, JelEntity } from "./internal/types";
2
2
  import { $ } from "./internal/element";
3
+ import { DomEntity } from "./internal/types";
3
4
  export { createEntity } from "./internal/util";
4
- export { createEventSource, interval, timeout, SubjectEmitter, toEventEmitter } from "./internal/emitter";
5
+ export { createEventSource, interval, timeout, SubjectEmitter, toEventEmitter, type EventEmitter, combineEmitters } from "./internal/emitter";
5
6
  export { $ };
6
- export declare const $body: import(".").DomEntity<HTMLElement>;
7
+ export declare const $body: 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, timeout, SubjectEmitter, toEventEmitter } from "./internal/emitter";
3
+ export { createEventSource, interval, timeout, SubjectEmitter, toEventEmitter, combineEmitters } from "./internal/emitter";
4
4
  export { $ };
5
- export const $body = $(document.body);
5
+ export const $body = "document" in globalThis ? $(document.body) : undefined;
@@ -1,5 +1,10 @@
1
1
  import { EmitterLike } from "./types";
2
2
  type Handler<T> = (value: T) => void;
3
+ type Period = {
4
+ asMilliseconds: number;
5
+ } | {
6
+ asSeconds: number;
7
+ };
3
8
  export type ListenFunc<T> = (handler: Handler<T>) => UnsubscribeFunc;
4
9
  export type UnsubscribeFunc = () => void;
5
10
  export declare class EventEmitter<T> {
@@ -24,6 +29,7 @@ export declare class EventEmitter<T> {
24
29
  * @returns Listenable: emits transformed values
25
30
  */
26
31
  map<R>(mapFunc: (value: T) => R): EventEmitter<R>;
32
+ mapAsync<R>(mapFunc: (value: T) => Promise<R>): EventEmitter<R>;
27
33
  /**
28
34
  * Creates a chainable emitter that selectively forwards emissions along the chain
29
35
  * @param check Function that takes an emitted value and returns true if the emission should be forwarded along the chain
@@ -68,8 +74,20 @@ export declare class EventEmitter<T> {
68
74
  * @param cb
69
75
  */
70
76
  fork(...cb: ((branch: this) => void)[]): this;
77
+ /**
78
+ * Creates a chainable emitter that forwards the parent's last emission after a period of time in which the parent doesn't emit
79
+ * @param ms Delay in milliseconds
80
+ * @returns Debounced emitter
81
+ */
71
82
  debounce(ms: number): EventEmitter<T>;
83
+ debounce(period: Period): EventEmitter<T>;
84
+ /**
85
+ * Creates a chainable emitter that forwards the parent's emissions, with a minimum delay between emissions during which parent emssions are ignored
86
+ * @param ms Delay in milliseconds
87
+ * @returns Throttled emitter
88
+ */
72
89
  throttle(ms: number): EventEmitter<T>;
90
+ throttle(period: Period): EventEmitter<T>;
73
91
  batch(ms: number): EventEmitter<T[]>;
74
92
  /**
75
93
  * Creates a chainable emitter that
@@ -80,6 +98,7 @@ export declare class EventEmitter<T> {
80
98
  */
81
99
  once(): EventEmitter<T>;
82
100
  delay(ms: number): EventEmitter<T>;
101
+ delay(period: Period): EventEmitter<T>;
83
102
  scan<S>(updater: (state: S, value: T) => S, initial: S): EventEmitter<S>;
84
103
  buffer(count: number): EventEmitter<T[]>;
85
104
  /**
@@ -121,6 +140,10 @@ export declare class EventEmitter<T> {
121
140
  */
122
141
  or(...emitters: EmitterLike<T>[]): EventEmitter<T>;
123
142
  or<U>(...emitters: EmitterLike<U>[]): EventEmitter<T | U>;
143
+ then<R>(handler: (value: T) => R): Promise<R>;
144
+ memo(): Memo<T | undefined>;
145
+ memo(initial: T): Memo<T>;
146
+ memo<U>(initial: U): Memo<T | U>;
124
147
  }
125
148
  /**
126
149
  * Creates a linked EventEmitter and emit() pair
@@ -157,16 +180,17 @@ export declare function createEventSource<T>(initialHandler?: Handler<T>): {
157
180
  emit: (value: T) => void;
158
181
  emitter: EventEmitter<T>;
159
182
  };
160
- export declare function createListenable<T>(sourceListen?: () => UnsubscribeFunc | undefined): {
161
- listen: (fn: (v: T) => void) => UnsubscribeFunc;
162
- emit: (value: T) => void;
163
- };
164
- export declare function interval(ms: number | {
165
- asMilliseconds: number;
166
- }): EventEmitter<number>;
167
- export declare function timeout(t: number | {
168
- asMilliseconds: number;
169
- }): EventEmitter<void>;
183
+ export declare function interval(ms: number): EventEmitter<number>;
184
+ export declare function interval(period: Period): EventEmitter<number>;
185
+ export declare function timeout(ms: number): EventEmitter<void>;
186
+ export declare function timeout(period: Period): EventEmitter<void>;
187
+ declare class Memo<T> {
188
+ private _value;
189
+ private unsubscribeFunc;
190
+ get value(): T;
191
+ constructor(source: EmitterLike<T>, initial: T);
192
+ dispose(): void;
193
+ }
170
194
  export declare class SubjectEmitter<T> extends EventEmitter<T> {
171
195
  private emit;
172
196
  private _value;
@@ -175,15 +199,15 @@ export declare class SubjectEmitter<T> extends EventEmitter<T> {
175
199
  next(value: T): void;
176
200
  }
177
201
  type EventSource<T, E extends string> = {
178
- on: (eventName: E, handler: (value: T) => void) => UnsubscribeFunc;
202
+ on: (eventName: E, handler: Handler<T>) => UnsubscribeFunc;
179
203
  } | {
180
- on: (eventName: E, handler: (value: T) => void) => void | UnsubscribeFunc;
181
- off: (eventName: E, handler: (value: T) => void) => void;
204
+ on: (eventName: E, handler: Handler<T>) => void | UnsubscribeFunc;
205
+ off: (eventName: E, handler: Handler<T>) => void;
182
206
  } | {
183
- addEventListener: (eventName: E, handler: (value: T) => void) => UnsubscribeFunc;
207
+ addEventListener: (eventName: E, handler: Handler<T>) => UnsubscribeFunc;
184
208
  } | {
185
- addEventListener: (eventName: E, handler: (value: T) => void) => void | UnsubscribeFunc;
186
- removeEventListener: (eventName: E, handler: (value: T) => void) => void;
209
+ addEventListener: (eventName: E, handler: Handler<T>) => void | UnsubscribeFunc;
210
+ removeEventListener: (eventName: E, handler: Handler<T>) => void;
187
211
  };
188
212
  /**
189
213
  * Create an EventEmitter from an event source. Event sources can be RxJS observables, existing EventEmitters, or objects that
@@ -192,4 +216,14 @@ type EventSource<T, E extends string> = {
192
216
  */
193
217
  export declare function toEventEmitter<T>(source: EmitterLike<T>): EventEmitter<T>;
194
218
  export declare function toEventEmitter<T, E extends string>(source: EventSource<T, E>, eventName: E): EventEmitter<T>;
219
+ export declare function toEventEmitter<T>(subscribe: ListenFunc<T>): EventEmitter<T>;
220
+ type Dictionary<T> = Record<string | symbol, T>;
221
+ type ExtractEmitterValue<T> = T extends EmitterLike<infer U> ? U : never;
222
+ type CombinedRecord<T extends Dictionary<EmitterLike<any>>> = {
223
+ readonly [K in keyof T]: ExtractEmitterValue<T[K]>;
224
+ };
225
+ export declare function combineEmitters<U extends Dictionary<EmitterLike<any>>>(emitters: U): EventEmitter<CombinedRecord<U>>;
226
+ export declare function combineEmitters<U extends EmitterLike<any>[]>(emitters: [...U]): EventEmitter<{
227
+ [K in keyof U]: ExtractEmitterValue<U[K]>;
228
+ }>;
195
229
  export {};
@@ -1,4 +1,9 @@
1
1
  import { isReactiveSource } from "./util";
2
+ function periodAsMilliseconds(t) {
3
+ if (typeof t == "number")
4
+ return t;
5
+ return "asMilliseconds" in t ? t.asMilliseconds : (t.asSeconds * 1000);
6
+ }
2
7
  export class EventEmitter {
3
8
  constructor(onListen) {
4
9
  this.onListen = onListen;
@@ -34,6 +39,10 @@ export class EventEmitter {
34
39
  const listen = this.transform((value, emit) => emit(mapFunc(value)));
35
40
  return new EventEmitter(listen);
36
41
  }
42
+ mapAsync(mapFunc) {
43
+ const listen = this.transform((value, emit) => mapFunc(value).then(emit));
44
+ return new EventEmitter(listen);
45
+ }
37
46
  /**
38
47
  * Creates a chainable emitter that selectively forwards emissions along the chain
39
48
  * @param check Function that takes an emitted value and returns true if the emission should be forwarded along the chain
@@ -101,14 +110,14 @@ export class EventEmitter {
101
110
  cb.forEach(cb => cb(this));
102
111
  return this;
103
112
  }
104
- debounce(ms) {
113
+ debounce(t) {
105
114
  let reset = null;
106
115
  const listen = this.transform((value, emit) => {
107
116
  reset === null || reset === void 0 ? void 0 : reset();
108
117
  const timeout = setTimeout(() => {
109
118
  reset = null;
110
119
  emit(value);
111
- }, ms);
120
+ }, periodAsMilliseconds(t));
112
121
  reset = () => {
113
122
  reset = null;
114
123
  clearTimeout(timeout);
@@ -116,11 +125,11 @@ export class EventEmitter {
116
125
  });
117
126
  return new EventEmitter(listen);
118
127
  }
119
- throttle(ms) {
128
+ throttle(t) {
120
129
  let lastTime = -Infinity;
121
130
  const listen = this.transform((value, emit) => {
122
131
  const now = performance.now();
123
- if (now >= lastTime + ms) {
132
+ if (now >= lastTime + periodAsMilliseconds(t)) {
124
133
  lastTime = now;
125
134
  emit(value);
126
135
  }
@@ -171,9 +180,9 @@ export class EventEmitter {
171
180
  });
172
181
  return new EventEmitter(listen);
173
182
  }
174
- delay(ms) {
183
+ delay(t) {
175
184
  return new EventEmitter(this.transform((value, emit) => {
176
- setTimeout(() => emit(value), ms);
185
+ setTimeout(() => emit(value), periodAsMilliseconds(t));
177
186
  }));
178
187
  }
179
188
  scan(updater, initial) {
@@ -245,11 +254,10 @@ export class EventEmitter {
245
254
  if (completed)
246
255
  return;
247
256
  parentUnsubscribe = this.apply(emit);
248
- const handler = () => {
257
+ notifierUnsub = toEventEmitter(notifier).listen(() => {
249
258
  completed = true;
250
259
  clear();
251
- };
252
- notifierUnsub = toEventEmitter(notifier).listen(handler);
260
+ });
253
261
  return clear;
254
262
  });
255
263
  return new EventEmitter(listen);
@@ -314,6 +322,14 @@ export class EventEmitter {
314
322
  return () => unsubs.forEach(unsub => unsub());
315
323
  });
316
324
  }
325
+ then(handler) {
326
+ return new Promise(resolve => {
327
+ this.once().apply(v => resolve(handler(v)));
328
+ });
329
+ }
330
+ memo(initial) {
331
+ return new Memo(this, initial);
332
+ }
317
333
  }
318
334
  /**
319
335
  * Creates a linked EventEmitter and emit() pair
@@ -355,7 +371,7 @@ export function createEventSource(initialHandler) {
355
371
  emitter: new EventEmitter(listen)
356
372
  };
357
373
  }
358
- export function createListenable(sourceListen) {
374
+ function createListenable(sourceListen) {
359
375
  const handlers = [];
360
376
  let onRemoveLast;
361
377
  const addListener = (fn) => {
@@ -377,19 +393,19 @@ export function createListenable(sourceListen) {
377
393
  emit: (value) => handlers.forEach(h => h.fn(value)),
378
394
  };
379
395
  }
380
- export function interval(ms) {
396
+ export function interval(t) {
381
397
  let intervalId = null;
382
398
  let idx = 0;
383
399
  const { emit, listen } = createListenable(() => {
384
400
  intervalId = setInterval(() => {
385
401
  emit(idx++);
386
- }, typeof ms == "number" ? ms : ms.asMilliseconds);
402
+ }, periodAsMilliseconds(t));
387
403
  return () => clearInterval(intervalId);
388
404
  });
389
405
  return new EventEmitter(listen);
390
406
  }
391
407
  export function timeout(t) {
392
- const ms = typeof t === "number" ? t : t.asMilliseconds;
408
+ const ms = periodAsMilliseconds(t);
393
409
  const targetTime = Date.now() + ms;
394
410
  let timeoutId = null;
395
411
  const { emit, listen } = createListenable(() => {
@@ -401,6 +417,22 @@ export function timeout(t) {
401
417
  });
402
418
  return new EventEmitter(listen);
403
419
  }
420
+ class Memo {
421
+ get value() {
422
+ return this._value;
423
+ }
424
+ constructor(source, initial) {
425
+ this._value = initial;
426
+ const emitter = toEventEmitter(source);
427
+ this.unsubscribeFunc = emitter.listen(v => this._value = v);
428
+ }
429
+ dispose() {
430
+ this.unsubscribeFunc();
431
+ this.unsubscribeFunc = () => {
432
+ throw new Error("Memo object already disposed");
433
+ };
434
+ }
435
+ }
404
436
  export class SubjectEmitter extends EventEmitter {
405
437
  constructor(initial) {
406
438
  const { emit, listen } = createListenable();
@@ -422,6 +454,8 @@ export class SubjectEmitter extends EventEmitter {
422
454
  export function toEventEmitter(source, eventName) {
423
455
  if (source instanceof EventEmitter)
424
456
  return source;
457
+ if (typeof source == "function")
458
+ return new EventEmitter(source);
425
459
  if (eventName !== undefined) {
426
460
  // addEL()
427
461
  if ("addEventListener" in source) {
@@ -456,3 +490,39 @@ export function toEventEmitter(source, eventName) {
456
490
  }
457
491
  throw new Error("Invalid event source");
458
492
  }
493
+ function combineArray(emitters) {
494
+ let values = Array.from({ length: emitters.length });
495
+ const { emit, listen } = createListenable(() => {
496
+ const unsubFuncs = emitters.map((emitter, idx) => {
497
+ return emitter.listen(v => {
498
+ values[idx] = { value: v };
499
+ if (values.every(v => v !== undefined))
500
+ emit(values.map(vc => vc.value));
501
+ });
502
+ });
503
+ return () => unsubFuncs.forEach(f => f());
504
+ });
505
+ return new EventEmitter(listen);
506
+ }
507
+ function combineRecord(emitters) {
508
+ const keys = Object.keys(emitters);
509
+ let values = {};
510
+ const { emit, listen } = createListenable(() => {
511
+ const unsubFuncs = keys.map(key => {
512
+ return emitters[key].listen(v => {
513
+ values[key] = { value: v };
514
+ if (keys.every(k => values[k] !== undefined)) {
515
+ const record = Object.fromEntries(Object.entries(values).map(([k, vc]) => [k, vc.value]));
516
+ emit(record);
517
+ }
518
+ });
519
+ });
520
+ return () => unsubFuncs.forEach(f => f());
521
+ });
522
+ return new EventEmitter(listen);
523
+ }
524
+ export function combineEmitters(emitters) {
525
+ if (Array.isArray(emitters))
526
+ return combineArray(emitters.map(toEventEmitter));
527
+ return combineRecord(Object.fromEntries(Object.entries(emitters).map(([k, e]) => [k, toEventEmitter(e)])));
528
+ }
@@ -35,7 +35,7 @@ type TagWithName = 'input' | 'textarea' | 'select' | 'form';
35
35
  type ContentlessElement = HTMLElementTagNameMap[ContentlessTag];
36
36
  export type ElementDescriptor<Tag extends HTMLTag> = {
37
37
  classes?: ElementClassDescriptor;
38
- attribs?: Record<string, string | number | boolean>;
38
+ attribs?: Record<string, string | number | boolean | undefined>;
39
39
  on?: {
40
40
  [E in keyof HTMLElementEventMap]+?: (event: HTMLElementEventMap[E]) => void;
41
41
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/jel",
3
- "version": "0.7.2",
3
+ "version": "0.9.1",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/jel-ts",
6
6
  "type": "github"