@manyducks.co/dolla 2.0.0 → 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 +133 -284
- package/dist/core/context.d.ts +22 -146
- package/dist/core/debug.d.ts +19 -0
- package/dist/core/index.d.ts +15 -16
- package/dist/core/markup/helpers.d.ts +34 -0
- package/dist/core/markup/html.d.ts +3 -0
- package/dist/core/{nodes → markup/nodes}/dom.d.ts +5 -4
- 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/ref.d.ts +6 -12
- package/dist/core/root.d.ts +36 -0
- package/dist/core/signals.d.ts +46 -76
- 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 +21 -33
- package/dist/http.js +89 -149
- package/dist/http.js.map +1 -1
- package/dist/index.js +4 -174
- package/dist/jsx-dev-runtime.d.ts +4 -3
- package/dist/jsx-dev-runtime.js +12 -9
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +5 -4
- package/dist/jsx-runtime.js +17 -12
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/router/index.d.ts +4 -3
- package/dist/router/router.d.ts +19 -162
- 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 +428 -5
- package/dist/router.js.map +1 -1
- 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 +21 -39
- package/dist/utils.d.ts +41 -29
- 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/package.json +19 -16
- package/dist/core/app.d.ts +0 -24
- package/dist/core/env.d.ts +0 -3
- package/dist/core/hooks.d.ts +0 -70
- package/dist/core/logger.d.ts +0 -42
- package/dist/core/logger.test.d.ts +0 -0
- package/dist/core/markup.d.ts +0 -82
- package/dist/core/markup.test.d.ts +0 -0
- package/dist/core/nodes/_markup.d.ts +0 -36
- package/dist/core/nodes/dynamic.d.ts +0 -22
- package/dist/core/nodes/element.d.ts +0 -27
- package/dist/core/nodes/portal.d.ts +0 -18
- package/dist/core/nodes/repeat.d.ts +0 -27
- package/dist/core/nodes/view.d.ts +0 -25
- package/dist/core/views/default-crash-view.d.ts +0 -25
- package/dist/core/views/for.d.ts +0 -21
- package/dist/core/views/fragment.d.ts +0 -7
- package/dist/core/views/portal.d.ts +0 -16
- package/dist/core/views/show.d.ts +0 -25
- package/dist/fragment-BahD_BJA.js +0 -7
- package/dist/fragment-BahD_BJA.js.map +0 -1
- package/dist/i18n/index.d.ts +0 -134
- package/dist/i18n.js +0 -309
- package/dist/i18n.js.map +0 -1
- package/dist/index-DRJlxs-Q.js +0 -535
- package/dist/index-DRJlxs-Q.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger-Aqi9m1CF.js +0 -565
- package/dist/logger-Aqi9m1CF.js.map +0 -1
- package/dist/markup-8jNhoqDe.js +0 -1089
- package/dist/markup-8jNhoqDe.js.map +0 -1
- package/dist/router/hooks.d.ts +0 -2
- package/dist/router/router.utils.d.ts +0 -93
- package/dist/typeChecking-5kmX0ulW.js +0 -65
- package/dist/typeChecking-5kmX0ulW.js.map +0 -1
- package/dist/typeChecking.d.ts +0 -95
- package/docs/buildless.md +0 -132
- package/docs/components.md +0 -238
- package/docs/hooks.md +0 -356
- package/docs/http.md +0 -178
- package/docs/i18n.md +0 -220
- package/docs/index.md +0 -10
- package/docs/markup.md +0 -136
- package/docs/mixins.md +0 -176
- package/docs/ref.md +0 -77
- package/docs/router.md +0 -281
- package/docs/setup.md +0 -137
- package/docs/signals.md +0 -262
- package/docs/stores.md +0 -113
- package/docs/views.md +0 -356
- package/notes/atomic.md +0 -452
- package/notes/elimination.md +0 -33
- package/notes/observable.md +0 -180
- package/notes/scratch.md +0 -565
- package/notes/splitting.md +0 -5
- package/notes/views.md +0 -195
- package/vite.config.js +0 -22
- /package/dist/core/{hooks.test.d.ts → markup/html.test.d.ts} +0 -0
- /package/dist/core/{ref.test.d.ts → markup/utils.test.d.ts} +0 -0
- /package/dist/router/{router.utils.test.d.ts → matcher.test.d.ts} +0 -0
- /package/dist/{typeChecking.test.d.ts → router/router.test.d.ts} +0 -0
package/notes/scratch.md
DELETED
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
# Scratch Note
|
|
2
|
-
|
|
3
|
-
## Dolla Custom Elements
|
|
4
|
-
|
|
5
|
-
Define custom HTML elements with Dolla.
|
|
6
|
-
|
|
7
|
-
```jsx
|
|
8
|
-
defineElement("my-counter", {
|
|
9
|
-
props: {
|
|
10
|
-
// Defines props and their types
|
|
11
|
-
},
|
|
12
|
-
setup: (props, ctx) => {
|
|
13
|
-
// Returns markup (this is a view)
|
|
14
|
-
return html` <div></div> `;
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Rendering standalone elements outside an app
|
|
22
|
-
|
|
23
|
-
Library needs to be easier to render standalone elements. Idea to replace constructView and a lot of the store management weirdness with a `createContext` function and a `render` function that takes markup and a context.
|
|
24
|
-
|
|
25
|
-
The context is basically a refactor of the old ElementContext and serves the same purpose.
|
|
26
|
-
|
|
27
|
-
```jsx
|
|
28
|
-
import { m, render, createContext } from "@manyducks.co/dolla";
|
|
29
|
-
|
|
30
|
-
const context = createContext();
|
|
31
|
-
context.addStore(SomeStore);
|
|
32
|
-
|
|
33
|
-
function ExampleView(props, ctx) {
|
|
34
|
-
// Views now access the Context directly.
|
|
35
|
-
const store = ctx.getStore(SomeStore);
|
|
36
|
-
|
|
37
|
-
return <h1>Hello World</h1>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const element = render(ExampleView, context);
|
|
41
|
-
|
|
42
|
-
element.mount(document.body);
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Monomorphic app context
|
|
48
|
-
|
|
49
|
-
> Replaces StoreContext, ViewContext, etc.
|
|
50
|
-
|
|
51
|
-
Routes are baked into the app once again, but
|
|
52
|
-
|
|
53
|
-
```jsx
|
|
54
|
-
import { createRoot } from "@manyducks.co/dolla";
|
|
55
|
-
import { example } from "./stores/example.js";
|
|
56
|
-
|
|
57
|
-
const root = createRoot();
|
|
58
|
-
|
|
59
|
-
root.use(example());
|
|
60
|
-
|
|
61
|
-
async function auth(_, state, redirect) {
|
|
62
|
-
// route context
|
|
63
|
-
// Routes run through each callback until one resolves to a renderable value.
|
|
64
|
-
// If redirect is called, the route is re-matched and no further callbacks are run for this route.
|
|
65
|
-
|
|
66
|
-
if (state.auth == null) {
|
|
67
|
-
redirect("/login");
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
root.route("/users/*", auth, (C) => {
|
|
72
|
-
C.route("/{#id}/*", (C) => {
|
|
73
|
-
C.route("/", (C) => <UserDetailRoute userId={C.params.id} />);
|
|
74
|
-
C.route("*", "./");
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
root.route("/users/*", auth, (route) => {
|
|
79
|
-
route("/{#id}/*", (route) => {
|
|
80
|
-
// TODO: It's possible to reference the wrong 'route'
|
|
81
|
-
// Track active context and throw error if the one you call belongs to the wrong context?
|
|
82
|
-
route("/", (_, state) => <UserDetailView userId={state.params.id} />);
|
|
83
|
-
route("*", "./");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
function ExampleView(props, ctx) {
|
|
88
|
-
// ctx.routes returns a special type of outlet that renders children based on
|
|
89
|
-
// the route segments that come after the ones at this ctx.
|
|
90
|
-
|
|
91
|
-
// The weakness of this idea is that routes can't be validated without initializing views.
|
|
92
|
-
return (
|
|
93
|
-
<div>
|
|
94
|
-
<Suspense fallback={<span>Loading...</span>}>
|
|
95
|
-
{ctx.routes((route) => {
|
|
96
|
-
route("/subroute", () => <OtherView />);
|
|
97
|
-
|
|
98
|
-
// Routes can be async.
|
|
99
|
-
route("/other", () => import("some-module"));
|
|
100
|
-
})}
|
|
101
|
-
</Suspense>
|
|
102
|
-
</div>
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Also Suspense. This can be simply implemented with events.
|
|
106
|
-
ctx.emit("suspense:begin", uniqueId);
|
|
107
|
-
// Then when done:
|
|
108
|
-
ctx.emit("suspense:end", uniqueId);
|
|
109
|
-
|
|
110
|
-
// The nearest Suspense view will track ids which are in suspense and show fallback content in the meantime.
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function Suspense(props, ctx) {
|
|
114
|
-
const [$tracked, setTracked] = createState({});
|
|
115
|
-
|
|
116
|
-
ctx.on("suspense:begin", (e) => {
|
|
117
|
-
setTracked((tracked) => {
|
|
118
|
-
return {
|
|
119
|
-
...tracked,
|
|
120
|
-
[e.detail]: new Date(),
|
|
121
|
-
};
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
ctx.on("suspense:end", (e) => {
|
|
126
|
-
setTracked((tracked) => {
|
|
127
|
-
const updated = Object.assign({}, tracked);
|
|
128
|
-
delete updated[e.detail];
|
|
129
|
-
return updated;
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// TODO: Hide suspended view without unmounting it. This might take special logic.
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Can also pass markup directly if you don't need the context.
|
|
137
|
-
root.route("/", auth, <HomeRoute />);
|
|
138
|
-
|
|
139
|
-
// Static redirect.
|
|
140
|
-
root.route("*", "/");
|
|
141
|
-
|
|
142
|
-
// Programmatic redirect.
|
|
143
|
-
root.route("*", (C) => {
|
|
144
|
-
C.log("hit wildcard");
|
|
145
|
-
C.redirect("/");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
root.mount(document.body);
|
|
149
|
-
|
|
150
|
-
// generate an HTML string for server side rendering.
|
|
151
|
-
root.toString("/some/path");
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
```js
|
|
157
|
-
class ClockStore extends Store {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
constructor() {
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
class CounterStore extends Store {
|
|
166
|
-
// Could have better name. This will catch any
|
|
167
|
-
// this.emit('counter:increment') or this.emit('counter:decrement') calls
|
|
168
|
-
// and update the state according to these functions.
|
|
169
|
-
value = new Emittable('counter', 0, {
|
|
170
|
-
increment: state => state + 1,
|
|
171
|
-
decrement: state => state - 1
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
type CounterEvents = {
|
|
176
|
-
increment: [amount: number];
|
|
177
|
-
decrement: [amount: number];
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
Bring the $ back and the name full circle.
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
import { $, $$ } from "@manyducks.co/dolla";
|
|
190
|
-
|
|
191
|
-
// Shorthand dolla sign
|
|
192
|
-
|
|
193
|
-
// An initial value (with optional options object) creates a state.
|
|
194
|
-
const [$count, setCount] = $(0);
|
|
195
|
-
// = createState(0)
|
|
196
|
-
|
|
197
|
-
// An array and a function derives a state.
|
|
198
|
-
const $doubled = $.map([$count], (count) => count * 2);
|
|
199
|
-
// = derive([$count], (count) => count * 2);
|
|
200
|
-
|
|
201
|
-
// A state returns the same state.
|
|
202
|
-
const $sameCount = $.from($count);
|
|
203
|
-
const $wrapped = $.from({ message: "This is a state with no setter." });
|
|
204
|
-
// = toState($count)
|
|
205
|
-
|
|
206
|
-
// Get value from a state. Values that are not states are returned directly.
|
|
207
|
-
const count = $.get($count);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
What about other operators like RxJS?
|
|
211
|
-
|
|
212
|
-
```js
|
|
213
|
-
// These would be functionally equivalent.
|
|
214
|
-
const $doubled = $count.pipe($.map((count) => count * 2));
|
|
215
|
-
const $doubled = $.map([$count], (count) => count * 2);
|
|
216
|
-
|
|
217
|
-
// Chainable. Get doubled value, but only update if it's between 10 and 100.
|
|
218
|
-
const $boundedDouble = $count.pipe(
|
|
219
|
-
// Transforms the value
|
|
220
|
-
$.map((count) => count * 2),
|
|
221
|
-
|
|
222
|
-
// Receives the value when it changes without affecting the output.
|
|
223
|
-
// Only receives values while this state is actively being watched.
|
|
224
|
-
$.tap((count) => console.log(`doubled value is ${count}`))
|
|
225
|
-
|
|
226
|
-
// Value only changes if it's within the range.
|
|
227
|
-
$.filter((count) => count >= 10 && count <= 100),
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// Could have a top level pipe operator
|
|
231
|
-
const $boundedDouble = $.pipe(
|
|
232
|
-
[$count],
|
|
233
|
-
$.map((count) => count * 2),
|
|
234
|
-
$.tap((count) => console.log(`doubled value is ${count}`))
|
|
235
|
-
$.filter((count) => count >= 10 && count <= 100),
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Could also be chainable
|
|
239
|
-
const $boundedDouble = $count
|
|
240
|
-
.map((count) => count * 2)
|
|
241
|
-
.tap((count) => console.log(`doubled value is ${count}`))
|
|
242
|
-
.filter((count) => count >= 10 && count <= 100);
|
|
243
|
-
|
|
244
|
-
// I kind of like this more than the current derive. It's cleaner.
|
|
245
|
-
$count.map(c => c * 2);
|
|
246
|
-
$count.merge([$other], (c, o) => c * o);
|
|
247
|
-
|
|
248
|
-
// Another way to merge multiple.
|
|
249
|
-
$.merge([$count, $other], (c, o) => c * o);
|
|
250
|
-
|
|
251
|
-
// What if you want to add something in the middle?
|
|
252
|
-
|
|
253
|
-
const $example = $count
|
|
254
|
-
.map((count) => count * 2)
|
|
255
|
-
.tap((count) => console.log(`doubled value is ${count}`))
|
|
256
|
-
.merge([$other1, $other2], (count, other1, other2) => /* ... */)
|
|
257
|
-
.filter((value) => value >= 10 && value <= 100);
|
|
258
|
-
|
|
259
|
-
// Is this a good pattern?
|
|
260
|
-
$count
|
|
261
|
-
.merge([$other], (count, other) => count * other)
|
|
262
|
-
.merge([$another], (merged, another) => merged * another);
|
|
263
|
-
// I think it gets a little weird to follow.
|
|
264
|
-
|
|
265
|
-
// equivalent to
|
|
266
|
-
derive(
|
|
267
|
-
[
|
|
268
|
-
derive([$count, $other], (count, other) => count * other),
|
|
269
|
-
$another
|
|
270
|
-
],
|
|
271
|
-
(merged, another) => merged * another)
|
|
272
|
-
// Is this a pattern? Yeah, I guess I do that. Just never in line like that.
|
|
273
|
-
|
|
274
|
-
// Do we want to handle errors?
|
|
275
|
-
// I feel like errors usually happen in watchers though.
|
|
276
|
-
$boundedDouble.watch((value) => {
|
|
277
|
-
// Received a value.
|
|
278
|
-
}, (error) => {
|
|
279
|
-
// Something threw an error.
|
|
280
|
-
});
|
|
281
|
-
// Or like this.
|
|
282
|
-
$boundedDouble.watch({
|
|
283
|
-
change: (value) => {
|
|
284
|
-
// Received a value.
|
|
285
|
-
// This code is most likely to throw an error.
|
|
286
|
-
// Should errors here be passed to the error callback?
|
|
287
|
-
// What is the point if you can just try/catch?
|
|
288
|
-
|
|
289
|
-
// Although if you don't then Dolla could use this to catch
|
|
290
|
-
// and trace errors better than it does now.
|
|
291
|
-
},
|
|
292
|
-
error: (error) => {
|
|
293
|
-
// Something threw an error.
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Filter derives a new state where the value only updates if the function returns truthy.
|
|
298
|
-
const $evens = $count.pipe($.filter((count) => count % 1 === 0));
|
|
299
|
-
// This is equivalent to
|
|
300
|
-
const $events = $.map([$count], (count) => count, { equals: (a, b) => a % 1 === 0 });
|
|
301
|
-
|
|
302
|
-
function filter(...args) {
|
|
303
|
-
if (isArray(args[0]) && isFunction(args[1])) {
|
|
304
|
-
// Standalone signature. Returns a new derived state.
|
|
305
|
-
} else if (args.length === 1 && isFunction(args[1])) {
|
|
306
|
-
// Curried signature. Returns a function that takes an array of states
|
|
307
|
-
// and returns one with args[1] as the equality check.
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
And you can write your own operators that implement these two signatures.
|
|
313
|
-
|
|
314
|
-
```js
|
|
315
|
-
// Here's one I might want to include.
|
|
316
|
-
// Use this to prevent ever getting a null value.
|
|
317
|
-
compare((next, previous) => next ?? previous ?? "default");
|
|
318
|
-
|
|
319
|
-
function compare(...args) {}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
I've been looking into other libraries that don't make you track your dependencies specifically. I think this is weird and unhinged to be honest. Calling functions with side effects that magically re-run things when the value changes is a truly weird and unexpected lifecycle. At least if you're explicitly tracking dependencies you know exactly what depends on what at a glance. Getting the computer to figure it out for you doesn't seem smart.
|
|
325
|
-
|
|
326
|
-
```js
|
|
327
|
-
import { $ } from "@manyducks.co/dolla";
|
|
328
|
-
|
|
329
|
-
const [count, setCount] = $(0);
|
|
330
|
-
|
|
331
|
-
const doubled = $.computed(() => count() * 2);
|
|
332
|
-
|
|
333
|
-
$.effect(() => {
|
|
334
|
-
console.log(doubled());
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
$.batch(() => {
|
|
338
|
-
// Set multiple things but defer updates to after this function returns.
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
// Helpers on $; can plug into template as is.
|
|
342
|
-
$.if(
|
|
343
|
-
$.computed(() => count() > 5),
|
|
344
|
-
<span>Greater than 5!</span>,
|
|
345
|
-
<span>Not greater than 5...</span>,
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
const switched = $.switch(count, [[1, "one"], [2, "two"], [3, "three"]], "more...");
|
|
349
|
-
|
|
350
|
-
$.repeat()
|
|
351
|
-
|
|
352
|
-
// TODO: How feasible is this?
|
|
353
|
-
<Repeat each={}>
|
|
354
|
-
{(item, index) => {
|
|
355
|
-
|
|
356
|
-
}}
|
|
357
|
-
</Repeat>
|
|
358
|
-
|
|
359
|
-
<Show when={condition}>
|
|
360
|
-
Condition is true.
|
|
361
|
-
</Show>
|
|
362
|
-
|
|
363
|
-
// Get
|
|
364
|
-
count();
|
|
365
|
-
|
|
366
|
-
// Set
|
|
367
|
-
count(52);
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
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.
|
|
373
|
-
|
|
374
|
-
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.
|
|
375
|
-
|
|
376
|
-
- Remove stores in favor of just exporting variables and functions from ES modules and importing them where desired.
|
|
377
|
-
-
|
|
378
|
-
|
|
379
|
-
```jsx
|
|
380
|
-
import Dolla from "@manyducks.co/dolla";
|
|
381
|
-
|
|
382
|
-
// Languages: add translation, set language and get localized string as a signal
|
|
383
|
-
Dolla.i18n.setup({
|
|
384
|
-
initialLanguage: Dolla.i18n.detect({ fallback: "ja" }), // Detect user's language and fall back to passed value
|
|
385
|
-
languages: [
|
|
386
|
-
{ name: "ja", path: "/static/locales/ja.json" },
|
|
387
|
-
{
|
|
388
|
-
name: "en",
|
|
389
|
-
fetch: async () => {
|
|
390
|
-
// Pass a path string, or if additional logic is needed, a fetch function.
|
|
391
|
-
const res = await Dolla.http.get("/static/locales/en.json");
|
|
392
|
-
return res.body;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
]
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
Dolla.i18n.$locale
|
|
399
|
-
Dolla.i18n.t$()
|
|
400
|
-
|
|
401
|
-
// A single setup call to keep things contained (must happen before mount)
|
|
402
|
-
Dolla.router.setup({
|
|
403
|
-
// Initial path must point to a route that actually exists (will be validated on mount) (initialPath is "/" by default)
|
|
404
|
-
initialPath: "/",
|
|
405
|
-
routes: [
|
|
406
|
-
{ path: "/", view: SomeView }
|
|
407
|
-
]
|
|
408
|
-
});
|
|
409
|
-
// And then you can route from anywhere.
|
|
410
|
-
Dolla.router.go("/some/path");
|
|
411
|
-
// Or get route information from anywhere.
|
|
412
|
-
Dolla.router.$path;
|
|
413
|
-
Dolla.router.$params;
|
|
414
|
-
|
|
415
|
-
// Also utils are available
|
|
416
|
-
const joinedPath = Dolla.router.utils.joinPath("/api/records", "5");
|
|
417
|
-
const resolvedPath = Dolla.router.utils.resolvePath("../"); // Resolves with window.location.href as the base
|
|
418
|
-
|
|
419
|
-
// Initializes the app and matches first route
|
|
420
|
-
Dolla.mount("#app");
|
|
421
|
-
// If you pass a view as the second argument it becomes the root view (this works for simple apps without a router)
|
|
422
|
-
Dolla.mount("#app", MyRootView);
|
|
423
|
-
// If router setup function wasn't called then the root view is mounted equivalent to the following:
|
|
424
|
-
Dolla.router.setup({
|
|
425
|
-
defaultPath: "/",
|
|
426
|
-
routes: [
|
|
427
|
-
{ path: "/*", view: MyRootView },
|
|
428
|
-
]
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// Add HTTP middleware
|
|
432
|
-
Dolla.http.use(async (req, next) => {
|
|
433
|
-
const res = await next()
|
|
434
|
-
});
|
|
435
|
-
// Make HTTP calls
|
|
436
|
-
const res = await Dolla.get("/some/path");
|
|
437
|
-
|
|
438
|
-
// Adjust log level
|
|
439
|
-
Dolla.setLogLevel(Dolla.LOG_LEVEL_INFO);
|
|
440
|
-
Dolla.setLogFilter("*,-Dolla/*")
|
|
441
|
-
// Create a scoped logger
|
|
442
|
-
const debug = Dolla.createLogger("debug-logger");
|
|
443
|
-
debug.log("HELLO");
|
|
444
|
-
debug.warn("THIS IS A SCOPED LOGGER");
|
|
445
|
-
|
|
446
|
-
// Efficiently and safely read and mutate the DOM using Dolla's render batching
|
|
447
|
-
Dolla.batch.read(() => {
|
|
448
|
-
// Reference DOM nodes
|
|
449
|
-
});
|
|
450
|
-
Dolla.batch.write(() => {
|
|
451
|
-
// Mutate the DOM as part of Dolla's next batch
|
|
452
|
-
}, "some-key");
|
|
453
|
-
|
|
454
|
-
// Respond to lifecycle events
|
|
455
|
-
Dolla.onMount(() => {});
|
|
456
|
-
Dolla.onRouteMatch(() => {});
|
|
457
|
-
// Dolla.onWhatever(() => {});
|
|
458
|
-
|
|
459
|
-
interface SomeViewProps {}
|
|
460
|
-
|
|
461
|
-
function SomeView (props: SomeViewProps, ctx: Dolla.ViewContext) {
|
|
462
|
-
const debug = Dolla.createLogger("SomeView");
|
|
463
|
-
|
|
464
|
-
// returns a signal and a setter function
|
|
465
|
-
const [$someValue, setSomeValue] = Dolla.createState(4);
|
|
466
|
-
|
|
467
|
-
// Router is now a part of the Dolla object
|
|
468
|
-
Dolla.router.$path;
|
|
469
|
-
Dolla.router.$params;
|
|
470
|
-
|
|
471
|
-
Dolla.router.go("/some-other-path");
|
|
472
|
-
|
|
473
|
-
ctx.watch([$someValue], (value) => {
|
|
474
|
-
debug.log(value);
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
// View helpers are on ViewContext
|
|
478
|
-
ctx.repeat()
|
|
479
|
-
ctx.cond()
|
|
480
|
-
ctx.render([...states], (...values) => {
|
|
481
|
-
// return Renderable (equivalent to Dolla.derive(states, (...values) => Renderable))
|
|
482
|
-
})
|
|
483
|
-
ctx.portal()
|
|
484
|
-
ctx.outlet()
|
|
485
|
-
|
|
486
|
-
// TODO: Add Dolla.dialog.show() and Dolla.toast.show() or create separate libraries?
|
|
487
|
-
|
|
488
|
-
return <h1>{ctx.t$("home.headerText")}</h1>;
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
```tsx
|
|
493
|
-
// import { signal, computed } from "@manyducks.co/dolla";
|
|
494
|
-
|
|
495
|
-
function signal(initialValue, options = {}) {}
|
|
496
|
-
|
|
497
|
-
function computed();
|
|
498
|
-
|
|
499
|
-
function WhateverView(props, c) {
|
|
500
|
-
// IDEA: Have state, computed and effect be methods on the view context.
|
|
501
|
-
// PROBLEM: Then what are the types when passing as props? State? ComputedState? or just a generic Dynamic<T> for readable/writable?
|
|
502
|
-
|
|
503
|
-
// Context variables (replacement for stores)
|
|
504
|
-
c.set("name", {
|
|
505
|
-
value: 5,
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
// Can get in the same context scope or on a child.
|
|
509
|
-
// Throws an error if value is not set.
|
|
510
|
-
c.get("name");
|
|
511
|
-
|
|
512
|
-
// If we use this $readable and set function pattern then we only have a single type of signal which is read-only.
|
|
513
|
-
// Take props as Signal<T> and deal with setting in callbacks. Simple and predictable.
|
|
514
|
-
const [$count, setCount] = signal(5);
|
|
515
|
-
|
|
516
|
-
const $doubled = derived($count, (value) => value * 2);
|
|
517
|
-
|
|
518
|
-
// const watcher = new SignalWatcher($count, (value) => {
|
|
519
|
-
// c.debug.log("watcher received value: " + value);
|
|
520
|
-
// });
|
|
521
|
-
|
|
522
|
-
// watcher.start();
|
|
523
|
-
// watcher.stop();
|
|
524
|
-
|
|
525
|
-
$count.get(); // returns the current value
|
|
526
|
-
setCount(10); // updates the value
|
|
527
|
-
|
|
528
|
-
// Observe and trigger side effects.
|
|
529
|
-
c.watch($count, (count) => {
|
|
530
|
-
c.debug.log(`The value of count is: ${count}`);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
// Exposes language and translation tools.
|
|
534
|
-
c.i18n.translate$("");
|
|
535
|
-
c.i18n.$language;
|
|
536
|
-
c.i18n.setLanguage();
|
|
537
|
-
|
|
538
|
-
// Exposes internal HTTP client.
|
|
539
|
-
c.http.get(); // put, post, patch, delete, etc.
|
|
540
|
-
|
|
541
|
-
// Exposes router helpers and variables.
|
|
542
|
-
c.route.go("/");
|
|
543
|
-
c.route.$params;
|
|
544
|
-
c.route.$path;
|
|
545
|
-
c.route.$query;
|
|
546
|
-
c.route.setQuery({
|
|
547
|
-
value: 1,
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
return html`
|
|
551
|
-
<h1>This is the template</h1>
|
|
552
|
-
|
|
553
|
-
${render($count, (count) => {
|
|
554
|
-
// Render rebuilds the markup within when any of the dependencies change.
|
|
555
|
-
return html`<p>The count is ${count}</p>`;
|
|
556
|
-
|
|
557
|
-
// Other view helpers are also provided as exports
|
|
558
|
-
repeat();
|
|
559
|
-
cond();
|
|
560
|
-
outlet();
|
|
561
|
-
portal();
|
|
562
|
-
})}
|
|
563
|
-
`;
|
|
564
|
-
}
|
|
565
|
-
```
|
package/notes/splitting.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Splitting
|
|
2
|
-
|
|
3
|
-
Thinking again of splitting this out into multiple libraries. Or at least having the base signals+markup be its own standalone thing that the rest of the framework is built on.
|
|
4
|
-
|
|
5
|
-
This implementation of signals + templates would be useful for web components.
|