@oscarpalmer/atoms 0.26.0 → 0.27.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.
@@ -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
@@ -597,15 +597,33 @@ class Manager {
597
597
  var cloned = new Map;
598
598
  var frames = new Map;
599
599
  // src/js/signal.ts
600
- var _getter = function(instance) {
601
- const last = running[running.length - 1];
600
+ function computed(callback) {
601
+ return new Computed(callback);
602
+ }
603
+ function effect(callback) {
604
+ return new Effect(callback);
605
+ }
606
+ var getValue = function(instance) {
607
+ const last = effects[effects.length - 1];
602
608
  if (last !== undefined) {
603
609
  instance._effects.add(last);
604
610
  last._values.add(instance);
605
611
  }
606
612
  return instance._value;
607
613
  };
608
- var _setter = function(instance, value2, run) {
614
+ function isComputed(value2) {
615
+ return value2 instanceof Computed;
616
+ }
617
+ function isEffect(value2) {
618
+ return value2 instanceof Effect;
619
+ }
620
+ function isReactive(value2) {
621
+ return value2 instanceof Reactive;
622
+ }
623
+ function isSignal(value2) {
624
+ return value2 instanceof Signal;
625
+ }
626
+ var setValue = function(instance, value2, run) {
609
627
  if (!run && Object.is(value2, instance._value)) {
610
628
  return;
611
629
  }
@@ -615,32 +633,17 @@ var _setter = function(instance, value2, run) {
615
633
  return;
616
634
  }
617
635
  instance._frame = requestAnimationFrame(() => {
618
- for (const effect of instance._effects) {
619
- effect._callback();
636
+ for (const effect2 of instance._effects) {
637
+ effect2._callback();
620
638
  }
621
639
  instance._frame = undefined;
622
640
  });
623
641
  };
624
- function computed(callback) {
625
- return new Computed(callback);
626
- }
627
- function effect(callback) {
628
- return new Effect(callback);
629
- }
630
- function isComputed(value2) {
631
- return value2 instanceof Computed;
632
- }
633
- function isEffect(value2) {
634
- return value2 instanceof Effect;
635
- }
636
- function isSignal(value2) {
637
- return value2 instanceof Signal;
638
- }
639
642
  function signal(value2) {
640
643
  return new Signal(value2);
641
644
  }
642
645
 
643
- class Value {
646
+ class Reactive {
644
647
  _active = true;
645
648
  _effects = new Set;
646
649
  _frame;
@@ -655,16 +658,14 @@ class Value {
655
658
  }
656
659
  }
657
660
 
658
- class Computed extends Value {
661
+ class Computed extends Reactive {
659
662
  _effect;
660
663
  get value() {
661
- return _getter(this);
664
+ return getValue(this);
662
665
  }
663
666
  constructor(callback) {
664
667
  super();
665
- this._effect = effect(() => {
666
- _setter(this, callback(), false);
667
- });
668
+ this._effect = effect(() => setValue(this, callback(), false));
668
669
  }
669
670
  run() {
670
671
  this._effect.run();
@@ -687,9 +688,9 @@ class Effect {
687
688
  return;
688
689
  }
689
690
  this._active = true;
690
- const index = running.push(this) - 1;
691
+ const index = effects.push(this) - 1;
691
692
  this._callback();
692
- running.splice(index, 1);
693
+ effects.splice(index, 1);
693
694
  }
694
695
  stop() {
695
696
  if (!this._active) {
@@ -703,13 +704,13 @@ class Effect {
703
704
  }
704
705
  }
705
706
 
706
- class Signal extends Value {
707
+ class Signal extends Reactive {
707
708
  _value;
708
709
  get value() {
709
- return _getter(this);
710
+ return getValue(this);
710
711
  }
711
712
  set value(value2) {
712
- _setter(this, value2, false);
713
+ setValue(this, value2, false);
713
714
  }
714
715
  constructor(_value) {
715
716
  super();
@@ -720,13 +721,13 @@ class Signal extends Value {
720
721
  return;
721
722
  }
722
723
  this._active = true;
723
- _setter(this, this._value, true);
724
+ setValue(this, this._value, true);
724
725
  }
725
726
  stop() {
726
727
  this._active = false;
727
728
  }
728
729
  }
729
- var running = [];
730
+ var effects = [];
730
731
  // src/js/timer.ts
731
732
  function repeat(callback, options) {
732
733
  const count = typeof options?.count === "number" ? options.count : Infinity;
@@ -823,6 +824,7 @@ export {
823
824
  proxy,
824
825
  merge,
825
826
  isSignal,
827
+ isReactive,
826
828
  isEffect,
827
829
  isComputed,
828
830
  insert,
package/dist/js/signal.js CHANGED
@@ -1,13 +1,31 @@
1
1
  // src/js/signal.ts
2
- var _getter = function(instance) {
3
- const last = running[running.length - 1];
2
+ function computed(callback) {
3
+ return new Computed(callback);
4
+ }
5
+ function effect(callback) {
6
+ return new Effect(callback);
7
+ }
8
+ var getValue = function(instance) {
9
+ const last = effects[effects.length - 1];
4
10
  if (last !== undefined) {
5
11
  instance._effects.add(last);
6
12
  last._values.add(instance);
7
13
  }
8
14
  return instance._value;
9
15
  };
10
- var _setter = function(instance, value, run) {
16
+ function isComputed(value) {
17
+ return value instanceof Computed;
18
+ }
19
+ function isEffect(value) {
20
+ return value instanceof Effect;
21
+ }
22
+ function isReactive(value) {
23
+ return value instanceof Reactive;
24
+ }
25
+ function isSignal(value) {
26
+ return value instanceof Signal;
27
+ }
28
+ var setValue = function(instance, value, run) {
11
29
  if (!run && Object.is(value, instance._value)) {
12
30
  return;
13
31
  }
@@ -17,32 +35,17 @@ var _setter = function(instance, value, run) {
17
35
  return;
18
36
  }
19
37
  instance._frame = requestAnimationFrame(() => {
20
- for (const effect of instance._effects) {
21
- effect._callback();
38
+ for (const effect2 of instance._effects) {
39
+ effect2._callback();
22
40
  }
23
41
  instance._frame = undefined;
24
42
  });
25
43
  };
26
- function computed(callback) {
27
- return new Computed(callback);
28
- }
29
- function effect(callback) {
30
- return new Effect(callback);
31
- }
32
- function isComputed(value) {
33
- return value instanceof Computed;
34
- }
35
- function isEffect(value) {
36
- return value instanceof Effect;
37
- }
38
- function isSignal(value) {
39
- return value instanceof Signal;
40
- }
41
44
  function signal(value) {
42
45
  return new Signal(value);
43
46
  }
44
47
 
45
- class Value {
48
+ class Reactive {
46
49
  _active = true;
47
50
  _effects = new Set;
48
51
  _frame;
@@ -57,16 +60,14 @@ class Value {
57
60
  }
58
61
  }
59
62
 
60
- class Computed extends Value {
63
+ class Computed extends Reactive {
61
64
  _effect;
62
65
  get value() {
63
- return _getter(this);
66
+ return getValue(this);
64
67
  }
65
68
  constructor(callback) {
66
69
  super();
67
- this._effect = effect(() => {
68
- _setter(this, callback(), false);
69
- });
70
+ this._effect = effect(() => setValue(this, callback(), false));
70
71
  }
71
72
  run() {
72
73
  this._effect.run();
@@ -89,9 +90,9 @@ class Effect {
89
90
  return;
90
91
  }
91
92
  this._active = true;
92
- const index = running.push(this) - 1;
93
+ const index = effects.push(this) - 1;
93
94
  this._callback();
94
- running.splice(index, 1);
95
+ effects.splice(index, 1);
95
96
  }
96
97
  stop() {
97
98
  if (!this._active) {
@@ -105,13 +106,13 @@ class Effect {
105
106
  }
106
107
  }
107
108
 
108
- class Signal extends Value {
109
+ class Signal extends Reactive {
109
110
  _value;
110
111
  get value() {
111
- return _getter(this);
112
+ return getValue(this);
112
113
  }
113
114
  set value(value) {
114
- _setter(this, value, false);
115
+ setValue(this, value, false);
115
116
  }
116
117
  constructor(_value) {
117
118
  super();
@@ -122,16 +123,17 @@ class Signal extends Value {
122
123
  return;
123
124
  }
124
125
  this._active = true;
125
- _setter(this, this._value, true);
126
+ setValue(this, this._value, true);
126
127
  }
127
128
  stop() {
128
129
  this._active = false;
129
130
  }
130
131
  }
131
- var running = [];
132
+ var effects = [];
132
133
  export {
133
134
  signal,
134
135
  isSignal,
136
+ isReactive,
135
137
  isEffect,
136
138
  isComputed,
137
139
  effect,
@@ -1,13 +1,31 @@
1
1
  // src/js/signal.ts
2
- var _getter = function(instance) {
3
- const last = running[running.length - 1];
2
+ function computed(callback) {
3
+ return new Computed(callback);
4
+ }
5
+ function effect(callback) {
6
+ return new Effect(callback);
7
+ }
8
+ var getValue = function(instance) {
9
+ const last = effects[effects.length - 1];
4
10
  if (last !== undefined) {
5
11
  instance._effects.add(last);
6
12
  last._values.add(instance);
7
13
  }
8
14
  return instance._value;
9
15
  };
10
- var _setter = function(instance, value, run) {
16
+ function isComputed(value) {
17
+ return value instanceof Computed;
18
+ }
19
+ function isEffect(value) {
20
+ return value instanceof Effect;
21
+ }
22
+ function isReactive(value) {
23
+ return value instanceof Reactive;
24
+ }
25
+ function isSignal(value) {
26
+ return value instanceof Signal;
27
+ }
28
+ var setValue = function(instance, value, run) {
11
29
  if (!run && Object.is(value, instance._value)) {
12
30
  return;
13
31
  }
@@ -17,32 +35,17 @@ var _setter = function(instance, value, run) {
17
35
  return;
18
36
  }
19
37
  instance._frame = requestAnimationFrame(() => {
20
- for (const effect of instance._effects) {
21
- effect._callback();
38
+ for (const effect2 of instance._effects) {
39
+ effect2._callback();
22
40
  }
23
41
  instance._frame = undefined;
24
42
  });
25
43
  };
26
- function computed(callback) {
27
- return new Computed(callback);
28
- }
29
- function effect(callback) {
30
- return new Effect(callback);
31
- }
32
- function isComputed(value) {
33
- return value instanceof Computed;
34
- }
35
- function isEffect(value) {
36
- return value instanceof Effect;
37
- }
38
- function isSignal(value) {
39
- return value instanceof Signal;
40
- }
41
44
  function signal(value) {
42
45
  return new Signal(value);
43
46
  }
44
47
 
45
- class Value {
48
+ class Reactive {
46
49
  _active = true;
47
50
  _effects = new Set;
48
51
  _frame;
@@ -57,16 +60,14 @@ class Value {
57
60
  }
58
61
  }
59
62
 
60
- class Computed extends Value {
63
+ class Computed extends Reactive {
61
64
  _effect;
62
65
  get value() {
63
- return _getter(this);
66
+ return getValue(this);
64
67
  }
65
68
  constructor(callback) {
66
69
  super();
67
- this._effect = effect(() => {
68
- _setter(this, callback(), false);
69
- });
70
+ this._effect = effect(() => setValue(this, callback(), false));
70
71
  }
71
72
  run() {
72
73
  this._effect.run();
@@ -89,9 +90,9 @@ class Effect {
89
90
  return;
90
91
  }
91
92
  this._active = true;
92
- const index = running.push(this) - 1;
93
+ const index = effects.push(this) - 1;
93
94
  this._callback();
94
- running.splice(index, 1);
95
+ effects.splice(index, 1);
95
96
  }
96
97
  stop() {
97
98
  if (!this._active) {
@@ -105,13 +106,13 @@ class Effect {
105
106
  }
106
107
  }
107
108
 
108
- class Signal extends Value {
109
+ class Signal extends Reactive {
109
110
  _value;
110
111
  get value() {
111
- return _getter(this);
112
+ return getValue(this);
112
113
  }
113
114
  set value(value) {
114
- _setter(this, value, false);
115
+ setValue(this, value, false);
115
116
  }
116
117
  constructor(_value) {
117
118
  super();
@@ -122,16 +123,17 @@ class Signal extends Value {
122
123
  return;
123
124
  }
124
125
  this._active = true;
125
- _setter(this, this._value, true);
126
+ setValue(this, this._value, true);
126
127
  }
127
128
  stop() {
128
129
  this._active = false;
129
130
  }
130
131
  }
131
- var running = [];
132
+ var effects = [];
132
133
  export {
133
134
  signal,
134
135
  isSignal,
136
+ isReactive,
135
137
  isEffect,
136
138
  isComputed,
137
139
  effect,
package/package.json CHANGED
@@ -111,5 +111,5 @@
111
111
  },
112
112
  "type": "module",
113
113
  "types": "./types/index.d.ts",
114
- "version": "0.26.0"
114
+ "version": "0.27.0"
115
115
  }
package/src/js/signal.ts CHANGED
@@ -1,16 +1,19 @@
1
1
  type InternalEffect = {
2
2
  _callback: () => void;
3
- _values: Set<InternalValue>;
3
+ _values: Set<InternalReactive>;
4
4
  };
5
5
 
6
- type InternalValue = {
6
+ type InternalReactive = {
7
7
  _active: boolean;
8
8
  _effects: Set<InternalEffect>;
9
9
  _frame: number | undefined;
10
10
  _value: unknown;
11
11
  };
12
12
 
13
- abstract class Value<T = unknown> {
13
+ /**
14
+ * The base class for reactive values
15
+ */
16
+ abstract class Reactive<T = unknown> {
14
17
  protected _active = true;
15
18
  protected _effects = new Set<InternalEffect>();
16
19
  protected _frame: number | undefined;
@@ -56,22 +59,20 @@ abstract class Value<T = unknown> {
56
59
  /**
57
60
  * A computed, reactive value
58
61
  */
59
- class Computed<T> extends Value<T> {
62
+ class Computed<T> extends Reactive<T> {
60
63
  private readonly _effect: Effect;
61
64
 
62
65
  /**
63
66
  * @inheritdoc
64
67
  */
65
68
  get value(): T {
66
- return _getter(this as never) as T;
69
+ return getValue(this as never) as T;
67
70
  }
68
71
 
69
72
  constructor(callback: () => T) {
70
73
  super();
71
74
 
72
- this._effect = effect(() => {
73
- _setter(this as never, callback(), false);
74
- });
75
+ this._effect = effect(() => setValue(this as never, callback(), false));
75
76
  }
76
77
 
77
78
  /**
@@ -94,7 +95,7 @@ class Computed<T> extends Value<T> {
94
95
  */
95
96
  class Effect {
96
97
  private _active = false;
97
- private readonly _values = new Set<InternalValue>();
98
+ private readonly _values = new Set<InternalReactive>();
98
99
 
99
100
  constructor(private readonly _callback: () => void) {
100
101
  this.run();
@@ -110,11 +111,11 @@ class Effect {
110
111
 
111
112
  this._active = true;
112
113
 
113
- const index = running.push(this as never) - 1;
114
+ const index = effects.push(this as never) - 1;
114
115
 
115
116
  this._callback();
116
117
 
117
- running.splice(index, 1);
118
+ effects.splice(index, 1);
118
119
  }
119
120
 
120
121
  /**
@@ -138,19 +139,19 @@ class Effect {
138
139
  /**
139
140
  * A reactive value
140
141
  */
141
- class Signal<T> extends Value<T> {
142
+ class Signal<T> extends Reactive<T> {
142
143
  /**
143
144
  * @inheritdoc
144
145
  */
145
146
  get value(): T {
146
- return _getter(this as never) as T;
147
+ return getValue(this as never) as T;
147
148
  }
148
149
 
149
150
  /**
150
151
  * Sets the value
151
152
  */
152
153
  set value(value: T) {
153
- _setter(this as never, value, false);
154
+ setValue(this as never, value, false);
154
155
  }
155
156
 
156
157
  constructor(protected readonly _value: T) {
@@ -167,7 +168,7 @@ class Signal<T> extends Value<T> {
167
168
 
168
169
  this._active = true;
169
170
 
170
- _setter(this as never, this._value, true);
171
+ setValue(this as never, this._value, true);
171
172
  }
172
173
 
173
174
  /**
@@ -178,40 +179,7 @@ class Signal<T> extends Value<T> {
178
179
  }
179
180
  }
180
181
 
181
- const running: InternalEffect[] = [];
182
-
183
- function _getter(instance: InternalValue): unknown {
184
- const last = running[running.length - 1];
185
-
186
- if (last !== undefined) {
187
- instance._effects.add(last);
188
- last._values.add(instance);
189
- }
190
-
191
- return instance._value;
192
- }
193
-
194
- function _setter<T>(instance: InternalValue, value: T, run: boolean): void {
195
- if (!run && Object.is(value, instance._value)) {
196
- return;
197
- }
198
-
199
- instance._value = value;
200
-
201
- cancelAnimationFrame(instance._frame as never);
202
-
203
- if (!instance._active) {
204
- return;
205
- }
206
-
207
- instance._frame = requestAnimationFrame(() => {
208
- for (const effect of instance._effects) {
209
- effect._callback();
210
- }
211
-
212
- instance._frame = undefined;
213
- });
214
- }
182
+ const effects: InternalEffect[] = [];
215
183
 
216
184
  /**
217
185
  * Creates a computed, reactive value
@@ -227,6 +195,17 @@ export function effect(callback: () => void): Effect {
227
195
  return new Effect(callback);
228
196
  }
229
197
 
198
+ function getValue(instance: InternalReactive): unknown {
199
+ const last = effects[effects.length - 1];
200
+
201
+ if (last !== undefined) {
202
+ instance._effects.add(last);
203
+ last._values.add(instance);
204
+ }
205
+
206
+ return instance._value;
207
+ }
208
+
230
209
  /**
231
210
  * Is the value a computed, reactive value?
232
211
  */
@@ -241,6 +220,10 @@ export function isEffect(value: unknown): value is Effect {
241
220
  return value instanceof Effect;
242
221
  }
243
222
 
223
+ export function isReactive(value: unknown): value is Reactive<unknown> {
224
+ return value instanceof Reactive;
225
+ }
226
+
244
227
  /**
245
228
  * Is the value a reactive value?
246
229
  */
@@ -248,6 +231,28 @@ export function isSignal(value: unknown): value is Signal<unknown> {
248
231
  return value instanceof Signal;
249
232
  }
250
233
 
234
+ function setValue<T>(instance: InternalReactive, value: T, run: boolean): void {
235
+ if (!run && Object.is(value, instance._value)) {
236
+ return;
237
+ }
238
+
239
+ instance._value = value;
240
+
241
+ cancelAnimationFrame(instance._frame as never);
242
+
243
+ if (!instance._active) {
244
+ return;
245
+ }
246
+
247
+ instance._frame = requestAnimationFrame(() => {
248
+ for (const effect of instance._effects) {
249
+ effect._callback();
250
+ }
251
+
252
+ instance._frame = undefined;
253
+ });
254
+ }
255
+
251
256
  /**
252
257
  * Creates a reactive value
253
258
  */
@@ -255,4 +260,4 @@ export function signal<T>(value: T): Signal<T> {
255
260
  return new Signal(value);
256
261
  }
257
262
 
258
- export type {Computed, Effect, Signal};
263
+ export type {Computed, Effect, Reactive, Signal};
package/types/signal.d.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  type InternalEffect = {
2
2
  _callback: () => void;
3
- _values: Set<InternalValue>;
3
+ _values: Set<InternalReactive>;
4
4
  };
5
- type InternalValue = {
5
+ type InternalReactive = {
6
6
  _active: boolean;
7
7
  _effects: Set<InternalEffect>;
8
8
  _frame: number | undefined;
9
9
  _value: unknown;
10
10
  };
11
- declare abstract class Value<T = unknown> {
11
+ /**
12
+ * The base class for reactive values
13
+ */
14
+ declare abstract class Reactive<T = unknown> {
12
15
  protected _active: boolean;
13
16
  protected _effects: Set<InternalEffect>;
14
17
  protected _frame: number | undefined;
@@ -41,7 +44,7 @@ declare abstract class Value<T = unknown> {
41
44
  /**
42
45
  * A computed, reactive value
43
46
  */
44
- declare class Computed<T> extends Value<T> {
47
+ declare class Computed<T> extends Reactive<T> {
45
48
  private readonly _effect;
46
49
  /**
47
50
  * @inheritdoc
@@ -77,7 +80,7 @@ declare class Effect {
77
80
  /**
78
81
  * A reactive value
79
82
  */
80
- declare class Signal<T> extends Value<T> {
83
+ declare class Signal<T> extends Reactive<T> {
81
84
  protected readonly _value: T;
82
85
  /**
83
86
  * @inheritdoc
@@ -113,6 +116,7 @@ export declare function isComputed(value: unknown): value is Computed<unknown>;
113
116
  * Is the value a reactive effect?
114
117
  */
115
118
  export declare function isEffect(value: unknown): value is Effect;
119
+ export declare function isReactive(value: unknown): value is Reactive<unknown>;
116
120
  /**
117
121
  * Is the value a reactive value?
118
122
  */
@@ -121,4 +125,4 @@ export declare function isSignal(value: unknown): value is Signal<unknown>;
121
125
  * Creates a reactive value
122
126
  */
123
127
  export declare function signal<T>(value: T): Signal<T>;
124
- export type { Computed, Effect, Signal };
128
+ export type { Computed, Effect, Reactive, Signal };