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.
@@ -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 */
@@ -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 */
@@ -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
- }
@@ -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 */