create-foldkit-app 0.29.0 → 0.29.1
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/dist/templates/base/AGENTS.md +5 -112
- package/dist/templates/base/FOLDKIT.md +144 -0
- package/dist/templates/examples/api-cache/src/main.ts +36 -40
- package/dist/templates/examples/auth/src/main.ts +16 -15
- package/dist/templates/examples/auth/src/page/loggedIn/update.ts +10 -11
- package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +44 -55
- package/dist/templates/examples/auth/src/page/loggedOut/update.ts +8 -12
- package/dist/templates/examples/auth/src/update.ts +67 -73
- package/dist/templates/examples/canvas-art/src/main.ts +32 -31
- package/dist/templates/examples/charting/src/init.test.ts +6 -3
- package/dist/templates/examples/charting/src/init.ts +4 -4
- package/dist/templates/examples/charting/src/update.ts +34 -38
- package/dist/templates/examples/counter/src/main.ts +13 -13
- package/dist/templates/examples/counters/src/counter.ts +9 -8
- package/dist/templates/examples/counters/src/main.ts +20 -26
- package/dist/templates/examples/crash-view/src/main.ts +5 -3
- package/dist/templates/examples/embedding/src/main.ts +11 -9
- package/dist/templates/examples/form/src/main.ts +79 -89
- package/dist/templates/examples/generative-art/src/main.ts +6 -4
- package/dist/templates/examples/generative-art/src/update.ts +26 -23
- package/dist/templates/examples/interrupting-commands/src/main.ts +68 -66
- package/dist/templates/examples/job-application/src/main.ts +3 -4
- package/dist/templates/examples/job-application/src/step/attachments.ts +16 -19
- package/dist/templates/examples/job-application/src/step/coverLetter.ts +5 -5
- package/dist/templates/examples/job-application/src/step/education/education.ts +12 -17
- package/dist/templates/examples/job-application/src/step/education/entry.ts +29 -42
- package/dist/templates/examples/job-application/src/step/personalInfo.ts +35 -44
- package/dist/templates/examples/job-application/src/step/skills/entry.ts +15 -21
- package/dist/templates/examples/job-application/src/step/skills/skills.ts +12 -17
- package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +41 -58
- package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +12 -17
- package/dist/templates/examples/job-application/src/update.ts +26 -29
- package/dist/templates/examples/kanban/src/main.ts +3 -4
- package/dist/templates/examples/kanban/src/update.ts +31 -31
- package/dist/templates/examples/managed-resource-layer/src/main.ts +30 -34
- package/dist/templates/examples/map/src/main.ts +48 -56
- package/dist/templates/examples/pixel-art/src/comparison.bench.test.ts +34 -13
- package/dist/templates/examples/pixel-art/src/main.bench.ts +3 -3
- package/dist/templates/examples/pixel-art/src/main.ts +3 -4
- package/dist/templates/examples/pixel-art/src/update.ts +109 -100
- package/dist/templates/examples/query-sync/src/main.ts +27 -25
- package/dist/templates/examples/route-transitions/src/main.ts +46 -43
- package/dist/templates/examples/route-transitions/src/story.test.ts +11 -11
- package/dist/templates/examples/routing/src/main.ts +16 -14
- package/dist/templates/examples/routing/src/page/people.ts +23 -28
- package/dist/templates/examples/shopping-cart/src/main.ts +41 -42
- package/dist/templates/examples/shopping-cart/src/page/products.ts +28 -35
- package/dist/templates/examples/slow-warnings/src/main.ts +23 -32
- package/dist/templates/examples/snake/src/main.ts +135 -143
- package/dist/templates/examples/ssg/src/main.ts +20 -17
- package/dist/templates/examples/ssr/src/main.ts +20 -24
- package/dist/templates/examples/state-machine/src/main.ts +24 -11
- package/dist/templates/examples/state-machine/src/story.test.ts +2 -2
- package/dist/templates/examples/stopwatch/src/main.ts +42 -50
- package/dist/templates/examples/todo/src/main.ts +152 -160
- package/dist/templates/examples/ui-showcase/src/main.ts +53 -54
- package/dist/templates/examples/ui-showcase/src/scene.test.ts +2 -2
- package/dist/templates/examples/ui-showcase/src/story.test.ts +2 -2
- package/dist/templates/examples/ui-showcase/src/ui/init.ts +6 -7
- package/dist/templates/examples/ui-showcase/src/ui/update.ts +265 -336
- package/dist/templates/examples/view-transitions/src/main.ts +18 -17
- package/dist/templates/examples/weather/src/main.ts +33 -40
- package/dist/templates/examples/web-components/src/main.ts +14 -17
- package/dist/templates/examples/websocket-chat/src/main.ts +44 -50
- package/dist/templates/release.json +8 -8
- package/dist/templates/rendering/ssg/src/main.ts +20 -17
- package/dist/templates/rendering/ssr/src/main.ts +20 -24
- package/package.json +1 -1
|
@@ -147,8 +147,8 @@ const nextSequenceNumber = (
|
|
|
147
147
|
|
|
148
148
|
const logTransition =
|
|
149
149
|
(transition: AppTransition): Step =>
|
|
150
|
-
model =>
|
|
151
|
-
evo(model, {
|
|
150
|
+
model => ({
|
|
151
|
+
model: evo(model, {
|
|
152
152
|
transitionLog: transitionLog =>
|
|
153
153
|
pipe(
|
|
154
154
|
transitionLog,
|
|
@@ -160,54 +160,58 @@ const logTransition =
|
|
|
160
160
|
Array.take(MAX_LOGGED_TRANSITIONS),
|
|
161
161
|
),
|
|
162
162
|
}),
|
|
163
|
-
|
|
164
|
-
]
|
|
163
|
+
})
|
|
165
164
|
|
|
166
165
|
const loadCatalogOnGalleryEntry =
|
|
167
166
|
(transition: AppTransition): Step =>
|
|
168
167
|
model =>
|
|
169
168
|
Transition.isEntering(transition, 'Gallery') &&
|
|
170
169
|
model.catalogStatus !== 'Loading'
|
|
171
|
-
?
|
|
172
|
-
|
|
170
|
+
? {
|
|
171
|
+
model: evo(model, { catalogStatus: () => 'Loading' }),
|
|
172
|
+
commands: [LoadCatalog()],
|
|
173
|
+
}
|
|
174
|
+
: { model }
|
|
173
175
|
|
|
174
176
|
const loadPaintingOnEntry =
|
|
175
177
|
(transition: AppTransition): Step =>
|
|
176
178
|
model =>
|
|
177
179
|
Option.match(Transition.entered(transition, 'Painting'), {
|
|
178
|
-
onNone: () =>
|
|
179
|
-
onSome: ({ paintingId }) =>
|
|
180
|
-
evo(model, {
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
onNone: () => ({ model }),
|
|
181
|
+
onSome: ({ paintingId }) => ({
|
|
182
|
+
model: evo(model, {
|
|
183
|
+
paintingStatus: () => PaintingLoading({ paintingId }),
|
|
184
|
+
}),
|
|
185
|
+
commands: [LoadPainting({ paintingId })],
|
|
186
|
+
}),
|
|
183
187
|
})
|
|
184
188
|
|
|
185
189
|
const reloadPaintingOnIdChange =
|
|
186
190
|
(transition: AppTransition): Step =>
|
|
187
191
|
model =>
|
|
188
192
|
Option.match(Transition.stayed(transition, 'Painting'), {
|
|
189
|
-
onNone: () =>
|
|
193
|
+
onNone: () => ({ model }),
|
|
190
194
|
onSome: ({ previousRoute, nextRoute }) =>
|
|
191
195
|
previousRoute.paintingId === nextRoute.paintingId
|
|
192
|
-
?
|
|
193
|
-
:
|
|
194
|
-
evo(model, {
|
|
196
|
+
? { model }
|
|
197
|
+
: {
|
|
198
|
+
model: evo(model, {
|
|
195
199
|
paintingStatus: () =>
|
|
196
200
|
PaintingLoading({ paintingId: nextRoute.paintingId }),
|
|
197
201
|
}),
|
|
198
|
-
[LoadPainting({ paintingId: nextRoute.paintingId })],
|
|
199
|
-
|
|
202
|
+
commands: [LoadPainting({ paintingId: nextRoute.paintingId })],
|
|
203
|
+
},
|
|
200
204
|
})
|
|
201
205
|
|
|
202
206
|
const saveDraftOnStudioExit =
|
|
203
207
|
(transition: AppTransition): Step =>
|
|
204
208
|
model =>
|
|
205
209
|
Option.match(Transition.exited(transition, 'Studio'), {
|
|
206
|
-
onNone: () =>
|
|
210
|
+
onNone: () => ({ model }),
|
|
207
211
|
onSome: () =>
|
|
208
212
|
model.studioDraft === ''
|
|
209
|
-
?
|
|
210
|
-
:
|
|
213
|
+
? { model }
|
|
214
|
+
: { model, commands: [SaveDraft({ draft: model.studioDraft })] },
|
|
211
215
|
})
|
|
212
216
|
|
|
213
217
|
const handleTransition = (
|
|
@@ -224,18 +228,21 @@ const handleTransition = (
|
|
|
224
228
|
|
|
225
229
|
export const update = (model: Model, message: Message) =>
|
|
226
230
|
Message.match<UpdateReturn>(message, {
|
|
227
|
-
CompletedNavigateInternal: () =>
|
|
228
|
-
CompletedLoadExternal: () =>
|
|
231
|
+
CompletedNavigateInternal: () => ({ model }),
|
|
232
|
+
CompletedLoadExternal: () => ({ model }),
|
|
229
233
|
|
|
230
234
|
ClickedLink: ({ request }) =>
|
|
231
235
|
M.value(request).pipe(
|
|
232
236
|
withUpdateReturn,
|
|
233
237
|
M.tagsExhaustive({
|
|
234
|
-
Internal: ({ url }) =>
|
|
238
|
+
Internal: ({ url }) => ({
|
|
235
239
|
model,
|
|
236
|
-
[NavigateInternal({ url: urlToString(url) })],
|
|
237
|
-
|
|
238
|
-
External: ({ href }) =>
|
|
240
|
+
commands: [NavigateInternal({ url: urlToString(url) })],
|
|
241
|
+
}),
|
|
242
|
+
External: ({ href }) => ({
|
|
243
|
+
model,
|
|
244
|
+
commands: [LoadExternal({ href })],
|
|
245
|
+
}),
|
|
239
246
|
}),
|
|
240
247
|
),
|
|
241
248
|
|
|
@@ -248,31 +255,27 @@ export const update = (model: Model, message: Message) =>
|
|
|
248
255
|
)
|
|
249
256
|
},
|
|
250
257
|
|
|
251
|
-
SucceededLoadCatalog: () =>
|
|
252
|
-
evo(model, { catalogStatus: () => 'Ready' }),
|
|
253
|
-
|
|
254
|
-
],
|
|
258
|
+
SucceededLoadCatalog: () => ({
|
|
259
|
+
model: evo(model, { catalogStatus: () => 'Ready' }),
|
|
260
|
+
}),
|
|
255
261
|
|
|
256
262
|
SucceededLoadPainting: ({ paintingId }) =>
|
|
257
263
|
model.paintingStatus._tag === 'PaintingLoading' &&
|
|
258
264
|
model.paintingStatus.paintingId === paintingId
|
|
259
|
-
?
|
|
260
|
-
evo(model, {
|
|
265
|
+
? {
|
|
266
|
+
model: evo(model, {
|
|
261
267
|
paintingStatus: () => PaintingReady({ paintingId }),
|
|
262
268
|
}),
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: [model, []],
|
|
269
|
+
}
|
|
270
|
+
: { model },
|
|
266
271
|
|
|
267
|
-
UpdatedStudioDraft: ({ value }) =>
|
|
268
|
-
evo(model, { studioDraft: () => value }),
|
|
269
|
-
|
|
270
|
-
],
|
|
272
|
+
UpdatedStudioDraft: ({ value }) => ({
|
|
273
|
+
model: evo(model, { studioDraft: () => value }),
|
|
274
|
+
}),
|
|
271
275
|
|
|
272
|
-
SucceededSaveDraft: ({ draft }) =>
|
|
273
|
-
evo(model, { maybeSavedDraft: () => Option.some(draft) }),
|
|
274
|
-
|
|
275
|
-
],
|
|
276
|
+
SucceededSaveDraft: ({ draft }) => ({
|
|
277
|
+
model: evo(model, { maybeSavedDraft: () => Option.some(draft) }),
|
|
278
|
+
}),
|
|
276
279
|
})
|
|
277
280
|
|
|
278
281
|
// INIT
|
|
@@ -40,26 +40,26 @@ const modelOn = (route: AppRoute): Model =>
|
|
|
40
40
|
|
|
41
41
|
describe('init', () => {
|
|
42
42
|
test('a cold load into the gallery logs the transition and loads the catalog', () => {
|
|
43
|
-
const
|
|
43
|
+
const init_ = init(urlOrThrow('http://localhost/gallery'))
|
|
44
44
|
|
|
45
|
-
expect(model.route._tag).toBe('Gallery')
|
|
46
|
-
expect(model.catalogStatus).toBe('Loading')
|
|
47
|
-
expect(model.transitionLog).toHaveLength(1)
|
|
45
|
+
expect(init_.model.route._tag).toBe('Gallery')
|
|
46
|
+
expect(init_.model.catalogStatus).toBe('Loading')
|
|
47
|
+
expect(init_.model.transitionLog).toHaveLength(1)
|
|
48
48
|
expect(
|
|
49
|
-
Array.map(model.transitionLog, entry => entry.maybePreviousRoute),
|
|
49
|
+
Array.map(init_.model.transitionLog, entry => entry.maybePreviousRoute),
|
|
50
50
|
).toStrictEqual([Option.none()])
|
|
51
|
-
expect(Array.map(commands, command => command.name)).toContain(
|
|
51
|
+
expect(Array.map(init_.commands ?? [], command => command.name)).toContain(
|
|
52
52
|
'LoadCatalog',
|
|
53
53
|
)
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
test('a cold load into the home route loads nothing', () => {
|
|
57
|
-
const
|
|
57
|
+
const homeInit = init(urlOrThrow('http://localhost/'))
|
|
58
58
|
|
|
59
|
-
expect(model.route._tag).toBe('Home')
|
|
60
|
-
expect(model.catalogStatus).toBe('Idle')
|
|
61
|
-
expect(model.transitionLog).toHaveLength(1)
|
|
62
|
-
expect(commands).toStrictEqual([])
|
|
59
|
+
expect(homeInit.model.route._tag).toBe('Home')
|
|
60
|
+
expect(homeInit.model.catalogStatus).toBe('Idle')
|
|
61
|
+
expect(homeInit.model.transitionLog).toHaveLength(1)
|
|
62
|
+
expect(homeInit.commands ?? []).toStrictEqual([])
|
|
63
63
|
})
|
|
64
64
|
})
|
|
65
65
|
|
|
@@ -69,14 +69,13 @@ export const init: Runtime.RoutingApplicationInit<Model, Message> = (
|
|
|
69
69
|
M.orElse(() => PeopleRoute({ searchText: Option.none() })),
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Command.mapMessages(peopleCommands, childMessage =>
|
|
72
|
+
const peopleInit = People.init(initialPeopleRoute)
|
|
73
|
+
return {
|
|
74
|
+
model: { route, peoplePage: peopleInit.model },
|
|
75
|
+
commands: Command.mapMessages(peopleInit.commands, childMessage =>
|
|
77
76
|
Message.GotPeopleMessage({ message: childMessage }),
|
|
78
77
|
),
|
|
79
|
-
|
|
78
|
+
}
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
// COMMAND
|
|
@@ -97,7 +96,7 @@ const LoadExternal = Command.define('LoadExternal', {
|
|
|
97
96
|
|
|
98
97
|
// UPDATE
|
|
99
98
|
|
|
100
|
-
type UpdateReturn =
|
|
99
|
+
type UpdateReturn = Update.Return<Model, Message>
|
|
101
100
|
const withUpdateReturn = M.withReturnType<UpdateReturn>()
|
|
102
101
|
|
|
103
102
|
const foldPeopleEntry = <Input>(
|
|
@@ -117,22 +116,25 @@ const foldPeopleRouteChanged = foldPeopleEntry(People.informRouteChanged)
|
|
|
117
116
|
|
|
118
117
|
const setRoute =
|
|
119
118
|
(nextRoute: AppRoute): Update.Step<Model, Message> =>
|
|
120
|
-
model =>
|
|
119
|
+
model => ({ model: evo(model, { route: () => nextRoute }) })
|
|
121
120
|
|
|
122
121
|
export const update = (model: Model, message: Message) =>
|
|
123
122
|
Message.match<UpdateReturn>(message, {
|
|
124
|
-
CompletedNavigateInternal: () =>
|
|
125
|
-
CompletedLoadExternal: () =>
|
|
123
|
+
CompletedNavigateInternal: () => ({ model }),
|
|
124
|
+
CompletedLoadExternal: () => ({ model }),
|
|
126
125
|
|
|
127
126
|
ClickedLink: ({ request }) =>
|
|
128
127
|
M.value(request).pipe(
|
|
129
128
|
withUpdateReturn,
|
|
130
129
|
M.tagsExhaustive({
|
|
131
|
-
Internal: ({ url }) =>
|
|
130
|
+
Internal: ({ url }) => ({
|
|
132
131
|
model,
|
|
133
|
-
[NavigateInternal({ url: urlToString(url) })],
|
|
134
|
-
|
|
135
|
-
External: ({ href }) =>
|
|
132
|
+
commands: [NavigateInternal({ url: urlToString(url) })],
|
|
133
|
+
}),
|
|
134
|
+
External: ({ href }) => ({
|
|
135
|
+
model,
|
|
136
|
+
commands: [LoadExternal({ href })],
|
|
137
|
+
}),
|
|
136
138
|
}),
|
|
137
139
|
),
|
|
138
140
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
String,
|
|
9
9
|
pipe,
|
|
10
10
|
} from 'effect'
|
|
11
|
-
import { Command, Submodel } from 'foldkit'
|
|
11
|
+
import { Command, Submodel, type Update } from 'foldkit'
|
|
12
12
|
import { Html, HtmlBuilder } from 'foldkit/html'
|
|
13
13
|
import { defineMessageUnion } from 'foldkit/message'
|
|
14
14
|
import { pushUrl } from 'foldkit/navigation'
|
|
@@ -110,18 +110,18 @@ export type Message = typeof Message.Type
|
|
|
110
110
|
|
|
111
111
|
// INIT
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
):
|
|
113
|
+
type InitReturn = Update.Return<Model, Message>
|
|
114
|
+
|
|
115
|
+
export const init = (route: PeopleRoute): InitReturn => {
|
|
116
116
|
const searchText = routeSearchText(route)
|
|
117
|
-
return
|
|
118
|
-
{
|
|
117
|
+
return {
|
|
118
|
+
model: {
|
|
119
119
|
searchInput: searchText,
|
|
120
120
|
searchHistory: addSearchToHistory([], searchText),
|
|
121
121
|
results: SearchLoading(),
|
|
122
122
|
},
|
|
123
|
-
[FetchPeople({ searchText })],
|
|
124
|
-
|
|
123
|
+
commands: [FetchPeople({ searchText })],
|
|
124
|
+
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// COMMAND
|
|
@@ -151,48 +151,43 @@ export const FetchPeople = Command.define('FetchPeople', {
|
|
|
151
151
|
|
|
152
152
|
// UPDATE
|
|
153
153
|
|
|
154
|
-
export type UpdateReturn =
|
|
155
|
-
Model,
|
|
156
|
-
ReadonlyArray<Command.Command<Message>>,
|
|
157
|
-
]
|
|
154
|
+
export type UpdateReturn = Update.Return<Model, Message>
|
|
158
155
|
|
|
159
156
|
export const update = (model: Model, message: Message) =>
|
|
160
157
|
Message.match<UpdateReturn>(message, {
|
|
161
|
-
ChangedSearchInput: ({ value }) =>
|
|
162
|
-
evo(model, { searchInput: () => value }),
|
|
163
|
-
|
|
164
|
-
],
|
|
158
|
+
ChangedSearchInput: ({ value }) => ({
|
|
159
|
+
model: evo(model, { searchInput: () => value }),
|
|
160
|
+
}),
|
|
165
161
|
|
|
166
|
-
SubmittedSearch: () =>
|
|
162
|
+
SubmittedSearch: () => ({
|
|
167
163
|
model,
|
|
168
|
-
[
|
|
164
|
+
commands: [
|
|
169
165
|
PushSearchUrl({
|
|
170
166
|
searchText: Option.fromNullishOr(model.searchInput || null),
|
|
171
167
|
}),
|
|
172
168
|
],
|
|
173
|
-
|
|
169
|
+
}),
|
|
174
170
|
|
|
175
171
|
ChangedRoute: ({ route }) => {
|
|
176
172
|
const searchText = routeSearchText(route)
|
|
177
|
-
return
|
|
178
|
-
evo(model, {
|
|
173
|
+
return {
|
|
174
|
+
model: evo(model, {
|
|
179
175
|
searchInput: () => searchText,
|
|
180
176
|
searchHistory: searchHistory =>
|
|
181
177
|
addSearchToHistory(searchHistory, searchText),
|
|
182
178
|
results: () => SearchLoading(),
|
|
183
179
|
}),
|
|
184
|
-
[FetchPeople({ searchText })],
|
|
185
|
-
|
|
180
|
+
commands: [FetchPeople({ searchText })],
|
|
181
|
+
}
|
|
186
182
|
},
|
|
187
183
|
|
|
188
|
-
SucceededFetchPeople: ({ query, people: fetchedPeople }) =>
|
|
189
|
-
evo(model, {
|
|
184
|
+
SucceededFetchPeople: ({ query, people: fetchedPeople }) => ({
|
|
185
|
+
model: evo(model, {
|
|
190
186
|
results: () => SearchLoaded({ query, people: fetchedPeople }),
|
|
191
187
|
}),
|
|
192
|
-
|
|
193
|
-
],
|
|
188
|
+
}),
|
|
194
189
|
|
|
195
|
-
CompletedPushSearchUrl: () =>
|
|
190
|
+
CompletedPushSearchUrl: () => ({ model }),
|
|
196
191
|
})
|
|
197
192
|
|
|
198
193
|
/** Tells the People page that the route changed. People does not own the
|
|
@@ -51,16 +51,15 @@ export type Message = typeof Message.Type
|
|
|
51
51
|
export const init: Runtime.RoutingApplicationInit<Model, Message> = (
|
|
52
52
|
url: Url,
|
|
53
53
|
) => {
|
|
54
|
-
return
|
|
55
|
-
{
|
|
54
|
+
return {
|
|
55
|
+
model: {
|
|
56
56
|
route: urlToAppRoute(url),
|
|
57
57
|
cart: [],
|
|
58
58
|
deliveryInstructions: '',
|
|
59
59
|
orderPlaced: false,
|
|
60
60
|
productsPage: Products.init(products),
|
|
61
61
|
},
|
|
62
|
-
|
|
63
|
-
]
|
|
62
|
+
}
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
// COMMAND
|
|
@@ -81,7 +80,7 @@ const LoadExternal = Command.define('LoadExternal', {
|
|
|
81
80
|
|
|
82
81
|
// UPDATE
|
|
83
82
|
|
|
84
|
-
type UpdateReturn =
|
|
83
|
+
type UpdateReturn = Update.Return<Model, Message>
|
|
85
84
|
const withUpdateReturn = M.withReturnType<UpdateReturn>()
|
|
86
85
|
|
|
87
86
|
const foldProductsOutMessage = M.type<Products.OutMessage>().pipe(
|
|
@@ -89,13 +88,17 @@ const foldProductsOutMessage = M.type<Products.OutMessage>().pipe(
|
|
|
89
88
|
M.tagsExhaustive({
|
|
90
89
|
AddedToCart:
|
|
91
90
|
({ item }) =>
|
|
92
|
-
model =>
|
|
91
|
+
model => ({ model: evo(model, { cart: Cart.addItem(item) }) }),
|
|
93
92
|
IncrementedQuantity:
|
|
94
93
|
({ itemId }) =>
|
|
95
|
-
model =>
|
|
94
|
+
model => ({
|
|
95
|
+
model: evo(model, { cart: Cart.incrementQuantity(itemId) }),
|
|
96
|
+
}),
|
|
96
97
|
DecrementedQuantity:
|
|
97
98
|
({ itemId }) =>
|
|
98
|
-
model =>
|
|
99
|
+
model => ({
|
|
100
|
+
model: evo(model, { cart: Cart.decrementQuantity(itemId) }),
|
|
101
|
+
}),
|
|
99
102
|
}),
|
|
100
103
|
)
|
|
101
104
|
|
|
@@ -110,74 +113,70 @@ const foldProducts = Update.foldChild({
|
|
|
110
113
|
|
|
111
114
|
export const update = (model: Model, message: Message) =>
|
|
112
115
|
Message.match<UpdateReturn>(message, {
|
|
113
|
-
CompletedNavigateInternal: () =>
|
|
114
|
-
CompletedLoadExternal: () =>
|
|
116
|
+
CompletedNavigateInternal: () => ({ model }),
|
|
117
|
+
CompletedLoadExternal: () => ({ model }),
|
|
115
118
|
|
|
116
119
|
ClickedLink: ({ request }) =>
|
|
117
120
|
M.value(request).pipe(
|
|
118
121
|
withUpdateReturn,
|
|
119
122
|
M.tagsExhaustive({
|
|
120
|
-
Internal: ({ url }) =>
|
|
123
|
+
Internal: ({ url }) => ({
|
|
121
124
|
model,
|
|
122
|
-
[NavigateInternal({ url: urlToString(url) })],
|
|
123
|
-
|
|
125
|
+
commands: [NavigateInternal({ url: urlToString(url) })],
|
|
126
|
+
}),
|
|
124
127
|
|
|
125
|
-
External: ({ href }) =>
|
|
128
|
+
External: ({ href }) => ({
|
|
129
|
+
model,
|
|
130
|
+
commands: [LoadExternal({ href })],
|
|
131
|
+
}),
|
|
126
132
|
}),
|
|
127
133
|
),
|
|
128
134
|
|
|
129
|
-
ChangedUrl: ({ url }) =>
|
|
130
|
-
evo(model, {
|
|
135
|
+
ChangedUrl: ({ url }) => ({
|
|
136
|
+
model: evo(model, {
|
|
131
137
|
route: () => urlToAppRoute(url),
|
|
132
138
|
}),
|
|
133
|
-
|
|
134
|
-
],
|
|
139
|
+
}),
|
|
135
140
|
|
|
136
141
|
GotProductsMessage: ({ message }) => foldProducts(model, message),
|
|
137
142
|
|
|
138
|
-
ClickedIncrementQuantity: ({ itemId }) =>
|
|
139
|
-
evo(model, {
|
|
143
|
+
ClickedIncrementQuantity: ({ itemId }) => ({
|
|
144
|
+
model: evo(model, {
|
|
140
145
|
cart: Cart.incrementQuantity(itemId),
|
|
141
146
|
}),
|
|
142
|
-
|
|
143
|
-
],
|
|
147
|
+
}),
|
|
144
148
|
|
|
145
|
-
ClickedDecrementQuantity: ({ itemId }) =>
|
|
146
|
-
evo(model, {
|
|
149
|
+
ClickedDecrementQuantity: ({ itemId }) => ({
|
|
150
|
+
model: evo(model, {
|
|
147
151
|
cart: Cart.decrementQuantity(itemId),
|
|
148
152
|
}),
|
|
149
|
-
|
|
150
|
-
],
|
|
153
|
+
}),
|
|
151
154
|
|
|
152
|
-
ClickedRemoveCartItem: ({ itemId }) =>
|
|
153
|
-
evo(model, {
|
|
155
|
+
ClickedRemoveCartItem: ({ itemId }) => ({
|
|
156
|
+
model: evo(model, {
|
|
154
157
|
cart: Cart.removeItem(itemId),
|
|
155
158
|
}),
|
|
156
|
-
|
|
157
|
-
],
|
|
159
|
+
}),
|
|
158
160
|
|
|
159
|
-
ClickedClearCart: () =>
|
|
160
|
-
evo(model, {
|
|
161
|
+
ClickedClearCart: () => ({
|
|
162
|
+
model: evo(model, {
|
|
161
163
|
cart: () => [],
|
|
162
164
|
}),
|
|
163
|
-
|
|
164
|
-
],
|
|
165
|
+
}),
|
|
165
166
|
|
|
166
|
-
UpdatedDeliveryInstructions: ({ value }) =>
|
|
167
|
-
evo(model, {
|
|
167
|
+
UpdatedDeliveryInstructions: ({ value }) => ({
|
|
168
|
+
model: evo(model, {
|
|
168
169
|
deliveryInstructions: () => value,
|
|
169
170
|
}),
|
|
170
|
-
|
|
171
|
-
],
|
|
171
|
+
}),
|
|
172
172
|
|
|
173
|
-
ClickedPlaceOrder: () =>
|
|
174
|
-
evo(model, {
|
|
173
|
+
ClickedPlaceOrder: () => ({
|
|
174
|
+
model: evo(model, {
|
|
175
175
|
orderPlaced: () => true,
|
|
176
176
|
cart: () => [],
|
|
177
177
|
deliveryInstructions: () => '',
|
|
178
178
|
}),
|
|
179
|
-
|
|
180
|
-
],
|
|
179
|
+
}),
|
|
181
180
|
})
|
|
182
181
|
|
|
183
182
|
// VIEW
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Array, Effect, Option, Schema as S } from 'effect'
|
|
2
|
-
import { Command, Submodel } from 'foldkit'
|
|
2
|
+
import { Command, Submodel, type Update } from 'foldkit'
|
|
3
3
|
import { defineMessageUnion } from 'foldkit/message'
|
|
4
4
|
import { replaceUrl } from 'foldkit/navigation'
|
|
5
5
|
import { evo } from 'foldkit/struct'
|
|
@@ -61,46 +61,39 @@ const ReplaceSearchUrl = Command.define('ReplaceSearchUrl', {
|
|
|
61
61
|
|
|
62
62
|
// UPDATE
|
|
63
63
|
|
|
64
|
-
type UpdateReturn = readonly [
|
|
65
|
-
Model,
|
|
66
|
-
ReadonlyArray<Command.Command<Message>>,
|
|
67
|
-
Option.Option<OutMessage>,
|
|
68
|
-
]
|
|
69
|
-
|
|
70
64
|
export const update = (model: Model, message: Message) =>
|
|
71
|
-
Message.match<
|
|
72
|
-
|
|
65
|
+
Message.match<Update.ReturnWithOutMessage<Model, Message, OutMessage>>(
|
|
66
|
+
message,
|
|
67
|
+
{
|
|
68
|
+
CompletedReplaceSearchUrl: () => ({ model }),
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
ChangedSearchInput: ({ value }) => ({
|
|
71
|
+
model: evo(model, { searchText: () => value }),
|
|
72
|
+
commands: [
|
|
73
|
+
ReplaceSearchUrl({
|
|
74
|
+
url: productsRouter({
|
|
75
|
+
searchText: Option.fromNullishOr(value || null),
|
|
76
|
+
}),
|
|
80
77
|
}),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Option.none(),
|
|
84
|
-
],
|
|
78
|
+
],
|
|
79
|
+
}),
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
],
|
|
81
|
+
ClickedAddToCart: ({ item }) => ({
|
|
82
|
+
model,
|
|
83
|
+
outMessage: OutMessage.AddedToCart({ item }),
|
|
84
|
+
}),
|
|
91
85
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
],
|
|
86
|
+
ClickedIncrementQuantity: ({ itemId }) => ({
|
|
87
|
+
model,
|
|
88
|
+
outMessage: OutMessage.IncrementedQuantity({ itemId }),
|
|
89
|
+
}),
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
ClickedDecrementQuantity: ({ itemId }) => ({
|
|
92
|
+
model,
|
|
93
|
+
outMessage: OutMessage.DecrementedQuantity({ itemId }),
|
|
94
|
+
}),
|
|
95
|
+
},
|
|
96
|
+
)
|
|
104
97
|
|
|
105
98
|
// VIEW
|
|
106
99
|
|