@oscarpalmer/atoms 0.26.0 → 0.28.0

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/css/a11y.css CHANGED
@@ -1,3 +1,6 @@
1
+ .visually-hidden {
2
+ position: absolute !important;
3
+ }
1
4
  .visually-hidden:not(:active):not(:focus):not(:focus-within) {
2
5
  width: 1px !important;
3
6
  height: 1px !important;
@@ -0,0 +1,123 @@
1
+ // src/js/html/attribute.ts
2
+ import {
3
+ effect,
4
+ isComputed,
5
+ isSignal
6
+ } from "../signal";
7
+ import {addEvent} from "./event";
8
+ import {getIndex} from "./node";
9
+ function isBadAttribute(attribute) {
10
+ const { name, value } = attribute;
11
+ return /^on/i.test(name) || /^(href|src|xlink:href)$/i.test(name) && /(data:text\/html|javascript:)/i.test(value);
12
+ }
13
+ function isBooleanAttribute(name) {
14
+ return booleanAttributes.has(name.toLowerCase());
15
+ }
16
+ function mapAttributes(values, element) {
17
+ const attributes = Array.from(element.attributes);
18
+ const { length } = attributes;
19
+ let index = 0;
20
+ for (;index < length; index += 1) {
21
+ const attribute = attributes[index];
22
+ const isBad = isBadAttribute(attribute);
23
+ const value = getIndex(attribute.value, true);
24
+ if (isBad || value === -1 || !(element instanceof HTMLElement || element instanceof SVGElement)) {
25
+ if (isBad) {
26
+ element.removeAttribute(attribute.name);
27
+ }
28
+ continue;
29
+ }
30
+ if (attribute.name.startsWith("@")) {
31
+ addEvent(element, attribute.name, values[value]);
32
+ } else {
33
+ setReactive(element, attribute.name, values[value]);
34
+ }
35
+ }
36
+ }
37
+ var setAnyAttribute = function(element, name, reactive) {
38
+ const isValue = /^value$/i.test(name);
39
+ return effect(() => {
40
+ const { value } = reactive;
41
+ if (isValue) {
42
+ element.value = value;
43
+ return;
44
+ }
45
+ if (value == null) {
46
+ element.removeAttribute(name);
47
+ } else {
48
+ element.setAttribute(name, value);
49
+ }
50
+ });
51
+ };
52
+ var setBooleanAttribute = function(element, name, reactive) {
53
+ return effect(() => {
54
+ const { value } = reactive;
55
+ if (value === true) {
56
+ element.setAttribute(name, "");
57
+ } else {
58
+ element.removeAttribute(name);
59
+ }
60
+ });
61
+ };
62
+ var setClassAttribute = function(element, name, reactive) {
63
+ const classes = name.split(".").slice(1).map((name2) => name2.trim()).filter((name2) => name2.length > 0);
64
+ if (classes.length === 0) {
65
+ return;
66
+ }
67
+ return effect(() => {
68
+ if (reactive.value === true) {
69
+ element.classList.add(...classes);
70
+ } else {
71
+ element.classList.remove(...classes);
72
+ }
73
+ });
74
+ };
75
+ var setReactive = function(element, name, value) {
76
+ element.removeAttribute(name);
77
+ const reactive = typeof value === "function" ? value() : value;
78
+ if (!(isComputed(reactive) || isSignal(reactive))) {
79
+ return;
80
+ }
81
+ switch (true) {
82
+ case isBooleanAttribute(name):
83
+ return setBooleanAttribute(element, name, reactive);
84
+ case /^class\./i.test(name):
85
+ return setClassAttribute(element, name, reactive);
86
+ case /^style\./i.test(name):
87
+ return setStyleAttribute(element, name, reactive);
88
+ default:
89
+ return setAnyAttribute(element, name, reactive);
90
+ }
91
+ };
92
+ var setStyleAttribute = function(element, name, reactive) {
93
+ const [, first, second] = name.split(".");
94
+ const property = first.trim();
95
+ const suffix = second?.trim();
96
+ if (property.length === 0 || suffix !== undefined && suffix.length === 0) {
97
+ return;
98
+ }
99
+ return effect(() => {
100
+ const { value } = reactive;
101
+ if (value == null || value === false || value === true && suffix == null) {
102
+ element.style.removeProperty(property);
103
+ } else {
104
+ element.style.setProperty(property, value === true ? suffix : `${value}${suffix ?? ""}`);
105
+ }
106
+ });
107
+ };
108
+ var booleanAttributes = new Set([
109
+ "checked",
110
+ "disabled",
111
+ "hidden",
112
+ "inert",
113
+ "multiple",
114
+ "open",
115
+ "readonly",
116
+ "required",
117
+ "selected"
118
+ ]);
119
+ export {
120
+ mapAttributes,
121
+ isBooleanAttribute,
122
+ isBadAttribute
123
+ };
@@ -0,0 +1,9 @@
1
+ // src/js/html/event.ts
2
+ function addEvent(element, name, value) {
3
+ if (typeof value === "function") {
4
+ element.addEventListener(name.slice(1), value);
5
+ }
6
+ }
7
+ export {
8
+ addEvent
9
+ };
@@ -0,0 +1,56 @@
1
+ // src/js/html/index.ts
2
+ import {createNodes, mapNodes} from "./node";
3
+ var getExpression = function(values, value, expression) {
4
+ if (expression == null) {
5
+ return value;
6
+ }
7
+ if (typeof expression === "function" || expression instanceof Node || expression instanceof Templated) {
8
+ values.push(expression);
9
+ return `${value}<!--atoms.${values.length - 1}-->`;
10
+ }
11
+ if (Array.isArray(expression)) {
12
+ const { length } = expression;
13
+ let html = "";
14
+ let index = 0;
15
+ for (;index < length; index += 1) {
16
+ html += getExpression(values, "", expression[index]);
17
+ }
18
+ return `${value}${html}`;
19
+ }
20
+ return `${value}${expression}`;
21
+ };
22
+ var getHtml = function(data) {
23
+ const { expressions, strings, values } = data;
24
+ const { length } = strings;
25
+ let html = "";
26
+ let index = 0;
27
+ for (;index < length; index += 1) {
28
+ html += getExpression(values, strings[index], expressions[index]);
29
+ }
30
+ return html;
31
+ };
32
+ function html(strings, ...expressions) {
33
+ return new Templated(strings, expressions);
34
+ }
35
+ function isTemplated(value) {
36
+ return value instanceof Templated;
37
+ }
38
+
39
+ class Templated {
40
+ constructor(strings, expressions) {
41
+ this.data = {
42
+ expressions,
43
+ strings,
44
+ values: []
45
+ };
46
+ }
47
+ render() {
48
+ const html2 = getHtml(this.data);
49
+ const nodes = createNodes(html2);
50
+ return mapNodes(this.data.values, nodes);
51
+ }
52
+ }
53
+ export {
54
+ isTemplated,
55
+ html
56
+ };
@@ -0,0 +1,5 @@
1
+ // src/js/html/models.ts
2
+ var models_default = undefined;
3
+ export {
4
+ models_default as default
5
+ };
@@ -0,0 +1,74 @@
1
+ // src/js/html/node.ts
2
+ import {isTemplated} from "../html";
3
+ import {effect, isComputed, isSignal} from "../signal";
4
+ import {mapAttributes} from "./attribute";
5
+ function createNodes(html2) {
6
+ const template = document.createElement("template");
7
+ template.innerHTML = html2;
8
+ const cloned = template.content.cloneNode(true);
9
+ const scripts = Array.from(cloned.querySelectorAll("script"));
10
+ for (const script of scripts) {
11
+ script.remove();
12
+ }
13
+ cloned.normalize();
14
+ return cloned;
15
+ }
16
+ function getIndex(content, full) {
17
+ const [, index] = (full ? /^<!--atoms\.(\d+)-->$/ : /^atoms\.(\d+)$/).exec(content) ?? [];
18
+ return index == null ? -1 : Number.parseInt(index, 10);
19
+ }
20
+ var getNode = function(value) {
21
+ if (value instanceof Node) {
22
+ return value;
23
+ }
24
+ return isTemplated(value) ? value.render() : document.createTextNode(String(value));
25
+ };
26
+ function mapNodes(values, node) {
27
+ const children = Array.from(node.childNodes);
28
+ const { length } = children;
29
+ let index = 0;
30
+ for (;index < length; index += 1) {
31
+ const node2 = children[index];
32
+ const value = node2.nodeType === 8 ? getIndex(node2.nodeValue ?? "", false) : -1;
33
+ if (value > -1) {
34
+ setNode(node2, values[value]);
35
+ continue;
36
+ }
37
+ if (node2 instanceof Element) {
38
+ mapAttributes(values, node2);
39
+ }
40
+ if (node2.hasChildNodes()) {
41
+ mapNodes(values, node2);
42
+ }
43
+ }
44
+ return node;
45
+ }
46
+ var setNode = function(comment, value) {
47
+ if (typeof value === "function") {
48
+ setReactive(comment, value);
49
+ return;
50
+ }
51
+ const node = getNode(value);
52
+ comment.replaceWith(...node.constructor.name === "DocumentFragment" ? Array.from(node.childNodes) : [node]);
53
+ };
54
+ var setReactive = function(comment, callback) {
55
+ const reactive = callback();
56
+ if (!(isComputed(reactive) || isSignal(reactive))) {
57
+ return;
58
+ }
59
+ const text = document.createTextNode("");
60
+ effect(() => {
61
+ const { value } = reactive;
62
+ text.textContent = String(value);
63
+ if (value == null && text.parentNode != null) {
64
+ text.replaceWith(comment);
65
+ } else if (value != null && comment.parentNode != null) {
66
+ comment.replaceWith(text);
67
+ }
68
+ });
69
+ };
70
+ export {
71
+ mapNodes,
72
+ getIndex,
73
+ createNodes
74
+ };
package/dist/js/index.js CHANGED
@@ -284,6 +284,21 @@ function isPlainObject(value) {
284
284
  return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
285
285
  }
286
286
 
287
+ // src/js/queue.ts
288
+ function queue(callback) {
289
+ queued.add(callback);
290
+ if (queued.size > 0) {
291
+ queueMicrotask(() => {
292
+ const callbacks = Array.from(queued);
293
+ queued.clear();
294
+ for (const callback2 of callbacks) {
295
+ callback2();
296
+ }
297
+ });
298
+ }
299
+ }
300
+ var queued = new Set;
301
+
287
302
  // src/js/value.ts
288
303
  var _cloneNested = function(value) {
289
304
  const cloned = Array.isArray(value) ? [] : {};
@@ -535,13 +550,10 @@ var _isProxy = function(value2) {
535
550
  return value2?.$ instanceof Manager;
536
551
  };
537
552
  var _onChange = function(manager, value2) {
538
- cancelAnimationFrame(frames.get(manager));
539
553
  if (!cloned.has(manager)) {
540
554
  cloned.set(manager, value2);
541
555
  }
542
- frames.set(manager, requestAnimationFrame(() => {
543
- _emit(manager);
544
- }));
556
+ queue(manager.emitter);
545
557
  };
546
558
  function cloneProxy(proxy) {
547
559
  if (!_isProxy(proxy)) {
@@ -575,6 +587,9 @@ class Manager {
575
587
  }
576
588
  constructor(owner) {
577
589
  this.owner = owner;
590
+ this.emitter = function() {
591
+ _emit(this);
592
+ }.bind(this);
578
593
  }
579
594
  clone() {
580
595
  return _createProxy(undefined, clone(merge(this.owner)));
@@ -595,55 +610,51 @@ class Manager {
595
610
  }
596
611
  }
597
612
  var cloned = new Map;
598
- var frames = new Map;
599
613
  // src/js/signal.ts
600
- var _getter = function(instance) {
601
- const last = running[running.length - 1];
602
- if (last !== undefined) {
603
- instance._effects.add(last);
604
- last._values.add(instance);
605
- }
606
- return instance._value;
607
- };
608
- var _setter = function(instance, value2, run) {
609
- if (!run && Object.is(value2, instance._value)) {
610
- return;
611
- }
612
- instance._value = value2;
613
- cancelAnimationFrame(instance._frame);
614
- if (!instance._active) {
615
- return;
616
- }
617
- instance._frame = requestAnimationFrame(() => {
618
- for (const effect of instance._effects) {
619
- effect._callback();
620
- }
621
- instance._frame = undefined;
622
- });
623
- };
624
614
  function computed(callback) {
625
615
  return new Computed(callback);
626
616
  }
627
617
  function effect(callback) {
628
618
  return new Effect(callback);
629
619
  }
620
+ var getValue = function(reactive) {
621
+ const effect2 = effects[effects.length - 1];
622
+ if (effect2 !== undefined) {
623
+ reactive._effects.add(effect2);
624
+ effect2._reactives.add(reactive);
625
+ }
626
+ return reactive._value;
627
+ };
630
628
  function isComputed(value2) {
631
629
  return value2 instanceof Computed;
632
630
  }
633
631
  function isEffect(value2) {
634
632
  return value2 instanceof Effect;
635
633
  }
634
+ function isReactive(value2) {
635
+ return value2 instanceof Reactive;
636
+ }
636
637
  function isSignal(value2) {
637
638
  return value2 instanceof Signal;
638
639
  }
640
+ var setValue = function(reactive, value2, run) {
641
+ if (!run && Object.is(value2, reactive._value)) {
642
+ return;
643
+ }
644
+ reactive._value = value2;
645
+ if (reactive._active) {
646
+ for (const effect2 of reactive._effects) {
647
+ queue(effect2._callback);
648
+ }
649
+ }
650
+ };
639
651
  function signal(value2) {
640
652
  return new Signal(value2);
641
653
  }
642
654
 
643
- class Value {
655
+ class Reactive {
644
656
  _active = true;
645
657
  _effects = new Set;
646
- _frame;
647
658
  peek() {
648
659
  return this._value;
649
660
  }
@@ -655,16 +666,14 @@ class Value {
655
666
  }
656
667
  }
657
668
 
658
- class Computed extends Value {
669
+ class Computed extends Reactive {
659
670
  _effect;
660
671
  get value() {
661
- return _getter(this);
672
+ return getValue(this);
662
673
  }
663
674
  constructor(callback) {
664
675
  super();
665
- this._effect = effect(() => {
666
- _setter(this, callback(), false);
667
- });
676
+ this._effect = effect(() => setValue(this, callback(), false));
668
677
  }
669
678
  run() {
670
679
  this._effect.run();
@@ -677,7 +686,7 @@ class Computed extends Value {
677
686
  class Effect {
678
687
  _callback;
679
688
  _active = false;
680
- _values = new Set;
689
+ _reactives = new Set;
681
690
  constructor(_callback) {
682
691
  this._callback = _callback;
683
692
  this.run();
@@ -687,29 +696,29 @@ class Effect {
687
696
  return;
688
697
  }
689
698
  this._active = true;
690
- const index = running.push(this) - 1;
699
+ const index = effects.push(this) - 1;
691
700
  this._callback();
692
- running.splice(index, 1);
701
+ effects.splice(index, 1);
693
702
  }
694
703
  stop() {
695
704
  if (!this._active) {
696
705
  return;
697
706
  }
698
707
  this._active = false;
699
- for (const value2 of this._values) {
708
+ for (const value2 of this._reactives) {
700
709
  value2._effects.delete(this);
701
710
  }
702
- this._values.clear();
711
+ this._reactives.clear();
703
712
  }
704
713
  }
705
714
 
706
- class Signal extends Value {
715
+ class Signal extends Reactive {
707
716
  _value;
708
717
  get value() {
709
- return _getter(this);
718
+ return getValue(this);
710
719
  }
711
720
  set value(value2) {
712
- _setter(this, value2, false);
721
+ setValue(this, value2, false);
713
722
  }
714
723
  constructor(_value) {
715
724
  super();
@@ -720,16 +729,16 @@ class Signal extends Value {
720
729
  return;
721
730
  }
722
731
  this._active = true;
723
- _setter(this, this._value, true);
732
+ setValue(this, this._value, true);
724
733
  }
725
734
  stop() {
726
735
  this._active = false;
727
736
  }
728
737
  }
729
- var running = [];
738
+ var effects = [];
730
739
  // src/js/timer.ts
731
740
  function repeat(callback, options) {
732
- const count = typeof options?.count === "number" ? options.count : Infinity;
741
+ const count = typeof options?.count === "number" ? options.count : Number.POSITIVE_INFINITY;
733
742
  return new Timer(callback, { ...options ?? {}, ...{ count } }).start();
734
743
  }
735
744
  function wait(callback, time) {
@@ -819,10 +828,12 @@ export {
819
828
  signal,
820
829
  set,
821
830
  repeat,
831
+ queue,
822
832
  push,
823
833
  proxy,
824
834
  merge,
825
835
  isSignal,
836
+ isReactive,
826
837
  isEffect,
827
838
  isComputed,
828
839
  insert,
package/dist/js/index.mjs CHANGED
@@ -4,6 +4,7 @@ export * from "./element";
4
4
  export * from "./event";
5
5
  export * from "./number";
6
6
  export * from "./proxy";
7
+ export * from "./queue";
7
8
  export * from "./signal";
8
9
  export * from "./string";
9
10
  export * from "./timer";
package/dist/js/proxy.js CHANGED
@@ -19,6 +19,21 @@ function isPlainObject(value) {
19
19
  return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
20
20
  }
21
21
 
22
+ // src/js/queue.ts
23
+ function queue(callback) {
24
+ queued.add(callback);
25
+ if (queued.size > 0) {
26
+ queueMicrotask(() => {
27
+ const callbacks = Array.from(queued);
28
+ queued.clear();
29
+ for (const callback2 of callbacks) {
30
+ callback2();
31
+ }
32
+ });
33
+ }
34
+ }
35
+ var queued = new Set;
36
+
22
37
  // src/js/value.ts
23
38
  var _cloneNested = function(value) {
24
39
  const cloned = Array.isArray(value) ? [] : {};
@@ -239,13 +254,10 @@ var _isProxy = function(value2) {
239
254
  return value2?.$ instanceof Manager;
240
255
  };
241
256
  var _onChange = function(manager, value2) {
242
- cancelAnimationFrame(frames.get(manager));
243
257
  if (!cloned.has(manager)) {
244
258
  cloned.set(manager, value2);
245
259
  }
246
- frames.set(manager, requestAnimationFrame(() => {
247
- _emit(manager);
248
- }));
260
+ queue(manager.emitter);
249
261
  };
250
262
  function cloneProxy(proxy) {
251
263
  if (!_isProxy(proxy)) {
@@ -279,6 +291,9 @@ class Manager {
279
291
  }
280
292
  constructor(owner) {
281
293
  this.owner = owner;
294
+ this.emitter = function() {
295
+ _emit(this);
296
+ }.bind(this);
282
297
  }
283
298
  clone() {
284
299
  return _createProxy(undefined, clone(merge(this.owner)));
@@ -299,7 +314,6 @@ class Manager {
299
314
  }
300
315
  }
301
316
  var cloned = new Map;
302
- var frames = new Map;
303
317
  export {
304
318
  unsubscribe,
305
319
  subscribe,
package/dist/js/proxy.mjs CHANGED
@@ -1,5 +1,8 @@
1
1
  // src/js/proxy.ts
2
- import {isArrayOrPlainObject} from "./is";
2
+ import {
3
+ isArrayOrPlainObject
4
+ } from "./is";
5
+ import {queue as queue2} from "./queue";
3
6
  import {clone, diff, get, merge} from "./value";
4
7
  var _createProxy = function(existing, value2) {
5
8
  if (!isArrayOrPlainObject(value2) || _isProxy(value2) && value2.$ === existing) {
@@ -60,13 +63,10 @@ var _isProxy = function(value2) {
60
63
  return value2?.$ instanceof Manager;
61
64
  };
62
65
  var _onChange = function(manager, value2) {
63
- cancelAnimationFrame(frames.get(manager));
64
66
  if (!cloned.has(manager)) {
65
67
  cloned.set(manager, value2);
66
68
  }
67
- frames.set(manager, requestAnimationFrame(() => {
68
- _emit(manager);
69
- }));
69
+ queue2(manager.emitter);
70
70
  };
71
71
  function cloneProxy(proxy) {
72
72
  if (!_isProxy(proxy)) {
@@ -100,6 +100,9 @@ class Manager {
100
100
  }
101
101
  constructor(owner) {
102
102
  this.owner = owner;
103
+ this.emitter = function() {
104
+ _emit(this);
105
+ }.bind(this);
103
106
  }
104
107
  clone() {
105
108
  return _createProxy(undefined, clone(merge(this.owner)));
@@ -120,7 +123,6 @@ class Manager {
120
123
  }
121
124
  }
122
125
  var cloned = new Map;
123
- var frames = new Map;
124
126
  export {
125
127
  unsubscribe,
126
128
  subscribe,
@@ -0,0 +1,17 @@
1
+ // src/js/queue.ts
2
+ function queue(callback) {
3
+ queued.add(callback);
4
+ if (queued.size > 0) {
5
+ queueMicrotask(() => {
6
+ const callbacks = Array.from(queued);
7
+ queued.clear();
8
+ for (const callback2 of callbacks) {
9
+ callback2();
10
+ }
11
+ });
12
+ }
13
+ }
14
+ var queued = new Set;
15
+ export {
16
+ queue
17
+ };
@@ -0,0 +1,17 @@
1
+ // src/js/queue.ts
2
+ function queue(callback) {
3
+ queued.add(callback);
4
+ if (queued.size > 0) {
5
+ queueMicrotask(() => {
6
+ const callbacks = Array.from(queued);
7
+ queued.clear();
8
+ for (const callback2 of callbacks) {
9
+ callback2();
10
+ }
11
+ });
12
+ }
13
+ }
14
+ var queued = new Set;
15
+ export {
16
+ queue
17
+ };