@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.
- package/dist/js/html/attribute.mjs +123 -0
- package/dist/js/html/event.mjs +9 -0
- package/dist/js/html/index.mjs +56 -0
- package/dist/js/html/models.mjs +5 -0
- package/dist/js/html/node.mjs +74 -0
- package/dist/js/index.js +35 -33
- package/dist/js/signal.js +35 -33
- package/dist/js/signal.mjs +35 -33
- package/package.json +1 -1
- package/src/js/signal.ts +55 -50
- package/types/signal.d.ts +10 -6
|
@@ -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,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,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
|
-
|
|
601
|
-
|
|
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
|
-
|
|
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
|
|
619
|
-
|
|
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
|
|
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
|
|
661
|
+
class Computed extends Reactive {
|
|
659
662
|
_effect;
|
|
660
663
|
get value() {
|
|
661
|
-
return
|
|
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 =
|
|
691
|
+
const index = effects.push(this) - 1;
|
|
691
692
|
this._callback();
|
|
692
|
-
|
|
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
|
|
707
|
+
class Signal extends Reactive {
|
|
707
708
|
_value;
|
|
708
709
|
get value() {
|
|
709
|
-
return
|
|
710
|
+
return getValue(this);
|
|
710
711
|
}
|
|
711
712
|
set value(value2) {
|
|
712
|
-
|
|
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
|
-
|
|
724
|
+
setValue(this, this._value, true);
|
|
724
725
|
}
|
|
725
726
|
stop() {
|
|
726
727
|
this._active = false;
|
|
727
728
|
}
|
|
728
729
|
}
|
|
729
|
-
var
|
|
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
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
21
|
-
|
|
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
|
|
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
|
|
63
|
+
class Computed extends Reactive {
|
|
61
64
|
_effect;
|
|
62
65
|
get value() {
|
|
63
|
-
return
|
|
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 =
|
|
93
|
+
const index = effects.push(this) - 1;
|
|
93
94
|
this._callback();
|
|
94
|
-
|
|
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
|
|
109
|
+
class Signal extends Reactive {
|
|
109
110
|
_value;
|
|
110
111
|
get value() {
|
|
111
|
-
return
|
|
112
|
+
return getValue(this);
|
|
112
113
|
}
|
|
113
114
|
set value(value) {
|
|
114
|
-
|
|
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
|
-
|
|
126
|
+
setValue(this, this._value, true);
|
|
126
127
|
}
|
|
127
128
|
stop() {
|
|
128
129
|
this._active = false;
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
var
|
|
132
|
+
var effects = [];
|
|
132
133
|
export {
|
|
133
134
|
signal,
|
|
134
135
|
isSignal,
|
|
136
|
+
isReactive,
|
|
135
137
|
isEffect,
|
|
136
138
|
isComputed,
|
|
137
139
|
effect,
|
package/dist/js/signal.mjs
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
// src/js/signal.ts
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
21
|
-
|
|
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
|
|
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
|
|
63
|
+
class Computed extends Reactive {
|
|
61
64
|
_effect;
|
|
62
65
|
get value() {
|
|
63
|
-
return
|
|
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 =
|
|
93
|
+
const index = effects.push(this) - 1;
|
|
93
94
|
this._callback();
|
|
94
|
-
|
|
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
|
|
109
|
+
class Signal extends Reactive {
|
|
109
110
|
_value;
|
|
110
111
|
get value() {
|
|
111
|
-
return
|
|
112
|
+
return getValue(this);
|
|
112
113
|
}
|
|
113
114
|
set value(value) {
|
|
114
|
-
|
|
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
|
-
|
|
126
|
+
setValue(this, this._value, true);
|
|
126
127
|
}
|
|
127
128
|
stop() {
|
|
128
129
|
this._active = false;
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
var
|
|
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
package/src/js/signal.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
type InternalEffect = {
|
|
2
2
|
_callback: () => void;
|
|
3
|
-
_values: Set<
|
|
3
|
+
_values: Set<InternalReactive>;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
-
type
|
|
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
|
-
|
|
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
|
|
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
|
|
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<
|
|
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 =
|
|
114
|
+
const index = effects.push(this as never) - 1;
|
|
114
115
|
|
|
115
116
|
this._callback();
|
|
116
117
|
|
|
117
|
-
|
|
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
|
|
142
|
+
class Signal<T> extends Reactive<T> {
|
|
142
143
|
/**
|
|
143
144
|
* @inheritdoc
|
|
144
145
|
*/
|
|
145
146
|
get value(): T {
|
|
146
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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<
|
|
3
|
+
_values: Set<InternalReactive>;
|
|
4
4
|
};
|
|
5
|
-
type
|
|
5
|
+
type InternalReactive = {
|
|
6
6
|
_active: boolean;
|
|
7
7
|
_effects: Set<InternalEffect>;
|
|
8
8
|
_frame: number | undefined;
|
|
9
9
|
_value: unknown;
|
|
10
10
|
};
|
|
11
|
-
|
|
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
|
|
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
|
|
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 };
|