@manyducks.co/dolla 2.0.0-alpha.9 → 3.0.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/README.md +132 -573
- package/dist/core/context.d.ts +23 -0
- package/dist/core/debug.d.ts +19 -0
- package/dist/core/index.d.ts +24 -0
- package/dist/core/markup/helpers.d.ts +34 -0
- package/dist/core/markup/html.d.ts +3 -0
- package/dist/core/markup/html.test.d.ts +1 -0
- package/dist/core/markup/nodes/dom.d.ts +14 -0
- package/dist/core/markup/nodes/dynamic.d.ts +16 -0
- package/dist/core/markup/nodes/element.d.ts +14 -0
- package/dist/core/markup/nodes/portal.d.ts +15 -0
- package/dist/core/markup/nodes/repeat.d.ts +21 -0
- package/dist/core/markup/nodes/view.d.ts +17 -0
- package/dist/core/markup/scheduler.d.ts +1 -0
- package/dist/core/markup/types.d.ts +62 -0
- package/dist/core/markup/utils.d.ts +22 -0
- package/dist/core/markup/utils.test.d.ts +1 -0
- package/dist/core/ref.d.ts +13 -0
- package/dist/core/root.d.ts +36 -0
- package/dist/core/signals.d.ts +70 -0
- package/dist/core/signals.test.d.ts +1 -0
- package/dist/core/symbols.d.ts +2 -0
- package/dist/core-BLkJ-xuh.js +242 -0
- package/dist/core-BLkJ-xuh.js.map +1 -0
- package/dist/http/index.d.ts +43 -0
- package/dist/http.js +90 -0
- package/dist/http.js.map +1 -0
- package/dist/index.js +4 -1428
- package/dist/jsx-dev-runtime.d.ts +4 -2
- package/dist/jsx-dev-runtime.js +12 -16
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +5 -3
- package/dist/jsx-runtime.js +17 -18
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/router/index.d.ts +4 -0
- package/dist/router/matcher.test.d.ts +1 -0
- package/dist/router/router.d.ts +23 -0
- package/dist/router/router.test.d.ts +1 -0
- package/dist/router/store.d.ts +12 -0
- package/dist/router/types.d.ts +152 -0
- package/dist/router/utils.d.ts +99 -0
- package/dist/router/utils.test.d.ts +1 -0
- package/dist/router.js +429 -0
- package/dist/router.js.map +1 -0
- package/dist/signals-CMJPGr_M.js +354 -0
- package/dist/signals-CMJPGr_M.js.map +1 -0
- package/dist/translate/index.d.ts +82 -0
- package/dist/translate.js +125 -0
- package/dist/translate.js.map +1 -0
- package/dist/types.d.ts +83 -29
- package/dist/utils.d.ts +46 -12
- package/dist/utils.test.d.ts +1 -0
- package/dist/view-cBN-hn_T.js +360 -0
- package/dist/view-cBN-hn_T.js.map +1 -0
- package/dist/virtual/index.d.ts +1 -0
- package/dist/virtual/list.d.ts +53 -0
- package/index.d.ts +2 -2
- package/package.json +34 -17
- package/build.js +0 -34
- package/dist/index.d.ts +0 -21
- package/dist/index.js.map +0 -1
- package/dist/markup.d.ts +0 -108
- package/dist/modules/dolla.d.ts +0 -111
- package/dist/modules/http.d.ts +0 -57
- package/dist/modules/i18n.d.ts +0 -59
- package/dist/modules/render.d.ts +0 -17
- package/dist/modules/router.d.ts +0 -152
- package/dist/nodes/cond.d.ts +0 -26
- package/dist/nodes/html.d.ts +0 -31
- package/dist/nodes/observer.d.ts +0 -29
- package/dist/nodes/outlet.d.ts +0 -22
- package/dist/nodes/portal.d.ts +0 -19
- package/dist/nodes/repeat.d.ts +0 -34
- package/dist/nodes/text.d.ts +0 -19
- package/dist/passthrough-9kwwjgWk.js +0 -1279
- package/dist/passthrough-9kwwjgWk.js.map +0 -1
- package/dist/routing.d.ts +0 -79
- package/dist/state.d.ts +0 -101
- package/dist/typeChecking.d.ts +0 -191
- package/dist/view.d.ts +0 -65
- package/dist/views/default-crash-view.d.ts +0 -18
- package/dist/views/passthrough.d.ts +0 -5
- package/notes/context-vars.md +0 -21
- package/notes/readme-scratch.md +0 -222
- package/notes/route-middleware.md +0 -42
- package/notes/scratch.md +0 -233
- package/notes/views.md +0 -195
- package/tests/state.test.js +0 -135
- package/vite.config.js +0 -28
- /package/dist/{routing.test.d.ts → core/context.test.d.ts} +0 -0
package/notes/scratch.md
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
# Scratch Note
|
|
2
|
-
|
|
3
|
-
Bring the $ back and the name full circle.
|
|
4
|
-
|
|
5
|
-
```js
|
|
6
|
-
import { $, $$ } from "@manyducks.co/dolla";
|
|
7
|
-
|
|
8
|
-
// Shorthand dolla sign
|
|
9
|
-
|
|
10
|
-
// An initial value (with optional options object) creates a state.
|
|
11
|
-
const [$count, setCount] = $(0);
|
|
12
|
-
// = createState(0)
|
|
13
|
-
|
|
14
|
-
// An array and a function derives a state.
|
|
15
|
-
const $doubled = $([$count], (count) => count * 2);
|
|
16
|
-
// = derive([$count], (count) => count * 2);
|
|
17
|
-
|
|
18
|
-
// A state returns the same state.
|
|
19
|
-
const $sameCount = $.of($count);
|
|
20
|
-
const $wrapped = $.of({ message: "This is a state with no setter." });
|
|
21
|
-
// = toState($count)
|
|
22
|
-
|
|
23
|
-
// Get value from a state. Values that are not states are returned directly.
|
|
24
|
-
const count = $.value($count);
|
|
25
|
-
|
|
26
|
-
// An initial value creates a SettableState
|
|
27
|
-
const $$count = $$(5);
|
|
28
|
-
// = createSettableState(5);
|
|
29
|
-
|
|
30
|
-
// Merge state and setter into a SettableState
|
|
31
|
-
const $$count = $$($count, setCount);
|
|
32
|
-
// = toSettableState($count, setCount);
|
|
33
|
-
|
|
34
|
-
// Split a SettableState into a state and setter
|
|
35
|
-
const [$count, setCount] = $($$count);
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
What if Dolla was just a global object that you don't instantiate. I have never personally run into a use case for having more than one app on a page at once. In all my projects, the page and the app are synonymous.
|
|
41
|
-
|
|
42
|
-
Doing this would make it possible to access things inside the Dolla app from _outside_ code such as Quill blots. Effectively all code that has access to your Dolla import is _inside_ the app.
|
|
43
|
-
|
|
44
|
-
- Remove stores in favor of just exporting variables and functions from ES modules and importing them where desired.
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
```jsx
|
|
48
|
-
import Dolla from "@manyducks.co/dolla";
|
|
49
|
-
|
|
50
|
-
// Languages: add translation, set language and get localized string as a signal
|
|
51
|
-
Dolla.i18n.setup({
|
|
52
|
-
initialLanguage: Dolla.i18n.detect({ fallback: "ja" }), // Detect user's language and fall back to passed value
|
|
53
|
-
languages: [
|
|
54
|
-
{ name: "ja", path: "/static/locales/ja.json" },
|
|
55
|
-
{
|
|
56
|
-
name: "en",
|
|
57
|
-
fetch: async () => {
|
|
58
|
-
// Pass a path string, or if additional logic is needed, a fetch function.
|
|
59
|
-
const res = await Dolla.http.get("/static/locales/en.json");
|
|
60
|
-
return res.body;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
Dolla.i18n.$locale
|
|
67
|
-
Dolla.i18n.t$()
|
|
68
|
-
|
|
69
|
-
// A single setup call to keep things contained (must happen before mount)
|
|
70
|
-
Dolla.router.setup({
|
|
71
|
-
// Initial path must point to a route that actually exists (will be validated on mount) (initialPath is "/" by default)
|
|
72
|
-
initialPath: "/",
|
|
73
|
-
routes: [
|
|
74
|
-
{ path: "/", view: SomeView }
|
|
75
|
-
]
|
|
76
|
-
});
|
|
77
|
-
// And then you can route from anywhere.
|
|
78
|
-
Dolla.router.go("/some/path");
|
|
79
|
-
// Or get route information from anywhere.
|
|
80
|
-
Dolla.router.$path;
|
|
81
|
-
Dolla.router.$params;
|
|
82
|
-
|
|
83
|
-
// Also utils are available
|
|
84
|
-
const joinedPath = Dolla.router.utils.joinPath("/api/records", "5");
|
|
85
|
-
const resolvedPath = Dolla.router.utils.resolvePath("../"); // Resolves with window.location.href as the base
|
|
86
|
-
|
|
87
|
-
// Initializes the app and matches first route
|
|
88
|
-
Dolla.mount("#app");
|
|
89
|
-
// If you pass a view as the second argument it becomes the root view (this works for simple apps without a router)
|
|
90
|
-
Dolla.mount("#app", MyRootView);
|
|
91
|
-
// If router setup function wasn't called then the root view is mounted equivalent to the following:
|
|
92
|
-
Dolla.router.setup({
|
|
93
|
-
defaultPath: "/",
|
|
94
|
-
routes: [
|
|
95
|
-
{ path: "/*", view: MyRootView },
|
|
96
|
-
]
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Add HTTP middleware
|
|
100
|
-
Dolla.http.use(async (req, next) => {
|
|
101
|
-
const res = await next()
|
|
102
|
-
});
|
|
103
|
-
// Make HTTP calls
|
|
104
|
-
const res = await Dolla.get("/some/path");
|
|
105
|
-
|
|
106
|
-
// Adjust log level
|
|
107
|
-
Dolla.setLogLevel(Dolla.LOG_LEVEL_INFO);
|
|
108
|
-
Dolla.setLogFilter("*,-Dolla/*")
|
|
109
|
-
// Create a scoped logger
|
|
110
|
-
const debug = Dolla.createLogger("debug-logger");
|
|
111
|
-
debug.log("HELLO");
|
|
112
|
-
debug.warn("THIS IS A SCOPED LOGGER");
|
|
113
|
-
|
|
114
|
-
// Efficiently and safely read and mutate the DOM using Dolla's render batching
|
|
115
|
-
Dolla.render.read(() => {
|
|
116
|
-
// Reference DOM nodes
|
|
117
|
-
});
|
|
118
|
-
Dolla.render.update(() => {
|
|
119
|
-
// Mutate the DOM as part of Dolla's next batch
|
|
120
|
-
}, "some-key");
|
|
121
|
-
|
|
122
|
-
// Respond to lifecycle events
|
|
123
|
-
Dolla.onMount(() => {});
|
|
124
|
-
Dolla.onRouteMatch(() => {});
|
|
125
|
-
// Dolla.onWhatever(() => {});
|
|
126
|
-
|
|
127
|
-
interface SomeViewProps {}
|
|
128
|
-
|
|
129
|
-
function SomeView (props: SomeViewProps, ctx: Dolla.ViewContext) {
|
|
130
|
-
const debug = Dolla.createLogger("SomeView");
|
|
131
|
-
|
|
132
|
-
// returns a signal and a setter function
|
|
133
|
-
const [$someValue, setSomeValue] = Dolla.createState(4);
|
|
134
|
-
|
|
135
|
-
// Router is now a part of the Dolla object
|
|
136
|
-
Dolla.router.$path;
|
|
137
|
-
Dolla.router.$params;
|
|
138
|
-
|
|
139
|
-
Dolla.router.go("/some-other-path");
|
|
140
|
-
|
|
141
|
-
ctx.watch([$someValue], (value) => {
|
|
142
|
-
debug.log(value);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// View helpers are on ViewContext
|
|
146
|
-
ctx.repeat()
|
|
147
|
-
ctx.cond()
|
|
148
|
-
ctx.render([...states], (...values) => {
|
|
149
|
-
// return Renderable (equivalent to Dolla.derive(states, (...values) => Renderable))
|
|
150
|
-
})
|
|
151
|
-
ctx.portal()
|
|
152
|
-
ctx.outlet()
|
|
153
|
-
|
|
154
|
-
// TODO: Add Dolla.dialog.show() and Dolla.toast.show() or create separate libraries?
|
|
155
|
-
|
|
156
|
-
return <h1>{ctx.t$("home.headerText")}</h1>;
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
// import { signal, computed } from "@manyducks.co/dolla";
|
|
162
|
-
|
|
163
|
-
function signal(initialValue, options = {}) {}
|
|
164
|
-
|
|
165
|
-
function computed();
|
|
166
|
-
|
|
167
|
-
function WhateverView(props, c) {
|
|
168
|
-
// IDEA: Have state, computed and effect be methods on the view context.
|
|
169
|
-
// PROBLEM: Then what are the types when passing as props? State? ComputedState? or just a generic Dynamic<T> for readable/writable?
|
|
170
|
-
|
|
171
|
-
// Context variables (replacement for stores)
|
|
172
|
-
c.set("name", {
|
|
173
|
-
value: 5,
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Can get in the same context scope or on a child.
|
|
177
|
-
// Throws an error if value is not set.
|
|
178
|
-
c.get("name");
|
|
179
|
-
|
|
180
|
-
// If we use this $readable and set function pattern then we only have a single type of signal which is read-only.
|
|
181
|
-
// Take props as Signal<T> and deal with setting in callbacks. Simple and predictable.
|
|
182
|
-
const [$count, setCount] = signal(5);
|
|
183
|
-
|
|
184
|
-
const $doubled = derived($count, (value) => value * 2);
|
|
185
|
-
|
|
186
|
-
// const watcher = new SignalWatcher($count, (value) => {
|
|
187
|
-
// c.debug.log("watcher received value: " + value);
|
|
188
|
-
// });
|
|
189
|
-
|
|
190
|
-
// watcher.start();
|
|
191
|
-
// watcher.stop();
|
|
192
|
-
|
|
193
|
-
$count.get(); // returns the current value
|
|
194
|
-
setCount(10); // updates the value
|
|
195
|
-
|
|
196
|
-
// Observe and trigger side effects.
|
|
197
|
-
c.watch($count, (count) => {
|
|
198
|
-
c.debug.log(`The value of count is: ${count}`);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Exposes language and translation tools.
|
|
202
|
-
c.i18n.translate$("");
|
|
203
|
-
c.i18n.$language;
|
|
204
|
-
c.i18n.setLanguage();
|
|
205
|
-
|
|
206
|
-
// Exposes internal HTTP client.
|
|
207
|
-
c.http.get(); // put, post, patch, delete, etc.
|
|
208
|
-
|
|
209
|
-
// Exposes router helpers and variables.
|
|
210
|
-
c.route.go("/");
|
|
211
|
-
c.route.$params;
|
|
212
|
-
c.route.$path;
|
|
213
|
-
c.route.$query;
|
|
214
|
-
c.route.setQuery({
|
|
215
|
-
value: 1,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
return html`
|
|
219
|
-
<h1>This is the template</h1>
|
|
220
|
-
|
|
221
|
-
${render($count, (count) => {
|
|
222
|
-
// Render rebuilds the markup within when any of the dependencies change.
|
|
223
|
-
return html`<p>The count is ${count}</p>`;
|
|
224
|
-
|
|
225
|
-
// Other view helpers are also provided as exports
|
|
226
|
-
repeat();
|
|
227
|
-
cond();
|
|
228
|
-
outlet();
|
|
229
|
-
portal();
|
|
230
|
-
})}
|
|
231
|
-
`;
|
|
232
|
-
}
|
|
233
|
-
```
|
package/notes/views.md
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
```tsx
|
|
2
|
-
import { View, Store, $, $$ } from "@manyducks.co/dolla";
|
|
3
|
-
|
|
4
|
-
const SomeView = View("SomeView")
|
|
5
|
-
.props((t) => ({
|
|
6
|
-
// Define prop types which are validated and enforced in dev mode
|
|
7
|
-
$name: t.readable(t.string().optional()).optional(), // Readable<string | undefined> | undefined
|
|
8
|
-
}))
|
|
9
|
-
.build(({ $name }, ctx) => {
|
|
10
|
-
const { $value } = ctx.getStore(SomeStore);
|
|
11
|
-
|
|
12
|
-
return <h1>Hello {$name}</h1>;
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const SomeStore = Store("SomeStore").build((ctx) => {
|
|
16
|
-
const $$value = $$(0);
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
$value: $($$value),
|
|
20
|
-
};
|
|
21
|
-
});
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Thoughts on reintegrating core APIs instead of keeping them as modular stores. In the case of things like the router, they really are core pieces masquerading as modular stores.
|
|
25
|
-
|
|
26
|
-
```tsx
|
|
27
|
-
import { App } from "@manyducks.co/dolla";
|
|
28
|
-
|
|
29
|
-
const app = new App();
|
|
30
|
-
|
|
31
|
-
// All routes are defined using a config object.
|
|
32
|
-
app.route({
|
|
33
|
-
path: "/",
|
|
34
|
-
view: RootView,
|
|
35
|
-
routes: [
|
|
36
|
-
{ path: "/example", view: ExampleView },
|
|
37
|
-
{ path: "/notes", view: NotesView },
|
|
38
|
-
],
|
|
39
|
-
});
|
|
40
|
-
app.route({ path: "*", redirect: "/example" });
|
|
41
|
-
|
|
42
|
-
function SomeView(props, ctx) {
|
|
43
|
-
// Route info and routing are exposed on ctx.route
|
|
44
|
-
ctx.route.go("/some-route", { preserveQuery: true, replace: false });
|
|
45
|
-
ctx.route.back();
|
|
46
|
-
ctx.route.forward();
|
|
47
|
-
ctx.route.$$query;
|
|
48
|
-
ctx.route.$params;
|
|
49
|
-
ctx.route.$path;
|
|
50
|
-
ctx.route.$pattern;
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
HTTP is also integrated:
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
function SomeView(props, ctx) {
|
|
58
|
-
ctx.http.get("/some-route");
|
|
59
|
-
ctx.http.post("/some-route", { body: { data: 123 } });
|
|
60
|
-
// ... and the rest of the methods
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Language support is also integrated:
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
const app = new App();
|
|
68
|
-
|
|
69
|
-
app.language({ name: "en", path: "/locale/en.json" });
|
|
70
|
-
app.language({ name: "ja", path: "/locale/ja.json" });
|
|
71
|
-
app.setLanguage("en" /* or localStorage.getItem("appLanguage") or something */);
|
|
72
|
-
|
|
73
|
-
function SomeView(props, ctx) {
|
|
74
|
-
ctx.language.$current;
|
|
75
|
-
ctx.language.set("ja");
|
|
76
|
-
ctx.language.translate$("some.key");
|
|
77
|
-
|
|
78
|
-
// I would probably use it like this:
|
|
79
|
-
const { translate$ } = ctx.language;
|
|
80
|
-
translate$("some.key");
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Stores vs Context variables
|
|
85
|
-
|
|
86
|
-
Now, these are going away as stores. But what about stores? I'd like to replace stores with context variables, like this:
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
function ParentView(props, ctx) {
|
|
90
|
-
// Variables can be set in this context...
|
|
91
|
-
|
|
92
|
-
ctx.set("fixedValue", 1);
|
|
93
|
-
|
|
94
|
-
const $$writable = $$(2);
|
|
95
|
-
ctx.set("$$writableValue", $$writable);
|
|
96
|
-
|
|
97
|
-
return <ChildView />;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function ChildView(props, ctx) {
|
|
101
|
-
// ... and accessed in a child context.
|
|
102
|
-
|
|
103
|
-
const fixed = ctx.get<number>("fixedValue"); // 1
|
|
104
|
-
const $$writable = ctx.get<Writable<number>>("$$writableValue"); // Writable(2)
|
|
105
|
-
|
|
106
|
-
// Overriding values will not change parent variables. This value will take effect for this context and any child context.
|
|
107
|
-
ctx.set("fixedValue", 2);
|
|
108
|
-
|
|
109
|
-
return <span>{$$writable}</span>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Now stores can be written and used in this way:
|
|
113
|
-
|
|
114
|
-
function ExampleStore(ctx: ViewContext) {
|
|
115
|
-
ctx.beforeConnect(() => {
|
|
116
|
-
// Takes whatever arguments, in this case the ViewContext itself so it can attach lifecycle methods.
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
whatever: 1,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function ExampleView(props, ctx) {
|
|
125
|
-
ctx.set("example", ExampleStore(ctx));
|
|
126
|
-
|
|
127
|
-
// ...
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function ChildView(props, ctx) {
|
|
131
|
-
const example = ctx.get("example");
|
|
132
|
-
|
|
133
|
-
ctx.log(example.whatever); // prints: 1
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Or as a class-based thing:
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
import { View, Store } from "@manyducks.co/dolla";
|
|
141
|
-
|
|
142
|
-
// Stores are basically an observable map with computed properties.
|
|
143
|
-
const example = new Store({
|
|
144
|
-
computed: {
|
|
145
|
-
uppercased: (values) => values.key.toUpperCase(),
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Set one or more properties at a time.
|
|
150
|
-
example.set({ key: "value" });
|
|
151
|
-
|
|
152
|
-
// Get the current value of any property by name.
|
|
153
|
-
example.get("uppercased"); // "VALUE"
|
|
154
|
-
|
|
155
|
-
// Can subscribe to specific entries.
|
|
156
|
-
const sub = example.subscribe("key", (value) => {
|
|
157
|
-
// Do something when value changes.
|
|
158
|
-
});
|
|
159
|
-
sub.unsubscribe();
|
|
160
|
-
sub.current; // get current value of entry
|
|
161
|
-
|
|
162
|
-
// Get a two-way binding to a particular entry.
|
|
163
|
-
const bound = example.bind("key");
|
|
164
|
-
const boundSub = bound.subscribe((value) => {
|
|
165
|
-
// Do something when value changes.
|
|
166
|
-
});
|
|
167
|
-
boundSub.current; // get current value
|
|
168
|
-
bound.get();
|
|
169
|
-
bound.set(newValue);
|
|
170
|
-
|
|
171
|
-
// Now Views have a state that works in a very similar way.
|
|
172
|
-
|
|
173
|
-
interface ExampleViewState {
|
|
174
|
-
whatever: number;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
interface ExampleViewProps {
|
|
178
|
-
// Bound values can be passed as props to child views.
|
|
179
|
-
something: Bound<string>;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
class ExampleView extends View<ExampleViewState, ExampleViewProps> {
|
|
183
|
-
create() {
|
|
184
|
-
this.state.set({ whatever: 5 });
|
|
185
|
-
|
|
186
|
-
const whatever = this.state.bind("whatever");
|
|
187
|
-
|
|
188
|
-
this.state.subscribe("whatever", (value) => {
|
|
189
|
-
// This will be automatically cleaned up when the view is disconnected.
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
this.props.something.get(); // get the value of the bound prop.
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
```
|
package/tests/state.test.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert";
|
|
3
|
-
|
|
4
|
-
import { createState, derive } from "../dist/index.js";
|
|
5
|
-
|
|
6
|
-
test("signal", (t) => {
|
|
7
|
-
const [$count, setCount] = createState(5);
|
|
8
|
-
|
|
9
|
-
const defaultWatcher = t.mock.fn();
|
|
10
|
-
const stopDefault = $count.watch(defaultWatcher);
|
|
11
|
-
|
|
12
|
-
const lazyWatcher = t.mock.fn();
|
|
13
|
-
const stopLazy = $count.watch(lazyWatcher, { lazy: true });
|
|
14
|
-
|
|
15
|
-
assert.equal(defaultWatcher.mock.callCount(), 1, "watcher is called immediately by default");
|
|
16
|
-
assert.equal(lazyWatcher.mock.callCount(), 0, "lazy watcher is not called immediately");
|
|
17
|
-
|
|
18
|
-
assert.equal($count.get(), 5, "get returns the initial value");
|
|
19
|
-
|
|
20
|
-
setCount(12);
|
|
21
|
-
|
|
22
|
-
assert.equal($count.get(), 12, "setter updates the signal value");
|
|
23
|
-
|
|
24
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher is called");
|
|
25
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher is called");
|
|
26
|
-
|
|
27
|
-
assert.deepStrictEqual(defaultWatcher.mock.calls[1].arguments, [12], "default watcher is called with new value");
|
|
28
|
-
assert.deepStrictEqual(lazyWatcher.mock.calls[0].arguments, [12], "lazy watcher is called with new value");
|
|
29
|
-
|
|
30
|
-
setCount(12);
|
|
31
|
-
|
|
32
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher was not called with same value");
|
|
33
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher was not called with same value");
|
|
34
|
-
|
|
35
|
-
stopDefault();
|
|
36
|
-
stopLazy();
|
|
37
|
-
|
|
38
|
-
setCount(51);
|
|
39
|
-
|
|
40
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher has not been called again");
|
|
41
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher has not been called again");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("derive", (t) => {
|
|
45
|
-
const [$one, setOne] = createState(5);
|
|
46
|
-
const [$two, setTwo] = createState(20);
|
|
47
|
-
|
|
48
|
-
const deriveSum = t.mock.fn((one, two) => one + two);
|
|
49
|
-
const deriveProduct = t.mock.fn((one, two) => one * two);
|
|
50
|
-
|
|
51
|
-
const $sum = derive([$one, $two], deriveSum);
|
|
52
|
-
const $product = derive([$one, $two], deriveProduct);
|
|
53
|
-
|
|
54
|
-
assert.equal($sum.get(), 25, "sum is calculated correctly");
|
|
55
|
-
assert.equal($product.get(), 100, "product is calculated correctly");
|
|
56
|
-
|
|
57
|
-
assert.equal(deriveSum.mock.callCount(), 1, "derive function has only been called once");
|
|
58
|
-
|
|
59
|
-
$sum.get();
|
|
60
|
-
$sum.get();
|
|
61
|
-
$sum.get();
|
|
62
|
-
|
|
63
|
-
assert.equal(deriveSum.mock.callCount(), 1, "derive function still only called once as dependencies haven't changed");
|
|
64
|
-
|
|
65
|
-
const defaultWatcher = t.mock.fn();
|
|
66
|
-
const stopDefault = $sum.watch(defaultWatcher);
|
|
67
|
-
|
|
68
|
-
const lazyWatcher = t.mock.fn();
|
|
69
|
-
const stopLazy = $product.watch(lazyWatcher, { lazy: true });
|
|
70
|
-
|
|
71
|
-
assert.equal(defaultWatcher.mock.callCount(), 1, "default watcher has been called");
|
|
72
|
-
assert.equal(lazyWatcher.mock.callCount(), 0, "lazy watcher has not been called yet");
|
|
73
|
-
|
|
74
|
-
assert.deepStrictEqual(defaultWatcher.mock.calls[0].arguments, [25], "default watcher was called with initial value");
|
|
75
|
-
|
|
76
|
-
setOne(6);
|
|
77
|
-
|
|
78
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher has been called");
|
|
79
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher has been called");
|
|
80
|
-
|
|
81
|
-
assert.deepStrictEqual(defaultWatcher.mock.calls[1].arguments, [26], "default watcher was called with new value");
|
|
82
|
-
assert.deepStrictEqual(lazyWatcher.mock.calls[0].arguments, [120], "lazy watcher was called with new value");
|
|
83
|
-
|
|
84
|
-
setTwo(20);
|
|
85
|
-
|
|
86
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher was not called with same value");
|
|
87
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher was not called with same value");
|
|
88
|
-
|
|
89
|
-
stopDefault();
|
|
90
|
-
stopLazy();
|
|
91
|
-
|
|
92
|
-
setOne(4);
|
|
93
|
-
|
|
94
|
-
assert.equal(defaultWatcher.mock.callCount(), 2, "default watcher was not called after stop");
|
|
95
|
-
assert.equal(lazyWatcher.mock.callCount(), 1, "lazy watcher was not called after stop");
|
|
96
|
-
|
|
97
|
-
assert.equal($sum.get(), 24, "sum is derived correctly");
|
|
98
|
-
assert.equal($product.get(), 80, "product is derived correctly");
|
|
99
|
-
|
|
100
|
-
assert.equal(deriveSum.mock.callCount(), 3, "sum has only been derived three times");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("derive nested signals", (t) => {
|
|
104
|
-
const [$value, setValue] = createState(5);
|
|
105
|
-
|
|
106
|
-
const [$object, setObject] = createState({
|
|
107
|
-
href: derive([$value], (value) => `/projects/${value}/test`),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// o.href here is itself a derived value
|
|
111
|
-
const $href = derive([$object], (o) => o.href);
|
|
112
|
-
|
|
113
|
-
const watcher = t.mock.fn();
|
|
114
|
-
const stop = $href.watch(watcher);
|
|
115
|
-
|
|
116
|
-
assert.equal(watcher.mock.callCount(), 1);
|
|
117
|
-
assert.deepStrictEqual(watcher.mock.calls[0].arguments, ["/projects/5/test"]);
|
|
118
|
-
|
|
119
|
-
// Update value which href depends on.
|
|
120
|
-
setValue(12);
|
|
121
|
-
|
|
122
|
-
assert.equal(watcher.mock.callCount(), 2);
|
|
123
|
-
assert.deepStrictEqual(watcher.mock.calls[1].arguments, ["/projects/12/test"]);
|
|
124
|
-
|
|
125
|
-
// Now set the original object and replace the derived href.
|
|
126
|
-
// See that watcher still receives the latest value.
|
|
127
|
-
setObject({
|
|
128
|
-
href: derive([$value], (value) => `/projects/${value}/changed`),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
assert.equal(watcher.mock.callCount(), 3);
|
|
132
|
-
assert.deepStrictEqual(watcher.mock.calls[2].arguments, ["/projects/12/changed"]);
|
|
133
|
-
|
|
134
|
-
stop();
|
|
135
|
-
});
|
package/vite.config.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { defineConfig } from "vite";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
build: {
|
|
6
|
-
// minify: "terser",
|
|
7
|
-
// minify: false,
|
|
8
|
-
sourcemap: true,
|
|
9
|
-
|
|
10
|
-
lib: {
|
|
11
|
-
entry: {
|
|
12
|
-
index: resolve(__dirname, "src/index.ts"),
|
|
13
|
-
"jsx-runtime": resolve(__dirname, "src/jsx-runtime.js"),
|
|
14
|
-
"jsx-dev-runtime": resolve(__dirname, "src/jsx-dev-runtime.js"),
|
|
15
|
-
},
|
|
16
|
-
name: "Dolla",
|
|
17
|
-
formats: ["es"],
|
|
18
|
-
},
|
|
19
|
-
// rollupOptions: {
|
|
20
|
-
// external: ["vue"],
|
|
21
|
-
// output: {
|
|
22
|
-
// globals: {
|
|
23
|
-
// vue: "Vue",
|
|
24
|
-
// },
|
|
25
|
-
// },
|
|
26
|
-
// },
|
|
27
|
-
},
|
|
28
|
-
});
|
|
File without changes
|