xote 1.0.2 → 1.0.3
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 +2 -2
- package/dist/xote.cjs +1 -0
- package/dist/xote.cjs.map +1 -0
- package/dist/xote.mjs +708 -0
- package/dist/xote.mjs.map +1 -0
- package/dist/xote.umd.js +1 -0
- package/dist/xote.umd.js.map +1 -0
- package/package.json +17 -1
- package/.gitattributes +0 -2
- package/.github/workflows/release.yml +0 -44
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs +0 -9
- package/dist/index.mjs.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/docs/CHANGELOG.md +0 -27
- package/index.html +0 -28
- package/rescript.json +0 -18
- package/src/Xote.res +0 -5
- package/src/Xote.res.mjs +0 -21
- package/src/Xote__Component.res +0 -151
- package/src/Xote__Component.res.mjs +0 -202
- package/src/Xote__Computed.res +0 -43
- package/src/Xote__Computed.res.mjs +0 -61
- package/src/Xote__Core.res +0 -105
- package/src/Xote__Core.res.mjs +0 -148
- package/src/Xote__Effect.res +0 -36
- package/src/Xote__Effect.res.mjs +0 -57
- package/src/Xote__Example.res +0 -266
- package/src/Xote__Example.res.mjs +0 -303
- package/src/Xote__Id.res +0 -5
- package/src/Xote__Id.res.mjs +0 -17
- package/src/Xote__Observer.res +0 -12
- package/src/Xote__Observer.res.mjs +0 -12
- package/src/Xote__Signal.res +0 -31
- package/src/Xote__Signal.res.mjs +0 -64
- package/src/demo/TodoApp.res +0 -289
- package/src/demo/TodoApp.res.mjs +0 -326
- package/vite.config.js +0 -45
package/src/Xote__Component.res
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
module Signal = Xote__Signal
|
|
2
|
-
module Effect = Xote__Effect
|
|
3
|
-
module Core = Xote__Core
|
|
4
|
-
module Computed = Xote__Computed
|
|
5
|
-
|
|
6
|
-
/* Type representing a virtual node */
|
|
7
|
-
type rec node =
|
|
8
|
-
| Element({
|
|
9
|
-
tag: string,
|
|
10
|
-
attrs: array<(string, string)>,
|
|
11
|
-
events: array<(string, Dom.event => unit)>,
|
|
12
|
-
children: array<node>,
|
|
13
|
-
})
|
|
14
|
-
| Text(string)
|
|
15
|
-
| SignalText(Core.t<string>)
|
|
16
|
-
| Fragment(array<node>)
|
|
17
|
-
| SignalFragment(Core.t<array<node>>)
|
|
18
|
-
|
|
19
|
-
/* Create a text node */
|
|
20
|
-
let text = (content: string): node => Text(content)
|
|
21
|
-
|
|
22
|
-
/* Create a reactive text node from a signal */
|
|
23
|
-
let textSignal = (signal: Core.t<string>): node => SignalText(signal)
|
|
24
|
-
|
|
25
|
-
/* Create a fragment (multiple children without wrapper) */
|
|
26
|
-
let fragment = (children: array<node>): node => Fragment(children)
|
|
27
|
-
|
|
28
|
-
/* Create a reactive fragment from a signal */
|
|
29
|
-
let signalFragment = (signal: Core.t<array<node>>): node => SignalFragment(signal)
|
|
30
|
-
|
|
31
|
-
/* Create a reactive list from a signal and render function */
|
|
32
|
-
let list = (signal: Core.t<array<'a>>, renderItem: 'a => node): node => {
|
|
33
|
-
let nodesSignal = Computed.make(() => {
|
|
34
|
-
Signal.get(signal)->Array.map(renderItem)
|
|
35
|
-
})
|
|
36
|
-
SignalFragment(nodesSignal)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/* Create an element */
|
|
40
|
-
let element = (
|
|
41
|
-
tag: string,
|
|
42
|
-
~attrs: array<(string, string)>=[]->Array.map(x => x),
|
|
43
|
-
~events: array<(string, Dom.event => unit)>=[]->Array.map(x => x),
|
|
44
|
-
~children: array<node>=[]->Array.map(x => x),
|
|
45
|
-
(),
|
|
46
|
-
): node => Element({tag, attrs, events, children})
|
|
47
|
-
|
|
48
|
-
/* Helper to create common elements */
|
|
49
|
-
let div = (~attrs=?, ~events=?, ~children=?, ()) => element("div", ~attrs?, ~events?, ~children?, ())
|
|
50
|
-
let span = (~attrs=?, ~events=?, ~children=?, ()) => element("span", ~attrs?, ~events?, ~children?, ())
|
|
51
|
-
let button = (~attrs=?, ~events=?, ~children=?, ()) =>
|
|
52
|
-
element("button", ~attrs?, ~events?, ~children?, ())
|
|
53
|
-
let input = (~attrs=?, ~events=?, ()) => element("input", ~attrs?, ~events?, ())
|
|
54
|
-
let h1 = (~attrs=?, ~events=?, ~children=?, ()) => element("h1", ~attrs?, ~events?, ~children?, ())
|
|
55
|
-
let h2 = (~attrs=?, ~events=?, ~children=?, ()) => element("h2", ~attrs?, ~events?, ~children?, ())
|
|
56
|
-
let h3 = (~attrs=?, ~events=?, ~children=?, ()) => element("h3", ~attrs?, ~events?, ~children?, ())
|
|
57
|
-
let p = (~attrs=?, ~events=?, ~children=?, ()) => element("p", ~attrs?, ~events?, ~children?, ())
|
|
58
|
-
let ul = (~attrs=?, ~events=?, ~children=?, ()) => element("ul", ~attrs?, ~events?, ~children?, ())
|
|
59
|
-
let li = (~attrs=?, ~events=?, ~children=?, ()) => element("li", ~attrs?, ~events?, ~children?, ())
|
|
60
|
-
|
|
61
|
-
/* External bindings for DOM manipulation */
|
|
62
|
-
@val @scope("document") external createElement: string => Dom.element = "createElement"
|
|
63
|
-
@val @scope("document") external createTextNode: string => Dom.element = "createTextNode"
|
|
64
|
-
@val @scope("document") external createDocumentFragment: unit => Dom.element = "createDocumentFragment"
|
|
65
|
-
@val @scope("document") external getElementById: string => Nullable.t<Dom.element> = "getElementById"
|
|
66
|
-
|
|
67
|
-
@send external setAttribute: (Dom.element, string, string) => unit = "setAttribute"
|
|
68
|
-
@send external addEventListener: (Dom.element, string, Dom.event => unit) => unit = "addEventListener"
|
|
69
|
-
@send external appendChild: (Dom.element, Dom.element) => unit = "appendChild"
|
|
70
|
-
@set external setTextContent: (Dom.element, string) => unit = "textContent"
|
|
71
|
-
|
|
72
|
-
/* Render a virtual node to a real DOM element */
|
|
73
|
-
let rec render = (node: node): Dom.element => {
|
|
74
|
-
switch node {
|
|
75
|
-
| Text(content) => createTextNode(content)
|
|
76
|
-
| SignalText(signal) => {
|
|
77
|
-
let el = createTextNode(Signal.peek(signal))
|
|
78
|
-
|
|
79
|
-
/* Set up effect to update text when signal changes */
|
|
80
|
-
let _ = Effect.run(() => {
|
|
81
|
-
let content = Signal.get(signal)
|
|
82
|
-
el->setTextContent(content)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
el
|
|
86
|
-
}
|
|
87
|
-
| Element({tag, attrs, events, children}) => {
|
|
88
|
-
let el = createElement(tag)
|
|
89
|
-
|
|
90
|
-
/* Set attributes */
|
|
91
|
-
attrs->Array.forEach(((key, value)) => {
|
|
92
|
-
el->setAttribute(key, value)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
/* Attach event listeners */
|
|
96
|
-
events->Array.forEach(((eventName, handler)) => {
|
|
97
|
-
el->addEventListener(eventName, handler)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
/* Append children */
|
|
101
|
-
children->Array.forEach(child => {
|
|
102
|
-
let childEl = render(child)
|
|
103
|
-
el->appendChild(childEl)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
el
|
|
107
|
-
}
|
|
108
|
-
| Fragment(children) => {
|
|
109
|
-
let fragment = createDocumentFragment()
|
|
110
|
-
children->Array.forEach(child => {
|
|
111
|
-
let childEl = render(child)
|
|
112
|
-
fragment->appendChild(childEl)
|
|
113
|
-
})
|
|
114
|
-
fragment
|
|
115
|
-
}
|
|
116
|
-
| SignalFragment(signal) => {
|
|
117
|
-
/* Create a container element to hold the dynamic children */
|
|
118
|
-
let container = createElement("div")
|
|
119
|
-
setAttribute(container, "data-signal-fragment", "true")
|
|
120
|
-
setAttribute(container, "style", "display: contents")
|
|
121
|
-
|
|
122
|
-
/* Set up effect to update children when signal changes */
|
|
123
|
-
let _ = Effect.run(() => {
|
|
124
|
-
let children = Signal.get(signal)
|
|
125
|
-
/* Clear existing children */
|
|
126
|
-
%raw(`container.innerHTML = ''`)
|
|
127
|
-
/* Render and append new children */
|
|
128
|
-
children->Array.forEach(child => {
|
|
129
|
-
let childEl = render(child)
|
|
130
|
-
container->appendChild(childEl)
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
container
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/* Mount a node to a container element */
|
|
140
|
-
let mount = (node: node, container: Dom.element): unit => {
|
|
141
|
-
let el = render(node)
|
|
142
|
-
container->appendChild(el)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/* Mount a node to a container by ID */
|
|
146
|
-
let mountById = (node: node, containerId: string): unit => {
|
|
147
|
-
switch getElementById(containerId)->Nullable.toOption {
|
|
148
|
-
| Some(container) => mount(node, container)
|
|
149
|
-
| None => Console.error("Container element not found: " ++ containerId)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Xote__Effect from "./Xote__Effect.res.mjs";
|
|
4
|
-
import * as Xote__Signal from "./Xote__Signal.res.mjs";
|
|
5
|
-
import * as Xote__Computed from "./Xote__Computed.res.mjs";
|
|
6
|
-
|
|
7
|
-
function text(content) {
|
|
8
|
-
return {
|
|
9
|
-
TAG: "Text",
|
|
10
|
-
_0: content
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function textSignal(signal) {
|
|
15
|
-
return {
|
|
16
|
-
TAG: "SignalText",
|
|
17
|
-
_0: signal
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function fragment(children) {
|
|
22
|
-
return {
|
|
23
|
-
TAG: "Fragment",
|
|
24
|
-
_0: children
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function signalFragment(signal) {
|
|
29
|
-
return {
|
|
30
|
-
TAG: "SignalFragment",
|
|
31
|
-
_0: signal
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function list(signal, renderItem) {
|
|
36
|
-
var nodesSignal = Xote__Computed.make(function () {
|
|
37
|
-
return Xote__Signal.get(signal).map(renderItem);
|
|
38
|
-
});
|
|
39
|
-
return {
|
|
40
|
-
TAG: "SignalFragment",
|
|
41
|
-
_0: nodesSignal
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function element(tag, attrsOpt, eventsOpt, childrenOpt, param) {
|
|
46
|
-
var attrs = attrsOpt !== undefined ? attrsOpt : [].map(function (x) {
|
|
47
|
-
return x;
|
|
48
|
-
});
|
|
49
|
-
var events = eventsOpt !== undefined ? eventsOpt : [].map(function (x) {
|
|
50
|
-
return x;
|
|
51
|
-
});
|
|
52
|
-
var children = childrenOpt !== undefined ? childrenOpt : [].map(function (x) {
|
|
53
|
-
return x;
|
|
54
|
-
});
|
|
55
|
-
return {
|
|
56
|
-
TAG: "Element",
|
|
57
|
-
tag: tag,
|
|
58
|
-
attrs: attrs,
|
|
59
|
-
events: events,
|
|
60
|
-
children: children
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function div(attrs, events, children, param) {
|
|
65
|
-
return element("div", attrs, events, children, undefined);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function span(attrs, events, children, param) {
|
|
69
|
-
return element("span", attrs, events, children, undefined);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function button(attrs, events, children, param) {
|
|
73
|
-
return element("button", attrs, events, children, undefined);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function input(attrs, events, param) {
|
|
77
|
-
return element("input", attrs, events, undefined, undefined);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function h1(attrs, events, children, param) {
|
|
81
|
-
return element("h1", attrs, events, children, undefined);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function h2(attrs, events, children, param) {
|
|
85
|
-
return element("h2", attrs, events, children, undefined);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function h3(attrs, events, children, param) {
|
|
89
|
-
return element("h3", attrs, events, children, undefined);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function p(attrs, events, children, param) {
|
|
93
|
-
return element("p", attrs, events, children, undefined);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function ul(attrs, events, children, param) {
|
|
97
|
-
return element("ul", attrs, events, children, undefined);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function li(attrs, events, children, param) {
|
|
101
|
-
return element("li", attrs, events, children, undefined);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function render(node) {
|
|
105
|
-
switch (node.TAG) {
|
|
106
|
-
case "Element" :
|
|
107
|
-
var el = document.createElement(node.tag);
|
|
108
|
-
node.attrs.forEach(function (param) {
|
|
109
|
-
el.setAttribute(param[0], param[1]);
|
|
110
|
-
});
|
|
111
|
-
node.events.forEach(function (param) {
|
|
112
|
-
el.addEventListener(param[0], param[1]);
|
|
113
|
-
});
|
|
114
|
-
node.children.forEach(function (child) {
|
|
115
|
-
var childEl = render(child);
|
|
116
|
-
el.appendChild(childEl);
|
|
117
|
-
});
|
|
118
|
-
return el;
|
|
119
|
-
case "Text" :
|
|
120
|
-
return document.createTextNode(node._0);
|
|
121
|
-
case "SignalText" :
|
|
122
|
-
var signal = node._0;
|
|
123
|
-
var el$1 = document.createTextNode(Xote__Signal.peek(signal));
|
|
124
|
-
Xote__Effect.run(function () {
|
|
125
|
-
var content = Xote__Signal.get(signal);
|
|
126
|
-
el$1.textContent = content;
|
|
127
|
-
});
|
|
128
|
-
return el$1;
|
|
129
|
-
case "Fragment" :
|
|
130
|
-
var fragment = document.createDocumentFragment();
|
|
131
|
-
node._0.forEach(function (child) {
|
|
132
|
-
var childEl = render(child);
|
|
133
|
-
fragment.appendChild(childEl);
|
|
134
|
-
});
|
|
135
|
-
return fragment;
|
|
136
|
-
case "SignalFragment" :
|
|
137
|
-
var signal$1 = node._0;
|
|
138
|
-
var container = document.createElement("div");
|
|
139
|
-
container.setAttribute("data-signal-fragment", "true");
|
|
140
|
-
container.setAttribute("style", "display: contents");
|
|
141
|
-
Xote__Effect.run(function () {
|
|
142
|
-
var children = Xote__Signal.get(signal$1);
|
|
143
|
-
((container.innerHTML = ''));
|
|
144
|
-
children.forEach(function (child) {
|
|
145
|
-
var childEl = render(child);
|
|
146
|
-
container.appendChild(childEl);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
return container;
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function mount(node, container) {
|
|
155
|
-
var el = render(node);
|
|
156
|
-
container.appendChild(el);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function mountById(node, containerId) {
|
|
160
|
-
var container = document.getElementById(containerId);
|
|
161
|
-
if (container == null) {
|
|
162
|
-
console.error("Container element not found: " + containerId);
|
|
163
|
-
return ;
|
|
164
|
-
} else {
|
|
165
|
-
return mount(node, container);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
var Signal;
|
|
170
|
-
|
|
171
|
-
var Effect;
|
|
172
|
-
|
|
173
|
-
var Core;
|
|
174
|
-
|
|
175
|
-
var Computed;
|
|
176
|
-
|
|
177
|
-
export {
|
|
178
|
-
Signal ,
|
|
179
|
-
Effect ,
|
|
180
|
-
Core ,
|
|
181
|
-
Computed ,
|
|
182
|
-
text ,
|
|
183
|
-
textSignal ,
|
|
184
|
-
fragment ,
|
|
185
|
-
signalFragment ,
|
|
186
|
-
list ,
|
|
187
|
-
element ,
|
|
188
|
-
div ,
|
|
189
|
-
span ,
|
|
190
|
-
button ,
|
|
191
|
-
input ,
|
|
192
|
-
h1 ,
|
|
193
|
-
h2 ,
|
|
194
|
-
h3 ,
|
|
195
|
-
p ,
|
|
196
|
-
ul ,
|
|
197
|
-
li ,
|
|
198
|
-
render ,
|
|
199
|
-
mount ,
|
|
200
|
-
mountById ,
|
|
201
|
-
}
|
|
202
|
-
/* No side effect */
|
package/src/Xote__Computed.res
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module IntSet = Belt.Set.Int
|
|
2
|
-
module IntMap = Belt.Map.Int
|
|
3
|
-
module Signal = Xote__Signal
|
|
4
|
-
module Core = Xote__Core
|
|
5
|
-
module Observer = Xote__Observer
|
|
6
|
-
module Id = Xote__Id
|
|
7
|
-
|
|
8
|
-
let make = (calc: unit => 'a): Core.t<'a> => {
|
|
9
|
-
/* create backing signal */
|
|
10
|
-
let s = Signal.make((Obj.magic(): 'a))
|
|
11
|
-
/* mark it as absent; force first compute */
|
|
12
|
-
let initialized = ref(false)
|
|
13
|
-
|
|
14
|
-
let id = Id.make()
|
|
15
|
-
let rec recompute = () => {
|
|
16
|
-
let next = calc()
|
|
17
|
-
if initialized.contents == false {
|
|
18
|
-
initialized := true
|
|
19
|
-
Signal.set(s, next)
|
|
20
|
-
} else {
|
|
21
|
-
Signal.set(s, next)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let rec o: Observer.t = {
|
|
26
|
-
id,
|
|
27
|
-
kind: #Computed(s.id),
|
|
28
|
-
run: recompute,
|
|
29
|
-
deps: IntSet.empty,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
Core.observers := IntMap.set(Core.observers.contents, id, o)
|
|
33
|
-
|
|
34
|
-
/* initial compute under tracking */
|
|
35
|
-
Core.clearDeps(o)
|
|
36
|
-
Core.currentObserverId := Some(id)
|
|
37
|
-
o.run()
|
|
38
|
-
Core.currentObserverId := None
|
|
39
|
-
|
|
40
|
-
/* When dependencies change, scheduler will run `recompute` which writes to s,
|
|
41
|
-
and that write will notify s's own dependents. */
|
|
42
|
-
s
|
|
43
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Xote__Id from "./Xote__Id.res.mjs";
|
|
4
|
-
import * as Xote__Core from "./Xote__Core.res.mjs";
|
|
5
|
-
import * as Belt_MapInt from "rescript/lib/es6/belt_MapInt.js";
|
|
6
|
-
import * as Xote__Signal from "./Xote__Signal.res.mjs";
|
|
7
|
-
|
|
8
|
-
function make(calc) {
|
|
9
|
-
var s = Xote__Signal.make();
|
|
10
|
-
var initialized = {
|
|
11
|
-
contents: false
|
|
12
|
-
};
|
|
13
|
-
var id = Xote__Id.make();
|
|
14
|
-
var recompute = function () {
|
|
15
|
-
var next = calc();
|
|
16
|
-
if (initialized.contents === false) {
|
|
17
|
-
initialized.contents = true;
|
|
18
|
-
return Xote__Signal.set(s, next);
|
|
19
|
-
} else {
|
|
20
|
-
return Xote__Signal.set(s, next);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
var o = {
|
|
24
|
-
id: id,
|
|
25
|
-
kind: {
|
|
26
|
-
NAME: "Computed",
|
|
27
|
-
VAL: s.id
|
|
28
|
-
},
|
|
29
|
-
run: recompute,
|
|
30
|
-
deps: undefined
|
|
31
|
-
};
|
|
32
|
-
Xote__Core.observers.contents = Belt_MapInt.set(Xote__Core.observers.contents, id, o);
|
|
33
|
-
Xote__Core.clearDeps(o);
|
|
34
|
-
Xote__Core.currentObserverId.contents = id;
|
|
35
|
-
o.run();
|
|
36
|
-
Xote__Core.currentObserverId.contents = undefined;
|
|
37
|
-
return s;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
var IntSet;
|
|
41
|
-
|
|
42
|
-
var IntMap;
|
|
43
|
-
|
|
44
|
-
var Signal;
|
|
45
|
-
|
|
46
|
-
var Core;
|
|
47
|
-
|
|
48
|
-
var Observer;
|
|
49
|
-
|
|
50
|
-
var Id;
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
IntSet ,
|
|
54
|
-
IntMap ,
|
|
55
|
-
Signal ,
|
|
56
|
-
Core ,
|
|
57
|
-
Observer ,
|
|
58
|
-
Id ,
|
|
59
|
-
make ,
|
|
60
|
-
}
|
|
61
|
-
/* No side effect */
|
package/src/Xote__Core.res
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
module IntSet = Belt.Set.Int
|
|
2
|
-
module IntMap = Belt.Map.Int
|
|
3
|
-
module Observer = Xote__Observer
|
|
4
|
-
module Id = Xote__Id
|
|
5
|
-
|
|
6
|
-
type t<'a> = {id: int, value: ref<'a>, version: ref<int>}
|
|
7
|
-
|
|
8
|
-
/* Global tables */
|
|
9
|
-
let observers: ref<IntMap.t<Observer.t>> = ref(IntMap.empty)
|
|
10
|
-
let signalObservers: ref<IntMap.t<IntSet.t>> = ref(IntMap.empty) /* signal id -> observer ids */
|
|
11
|
-
let signalPeeks: ref<IntSet.t> = ref(IntSet.empty) /* optional; for debugging */
|
|
12
|
-
|
|
13
|
-
/* Currently running observer for tracking */
|
|
14
|
-
let currentObserverId: ref<option<int>> = ref(None)
|
|
15
|
-
|
|
16
|
-
/* Simple scheduler */
|
|
17
|
-
let pending: ref<IntSet.t> = ref(IntSet.empty)
|
|
18
|
-
let batching = ref(false)
|
|
19
|
-
|
|
20
|
-
let ensureSignalBucket = (sid: int) => {
|
|
21
|
-
switch IntMap.get(signalObservers.contents, sid) {
|
|
22
|
-
| Some(_) => ()
|
|
23
|
-
| None => signalObservers := IntMap.set(signalObservers.contents, sid, IntSet.empty)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let addDep = (obsId: int, sid: int) => {
|
|
28
|
-
ensureSignalBucket(sid)
|
|
29
|
-
/* add obs -> dep */
|
|
30
|
-
let obs = Belt.Option.getExn(IntMap.get(observers.contents, obsId))
|
|
31
|
-
if currentObserverId.contents == Some(obsId) {
|
|
32
|
-
if obs.deps->IntSet.has(sid) == false {
|
|
33
|
-
obs.deps = obs.deps->IntSet.add(sid)
|
|
34
|
-
/* add dep -> obs */
|
|
35
|
-
let sset = Belt.Option.getExn(IntMap.get(signalObservers.contents, sid))
|
|
36
|
-
signalObservers := IntMap.set(signalObservers.contents, sid, sset->IntSet.add(obsId))
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let clearDeps = (obs: Observer.t) => {
|
|
42
|
-
/* remove obs from all signal buckets it was in */
|
|
43
|
-
obs.deps->IntSet.forEach(sid => {
|
|
44
|
-
switch IntMap.get(signalObservers.contents, sid) {
|
|
45
|
-
| None => ()
|
|
46
|
-
| Some(sset) =>
|
|
47
|
-
signalObservers := IntMap.set(signalObservers.contents, sid, sset->IntSet.remove(obs.id))
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
obs.deps = IntSet.empty
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let schedule = (obsId: int) => {
|
|
54
|
-
pending := pending.contents->IntSet.add(obsId)
|
|
55
|
-
if batching.contents == false {
|
|
56
|
-
/* flush immediately (sync microtask-ish) */
|
|
57
|
-
let toRun = pending.contents
|
|
58
|
-
pending := IntSet.empty
|
|
59
|
-
toRun->IntSet.forEach(id => {
|
|
60
|
-
switch IntMap.get(observers.contents, id) {
|
|
61
|
-
| None => ()
|
|
62
|
-
| Some(o) => {
|
|
63
|
-
/* re-track */
|
|
64
|
-
clearDeps(o)
|
|
65
|
-
currentObserverId := Some(id)
|
|
66
|
-
o.run()
|
|
67
|
-
currentObserverId := None
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let notify = (sid: int) => {
|
|
75
|
-
ensureSignalBucket(sid)
|
|
76
|
-
switch IntMap.get(signalObservers.contents, sid) {
|
|
77
|
-
| None => ()
|
|
78
|
-
| Some(sset) => sset->IntSet.forEach(schedule)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Run a function without tracking dependencies */
|
|
83
|
-
let untrack = (f: unit => 'a): 'a => {
|
|
84
|
-
let prev = currentObserverId.contents
|
|
85
|
-
currentObserverId := None
|
|
86
|
-
let r = f()
|
|
87
|
-
currentObserverId := prev
|
|
88
|
-
r
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* Batch: defer scheduling until after block ends */
|
|
92
|
-
let batch = (f: unit => 'a): 'a => {
|
|
93
|
-
let prev = batching.contents
|
|
94
|
-
batching := true
|
|
95
|
-
let r = f()
|
|
96
|
-
batching := prev
|
|
97
|
-
|
|
98
|
-
/* flush anything queued */
|
|
99
|
-
if pending.contents != IntSet.empty {
|
|
100
|
-
let toRun = pending.contents
|
|
101
|
-
pending := IntSet.empty
|
|
102
|
-
toRun->IntSet.forEach(id => schedule(id))
|
|
103
|
-
}
|
|
104
|
-
r
|
|
105
|
-
}
|
package/src/Xote__Core.res.mjs
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Caml_obj from "rescript/lib/es6/caml_obj.js";
|
|
4
|
-
import * as Belt_MapInt from "rescript/lib/es6/belt_MapInt.js";
|
|
5
|
-
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
|
|
6
|
-
import * as Belt_SetInt from "rescript/lib/es6/belt_SetInt.js";
|
|
7
|
-
import * as Caml_option from "rescript/lib/es6/caml_option.js";
|
|
8
|
-
|
|
9
|
-
var observers = {
|
|
10
|
-
contents: undefined
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
var signalObservers = {
|
|
14
|
-
contents: undefined
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
var signalPeeks = {
|
|
18
|
-
contents: undefined
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
var currentObserverId = {
|
|
22
|
-
contents: undefined
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
var pending = {
|
|
26
|
-
contents: undefined
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
var batching = {
|
|
30
|
-
contents: false
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function ensureSignalBucket(sid) {
|
|
34
|
-
var match = Belt_MapInt.get(signalObservers.contents, sid);
|
|
35
|
-
if (match !== undefined) {
|
|
36
|
-
return ;
|
|
37
|
-
} else {
|
|
38
|
-
signalObservers.contents = Belt_MapInt.set(signalObservers.contents, sid, undefined);
|
|
39
|
-
return ;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function addDep(obsId, sid) {
|
|
44
|
-
ensureSignalBucket(sid);
|
|
45
|
-
var obs = Belt_Option.getExn(Belt_MapInt.get(observers.contents, obsId));
|
|
46
|
-
if (!Caml_obj.equal(currentObserverId.contents, obsId)) {
|
|
47
|
-
return ;
|
|
48
|
-
}
|
|
49
|
-
if (Belt_SetInt.has(obs.deps, sid) !== false) {
|
|
50
|
-
return ;
|
|
51
|
-
}
|
|
52
|
-
obs.deps = Belt_SetInt.add(obs.deps, sid);
|
|
53
|
-
var sset = Belt_Option.getExn(Belt_MapInt.get(signalObservers.contents, sid));
|
|
54
|
-
signalObservers.contents = Belt_MapInt.set(signalObservers.contents, sid, Belt_SetInt.add(sset, obsId));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function clearDeps(obs) {
|
|
58
|
-
Belt_SetInt.forEach(obs.deps, (function (sid) {
|
|
59
|
-
var sset = Belt_MapInt.get(signalObservers.contents, sid);
|
|
60
|
-
if (sset !== undefined) {
|
|
61
|
-
signalObservers.contents = Belt_MapInt.set(signalObservers.contents, sid, Belt_SetInt.remove(Caml_option.valFromOption(sset), obs.id));
|
|
62
|
-
return ;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
}));
|
|
66
|
-
obs.deps = undefined;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function schedule(obsId) {
|
|
70
|
-
pending.contents = Belt_SetInt.add(pending.contents, obsId);
|
|
71
|
-
if (batching.contents !== false) {
|
|
72
|
-
return ;
|
|
73
|
-
}
|
|
74
|
-
var toRun = pending.contents;
|
|
75
|
-
pending.contents = undefined;
|
|
76
|
-
Belt_SetInt.forEach(toRun, (function (id) {
|
|
77
|
-
var o = Belt_MapInt.get(observers.contents, id);
|
|
78
|
-
if (o !== undefined) {
|
|
79
|
-
clearDeps(o);
|
|
80
|
-
currentObserverId.contents = id;
|
|
81
|
-
o.run();
|
|
82
|
-
currentObserverId.contents = undefined;
|
|
83
|
-
return ;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
}));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function notify(sid) {
|
|
90
|
-
ensureSignalBucket(sid);
|
|
91
|
-
var sset = Belt_MapInt.get(signalObservers.contents, sid);
|
|
92
|
-
if (sset !== undefined) {
|
|
93
|
-
return Belt_SetInt.forEach(Caml_option.valFromOption(sset), schedule);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function untrack(f) {
|
|
99
|
-
var prev = currentObserverId.contents;
|
|
100
|
-
currentObserverId.contents = undefined;
|
|
101
|
-
var r = f();
|
|
102
|
-
currentObserverId.contents = prev;
|
|
103
|
-
return r;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function batch(f) {
|
|
107
|
-
var prev = batching.contents;
|
|
108
|
-
batching.contents = true;
|
|
109
|
-
var r = f();
|
|
110
|
-
batching.contents = prev;
|
|
111
|
-
if (pending.contents !== undefined) {
|
|
112
|
-
var toRun = pending.contents;
|
|
113
|
-
pending.contents = undefined;
|
|
114
|
-
Belt_SetInt.forEach(toRun, (function (id) {
|
|
115
|
-
schedule(id);
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
return r;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
var IntSet;
|
|
122
|
-
|
|
123
|
-
var IntMap;
|
|
124
|
-
|
|
125
|
-
var Observer;
|
|
126
|
-
|
|
127
|
-
var Id;
|
|
128
|
-
|
|
129
|
-
export {
|
|
130
|
-
IntSet ,
|
|
131
|
-
IntMap ,
|
|
132
|
-
Observer ,
|
|
133
|
-
Id ,
|
|
134
|
-
observers ,
|
|
135
|
-
signalObservers ,
|
|
136
|
-
signalPeeks ,
|
|
137
|
-
currentObserverId ,
|
|
138
|
-
pending ,
|
|
139
|
-
batching ,
|
|
140
|
-
ensureSignalBucket ,
|
|
141
|
-
addDep ,
|
|
142
|
-
clearDeps ,
|
|
143
|
-
schedule ,
|
|
144
|
-
notify ,
|
|
145
|
-
untrack ,
|
|
146
|
-
batch ,
|
|
147
|
-
}
|
|
148
|
-
/* No side effect */
|