@manyducks.co/dolla 2.0.0-alpha.2 → 2.0.0-alpha.20
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 +168 -513
- package/dist/index.d.ts +12 -10
- package/dist/index.js +997 -732
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime.js +2 -2
- package/dist/jsx-runtime.js +2 -2
- package/dist/markup.d.ts +39 -26
- package/dist/modules/dolla.d.ts +28 -14
- package/dist/modules/i18n.d.ts +129 -0
- package/dist/modules/render.d.ts +5 -8
- package/dist/modules/router.d.ts +8 -8
- package/dist/nodes/cond.d.ts +7 -7
- package/dist/nodes/html.d.ts +12 -6
- package/dist/nodes/observer.d.ts +9 -8
- package/dist/nodes/outlet.d.ts +8 -8
- package/dist/nodes/portal.d.ts +4 -4
- package/dist/nodes/repeat.d.ts +14 -14
- package/dist/nodes/text.d.ts +6 -6
- package/dist/passthrough-DrILnMr2.js +1393 -0
- package/dist/passthrough-DrILnMr2.js.map +1 -0
- package/dist/state.d.ts +151 -0
- package/dist/types.d.ts +12 -12
- package/dist/utils.d.ts +0 -1
- package/dist/view.d.ts +45 -9
- package/docs/http.md +5 -0
- package/docs/i18n.md +5 -0
- package/docs/router.md +5 -0
- package/docs/states.md +5 -0
- package/docs/views.md +5 -0
- package/notes/context-vars.md +21 -0
- package/notes/readme-scratch.md +222 -0
- package/notes/route-middleware.md +42 -0
- package/notes/scratch.md +93 -6
- package/package.json +13 -13
- package/tests/{signals.test.js → state.test.js} +6 -6
- package/dist/modules/language.d.ts +0 -41
- package/dist/passthrough-DrtCifRF.js +0 -1228
- package/dist/passthrough-DrtCifRF.js.map +0 -1
- package/dist/signals.d.ts +0 -101
package/dist/view.d.ts
CHANGED
|
@@ -1,26 +1,41 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type MarkupElement, type ElementContext, type Markup } from "./markup.js";
|
|
2
2
|
import type { Logger } from "./modules/dolla.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type MaybeState, Setter, State, type StateValues, type StopFunction } from "./state.js";
|
|
4
4
|
/**
|
|
5
5
|
* Any valid value that a View can return.
|
|
6
6
|
*/
|
|
7
|
-
export type ViewResult = Node |
|
|
7
|
+
export type ViewResult = Node | State<any> | Markup | Markup[] | null;
|
|
8
8
|
export type ViewFunction<P> = (props: P, context: ViewContext) => ViewResult;
|
|
9
9
|
/**
|
|
10
10
|
* A view that has been constructed into DOM nodes.
|
|
11
11
|
*/
|
|
12
|
-
export interface
|
|
13
|
-
|
|
12
|
+
export interface ViewElement extends MarkupElement {
|
|
13
|
+
/**
|
|
14
|
+
* Take a ViewFunction and render it as a child of this view.
|
|
15
|
+
*/
|
|
16
|
+
setChildView(view: ViewFunction<{}>): ViewElement;
|
|
14
17
|
}
|
|
15
18
|
export interface ViewContext extends Logger {
|
|
16
19
|
/**
|
|
17
20
|
* A string ID unique to this view.
|
|
18
21
|
*/
|
|
19
22
|
readonly uid: string;
|
|
23
|
+
/**
|
|
24
|
+
* Sets a context variable and returns its value. Context variables are accessible on the same context and from those of child views.
|
|
25
|
+
*/
|
|
26
|
+
set<T>(key: string | symbol, value: T): T;
|
|
27
|
+
/**
|
|
28
|
+
* Gets the value of a context variable. Returns null if the variable is not set.
|
|
29
|
+
*/
|
|
30
|
+
get<T>(key: string | symbol): T | null;
|
|
31
|
+
/**
|
|
32
|
+
* Returns an object of all variables stored on this context.
|
|
33
|
+
*/
|
|
34
|
+
getAll(): Record<string | symbol, unknown>;
|
|
20
35
|
/**
|
|
21
36
|
* Sets the name of the view's built in logger.
|
|
22
37
|
*/
|
|
23
|
-
setName(name: string):
|
|
38
|
+
setName(name: string): ViewContext;
|
|
24
39
|
/**
|
|
25
40
|
* Registers a callback to run just before this view is mounted. DOM nodes are not yet attached to the page.
|
|
26
41
|
*/
|
|
@@ -38,13 +53,34 @@ export interface ViewContext extends Logger {
|
|
|
38
53
|
*/
|
|
39
54
|
onUnmount(callback: () => void): void;
|
|
40
55
|
/**
|
|
41
|
-
* Watch a set of
|
|
56
|
+
* Watch a set of states. The callback is called when any of the states receive a new value.
|
|
42
57
|
* Watchers will be automatically stopped when this view is unmounted.
|
|
43
58
|
*/
|
|
44
|
-
watch<T extends
|
|
59
|
+
watch<T extends MaybeState<any>[]>(states: [...T], callback: (...values: StateValues<T>) => void): StopFunction;
|
|
45
60
|
/**
|
|
46
61
|
* Returns a Markup element that displays this view's children.
|
|
47
62
|
*/
|
|
48
63
|
outlet(): Markup;
|
|
49
64
|
}
|
|
50
|
-
export declare
|
|
65
|
+
export declare class View<P> implements ViewElement {
|
|
66
|
+
uniqueId: string;
|
|
67
|
+
_elementContext: ElementContext;
|
|
68
|
+
_logger: Logger;
|
|
69
|
+
_view: ViewFunction<P>;
|
|
70
|
+
_props: P;
|
|
71
|
+
_element?: MarkupElement;
|
|
72
|
+
_$children: State<MarkupElement[]>;
|
|
73
|
+
_setChildren: Setter<MarkupElement[], MarkupElement[]>;
|
|
74
|
+
_watcher: import("./state.js").StateWatcher;
|
|
75
|
+
_beforeMountCallbacks: (() => any)[];
|
|
76
|
+
_onMountCallbacks: (() => any)[];
|
|
77
|
+
_beforeUnmountCallbacks: (() => any)[];
|
|
78
|
+
_onUnmountCallbacks: (() => any)[];
|
|
79
|
+
constructor(elementContext: ElementContext, view: ViewFunction<P>, props: P, children?: Markup[]);
|
|
80
|
+
get node(): Node;
|
|
81
|
+
isMounted: boolean;
|
|
82
|
+
mount(parent: Node, after?: Node): void;
|
|
83
|
+
unmount(parentIsUnmounting: boolean): void;
|
|
84
|
+
setChildView(fn: ViewFunction<{}>): View<{}>;
|
|
85
|
+
initialize(): void;
|
|
86
|
+
}
|
package/docs/http.md
ADDED
package/docs/i18n.md
ADDED
package/docs/router.md
ADDED
package/docs/states.md
ADDED
package/docs/views.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Idea: Context Variables
|
|
2
|
+
|
|
3
|
+
In designing how Dolla's version of 'context' works, I've been going through a few different ideas. The simplest seems to be the ability to store _context variables_ that, once set, are accessible on the same context or any child context.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
function SomeView(props, ctx) {
|
|
7
|
+
ctx.set("key", 5);
|
|
8
|
+
|
|
9
|
+
// ... and in a child view do
|
|
10
|
+
ctx.get("key");
|
|
11
|
+
// which returns null if the value isn't present.
|
|
12
|
+
// It's like localStorage for the view tree.
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
They can be typed, but always with a possibility to return null.
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const value = ctx.get<number>("key");
|
|
20
|
+
// value is number | null to force the programmer to check it.
|
|
21
|
+
```
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# README
|
|
2
|
+
|
|
3
|
+
> This note will eventually become the new README. Here I'm laying out my ideal framework API.
|
|
4
|
+
|
|
5
|
+
A basic component.
|
|
6
|
+
|
|
7
|
+
```jsx
|
|
8
|
+
import Dolla, { createState, derive } from "@manyducks.co/dolla";
|
|
9
|
+
|
|
10
|
+
function ExampleView(props, ctx) {
|
|
11
|
+
const [$count, setCount] = createState(5);
|
|
12
|
+
const $doubled = derive([$count], (n) => n * 2);
|
|
13
|
+
|
|
14
|
+
ctx.watch([$count], (count) => {
|
|
15
|
+
ctx.log("value of count is now %n", count);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return <p>{$count}</p>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Dolla.mount(document.body, ExampleView);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
<details open>
|
|
25
|
+
<summary>
|
|
26
|
+
<h2>Signals API</h2>
|
|
27
|
+
</summary>
|
|
28
|
+
|
|
29
|
+
The signals API. Dolla's signals use explicit tracking, meaning any function where signal values are tracked take an array of the signals you want to track. This way you know exactly what depends on what at a glance without any kind of hidden tracking logic behind the scenes. You are free to `.get()` the value of a signal without worrying about untracking it first.
|
|
30
|
+
|
|
31
|
+
```jsx
|
|
32
|
+
import { createState } from "@manyducks.co/dolla";
|
|
33
|
+
|
|
34
|
+
const [$count, setCount] = createState(256);
|
|
35
|
+
|
|
36
|
+
$count.get(); // 256; returns the current value
|
|
37
|
+
|
|
38
|
+
const stop = $count.watch((value) => {
|
|
39
|
+
// Runs once immediately, then again whenever the value changes.
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
setCount(512); // Update the value of $count. The new value is set and all watchers run synchronously.
|
|
43
|
+
|
|
44
|
+
stop(); // Stop watching for changes.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That is the basic signal API. Signals are all about composability. Here are some more advanced ways of working with them:
|
|
48
|
+
|
|
49
|
+
```jsx
|
|
50
|
+
import { createState, toState, valueOf, derive } from "@manyducks.co/dolla";
|
|
51
|
+
|
|
52
|
+
const [$count, setCount] = createState(72);
|
|
53
|
+
|
|
54
|
+
// Returns the value of the signal passed in. If the value is not a signal it is returned as is.
|
|
55
|
+
const count = valueOf($count);
|
|
56
|
+
const bool = valueOf(true);
|
|
57
|
+
|
|
58
|
+
// Creates a signal containing the value passed in. If the value is already a signal it is returned as is.
|
|
59
|
+
const $bool = toState(true);
|
|
60
|
+
const $anotherCount = toState($count);
|
|
61
|
+
|
|
62
|
+
// Derive a new signal from the value of another. Whenever $count changes, $doubled will follow.
|
|
63
|
+
const $doubled = derive([$count], (count) => count * 2);
|
|
64
|
+
|
|
65
|
+
// Derive a new signal from the values of several others. When any value in the list changes, $sum will be recomputed.
|
|
66
|
+
const $sum = derive([$count, $doubled], (count, doubled) => count + doubled);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The API if we call it State instead of Signal to distance from the Signal object in standardization process.
|
|
70
|
+
|
|
71
|
+
```jsx
|
|
72
|
+
import { createState, toState, valueOf, derive } from "@manyducks.co/dolla";
|
|
73
|
+
|
|
74
|
+
const [$count, setCount] = createState(72);
|
|
75
|
+
|
|
76
|
+
// Returns the value of the signal passed in. If the value is not a signal it is returned as is.
|
|
77
|
+
const count = valueOf($count);
|
|
78
|
+
const bool = valueOf(true);
|
|
79
|
+
|
|
80
|
+
// Creates a signal containing the value passed in. If the value is already a signal it is returned as is.
|
|
81
|
+
const $bool = toState(true);
|
|
82
|
+
const $anotherCount = toState($count);
|
|
83
|
+
|
|
84
|
+
// Derive a new signal from the value of another. Whenever $count changes, $doubled will follow.
|
|
85
|
+
const $doubled = derive([$count], (count) => count * 2);
|
|
86
|
+
|
|
87
|
+
// Derive a new signal from the values of several others. When any value in the list changes, $sum will be recomputed.
|
|
88
|
+
const $sum = derive([$count, $doubled], (count, doubled) => count + doubled);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
States also come in a settable variety, with the setter included on the same object. Sometimes you want to pass around a two-way binding and this is what SettableState is for.
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
import { createSettableState, fromSettable, toSettable } from "@manyducks.co/dolla";
|
|
95
|
+
|
|
96
|
+
// Settable states have their setter included.
|
|
97
|
+
const $$value = createSettableState("Test");
|
|
98
|
+
$$value.set("New Value");
|
|
99
|
+
|
|
100
|
+
// They can also be split into a State and Setter
|
|
101
|
+
const [$value, setValue] = fromSettableState($$value);
|
|
102
|
+
|
|
103
|
+
// And a State and Setter can be combined into a SettableState.
|
|
104
|
+
const $$otherValue = toSettableState($value, setValue);
|
|
105
|
+
|
|
106
|
+
// Or discard the setter and make it read-only using the good old toState function:
|
|
107
|
+
const $value = toState($$value);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Alternative API
|
|
111
|
+
|
|
112
|
+
```jsx
|
|
113
|
+
import { State } from "@manyducks.co/dolla";
|
|
114
|
+
|
|
115
|
+
const [$count, setCount] = State(72);
|
|
116
|
+
|
|
117
|
+
const count = State.unwrap($count);
|
|
118
|
+
const bool = State.unwrap(true);
|
|
119
|
+
|
|
120
|
+
const $bool = State.wrap(true);
|
|
121
|
+
const $sameCount = State.wrap($count);
|
|
122
|
+
|
|
123
|
+
const $doubled = State.from([$count], (count) => count * 2);
|
|
124
|
+
|
|
125
|
+
const $sum = State.from([$count, $doubled], (count, doubled) => count + doubled);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Yet another
|
|
129
|
+
|
|
130
|
+
```jsx
|
|
131
|
+
import Dolla from "@manyducks.co/dolla";
|
|
132
|
+
|
|
133
|
+
const [$count, setCount] = Dolla.state(72);
|
|
134
|
+
|
|
135
|
+
const count = Dolla.get($count);
|
|
136
|
+
const bool = Dolla.get(true);
|
|
137
|
+
|
|
138
|
+
const $bool = Dolla.toState(true);
|
|
139
|
+
const $sameCount = Dolla.toState($count);
|
|
140
|
+
|
|
141
|
+
const $doubled = Dolla.computed([$count], (count) => count * 2);
|
|
142
|
+
const $sum = Dolla.computed([$count, $doubled], (count, doubled) => count + doubled);
|
|
143
|
+
|
|
144
|
+
// or
|
|
145
|
+
|
|
146
|
+
import { state, computed, get, toState } from "@manyducks.co/dolla";
|
|
147
|
+
|
|
148
|
+
const [$count, setCount] = state(72);
|
|
149
|
+
|
|
150
|
+
const count = get($count);
|
|
151
|
+
const bool = get(true);
|
|
152
|
+
|
|
153
|
+
const $bool = toState(true);
|
|
154
|
+
const $sameCount = toState($count);
|
|
155
|
+
|
|
156
|
+
const $doubled = computed([$count], (count) => count * 2);
|
|
157
|
+
const $sum = computed([$count, $doubled], (count, doubled) => count + doubled);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Settable signals:
|
|
161
|
+
|
|
162
|
+
```jsx
|
|
163
|
+
import { createSettableState, createSetter, toSettableSignal, fromSettableSignal } from "@manyducks.co/dolla";
|
|
164
|
+
|
|
165
|
+
// Create a SettableSignal, which is basically a signal and its setter combined into a single object.
|
|
166
|
+
const $$settable = createSettableState("Example");
|
|
167
|
+
|
|
168
|
+
// The basic API is identical...
|
|
169
|
+
$$settable.get();
|
|
170
|
+
const stop = $$settable.watch((value) => {
|
|
171
|
+
// ...
|
|
172
|
+
});
|
|
173
|
+
stop();
|
|
174
|
+
|
|
175
|
+
// ... except for the addition of a setter.
|
|
176
|
+
$$settable.set("Set me directly");
|
|
177
|
+
|
|
178
|
+
// When you already have a signal and a setter, they can be combined into one.
|
|
179
|
+
const $$count = toSettableSignal($count, setCount);
|
|
180
|
+
|
|
181
|
+
// This updates the original $signal value.
|
|
182
|
+
$$count.set(386);
|
|
183
|
+
|
|
184
|
+
// TODO: You can also split a SettableSignal into a signal and its setter.
|
|
185
|
+
const [$readable, setReadable] = fromSettableSignal($$settable);
|
|
186
|
+
|
|
187
|
+
// Create a custom setter. Calling this will cap the value to 100.
|
|
188
|
+
const setCountBounded = createSetter($count, (next, current) => {
|
|
189
|
+
return Math.min(100, next);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
setCountBounded((current) => {
|
|
193
|
+
return current + 1;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Or make a proxy $$doubled -- but would you actually want to proxy things like this?
|
|
197
|
+
const [$count, setCount] = createState(5);
|
|
198
|
+
const $doubled = derive([$count], (count) => count * 2);
|
|
199
|
+
const $$doubled = toSettableSignal(
|
|
200
|
+
$doubled,
|
|
201
|
+
createSetter($doubled, (next, current) => {
|
|
202
|
+
setCount(next * 2);
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
I'm not really sure we need all of this. On the chopping block:
|
|
208
|
+
|
|
209
|
+
- The entire concept of settable signals
|
|
210
|
+
- `createSettableState`
|
|
211
|
+
- `toSettableSignal`
|
|
212
|
+
- `fromSettableSignal`
|
|
213
|
+
- `createSetter`
|
|
214
|
+
|
|
215
|
+
This makes the entire API just four functions:
|
|
216
|
+
|
|
217
|
+
- `createState`
|
|
218
|
+
- `derive`
|
|
219
|
+
- `toState`
|
|
220
|
+
- `valueOf`
|
|
221
|
+
|
|
222
|
+
</details>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Router Middleware
|
|
2
|
+
|
|
3
|
+
Allow handling route guards, preloading, etc with per-route middleware. When a route is matched, all middleware from higher layers are run again.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
Dolla.router.setup({
|
|
7
|
+
middleware: [/* does it make sense to have global middleware? */]
|
|
8
|
+
routes: [
|
|
9
|
+
{ path: "/login", middleware: [auth] },
|
|
10
|
+
{ path: "/", middleware: [auth], routes: [{ path: "/example", view: ExampleView }] }
|
|
11
|
+
]
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
async function auth(ctx) {
|
|
15
|
+
// This check can be implemented however it needs to be for the app.
|
|
16
|
+
const authed = await isAuthorized();
|
|
17
|
+
|
|
18
|
+
if (ctx.path === "/login") {
|
|
19
|
+
if (authed) {
|
|
20
|
+
ctx.redirect("/");
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
if (!authed) {
|
|
24
|
+
ctx.redirect("/login");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// If no redirect has happened and nothing has been returned then we're clear to proceed.
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// A middleware can also return Markup to stay on the URL but show something different.
|
|
31
|
+
async function randomVisitor(ctx) {
|
|
32
|
+
if (Math.random() > 0.99) {
|
|
33
|
+
return <LuckyVisitorView />
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Or preload async data and set a context variable before navigating.
|
|
38
|
+
async function preload(ctx) {
|
|
39
|
+
const data = await fetchData();
|
|
40
|
+
ctx.set("data", data);
|
|
41
|
+
}
|
|
42
|
+
```
|
package/notes/scratch.md
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
# Scratch Note
|
|
2
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
|
+
I've been looking into other libraries that don't make you track your dependencies specifically.
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import { $ } from "@manyducks.co/dolla";
|
|
44
|
+
|
|
45
|
+
const [count, setCount] = $(0);
|
|
46
|
+
|
|
47
|
+
const doubled = $.computed(() => count() * 2);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
$.effect(() => {
|
|
52
|
+
console.log(doubled());
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
$.batch(() => {
|
|
56
|
+
// Set multiple things but defer updates to after this function returns.
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Helpers on $; can plug into template as is.
|
|
60
|
+
$.if(
|
|
61
|
+
$.computed(() => count() > 5),
|
|
62
|
+
<span>Greater than 5!</span>,
|
|
63
|
+
<span>Not greater than 5...</span>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const switched = $.switch(count, [[1, "one"], [2, "two"], [3, "three"]], "more...");
|
|
67
|
+
|
|
68
|
+
$.repeat()
|
|
69
|
+
|
|
70
|
+
// TODO: How feasible is this?
|
|
71
|
+
<Repeat each={}>
|
|
72
|
+
{(item, index) => {
|
|
73
|
+
|
|
74
|
+
}}
|
|
75
|
+
</Repeat>
|
|
76
|
+
|
|
77
|
+
<Show when={condition}>
|
|
78
|
+
Condition is true.
|
|
79
|
+
</Show>
|
|
80
|
+
|
|
81
|
+
// Get
|
|
82
|
+
count();
|
|
83
|
+
|
|
84
|
+
// Set
|
|
85
|
+
count(52);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
3
90
|
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.
|
|
4
91
|
|
|
5
92
|
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.
|
|
@@ -11,8 +98,8 @@ Doing this would make it possible to access things inside the Dolla app from _ou
|
|
|
11
98
|
import Dolla from "@manyducks.co/dolla";
|
|
12
99
|
|
|
13
100
|
// Languages: add translation, set language and get localized string as a signal
|
|
14
|
-
Dolla.
|
|
15
|
-
initialLanguage: Dolla.
|
|
101
|
+
Dolla.i18n.setup({
|
|
102
|
+
initialLanguage: Dolla.i18n.detect({ fallback: "ja" }), // Detect user's language and fall back to passed value
|
|
16
103
|
languages: [
|
|
17
104
|
{ name: "ja", path: "/static/locales/ja.json" },
|
|
18
105
|
{
|
|
@@ -26,8 +113,8 @@ Dolla.language.setup({
|
|
|
26
113
|
]
|
|
27
114
|
});
|
|
28
115
|
|
|
29
|
-
Dolla.
|
|
30
|
-
Dolla.
|
|
116
|
+
Dolla.i18n.$locale
|
|
117
|
+
Dolla.i18n.t$()
|
|
31
118
|
|
|
32
119
|
// A single setup call to keep things contained (must happen before mount)
|
|
33
120
|
Dolla.router.setup({
|
|
@@ -78,7 +165,7 @@ debug.warn("THIS IS A SCOPED LOGGER");
|
|
|
78
165
|
Dolla.render.read(() => {
|
|
79
166
|
// Reference DOM nodes
|
|
80
167
|
});
|
|
81
|
-
Dolla.render.
|
|
168
|
+
Dolla.render.write(() => {
|
|
82
169
|
// Mutate the DOM as part of Dolla's next batch
|
|
83
170
|
}, "some-key");
|
|
84
171
|
|
|
@@ -93,7 +180,7 @@ function SomeView (props: SomeViewProps, ctx: Dolla.ViewContext) {
|
|
|
93
180
|
const debug = Dolla.createLogger("SomeView");
|
|
94
181
|
|
|
95
182
|
// returns a signal and a setter function
|
|
96
|
-
const [$someValue, setSomeValue] = Dolla.
|
|
183
|
+
const [$someValue, setSomeValue] = Dolla.createState(4);
|
|
97
184
|
|
|
98
185
|
// Router is now a part of the Dolla object
|
|
99
186
|
Dolla.router.$path;
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manyducks.co/dolla",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.20",
|
|
4
4
|
"description": "Front-end components, routing and state management.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": false,
|
|
9
|
-
"repository":
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/manyducksco/dolla.git"
|
|
12
|
+
},
|
|
10
13
|
"scripts": {
|
|
11
14
|
"test": "npm run build && node --test",
|
|
12
15
|
"build:esbuild": "tsc && node build.js",
|
|
@@ -36,20 +39,17 @@
|
|
|
36
39
|
"types": "./jsx-dev-runtime.d.ts"
|
|
37
40
|
}
|
|
38
41
|
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"fetch-ponyfill": "^7.1.0",
|
|
41
|
-
"history": "^5.3.0",
|
|
42
|
-
"nanoid": "^5.0.4",
|
|
43
|
-
"simple-color-hash": "^1.0.2",
|
|
44
|
-
"vite": "^6.0.7"
|
|
45
|
-
},
|
|
46
42
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^22.10.6",
|
|
48
44
|
"csstype": "^3.1.3",
|
|
49
45
|
"esbuild": "^0.24.2",
|
|
46
|
+
"fast-deep-equal": "^3.1.3",
|
|
47
|
+
"history": "^5.3.0",
|
|
50
48
|
"htm": "^3.1.1",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
49
|
+
"nanoid": "^5.0.9",
|
|
50
|
+
"prettier": "^3.4.2",
|
|
51
|
+
"simple-color-hash": "^1.0.2",
|
|
52
|
+
"typescript": "^5.7.3",
|
|
53
|
+
"vite": "^6.0.7"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { createState, derive } from "../dist/index.js";
|
|
5
5
|
|
|
6
6
|
test("signal", (t) => {
|
|
7
|
-
const [$count, setCount] =
|
|
7
|
+
const [$count, setCount] = createState(5);
|
|
8
8
|
|
|
9
9
|
const defaultWatcher = t.mock.fn();
|
|
10
10
|
const stopDefault = $count.watch(defaultWatcher);
|
|
@@ -42,8 +42,8 @@ test("signal", (t) => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
test("derive", (t) => {
|
|
45
|
-
const [$one, setOne] =
|
|
46
|
-
const [$two, setTwo] =
|
|
45
|
+
const [$one, setOne] = createState(5);
|
|
46
|
+
const [$two, setTwo] = createState(20);
|
|
47
47
|
|
|
48
48
|
const deriveSum = t.mock.fn((one, two) => one + two);
|
|
49
49
|
const deriveProduct = t.mock.fn((one, two) => one * two);
|
|
@@ -101,9 +101,9 @@ test("derive", (t) => {
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
test("derive nested signals", (t) => {
|
|
104
|
-
const [$value, setValue] =
|
|
104
|
+
const [$value, setValue] = createState(5);
|
|
105
105
|
|
|
106
|
-
const [$object, setObject] =
|
|
106
|
+
const [$object, setObject] = createState({
|
|
107
107
|
href: derive([$value], (value) => `/projects/${value}/test`),
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { type Signal } from "../signals.js";
|
|
2
|
-
import type { Stringable } from "../types.js";
|
|
3
|
-
import type { Dolla } from "./dolla.js";
|
|
4
|
-
/**
|
|
5
|
-
* An object where values are either a translated string or another nested Translation object.
|
|
6
|
-
*/
|
|
7
|
-
type LocalizedStrings = Record<string, string | Record<string, string | Record<string, string | Record<string, string>>>>;
|
|
8
|
-
export interface LanguageConfig {
|
|
9
|
-
name: string;
|
|
10
|
-
/**
|
|
11
|
-
* Path to a JSON file with translated strings for this language.
|
|
12
|
-
*/
|
|
13
|
-
path?: string;
|
|
14
|
-
/**
|
|
15
|
-
* A callback function that returns a Promise that resolves to the translation object for this language.
|
|
16
|
-
*/
|
|
17
|
-
fetch?: () => Promise<LocalizedStrings>;
|
|
18
|
-
}
|
|
19
|
-
export type LanguageSetupOptions = {
|
|
20
|
-
/**
|
|
21
|
-
* Default language to load on startup
|
|
22
|
-
*/
|
|
23
|
-
initialLanguage?: string | null;
|
|
24
|
-
languages: LanguageConfig[];
|
|
25
|
-
};
|
|
26
|
-
export declare class Language {
|
|
27
|
-
#private;
|
|
28
|
-
$current: Signal<string | undefined>;
|
|
29
|
-
constructor(dolla: Dolla);
|
|
30
|
-
get supportedLanguages(): string[];
|
|
31
|
-
setup(options: LanguageSetupOptions): void;
|
|
32
|
-
setLanguage(name: string): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Returns a Signal containing the value at `key`.
|
|
35
|
-
|
|
36
|
-
* @param key - Key to the translated value.
|
|
37
|
-
* @param values - A map of {{placeholder}} names and the values to replace them with.
|
|
38
|
-
*/
|
|
39
|
-
t(key: string, values?: Record<string, Stringable | Signal<Stringable>>): Signal<string>;
|
|
40
|
-
}
|
|
41
|
-
export {};
|