@manyducks.co/dolla 2.0.0-alpha.30 → 2.0.0-alpha.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/core/context.d.ts +12 -4
- package/dist/core/dolla.d.ts +17 -8
- package/dist/core/markup.d.ts +23 -2
- package/dist/core/nodes/view.d.ts +1 -2
- package/dist/core/store.d.ts +3 -9
- package/dist/core/symbols.d.ts +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +808 -797
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime.js +2 -2
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.js +2 -2
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/{passthrough-d2lcM0cd.js → markup-C-1VlVZi.js} +562 -592
- package/dist/markup-C-1VlVZi.js.map +1 -0
- package/dist/modules/router.d.ts +16 -6
- package/docs/i18n.md +2 -2
- package/docs/router.md +12 -9
- package/docs/state.md +2 -2
- package/docs/stores.md +5 -6
- package/docs/views.md +53 -67
- package/notes/scratch.md +135 -0
- package/notes/stores.md +14 -32
- package/package.json +1 -1
- package/build.js +0 -34
- package/dist/passthrough-d2lcM0cd.js.map +0 -1
package/docs/views.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Views are one of two component types in Dolla. We call them views because they deal specifically with presenting visible things to the user. The other type of component, [Stores](./stores.md), deal with data and events.
|
|
4
4
|
|
|
5
|
-
At its most basic, a view is a function that returns
|
|
5
|
+
At its most basic, a view is a function that returns markup.
|
|
6
6
|
|
|
7
7
|
```jsx
|
|
8
|
-
|
|
8
|
+
function ExampleView() {
|
|
9
9
|
return <h1>Hello World!</h1>;
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## View Props
|
|
@@ -15,11 +15,11 @@ const ExampleView = createView(function () {
|
|
|
15
15
|
A view function takes a `props` object as its first argument. This object contains all properties passed to the view when it's invoked.
|
|
16
16
|
|
|
17
17
|
```jsx
|
|
18
|
-
|
|
18
|
+
function ListItemView(props) {
|
|
19
19
|
return <li>{props.label}</li>;
|
|
20
|
-
}
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
function ListView() {
|
|
23
23
|
return (
|
|
24
24
|
<ul>
|
|
25
25
|
<ListItemView label="Squirrel" />
|
|
@@ -27,7 +27,7 @@ const ListView = createView(function () {
|
|
|
27
27
|
<ListItemView label="Groundhog" />
|
|
28
28
|
</ul>
|
|
29
29
|
);
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
As you may have guessed, you can pass States as props and slot them in in exactly the same way. This is important because Views do not re-render the way you might expect from other frameworks. Whatever you pass as props is what the View gets for its entire lifecycle.
|
|
@@ -39,7 +39,7 @@ As you may have guessed, you can pass States as props and slot them in in exactl
|
|
|
39
39
|
The `cond` helper does conditional rendering. When `$condition` is truthy, the second argument is rendered. When `$condition` is falsy the third argument is rendered. Either case can be left null or undefined if you don't want to render something for that condition.
|
|
40
40
|
|
|
41
41
|
```jsx
|
|
42
|
-
|
|
42
|
+
function ConditionalListView(props) {
|
|
43
43
|
return (
|
|
44
44
|
<div>
|
|
45
45
|
{cond(
|
|
@@ -57,7 +57,7 @@ const ConditionalListView = createView(function (props) {
|
|
|
57
57
|
)}
|
|
58
58
|
</div>
|
|
59
59
|
);
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
### `repeat($items, keyFn, renderFn)`
|
|
@@ -65,7 +65,7 @@ const ConditionalListView = createView(function (props) {
|
|
|
65
65
|
The `repeat` helper repeats a render function for each item in a list. The `keyFn` takes an item's value and returns a number, string or Symbol that uniquely identifies that list item. If `$items` changes or gets reordered, all rendered items with matching keys will be reused, those no longer in the list will be removed and those that didn't previously have a matching key are created.
|
|
66
66
|
|
|
67
67
|
```jsx
|
|
68
|
-
|
|
68
|
+
function RepeatedListView() {
|
|
69
69
|
const [$items, setItems] = createState(["Squirrel", "Chipmunk", "Groundhog"]);
|
|
70
70
|
|
|
71
71
|
return (
|
|
@@ -79,7 +79,7 @@ const RepeatedListView = createView(function () {
|
|
|
79
79
|
)}
|
|
80
80
|
</ul>
|
|
81
81
|
);
|
|
82
|
-
}
|
|
82
|
+
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
### `portal(content, parentNode)`
|
|
@@ -87,7 +87,7 @@ const RepeatedListView = createView(function () {
|
|
|
87
87
|
The `portal` helper displays DOM elements from a view as children of a parent element elsewhere in the document. Portals are typically used to display modals and other content that needs to appear at the top level of a document.
|
|
88
88
|
|
|
89
89
|
```jsx
|
|
90
|
-
|
|
90
|
+
function PortalView() {
|
|
91
91
|
const content = (
|
|
92
92
|
<div class="modal">
|
|
93
93
|
<p>This is a modal.</p>
|
|
@@ -96,80 +96,66 @@ const PortalView = createView(function () {
|
|
|
96
96
|
|
|
97
97
|
// Content will be appended to `document.body` while this view is connected.
|
|
98
98
|
return portal(document.body, content);
|
|
99
|
-
}
|
|
99
|
+
}
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
## View Context
|
|
103
103
|
|
|
104
104
|
A view function takes a context object as its second argument. The context provides a set of functions you can use to respond to lifecycle events, observe dynamic data, print debug messages and display child elements among other things.
|
|
105
105
|
|
|
106
|
-
The context can be accessed in one of two ways; as `this` when you pass a non-arrow function, or as the second parameter passed after the props object.
|
|
107
|
-
|
|
108
106
|
```jsx
|
|
109
|
-
|
|
110
|
-
const ExampleView = createView(function (props) {
|
|
111
|
-
this.onMount(() => {
|
|
112
|
-
this.log("HELLO!");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
return <h1>Hello World!</h1>;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Option 2: Access as second argument (for arrow functions)
|
|
119
|
-
const ExampleView = createView((props, ctx) => {
|
|
107
|
+
function ExampleView(props, ctx) {
|
|
120
108
|
ctx.onMount(() => {
|
|
121
109
|
ctx.log("HELLO!");
|
|
122
110
|
});
|
|
123
111
|
|
|
124
112
|
return <h1>Hello World!</h1>;
|
|
125
|
-
}
|
|
113
|
+
}
|
|
126
114
|
```
|
|
127
115
|
|
|
128
|
-
Which one you use is just an aesthetic preference, but I kind of like the classic `function` syntax with `this`.
|
|
129
|
-
|
|
130
116
|
### Printing Debug Messages
|
|
131
117
|
|
|
132
118
|
```jsx
|
|
133
|
-
|
|
119
|
+
function ExampleView(props, ctx) {
|
|
134
120
|
// Set the name of this view's context. Console messages are prefixed with name.
|
|
135
|
-
|
|
121
|
+
ctx.setName("CustomName");
|
|
136
122
|
|
|
137
123
|
// Print messages to the console. These are suppressed by default in the app's "production" mode.
|
|
138
124
|
// You can also change which of these are printed and filter messages from certain contexts in the `createApp` options object.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
125
|
+
ctx.info("Verbose debugging info that might be useful to know");
|
|
126
|
+
ctx.log("Standard messages");
|
|
127
|
+
ctx.warn("Something bad might be happening");
|
|
128
|
+
ctx.error("Uh oh!");
|
|
143
129
|
|
|
144
130
|
// If you encounter a bad enough situation, you can halt and disconnect the entire app.
|
|
145
|
-
|
|
131
|
+
ctx.crash(new Error("BOOM"));
|
|
146
132
|
|
|
147
133
|
return <h1>Hello World!</h1>;
|
|
148
|
-
}
|
|
134
|
+
}
|
|
149
135
|
```
|
|
150
136
|
|
|
151
137
|
### Lifecycle Events
|
|
152
138
|
|
|
153
139
|
```jsx
|
|
154
|
-
|
|
155
|
-
|
|
140
|
+
function ExampleView(props, ctx) {
|
|
141
|
+
ctx.beforeMount(() => {
|
|
156
142
|
// Do something before this view's DOM nodes are created.
|
|
157
143
|
});
|
|
158
144
|
|
|
159
|
-
|
|
145
|
+
ctx.onMount(() => {
|
|
160
146
|
// Do something immediately after this view is connected to the DOM.
|
|
161
147
|
});
|
|
162
148
|
|
|
163
|
-
|
|
149
|
+
ctx.beforeUnmount(() => {
|
|
164
150
|
// Do something before removing this view from the DOM.
|
|
165
151
|
});
|
|
166
152
|
|
|
167
|
-
|
|
153
|
+
ctx.onUnmount(() => {
|
|
168
154
|
// Do some cleanup after this view is disconnected from the DOM.
|
|
169
155
|
});
|
|
170
156
|
|
|
171
157
|
return <h1>Hello World!</h1>;
|
|
172
|
-
}
|
|
158
|
+
}
|
|
173
159
|
```
|
|
174
160
|
|
|
175
161
|
### Displaying Children
|
|
@@ -177,15 +163,15 @@ const ExampleView = createView(function (props) {
|
|
|
177
163
|
The context has an `outlet` function that can be used to display children at a location of your choosing.
|
|
178
164
|
|
|
179
165
|
```js
|
|
180
|
-
|
|
166
|
+
function LayoutView(props, ctx) {
|
|
181
167
|
return (
|
|
182
168
|
<div className="layout">
|
|
183
|
-
<div className="content">{
|
|
169
|
+
<div className="content">{ctx.outlet()}</div>
|
|
184
170
|
</div>
|
|
185
171
|
);
|
|
186
|
-
}
|
|
172
|
+
}
|
|
187
173
|
|
|
188
|
-
|
|
174
|
+
function ExampleView() {
|
|
189
175
|
// <h1> and <p> are displayed inside LayoutView's outlet.
|
|
190
176
|
return (
|
|
191
177
|
<LayoutView>
|
|
@@ -193,7 +179,7 @@ const ExampleView = createView(function () {
|
|
|
193
179
|
<p>This is inside the box.</p>
|
|
194
180
|
</LayoutView>
|
|
195
181
|
);
|
|
196
|
-
}
|
|
182
|
+
}
|
|
197
183
|
```
|
|
198
184
|
|
|
199
185
|
### Watching States
|
|
@@ -201,16 +187,16 @@ const ExampleView = createView(function () {
|
|
|
201
187
|
The `watch` function starts observing when the view is connected and stops when disconnected. This takes care of cleaning up watchers so you don't have to worry about memory leaks.
|
|
202
188
|
|
|
203
189
|
```jsx
|
|
204
|
-
|
|
190
|
+
function ExampleView(props, ctx) {
|
|
205
191
|
const [$count, setCount] = createState(0);
|
|
206
192
|
|
|
207
193
|
// This callback will run when any states in the dependency array receive new values.
|
|
208
|
-
|
|
209
|
-
|
|
194
|
+
ctx.watch([$count], (count) => {
|
|
195
|
+
ctx.log("count is now", count);
|
|
210
196
|
});
|
|
211
197
|
|
|
212
198
|
// ...
|
|
213
|
-
}
|
|
199
|
+
}
|
|
214
200
|
```
|
|
215
201
|
|
|
216
202
|
### Context Variables
|
|
@@ -219,24 +205,24 @@ const ExampleView = createView(function (props) {
|
|
|
219
205
|
|
|
220
206
|
### Context Events
|
|
221
207
|
|
|
222
|
-
Events can be emitted from views and [stores](./stores.md) using `
|
|
208
|
+
Events can be emitted from views and [stores](./stores.md) using `ctx.emit(eventName, data)`. Context events will bubble up the view tree just like native browser events bubble up the DOM tree.
|
|
223
209
|
|
|
224
210
|
```js
|
|
225
|
-
|
|
211
|
+
ctx.on("eventName", (event) => {
|
|
226
212
|
event.type; // "eventName"
|
|
227
213
|
event.detail; // the value that was passed when the event was emitted (or undefined if none)
|
|
228
214
|
});
|
|
229
215
|
|
|
230
|
-
|
|
216
|
+
ctx.once("eventName", (event) => {
|
|
231
217
|
// Receive only once and then stop listening.
|
|
232
218
|
});
|
|
233
219
|
|
|
234
220
|
// Remove a listener by reference.
|
|
235
221
|
// Listener must be the same exact function that was passed to `on` or `once`.
|
|
236
|
-
|
|
222
|
+
ctx.off("eventName", listener);
|
|
237
223
|
|
|
238
224
|
// Emit an event.
|
|
239
|
-
|
|
225
|
+
ctx.emit("eventName", { value: "This object will be exposed as event.detail" });
|
|
240
226
|
```
|
|
241
227
|
|
|
242
228
|
### Bubbling
|
|
@@ -244,12 +230,12 @@ this.emit("eventName", { value: "This object will be exposed as event.detail" })
|
|
|
244
230
|
Events bubble up through the view tree unless `stopPropagation` is called by a listener. In the following example we have a view listening for events that are emitted from a child of a child.
|
|
245
231
|
|
|
246
232
|
```js
|
|
247
|
-
|
|
233
|
+
function ParentView(props, ctx) {
|
|
248
234
|
// Listen for greetings that bubble up.
|
|
249
|
-
|
|
235
|
+
ctx.on("greeting", (event) => {
|
|
250
236
|
const { name, message } = event.detail;
|
|
251
237
|
|
|
252
|
-
|
|
238
|
+
ctx.log(`${name} says "${message}"!`);
|
|
253
239
|
});
|
|
254
240
|
|
|
255
241
|
return (
|
|
@@ -257,10 +243,10 @@ const ParentView = createView(function () {
|
|
|
257
243
|
<ChildView />
|
|
258
244
|
</div>
|
|
259
245
|
);
|
|
260
|
-
}
|
|
246
|
+
}
|
|
261
247
|
|
|
262
|
-
|
|
263
|
-
|
|
248
|
+
function ChildView(props, ctx) {
|
|
249
|
+
ctx.on("greeting", (event) => {
|
|
264
250
|
// Let's perform some censorship.
|
|
265
251
|
// If propagation is stopped this event will not bubble any further and ParentView won't see it.
|
|
266
252
|
if (containsForbiddenKnowledge(event.message)) {
|
|
@@ -273,9 +259,9 @@ const ChildView = createView(function () {
|
|
|
273
259
|
<ChildOfChildView />
|
|
274
260
|
</div>
|
|
275
261
|
);
|
|
276
|
-
}
|
|
262
|
+
}
|
|
277
263
|
|
|
278
|
-
|
|
264
|
+
function ChildOfChildView(props, ctx) {
|
|
279
265
|
return (
|
|
280
266
|
<form
|
|
281
267
|
onSubmit={(e) => {
|
|
@@ -288,7 +274,7 @@ const ChildOfChildView = createView(function () {
|
|
|
288
274
|
const message = e.currentTarget.message.value;
|
|
289
275
|
|
|
290
276
|
// Emit!
|
|
291
|
-
|
|
277
|
+
ctx.emit("greeting", { name, message });
|
|
292
278
|
}}
|
|
293
279
|
>
|
|
294
280
|
<input type="text" name="name" placeholder="Your Name" />
|
|
@@ -296,7 +282,7 @@ const ChildOfChildView = createView(function () {
|
|
|
296
282
|
<button type="submit">Submit</button>
|
|
297
283
|
</form>
|
|
298
284
|
);
|
|
299
|
-
}
|
|
285
|
+
}
|
|
300
286
|
```
|
|
301
287
|
|
|
302
288
|
---
|
package/notes/scratch.md
CHANGED
|
@@ -1,5 +1,140 @@
|
|
|
1
1
|
# Scratch Note
|
|
2
2
|
|
|
3
|
+
Idea: Monomorphic app context. Replaces StoreContext, ViewContext, etc.
|
|
4
|
+
|
|
5
|
+
Routes are baked into the app once again, but
|
|
6
|
+
|
|
7
|
+
```jsx
|
|
8
|
+
import { createRoot } from "@manyducks.co/dolla";
|
|
9
|
+
import { example } from "./stores/example.js";
|
|
10
|
+
|
|
11
|
+
const root = createRoot();
|
|
12
|
+
|
|
13
|
+
root.use(example());
|
|
14
|
+
|
|
15
|
+
async function auth(_, state, redirect) {
|
|
16
|
+
// route context
|
|
17
|
+
// Routes run through each callback until one resolves to a renderable value.
|
|
18
|
+
// If redirect is called, the route is re-matched and no further callbacks are run for this route.
|
|
19
|
+
|
|
20
|
+
if (state.auth == null) {
|
|
21
|
+
redirect("/login");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
root.route("/users/*", auth, (C) => {
|
|
26
|
+
C.route("/{#id}/*", (C) => {
|
|
27
|
+
C.route("/", (C) => <UserDetailRoute userId={C.params.id} />);
|
|
28
|
+
C.route("*", "./");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
root.route("/users/*", auth, (route) => {
|
|
33
|
+
route("/{#id}/*", (route) => {
|
|
34
|
+
// TODO: It's possible to reference the wrong 'route'
|
|
35
|
+
// Track active context and throw error if the one you call belongs to the wrong context?
|
|
36
|
+
route("/", (_, state) => <UserDetailView userId={state.params.id} />);
|
|
37
|
+
route("*", "./");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function ExampleView(props, ctx) {
|
|
42
|
+
// ctx.routes returns a special type of outlet that renders children based on
|
|
43
|
+
// the route segments that come after the ones at this ctx.
|
|
44
|
+
|
|
45
|
+
// The weakness of this idea is that routes can't be validated without initializing views.
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<Suspense fallback={<span>Loading...</span>}>
|
|
49
|
+
{ctx.routes((route) => {
|
|
50
|
+
route("/subroute", () => <OtherView />);
|
|
51
|
+
|
|
52
|
+
// Routes can be async.
|
|
53
|
+
route("/other", () => import("some-module"));
|
|
54
|
+
})}
|
|
55
|
+
</Suspense>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Also Suspense. This can be simply implemented with events.
|
|
60
|
+
ctx.emit("suspense:begin", uniqueId);
|
|
61
|
+
// Then when done:
|
|
62
|
+
ctx.emit("suspense:end", uniqueId);
|
|
63
|
+
|
|
64
|
+
// The nearest Suspense view will track ids which are in suspense and show fallback content in the meantime.
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Suspense(props, ctx) {
|
|
68
|
+
const [$tracked, setTracked] = createState({});
|
|
69
|
+
|
|
70
|
+
ctx.on("suspense:begin", (e) => {
|
|
71
|
+
setTracked((tracked) => {
|
|
72
|
+
return {
|
|
73
|
+
...tracked,
|
|
74
|
+
[e.detail]: new Date(),
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
ctx.on("suspense:end", (e) => {
|
|
80
|
+
setTracked((tracked) => {
|
|
81
|
+
const updated = Object.assign({}, tracked);
|
|
82
|
+
delete updated[e.detail];
|
|
83
|
+
return updated;
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// TODO: Hide suspended view without unmounting it. This might take special logic.
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Can also pass markup directly if you don't need the context.
|
|
91
|
+
root.route("/", auth, <HomeRoute />);
|
|
92
|
+
|
|
93
|
+
// Static redirect.
|
|
94
|
+
root.route("*", "/");
|
|
95
|
+
|
|
96
|
+
// Programmatic redirect.
|
|
97
|
+
root.route("*", (C) => {
|
|
98
|
+
C.log("hit wildcard");
|
|
99
|
+
C.redirect("/");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
root.mount(document.body);
|
|
103
|
+
|
|
104
|
+
// generate an HTML string for server side rendering.
|
|
105
|
+
root.toString("/some/path");
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
class ClockStore extends Store {
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
constructor() {
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class CounterStore extends Store {
|
|
120
|
+
// Could have better name. This will catch any
|
|
121
|
+
// this.emit('counter:increment') or this.emit('counter:decrement') calls
|
|
122
|
+
// and update the state according to these functions.
|
|
123
|
+
value = new Emittable('counter', 0, {
|
|
124
|
+
increment: state => state + 1,
|
|
125
|
+
decrement: state => state - 1
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
type CounterEvents = {
|
|
130
|
+
increment: [amount: number];
|
|
131
|
+
decrement: [amount: number];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
|
|
3
138
|
---
|
|
4
139
|
|
|
5
140
|
Bring the $ back and the name full circle.
|
package/notes/stores.md
CHANGED
|
@@ -3,71 +3,53 @@
|
|
|
3
3
|
Ideas for updating the API.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
|
-
import {
|
|
6
|
+
import { attachStore, useStore } from "@manyducks.co/dolla";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function CounterStore(initialCount = 0, ctx) {
|
|
9
9
|
const [$value, setValue] = createState(initialCount);
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
ctx.on("counter:increment", (e) => {
|
|
12
12
|
e.stopPropagation(); // Stop this event from bubbling up to counters at higher levels (if any).
|
|
13
13
|
setValue((current) => current + 1);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
ctx.on("counter:decrement", (e) => {
|
|
17
17
|
e.stopPropagation();
|
|
18
18
|
setValue((current) => current - 1);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
// Events can be emitted from this context in a store.
|
|
22
|
-
|
|
22
|
+
ctx.emit("otherEvent");
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
ctx.onMount(() => {
|
|
25
25
|
// Setup
|
|
26
26
|
// This is called based on the context the store is attached to.
|
|
27
27
|
// If Dolla, it's called when the app is mounted. If ViewContext, it's called when the view is mounted.
|
|
28
28
|
});
|
|
29
|
-
|
|
29
|
+
ctx.onUnmount(() => {
|
|
30
30
|
// Cleanup
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Context variables will be accessible on the same context (e.g. the view this is attached to and below)
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
ctx.get("context variable");
|
|
35
|
+
ctx.set("context variable", "context variable value");
|
|
36
36
|
|
|
37
37
|
// Stores don't have to return anything, but if they do it becomes accessible by using `useStore(ctx, Store)`.
|
|
38
38
|
return $value;
|
|
39
|
-
}
|
|
39
|
+
}
|
|
40
40
|
|
|
41
41
|
// Attach it to the app.
|
|
42
|
-
Dolla.attachStore(
|
|
42
|
+
Dolla.attachStore(CounterStore, 0);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
function ExampleView(props, ctx) {
|
|
45
45
|
// useStore lets you access the return value
|
|
46
46
|
// but the events will still be received and handled regardless
|
|
47
|
-
const $count =
|
|
48
|
-
|
|
49
|
-
// Convenience helper to attach and use in one step?
|
|
50
|
-
const $count = this.attachAndUseStore(Counter(0));
|
|
47
|
+
const $count = ctx.useStore(Counter);
|
|
51
48
|
|
|
52
49
|
return html`
|
|
53
50
|
<button onclick=${() => this.emit("counter:decrement")}>-1</button>
|
|
54
51
|
<span>${$count}</span>
|
|
55
52
|
<button onclick=${() => this.emit("counter:increment")}>+1</button>
|
|
56
53
|
`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ViewContext is also still passed as a second argument if you'd rather use arrow functions to define views.
|
|
60
|
-
const ExampleView = createView((props, self) => {
|
|
61
|
-
// useStore lets you access the return value
|
|
62
|
-
// but the events will still be received and handled regardless
|
|
63
|
-
const $count = self.useStore(Counter);
|
|
64
|
-
|
|
65
|
-
return html`
|
|
66
|
-
<button onclick=${() => self.emit("counter:decrement")}>-1</button>
|
|
67
|
-
<span>${$count}</span>
|
|
68
|
-
<button onclick=${() => self.emit("counter:increment")}>+1</button>
|
|
69
|
-
`;
|
|
70
|
-
});
|
|
54
|
+
}
|
|
71
55
|
```
|
|
72
|
-
|
|
73
|
-
This means `createStore` returns a function that is called to create a Store instance. The instance is
|
package/package.json
CHANGED
package/build.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import esbuild from "esbuild";
|
|
3
|
-
|
|
4
|
-
esbuild
|
|
5
|
-
.build({
|
|
6
|
-
entryPoints: ["src/index.ts"],
|
|
7
|
-
bundle: true,
|
|
8
|
-
metafile: true,
|
|
9
|
-
sourcemap: true,
|
|
10
|
-
// minify: process.env.NODE_ENV === "production",
|
|
11
|
-
outdir: "dist",
|
|
12
|
-
format: "esm",
|
|
13
|
-
})
|
|
14
|
-
.then((result) => {
|
|
15
|
-
fs.writeFileSync("esbuild-meta.json", JSON.stringify(result.metafile));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
esbuild.build({
|
|
19
|
-
entryPoints: ["src/jsx-runtime.js"],
|
|
20
|
-
bundle: false,
|
|
21
|
-
minify: false,
|
|
22
|
-
sourcemap: true,
|
|
23
|
-
outdir: "dist",
|
|
24
|
-
format: "esm",
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
esbuild.build({
|
|
28
|
-
entryPoints: ["src/jsx-dev-runtime.js"],
|
|
29
|
-
bundle: false,
|
|
30
|
-
minify: false,
|
|
31
|
-
sourcemap: true,
|
|
32
|
-
outdir: "dist",
|
|
33
|
-
format: "esm",
|
|
34
|
-
});
|