@taybart/corvid 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,6 +39,18 @@ dom.ready(() => {
39
39
  dom.onKey('E', ({ ctrl, alt, meta, shift }) => {
40
40
  console.log('E pressed')
41
41
  })
42
+
43
+ /*
44
+ * Given a template:
45
+ * <template id="tmpl-test">
46
+ * <div> hello ${name} </div>
47
+ * </template>
48
+ */
49
+ const tmpl = new dom.el('#tmpl-test')
50
+ // append template to el
51
+ username.appendTemplate(tmpl, { name: 'corvid' })
52
+ // or just set content
53
+ document.body.innerHTML = tmpl.render({ name: 'corvid' })
42
54
  })
43
55
  ```
44
56
 
package/dist/dom.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare function onKey(key: string, cb: (ev: {
13
13
  meta: boolean;
14
14
  shift: boolean;
15
15
  }) => void): void;
16
+ export declare function els(query: string, verbose?: boolean): el[];
16
17
  /*** element ***/
17
18
  type elOpts = {
18
19
  element?: HTMLElement;
@@ -28,6 +29,7 @@ export declare class el {
28
29
  el: HTMLElement | null;
29
30
  query: string;
30
31
  log: logger;
32
+ listeners: Record<string, Array<(ev: Event) => void>>;
31
33
  constructor(opts: HTMLElement | string | elOpts, verbose?: boolean);
32
34
  /*** dom manipulation ***/
33
35
  value(update?: string): string | el;
@@ -40,11 +42,24 @@ export declare class el {
40
42
  }): this;
41
43
  src(url: string): this;
42
44
  /*** Style ***/
43
- style(style: Object | string): this;
45
+ style(update: Object | string, stringify?: boolean): this | undefined;
46
+ hasClass(className: string): boolean;
44
47
  addClass(className: string): this;
45
48
  removeClass(className: string): this;
49
+ /*** Templates ***/
50
+ render(vars?: {}): string;
51
+ appendTemplate(template: el, vars: any): void;
46
52
  /*** Events ***/
47
53
  on(event: string, cb: (ev: Event) => void): this;
48
54
  listen(event: string, cb: (ev: Event) => void): this;
55
+ removeListeners(event: string): this;
49
56
  }
57
+ /**
58
+ * Get a template from a string
59
+ * https://stackoverflow.com/a/41015840
60
+ * @param str The string to interpolate
61
+ * @param params The parameters
62
+ * @return The interpolated string
63
+ */
64
+ export declare function interpolate(str: string, params: Object): string;
50
65
  export {};
package/dist/index.js CHANGED
@@ -26,22 +26,26 @@ __webpack_require__.d(strings_namespaceObject, {
26
26
  bytesToHuman: ()=>bytesToHuman,
27
27
  toKebab: ()=>toKebab
28
28
  });
29
- var dom_namespaceObject = {};
30
- __webpack_require__.r(dom_namespaceObject);
31
- __webpack_require__.d(dom_namespaceObject, {
32
- el: ()=>dom_el,
33
- onKey: ()=>onKey,
34
- ready: ()=>ready
35
- });
36
29
  var style_namespaceObject = {};
37
30
  __webpack_require__.r(style_namespaceObject);
38
31
  __webpack_require__.d(style_namespaceObject, {
39
32
  cssVar: ()=>cssVar,
40
33
  gradient: ()=>gradient,
34
+ handleThemeSwitch: ()=>handleThemeSwitch,
41
35
  isDarkMode: ()=>isDarkMode,
42
36
  onDarkMode: ()=>onDarkMode,
37
+ render: ()=>render,
43
38
  switchTheme: ()=>switchTheme
44
39
  });
40
+ var dom_namespaceObject = {};
41
+ __webpack_require__.r(dom_namespaceObject);
42
+ __webpack_require__.d(dom_namespaceObject, {
43
+ el: ()=>dom_el,
44
+ els: ()=>els,
45
+ interpolate: ()=>interpolate,
46
+ onKey: ()=>onKey,
47
+ ready: ()=>ready
48
+ });
45
49
  var network_namespaceObject = {};
46
50
  __webpack_require__.r(network_namespaceObject);
47
51
  __webpack_require__.d(network_namespaceObject, {
@@ -53,10 +57,12 @@ __webpack_require__.d(network_namespaceObject, {
53
57
  var local_storage_namespaceObject = {};
54
58
  __webpack_require__.r(local_storage_namespaceObject);
55
59
  __webpack_require__.d(local_storage_namespaceObject, {
60
+ clear: ()=>clear,
56
61
  get: ()=>get,
57
62
  listen: ()=>listen,
58
63
  set: ()=>set,
59
- setObj: ()=>setObj
64
+ setObj: ()=>setObj,
65
+ update: ()=>local_storage_update
60
66
  });
61
67
  function bytesToHuman(bytes, options = {}) {
62
68
  const { useSI = false, decimals = 2, includeUnits = true, targetUnit = null } = options;
@@ -103,6 +109,39 @@ function bytesToHuman(bytes, options = {}) {
103
109
  function toKebab(str) {
104
110
  return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, ofs)=>(ofs ? '-' : '') + s.toLowerCase());
105
111
  }
112
+ function cssVar(name) {
113
+ const style = window.getComputedStyle(document.body);
114
+ return style.getPropertyValue(name);
115
+ }
116
+ function render(style) {
117
+ let s = '';
118
+ Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
119
+ return s;
120
+ }
121
+ function isDarkMode() {
122
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
123
+ }
124
+ function onDarkMode(cb) {
125
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (ev)=>{
126
+ cb(ev.matches);
127
+ });
128
+ }
129
+ function switchTheme(theme) {
130
+ document.documentElement.setAttribute('data-theme', theme);
131
+ }
132
+ function handleThemeSwitch() {
133
+ switchTheme(isDarkMode() ? 'dark' : 'light');
134
+ onDarkMode((dark)=>{
135
+ switchTheme(dark ? 'dark' : 'light');
136
+ });
137
+ }
138
+ function gradient(start, end, value) {
139
+ value = Math.max(0, Math.min(100, value));
140
+ const red = Math.round(start.red + (end.red - start.red) * value / 100);
141
+ const green = Math.round(start.green + (end.green - start.green) * value / 100);
142
+ const blue = Math.round(start.blue + (end.blue - start.blue) * value / 100);
143
+ return `rgb(${red}, ${green}, ${blue})`;
144
+ }
106
145
  function _define_property(obj, key, value) {
107
146
  if (key in obj) Object.defineProperty(obj, key, {
108
147
  value: value,
@@ -213,10 +252,13 @@ function onKey(key, cb) {
213
252
  });
214
253
  });
215
254
  }
255
+ function els(query, verbose = false) {
256
+ return Array.from(document.querySelectorAll(query)).map((n)=>new dom_el(n, verbose));
257
+ }
216
258
  class dom_el {
217
259
  value(update) {
218
260
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
219
- if (update) {
261
+ if (void 0 !== update) {
220
262
  if ('value' in this.el) this.el.value = update;
221
263
  if ('src' in this.el) this.el.src = update;
222
264
  return this;
@@ -255,17 +297,25 @@ class dom_el {
255
297
  if (this.el && 'src' in this.el) this.el.src = url;
256
298
  return this;
257
299
  }
258
- style(style) {
300
+ style(update, stringify = false) {
259
301
  if (this.el) {
260
- if ('string' == typeof style) this.el.style = style;
261
- else if ('object' == typeof style) {
262
- let s = '';
263
- Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
302
+ if ('string' == typeof update) this.el.style = update;
303
+ else if ('object' == typeof update) {
304
+ if (!stringify) {
305
+ for (const [k, v] of Object.entries(update))this.el.style[k] = v;
306
+ return;
307
+ }
308
+ const s = render(update);
309
+ this.log.debug(`set style: ${this.el.style} -> ${s}`);
264
310
  this.el.style = s;
265
311
  }
266
312
  }
267
313
  return this;
268
314
  }
315
+ hasClass(className) {
316
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
317
+ return this.el.classList.contains(className);
318
+ }
269
319
  addClass(className) {
270
320
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
271
321
  this.el.classList.add(className);
@@ -276,24 +326,52 @@ class dom_el {
276
326
  this.el.classList.remove(className);
277
327
  return this;
278
328
  }
329
+ render(vars = {}) {
330
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
331
+ try {
332
+ return interpolate(this.el.innerHTML, vars);
333
+ } catch (e) {
334
+ throw new Error(`could not render template ${this.query}: ${e}`);
335
+ }
336
+ }
337
+ appendTemplate(template, vars) {
338
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
339
+ if (!template.el) throw new Error("template does not contain element");
340
+ const tmpl = template.render(vars);
341
+ this.el.insertAdjacentHTML('beforeend', tmpl);
342
+ }
279
343
  on(event, cb) {
280
344
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
345
+ if (!this.listeners[event]) this.listeners[event] = [];
346
+ this.listeners[event].push(cb);
281
347
  this.el.addEventListener(event, cb);
282
348
  return this;
283
349
  }
284
350
  listen(event, cb) {
285
351
  return this.on(event, cb);
286
352
  }
353
+ removeListeners(event) {
354
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
355
+ for (const cb of this.listeners[event])this.el.removeEventListener(event, cb);
356
+ this.listeners[event] = [];
357
+ return this;
358
+ }
287
359
  constructor(opts, verbose = false){
288
360
  dom_define_property(this, "el", void 0);
289
361
  dom_define_property(this, "query", '');
290
362
  dom_define_property(this, "log", void 0);
363
+ dom_define_property(this, "listeners", {});
291
364
  this.log = new logger(verbose ? utils_logLevel.debug : utils_logLevel.none, 'element');
292
365
  if ('string' == typeof opts) {
293
366
  this.query = opts;
294
367
  this.el = document.querySelector(opts);
295
368
  return;
296
369
  }
370
+ if (opts instanceof HTMLElement) {
371
+ this.log.debug(`using existing element: ${opts}`);
372
+ this.el = opts;
373
+ return;
374
+ }
297
375
  const { query, element, type, class: styleClass, style, id, content, parent } = opts;
298
376
  if (query) {
299
377
  this.log.debug(`using query: ${query}`);
@@ -326,27 +404,10 @@ class dom_el {
326
404
  }
327
405
  }
328
406
  }
329
- function cssVar(name) {
330
- const style = window.getComputedStyle(document.body);
331
- return style.getPropertyValue(name);
332
- }
333
- function isDarkMode() {
334
- return window.matchMedia('(prefers-color-scheme: dark)').matches;
335
- }
336
- function onDarkMode(cb) {
337
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (ev)=>{
338
- cb(ev.matches);
339
- });
340
- }
341
- function switchTheme(theme) {
342
- document.documentElement.setAttribute('data-theme', theme);
343
- }
344
- function gradient(start, end, value) {
345
- value = Math.max(0, Math.min(100, value));
346
- const red = Math.round(start.red + (end.red - start.red) * value / 100);
347
- const green = Math.round(start.green + (end.green - start.green) * value / 100);
348
- const blue = Math.round(start.blue + (end.blue - start.blue) * value / 100);
349
- return `rgb(${red}, ${green}, ${blue})`;
407
+ function interpolate(str, params) {
408
+ let names = Object.keys(params).map((k)=>`_${k}`);
409
+ let vals = Object.values(params);
410
+ return new Function(...names, `return \`${str.replace(/\$\{(\w*)\}/g, '${_$1}')}\`;`)(...vals);
350
411
  }
351
412
  function network_define_property(obj, key, value) {
352
413
  if (key in obj) Object.defineProperty(obj, key, {
@@ -438,47 +499,54 @@ class request {
438
499
  class ws {
439
500
  setup() {
440
501
  this.recursion_level += 1;
502
+ if (this.ws) for(let key in this.event_listeners)this.event_listeners[key].forEach((cb)=>{
503
+ this.ws.removeEventListener(key, cb);
504
+ });
441
505
  this.ws = new WebSocket(this.url);
442
506
  this.backoff = 100;
443
507
  this.ws.addEventListener('open', ()=>{
444
508
  const rl = this.recursion_level;
445
509
  this.log.debug(`on open: reconnected (${rl})`);
446
510
  this.is_connected = true;
511
+ if (!this.ws) return;
447
512
  for(let key in this.event_listeners)this.event_listeners[key].forEach((cb)=>{
448
- this.log.debug(`adding listener (${rl}): ${key}`);
449
- this.ws.addEventListener(key, cb);
513
+ if (this.ws) {
514
+ this.log.debug(`adding listener (${rl}): ${key}`);
515
+ this.ws.addEventListener(key, cb);
516
+ }
450
517
  });
451
518
  });
452
519
  this.ws.addEventListener('close', ()=>{
453
520
  this.log.debug('connection closed');
454
521
  this.is_connected = false;
455
522
  this.backoff = Math.min(2 * this.backoff, this.max_timeout);
456
- this.log.debug('backoff: ' + this.backoff);
457
- setTimeout(()=>{
523
+ this.log.debug(`backoff: ${this.backoff}`);
524
+ this.reconnect_timer = window.setTimeout(()=>{
458
525
  if (this.should_reconnect) {
459
526
  this.ws = null;
460
527
  this.setup();
461
528
  }
462
529
  }, this.backoff + 50 * Math.random());
463
530
  });
531
+ this.ws.addEventListener('error', this.log.error);
464
532
  }
465
533
  send(data) {
466
534
  if (!this.is_connected || !this.ws) throw new Error('not connected');
467
- this.ws.send(data);
535
+ this.ws.send(JSON.stringify(data));
468
536
  }
469
537
  onMessage(cb) {
470
538
  if (!this.ws) throw new Error('ws is null');
471
539
  if (!this.event_listeners.message) this.event_listeners.message = [];
472
- this.event_listeners.message.push((e)=>{
473
- const rl = this.recursion_level;
474
- this.log.debug(`message(${rl}): ${e.data}`);
475
- cb(e.data);
476
- });
477
- this.ws.addEventListener('message', (e)=>{
540
+ const handler = (e)=>{
478
541
  const rl = this.recursion_level;
479
542
  this.log.debug(`message(${rl}): ${e.data}`);
480
543
  cb(e.data);
481
- });
544
+ };
545
+ this.event_listeners.message.push(handler);
546
+ this.ws.addEventListener('message', handler);
547
+ }
548
+ onJSON(cb) {
549
+ this.onMessage((d)=>cb(JSON.parse(d)));
482
550
  }
483
551
  on(event, cb) {
484
552
  if (!this.ws) throw new Error('ws is null');
@@ -487,6 +555,7 @@ class ws {
487
555
  this.ws.addEventListener(event, cb);
488
556
  }
489
557
  close() {
558
+ if (this.reconnect_timer) clearTimeout(this.reconnect_timer);
490
559
  if (!this.is_connected || !this.ws) return;
491
560
  this.should_reconnect = false;
492
561
  this.ws.close();
@@ -494,11 +563,12 @@ class ws {
494
563
  constructor(url, verbose = false){
495
564
  network_define_property(this, "url", void 0);
496
565
  network_define_property(this, "ws", void 0);
497
- network_define_property(this, "backoff", 50);
566
+ network_define_property(this, "backoff", 100);
498
567
  network_define_property(this, "max_timeout", 10000);
499
568
  network_define_property(this, "should_reconnect", true);
500
569
  network_define_property(this, "is_connected", false);
501
570
  network_define_property(this, "recursion_level", 0);
571
+ network_define_property(this, "reconnect_timer", null);
502
572
  network_define_property(this, "log", void 0);
503
573
  network_define_property(this, "event_listeners", {});
504
574
  this.url = url;
@@ -526,8 +596,28 @@ class refresh {
526
596
  this.url = url;
527
597
  }
528
598
  }
529
- function get(key) {
530
- return localStorage.getItem(key);
599
+ function get(key, _default) {
600
+ let ret = localStorage.getItem(key);
601
+ if (!ret && _default) {
602
+ ret = _default;
603
+ if ('function' == typeof _default) ret = _default();
604
+ set(key, ret);
605
+ }
606
+ return ret;
607
+ }
608
+ function local_storage_update(key, update1, broadcast = false) {
609
+ const prev = get(key);
610
+ const value = update1(prev);
611
+ if (prev !== value || broadcast) {
612
+ const event = new CustomEvent('@corvid/ls-update', {
613
+ detail: {
614
+ key,
615
+ value
616
+ }
617
+ });
618
+ document.dispatchEvent(event);
619
+ }
620
+ localStorage.setItem(key, value);
531
621
  }
532
622
  function set(key, value, broadcast = false) {
533
623
  if ('object' == typeof key) return void setObj(key, value, broadcast);
@@ -572,4 +662,7 @@ function listen(key, cb) {
572
662
  }
573
663
  });
574
664
  }
665
+ function clear(key) {
666
+ localStorage.removeItem(key);
667
+ }
575
668
  export { clipboard, dom_namespaceObject as dom, genID, utils_logLevel as logLevel, logger, local_storage_namespaceObject as ls, network_namespaceObject as network, strings_namespaceObject as strings, style_namespaceObject as style };
@@ -1,8 +1,10 @@
1
1
  import { el } from './dom';
2
- export declare function get(key: string): any;
2
+ export declare function get(key: string, _default?: any): any;
3
+ export declare function update(key: string, update: (current: any) => any, broadcast?: boolean): void;
3
4
  export declare function set(key: string | object, value: any, broadcast?: boolean): void;
4
5
  export declare function setObj(update: object, prefix?: string, broadcast?: boolean): void;
5
6
  export declare function listen(key: string, cb: (update: {
6
7
  key: string;
7
8
  value: any;
8
9
  }) => void | el): void;
10
+ export declare function clear(key: string): void;
package/dist/network.d.ts CHANGED
@@ -44,12 +44,14 @@ export declare class ws {
44
44
  should_reconnect: boolean;
45
45
  is_connected: boolean;
46
46
  recursion_level: number;
47
+ reconnect_timer: number | null;
47
48
  log: logger;
48
49
  event_listeners: Record<string, Array<(data: any) => void>>;
49
50
  constructor(url: string, verbose?: boolean);
50
51
  setup(): void;
51
52
  send(data: any): void;
52
53
  onMessage(cb: (data: any) => void): void;
54
+ onJSON(cb: (data: any) => void): void;
53
55
  on(event: string, cb: (data: any) => void): void;
54
56
  close(): void;
55
57
  }
package/dist/qr.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ interface QRCodeConfig {
2
+ text: string;
3
+ size?: number;
4
+ ecLevel?: 'L' | 'M' | 'Q' | 'H';
5
+ minVersion?: number;
6
+ maxVersion?: number;
7
+ quiet?: number;
8
+ radius?: number;
9
+ background?: string | null;
10
+ fill?: string | GradientFill;
11
+ left?: number;
12
+ top?: number;
13
+ }
14
+ interface GradientFill {
15
+ type: 'linear-gradient' | 'radial-gradient';
16
+ position: number[];
17
+ colorStops: Array<[number, string]>;
18
+ }
19
+ export default class QrCreator {
20
+ static render(config: QRCodeConfig, element: HTMLElement | HTMLCanvasElement): void;
21
+ }
22
+ export {};
File without changes
package/dist/style.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * Style *
3
3
  **************/
4
4
  export declare function cssVar(name: string): string;
5
+ export declare function render(style: Object): string;
5
6
  /**
6
7
  * Check if the current theme is dark
7
8
  */
@@ -16,6 +17,10 @@ export declare function onDarkMode(cb: (isDark: boolean) => void): void;
16
17
  * @param theme - 'light' or 'dark'
17
18
  */
18
19
  export declare function switchTheme(theme: string): void;
20
+ /**
21
+ * Listen for changes in the dark mode preference
22
+ */
23
+ export declare function handleThemeSwitch(): void;
19
24
  /**
20
25
  * Calculate a color gradient
21
26
  * @param start - The starting color
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taybart/corvid",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {