create-foldkit-app 0.32.1 → 0.33.0-canary.a74bfca634bb
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/.oxfmtrc.json +28 -0
- package/dist/templates/base/.oxlintrc.json +4 -0
- package/dist/templates/base/FOLDKIT.md +1 -1
- package/dist/templates/base/package.json +1 -1
- package/dist/templates/examples/api-cache/package.json +0 -2
- package/dist/templates/examples/api-cache/src/scene.test.ts +1 -2
- package/dist/templates/examples/api-cache/src/story.test.ts +3 -4
- package/dist/templates/examples/auth/package.json +0 -2
- package/dist/templates/examples/canvas-art/package.json +0 -2
- package/dist/templates/examples/charting/package.json +0 -2
- package/dist/templates/examples/charting/src/scene.test.ts +1 -2
- package/dist/templates/examples/charting/src/story.test.ts +3 -4
- package/dist/templates/examples/counter/package.json +0 -2
- package/dist/templates/examples/counters/package.json +0 -2
- package/dist/templates/examples/crash-view/package.json +0 -2
- package/dist/templates/examples/embedding/package.json +0 -2
- package/dist/templates/examples/form/package.json +0 -2
- package/dist/templates/examples/generative-art/package.json +0 -2
- package/dist/templates/examples/interrupting-commands/package.json +0 -2
- package/dist/templates/examples/job-application/package.json +0 -2
- package/dist/templates/examples/job-application/src/main.fixture.ts +69 -0
- package/dist/templates/examples/job-application/src/main.ts +1 -1
- package/dist/templates/examples/job-application/src/scene.test.ts +6 -66
- package/dist/templates/examples/job-application/src/step/attachments/index.ts +2 -0
- package/dist/templates/examples/job-application/src/step/attachments/scene.test.ts +19 -0
- package/dist/templates/examples/job-application/src/step/attachments/story.test.ts +46 -0
- package/dist/templates/examples/job-application/src/{view/attachments.ts → step/attachments/view.ts} +4 -11
- package/dist/templates/examples/job-application/src/{view → step/coverLetter}/coverLetter.ts +36 -7
- package/dist/templates/examples/job-application/src/step/coverLetter/index.ts +1 -0
- package/dist/templates/examples/job-application/src/step/coverLetter/scene.test.ts +24 -0
- package/dist/templates/examples/job-application/src/step/coverLetter/story.test.ts +19 -0
- package/dist/templates/examples/job-application/src/step/education/{entry.ts → entry/entry.ts} +2 -2
- package/dist/templates/examples/job-application/src/step/education/entry/index.ts +11 -0
- package/dist/templates/examples/job-application/src/step/education/entry/story.test.ts +39 -0
- package/dist/templates/examples/job-application/src/step/education/entry/view.ts +160 -0
- package/dist/templates/examples/job-application/src/step/education/index.ts +11 -1
- package/dist/templates/examples/job-application/src/step/education/scene.test.ts +20 -0
- package/dist/templates/examples/job-application/src/step/education/story.test.ts +87 -0
- package/dist/templates/examples/job-application/src/{view/education.ts → step/education/view.ts} +6 -10
- package/dist/templates/examples/job-application/src/step/personalInfo/index.ts +11 -0
- package/dist/templates/examples/job-application/src/step/{personalInfo.ts → personalInfo/personalInfo.ts} +2 -2
- package/dist/templates/examples/job-application/src/step/personalInfo/scene.test.ts +38 -0
- package/dist/templates/examples/job-application/src/step/personalInfo/story.test.ts +87 -0
- package/dist/templates/examples/job-application/src/{view/personalInfo.ts → step/personalInfo/view.ts} +24 -34
- package/dist/templates/examples/job-application/src/step/skills/{entry.ts → entry/entry.ts} +2 -2
- package/dist/templates/examples/job-application/src/step/skills/entry/index.ts +11 -0
- package/dist/templates/examples/job-application/src/step/skills/entry/story.test.ts +27 -0
- package/dist/templates/examples/job-application/src/{view/skillEntry.ts → step/skills/entry/view.ts} +9 -12
- package/dist/templates/examples/job-application/src/step/skills/index.ts +11 -1
- package/dist/templates/examples/job-application/src/step/skills/scene.test.ts +16 -0
- package/dist/templates/examples/job-application/src/step/skills/story.test.ts +85 -0
- package/dist/templates/examples/job-application/src/step/skills/view.ts +47 -0
- package/dist/templates/examples/job-application/src/step/workHistory/{entry.ts → entry/entry.ts} +1 -1
- package/dist/templates/examples/job-application/src/step/workHistory/entry/index.ts +11 -0
- package/dist/templates/examples/job-application/src/step/workHistory/entry/story.test.ts +42 -0
- package/dist/templates/examples/job-application/src/{view/workEntry.ts → step/workHistory/entry/view.ts} +27 -42
- package/dist/templates/examples/job-application/src/step/workHistory/index.ts +11 -1
- package/dist/templates/examples/job-application/src/step/workHistory/scene.test.ts +20 -0
- package/dist/templates/examples/job-application/src/step/workHistory/story.test.ts +87 -0
- package/dist/templates/examples/job-application/src/{view/workHistory.ts → step/workHistory/view.ts} +4 -8
- package/dist/templates/examples/job-application/src/story.test.ts +28 -419
- package/dist/templates/examples/job-application/src/view/datePicker.ts +4 -4
- package/dist/templates/examples/job-application/src/view/field.ts +3 -3
- package/dist/templates/examples/job-application/src/view/index.ts +3 -358
- package/dist/templates/examples/job-application/src/view/stepNav.ts +2 -2
- package/dist/templates/examples/job-application/src/view/view.ts +359 -0
- package/dist/templates/examples/kanban/package.json +0 -2
- package/dist/templates/examples/kanban/src/scene.test.ts +1 -1
- package/dist/templates/examples/kanban/src/story.test.ts +22 -23
- package/dist/templates/examples/kanban/src/view/index.ts +1 -71
- package/dist/templates/examples/kanban/src/view/view.ts +71 -0
- package/dist/templates/examples/managed-resource-layer/package.json +0 -2
- package/dist/templates/examples/map/package.json +0 -2
- package/dist/templates/examples/pixel-art/package.json +0 -2
- package/dist/templates/examples/pixel-art/src/scene.test.ts +16 -9
- package/dist/templates/examples/pixel-art/src/story.test.ts +12 -7
- package/dist/templates/examples/query-sync/package.json +0 -2
- package/dist/templates/examples/query-sync/src/story.test.ts +4 -5
- package/dist/templates/examples/route-transitions/package.json +0 -2
- package/dist/templates/examples/routing/package.json +0 -2
- package/dist/templates/examples/routing/src/page/people.scene.test.ts +41 -0
- package/dist/templates/examples/routing/src/page/people.story.test.ts +73 -0
- package/dist/templates/examples/routing/src/scene.test.ts +2 -17
- package/dist/templates/examples/routing/src/story.test.ts +4 -34
- package/dist/templates/examples/shopping-cart/package.json +0 -2
- package/dist/templates/examples/shopping-cart/src/page/products.scene.test.ts +20 -0
- package/dist/templates/examples/shopping-cart/src/page/products.story.test.ts +56 -0
- package/dist/templates/examples/shopping-cart/src/page/products.ts +1 -1
- package/dist/templates/examples/shopping-cart/src/scene.test.ts +1 -4
- package/dist/templates/examples/shopping-cart/src/story.test.ts +3 -4
- package/dist/templates/examples/slow-warnings/package.json +0 -2
- package/dist/templates/examples/snake/package.json +0 -2
- package/dist/templates/examples/ssg/package.json +0 -2
- package/dist/templates/examples/ssr/package.json +0 -2
- package/dist/templates/examples/state-machine/package.json +0 -2
- package/dist/templates/examples/state-machine/src/main.ts +79 -87
- package/dist/templates/examples/state-machine/src/scene.test.ts +1 -2
- package/dist/templates/examples/stopwatch/package.json +0 -2
- package/dist/templates/examples/todo/package.json +0 -2
- package/dist/templates/examples/ui-showcase/package.json +0 -2
- package/dist/templates/examples/ui-showcase/src/story.test.ts +1 -2
- package/dist/templates/examples/view-transitions/package.json +0 -2
- package/dist/templates/examples/view-transitions/src/styles.css +3 -3
- package/dist/templates/examples/weather/package.json +0 -2
- package/dist/templates/examples/web-components/package.json +0 -2
- package/dist/templates/examples/websocket-chat/package.json +0 -2
- package/dist/templates/release.json +15 -16
- package/dist/templates/rendering/ssg/package.json +1 -1
- package/dist/templates/rendering/ssr/package.json +1 -1
- package/dist/utils/packages.js +1 -2
- package/package.json +3 -3
- package/dist/templates/base/.prettierignore +0 -5
- package/dist/templates/base/.prettierrc +0 -13
- package/dist/templates/examples/job-application/src/step/coverLetter.ts +0 -34
- package/dist/templates/examples/job-application/src/view/educationEntry.ts +0 -165
- package/dist/templates/examples/job-application/src/view/skills.ts +0 -49
- /package/dist/templates/examples/job-application/src/step/{attachments.ts → attachments/attachments.ts} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
+
"semi": false,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"arrowParens": "avoid",
|
|
6
|
+
"trailingComma": "all",
|
|
7
|
+
"printWidth": 80,
|
|
8
|
+
"sortImports": {
|
|
9
|
+
"ignoreCase": false,
|
|
10
|
+
"customGroups": [
|
|
11
|
+
{ "groupName": "scoped", "elementNamePattern": ["@*", "@*/**"] }
|
|
12
|
+
],
|
|
13
|
+
"groups": [
|
|
14
|
+
["builtin", "external"],
|
|
15
|
+
"scoped",
|
|
16
|
+
["parent", "sibling", "index"],
|
|
17
|
+
"unknown"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"sortPackageJson": false,
|
|
21
|
+
"ignorePatterns": [
|
|
22
|
+
"node_modules",
|
|
23
|
+
"dist",
|
|
24
|
+
"repos",
|
|
25
|
+
"*.tsbuildinfo",
|
|
26
|
+
"pnpm-lock.yaml"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -149,7 +149,7 @@ type Message = typeof Message.Type
|
|
|
149
149
|
|
|
150
150
|
Keep the `defineMessageUnion()` declaration and `type Message` alias adjacent. Construct values through the namespace (`Message.ClickedSubmit()`) and handle the union with `Message.match`. Never destructure constructors from `Message` or `OutMessage`; the owning namespace stays visible at every call site.
|
|
151
151
|
|
|
152
|
-
Keep each case's payload object on one line when it fits. Let
|
|
152
|
+
Keep each case's payload object on one line when it fits. Let Oxfmt wrap payloads that need more space, so the declaration remains easy to scan as one variant per line.
|
|
153
153
|
|
|
154
154
|
Messages are verb-first past-tense. Common prefixes: `Clicked*`, `Updated*` (input changes and external state updates), `Submitted*`, `Pressed*`, `Selected*`, `Succeeded*` / `Failed*` (paired async results), `Completed*` (every other Command result), `Got*` (child OutMessage in the Submodel pattern).
|
|
155
155
|
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
import { describe, test } from 'vitest'
|
|
13
13
|
|
|
14
14
|
import { Tabs } from '@foldkit/ui'
|
|
15
|
-
import { Message as TabsMessage } from '@foldkit/ui/tabs'
|
|
16
15
|
|
|
17
16
|
import { FetchPostDetail, FetchStats, Message, update, view } from './main'
|
|
18
17
|
import {
|
|
@@ -26,7 +25,7 @@ import {
|
|
|
26
25
|
|
|
27
26
|
const resolveFocusTab = Command.resolve(
|
|
28
27
|
Tabs.FocusTab,
|
|
29
|
-
|
|
28
|
+
Tabs.Message.CompletedFocusTab(),
|
|
30
29
|
)
|
|
31
30
|
|
|
32
31
|
describe('view', () => {
|
|
@@ -3,7 +3,6 @@ import { Command, given, message, model, story } from 'foldkit/story'
|
|
|
3
3
|
import { expect, test } from 'vitest'
|
|
4
4
|
|
|
5
5
|
import { Tabs } from '@foldkit/ui'
|
|
6
|
-
import { Message as TabsMessage } from '@foldkit/ui/tabs'
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
8
|
FetchPostDetail,
|
|
@@ -32,16 +31,16 @@ const postDetailTag = (model: Model, postId: string): string =>
|
|
|
32
31
|
)
|
|
33
32
|
|
|
34
33
|
const selectedPostsTab = Message.GotTabsMessage({
|
|
35
|
-
message:
|
|
34
|
+
message: Tabs.Message.SelectedTab({ index: 0, value: 'Posts' }),
|
|
36
35
|
})
|
|
37
36
|
|
|
38
37
|
const selectedStatsTab = Message.GotTabsMessage({
|
|
39
|
-
message:
|
|
38
|
+
message: Tabs.Message.SelectedTab({ index: 1, value: 'Stats' }),
|
|
40
39
|
})
|
|
41
40
|
|
|
42
41
|
const resolveFocusTab = Command.resolve(
|
|
43
42
|
Tabs.FocusTab,
|
|
44
|
-
|
|
43
|
+
Tabs.Message.CompletedFocusTab(),
|
|
45
44
|
)
|
|
46
45
|
|
|
47
46
|
test('first visit to the Stats tab fetches stats', () => {
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
"@foldkit/devtools": "workspace:*",
|
|
20
20
|
"@foldkit/vite-plugin": "workspace:*",
|
|
21
21
|
"@tailwindcss/vite": "^4.3.3",
|
|
22
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
23
22
|
"happy-dom": "^20.11.13",
|
|
24
|
-
"prettier": "^3.9.6",
|
|
25
23
|
"tailwindcss": "^4.3.3",
|
|
26
24
|
"typescript": "^7.0.2",
|
|
27
25
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -20,9 +20,7 @@
|
|
|
20
20
|
"@foldkit/devtools": "workspace:*",
|
|
21
21
|
"@foldkit/vite-plugin": "workspace:*",
|
|
22
22
|
"@tailwindcss/vite": "^4.3.3",
|
|
23
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
24
23
|
"happy-dom": "^20.11.13",
|
|
25
|
-
"prettier": "^3.9.6",
|
|
26
24
|
"tailwindcss": "^4.3.3",
|
|
27
25
|
"typescript": "^7.0.2",
|
|
28
26
|
"vite": "^8.2.2",
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
import { describe, test } from 'vitest'
|
|
13
13
|
|
|
14
14
|
import { RadioGroup } from '@foldkit/ui'
|
|
15
|
-
import { Message as RadioGroupMessage } from '@foldkit/ui/radioGroup'
|
|
16
15
|
|
|
17
16
|
import { SyncChart } from './command'
|
|
18
17
|
import { loadingModel, readyModel, sampleTelemetry } from './main.fixtures'
|
|
@@ -34,7 +33,7 @@ const acknowledgeChartSync = Command.resolve(
|
|
|
34
33
|
|
|
35
34
|
const resolveFocusOption = Command.resolve(
|
|
36
35
|
RadioGroup.FocusOption,
|
|
37
|
-
|
|
36
|
+
RadioGroup.Message.CompletedFocusOption(),
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
describe('view', () => {
|
|
@@ -2,7 +2,6 @@ import { Command, given, message, model, story } from 'foldkit/story'
|
|
|
2
2
|
import { expect, test } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import { RadioGroup } from '@foldkit/ui'
|
|
5
|
-
import { Message as RadioGroupMessage } from '@foldkit/ui/radioGroup'
|
|
6
5
|
|
|
7
6
|
import { FetchTelemetry, SyncChart } from './command'
|
|
8
7
|
import {
|
|
@@ -18,7 +17,7 @@ import { update } from './update'
|
|
|
18
17
|
|
|
19
18
|
const selectedChartMode = (chartMode: ChartMode) =>
|
|
20
19
|
Message.GotChartModeRadioGroupMessage({
|
|
21
|
-
message:
|
|
20
|
+
message: RadioGroup.Message.SelectedOption({
|
|
22
21
|
index: chartModes.indexOf(chartMode),
|
|
23
22
|
value: chartMode,
|
|
24
23
|
}),
|
|
@@ -26,7 +25,7 @@ const selectedChartMode = (chartMode: ChartMode) =>
|
|
|
26
25
|
|
|
27
26
|
const selectedPackage = (packageId: PackageId) =>
|
|
28
27
|
Message.GotPackageRadioGroupMessage({
|
|
29
|
-
message:
|
|
28
|
+
message: RadioGroup.Message.SelectedOption({
|
|
30
29
|
index: packageIds.indexOf(packageId),
|
|
31
30
|
value: packageId,
|
|
32
31
|
}),
|
|
@@ -34,7 +33,7 @@ const selectedPackage = (packageId: PackageId) =>
|
|
|
34
33
|
|
|
35
34
|
const resolveChartModeFocus = Command.resolve(
|
|
36
35
|
RadioGroup.FocusOption,
|
|
37
|
-
|
|
36
|
+
RadioGroup.Message.CompletedFocusOption(),
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
test('mounting the chart syncs current telemetry into ECharts', () => {
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
"@foldkit/devtools": "workspace:*",
|
|
20
20
|
"@foldkit/vite-plugin": "workspace:*",
|
|
21
21
|
"@tailwindcss/vite": "^4.3.3",
|
|
22
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
23
22
|
"happy-dom": "^20.11.13",
|
|
24
|
-
"prettier": "^3.9.6",
|
|
25
23
|
"tailwindcss": "^4.3.3",
|
|
26
24
|
"typescript": "^7.0.2",
|
|
27
25
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"@foldkit/devtools": "workspace:*",
|
|
19
19
|
"@foldkit/vite-plugin": "workspace:*",
|
|
20
20
|
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
22
21
|
"happy-dom": "^20.11.13",
|
|
23
|
-
"prettier": "^3.9.6",
|
|
24
22
|
"tailwindcss": "^4.3.3",
|
|
25
23
|
"typescript": "^7.0.2",
|
|
26
24
|
"vite": "^8.2.2",
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
"@foldkit/devtools": "workspace:*",
|
|
20
20
|
"@foldkit/vite-plugin": "workspace:*",
|
|
21
21
|
"@tailwindcss/vite": "^4.3.3",
|
|
22
|
-
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
23
22
|
"happy-dom": "^20.11.13",
|
|
24
|
-
"prettier": "^3.9.6",
|
|
25
23
|
"tailwindcss": "^4.3.3",
|
|
26
24
|
"typescript": "^7.0.2",
|
|
27
25
|
"vite": "^8.2.2",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Array } from 'effect'
|
|
2
|
+
import { Calendar } from 'foldkit'
|
|
3
|
+
import { Valid } from 'foldkit/fieldValidation'
|
|
4
|
+
import { evo } from 'foldkit/struct'
|
|
5
|
+
|
|
6
|
+
import { Menu, Tabs } from '@foldkit/ui'
|
|
7
|
+
|
|
8
|
+
import { type Model, Submission } from './model'
|
|
9
|
+
import {
|
|
10
|
+
Attachments,
|
|
11
|
+
CoverLetter,
|
|
12
|
+
Education,
|
|
13
|
+
PersonalInfo,
|
|
14
|
+
Skills,
|
|
15
|
+
WorkHistory,
|
|
16
|
+
} from './step'
|
|
17
|
+
|
|
18
|
+
export const today = Calendar.make(2026, 4, 16)
|
|
19
|
+
|
|
20
|
+
export const initialModel: Model = {
|
|
21
|
+
currentStep: 'PersonalInfo',
|
|
22
|
+
personalInfo: PersonalInfo.init(today),
|
|
23
|
+
workHistory: WorkHistory.init(today, 'work-history-entry-1'),
|
|
24
|
+
education: Education.init(today, 'education-entry-1'),
|
|
25
|
+
skills: Skills.init('skills-entry-1'),
|
|
26
|
+
coverLetter: CoverLetter.init(),
|
|
27
|
+
attachments: Attachments.init(),
|
|
28
|
+
isPreviewVisible: false,
|
|
29
|
+
submission: Submission.NotSubmitted(),
|
|
30
|
+
stepMenu: Menu.init({ id: 'step-menu' }),
|
|
31
|
+
stepTabs: Tabs.init({ id: 'step-tabs' }),
|
|
32
|
+
isSubmitAttempted: false,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const completeModel: Model = evo(initialModel, {
|
|
36
|
+
personalInfo: personalInfo =>
|
|
37
|
+
evo(personalInfo, {
|
|
38
|
+
firstName: () => Valid({ value: 'Jane' }),
|
|
39
|
+
lastName: () => Valid({ value: 'Doe' }),
|
|
40
|
+
email: () => Valid({ value: 'jane@example.com' }),
|
|
41
|
+
}),
|
|
42
|
+
workHistory: workHistory =>
|
|
43
|
+
evo(workHistory, {
|
|
44
|
+
entries: Array.map(entry =>
|
|
45
|
+
evo(entry, {
|
|
46
|
+
company: () => Valid({ value: 'Foldkit' }),
|
|
47
|
+
title: () => Valid({ value: 'Engineer' }),
|
|
48
|
+
}),
|
|
49
|
+
),
|
|
50
|
+
}),
|
|
51
|
+
education: education =>
|
|
52
|
+
evo(education, {
|
|
53
|
+
entries: Array.map(entry =>
|
|
54
|
+
evo(entry, {
|
|
55
|
+
school: () => Valid({ value: 'MIT' }),
|
|
56
|
+
degree: () => Valid({ value: 'BS' }),
|
|
57
|
+
fieldOfStudy: () => Valid({ value: 'CS' }),
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
}),
|
|
61
|
+
skills: skills =>
|
|
62
|
+
evo(skills, {
|
|
63
|
+
entries: Array.map(entry =>
|
|
64
|
+
evo(entry, {
|
|
65
|
+
name: () => Valid({ value: 'TypeScript' }),
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
}),
|
|
69
|
+
})
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Valid, Validating } from 'foldkit/fieldValidation'
|
|
1
|
+
import { Validating } from 'foldkit/fieldValidation'
|
|
3
2
|
import {
|
|
4
3
|
Command,
|
|
5
4
|
click,
|
|
@@ -12,75 +11,16 @@ import {
|
|
|
12
11
|
} from 'foldkit/scene'
|
|
13
12
|
import { describe, test } from 'vitest'
|
|
14
13
|
|
|
15
|
-
import {
|
|
16
|
-
import { Message as TabsMessage } from '@foldkit/ui/tabs'
|
|
14
|
+
import { Tabs } from '@foldkit/ui'
|
|
17
15
|
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
Attachments,
|
|
21
|
-
CoverLetter,
|
|
22
|
-
Education,
|
|
23
|
-
PersonalInfo,
|
|
24
|
-
Skills,
|
|
25
|
-
WorkHistory,
|
|
26
|
-
} from './step'
|
|
16
|
+
import { completeModel, initialModel } from './main.fixture'
|
|
17
|
+
import { Submission } from './model'
|
|
27
18
|
import { update } from './update'
|
|
28
|
-
import { view } from './view'
|
|
29
|
-
|
|
30
|
-
const today = Calendar.make(2026, 4, 16)
|
|
31
|
-
|
|
32
|
-
const initialModel: Model = {
|
|
33
|
-
currentStep: 'PersonalInfo',
|
|
34
|
-
personalInfo: PersonalInfo.init(today),
|
|
35
|
-
workHistory: WorkHistory.init(today, 'work-history-entry-1'),
|
|
36
|
-
education: Education.init(today, 'education-entry-1'),
|
|
37
|
-
skills: Skills.init('skills-entry-1'),
|
|
38
|
-
coverLetter: CoverLetter.init(),
|
|
39
|
-
attachments: Attachments.init(),
|
|
40
|
-
isPreviewVisible: false,
|
|
41
|
-
submission: Submission.NotSubmitted(),
|
|
42
|
-
stepMenu: Menu.init({ id: 'step-menu' }),
|
|
43
|
-
stepTabs: Tabs.init({ id: 'step-tabs' }),
|
|
44
|
-
isSubmitAttempted: false,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const completeModel: Model = {
|
|
48
|
-
...initialModel,
|
|
49
|
-
personalInfo: {
|
|
50
|
-
...initialModel.personalInfo,
|
|
51
|
-
firstName: Valid({ value: 'Jane' }),
|
|
52
|
-
lastName: Valid({ value: 'Doe' }),
|
|
53
|
-
email: Valid({ value: 'jane@example.com' }),
|
|
54
|
-
},
|
|
55
|
-
workHistory: {
|
|
56
|
-
...initialModel.workHistory,
|
|
57
|
-
entries: initialModel.workHistory.entries.map(entry => ({
|
|
58
|
-
...entry,
|
|
59
|
-
company: Valid({ value: 'Foldkit' }),
|
|
60
|
-
title: Valid({ value: 'Engineer' }),
|
|
61
|
-
})),
|
|
62
|
-
},
|
|
63
|
-
education: {
|
|
64
|
-
...initialModel.education,
|
|
65
|
-
entries: initialModel.education.entries.map(entry => ({
|
|
66
|
-
...entry,
|
|
67
|
-
school: Valid({ value: 'MIT' }),
|
|
68
|
-
degree: Valid({ value: 'BS' }),
|
|
69
|
-
fieldOfStudy: Valid({ value: 'CS' }),
|
|
70
|
-
})),
|
|
71
|
-
},
|
|
72
|
-
skills: {
|
|
73
|
-
...initialModel.skills,
|
|
74
|
-
entries: initialModel.skills.entries.map(entry => ({
|
|
75
|
-
...entry,
|
|
76
|
-
name: Valid({ value: 'TypeScript' }),
|
|
77
|
-
})),
|
|
78
|
-
},
|
|
79
|
-
}
|
|
19
|
+
import { view } from './view/view'
|
|
80
20
|
|
|
81
21
|
const resolveFocusTab = Command.resolve(
|
|
82
22
|
Tabs.FocusTab,
|
|
83
|
-
|
|
23
|
+
Tabs.Message.CompletedFocusTab(),
|
|
84
24
|
)
|
|
85
25
|
|
|
86
26
|
describe('view', () => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, given, role, scene, text } from 'foldkit/scene'
|
|
2
|
+
import { describe, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { init, update } from './attachments'
|
|
5
|
+
import { view } from './view'
|
|
6
|
+
|
|
7
|
+
describe('attachments', () => {
|
|
8
|
+
test('renders the resume and additional-files drop zones', () => {
|
|
9
|
+
scene(
|
|
10
|
+
{ update, view },
|
|
11
|
+
given(init()),
|
|
12
|
+
expect(role('heading', { name: 'Resume (PDF)' })).toExist(),
|
|
13
|
+
expect(text('Drop your resume or click to upload')).toExist(),
|
|
14
|
+
expect(
|
|
15
|
+
role('heading', { name: 'Additional Files (optional)' }),
|
|
16
|
+
).toExist(),
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { given, message, model, story } from 'foldkit/story'
|
|
2
|
+
import { describe, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { FileDrop } from '@foldkit/ui'
|
|
5
|
+
|
|
6
|
+
import { Message, init, update } from './attachments'
|
|
7
|
+
|
|
8
|
+
describe('attachments', () => {
|
|
9
|
+
test('a dropped resume is stored as maybeResume', () => {
|
|
10
|
+
const resume = new globalThis.File(['pdf-bytes'], 'resume.pdf', {
|
|
11
|
+
type: 'application/pdf',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
story(
|
|
15
|
+
update,
|
|
16
|
+
given(init()),
|
|
17
|
+
message(
|
|
18
|
+
Message.GotResumeDropMessage({
|
|
19
|
+
message: FileDrop.Message.DroppedFiles({ files: [resume] }),
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
22
|
+
model(model => {
|
|
23
|
+
expect(model.maybeResume._tag).toBe('Some')
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('dropped additional files are appended to the list', () => {
|
|
29
|
+
const file = new globalThis.File(['content'], 'portfolio.pdf', {
|
|
30
|
+
type: 'application/pdf',
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
story(
|
|
34
|
+
update,
|
|
35
|
+
given(init()),
|
|
36
|
+
message(
|
|
37
|
+
Message.GotAdditionalFilesDropMessage({
|
|
38
|
+
message: FileDrop.Message.DroppedFiles({ files: [file] }),
|
|
39
|
+
}),
|
|
40
|
+
),
|
|
41
|
+
model(model => {
|
|
42
|
+
expect(model.additionalFiles).toHaveLength(1)
|
|
43
|
+
}),
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
})
|
package/dist/templates/examples/job-application/src/{view/attachments.ts → step/attachments/view.ts}
RENAMED
|
@@ -5,8 +5,7 @@ import type { Html, HtmlBuilder } from 'foldkit/html'
|
|
|
5
5
|
|
|
6
6
|
import { Button, FileDrop } from '@foldkit/ui'
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { Message } from '../step/attachments'
|
|
8
|
+
import { Message, type Model } from './attachments'
|
|
10
9
|
|
|
11
10
|
const BYTES_PER_KB = 1024
|
|
12
11
|
const BYTES_PER_MB = BYTES_PER_KB * BYTES_PER_KB
|
|
@@ -27,10 +26,7 @@ const dropZoneClassName =
|
|
|
27
26
|
const fileKey = (file: File.File): string =>
|
|
28
27
|
`${File.name(file)}:${File.size(file)}:${file.lastModified}`
|
|
29
28
|
|
|
30
|
-
const resumeView = (
|
|
31
|
-
resume: File.File,
|
|
32
|
-
h: HtmlBuilder<Attachments.Message>,
|
|
33
|
-
): Html =>
|
|
29
|
+
const resumeView = (resume: File.File, h: HtmlBuilder<Message>): Html =>
|
|
34
30
|
h.div(
|
|
35
31
|
[
|
|
36
32
|
h.Class(
|
|
@@ -79,7 +75,7 @@ const resumeView = (
|
|
|
79
75
|
const additionalFileView = (
|
|
80
76
|
file: File.File,
|
|
81
77
|
fileIndex: number,
|
|
82
|
-
h: HtmlBuilder<
|
|
78
|
+
h: HtmlBuilder<Message>,
|
|
83
79
|
): Html =>
|
|
84
80
|
h.keyed('div')(
|
|
85
81
|
fileKey(file),
|
|
@@ -119,10 +115,7 @@ const additionalFileView = (
|
|
|
119
115
|
],
|
|
120
116
|
)
|
|
121
117
|
|
|
122
|
-
export const
|
|
123
|
-
Attachments.Model,
|
|
124
|
-
Attachments.Message
|
|
125
|
-
>((model, h): Html => {
|
|
118
|
+
export const view = Submodel.defineView<Model, Message>((model, h): Html => {
|
|
126
119
|
const { resumeDrop, maybeResume, additionalFilesDrop, additionalFiles } =
|
|
127
120
|
model
|
|
128
121
|
|
package/dist/templates/examples/job-application/src/{view → step/coverLetter}/coverLetter.ts
RENAMED
|
@@ -1,19 +1,48 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
|
-
import {
|
|
2
|
+
import { Schema } from 'effect'
|
|
3
|
+
import { Submodel, type Update } from 'foldkit'
|
|
3
4
|
import type { Html } from 'foldkit/html'
|
|
5
|
+
import { defineMessageUnion } from 'foldkit/message'
|
|
6
|
+
import { evo } from 'foldkit/struct'
|
|
4
7
|
|
|
5
8
|
import { Textarea } from '@foldkit/ui'
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
// MODEL
|
|
11
|
+
|
|
12
|
+
export const Model = Schema.Struct({
|
|
13
|
+
content: Schema.String,
|
|
14
|
+
})
|
|
15
|
+
export type Model = typeof Model.Type
|
|
16
|
+
|
|
17
|
+
// MESSAGE
|
|
18
|
+
|
|
19
|
+
export const Message = defineMessageUnion({
|
|
20
|
+
UpdatedContent: { value: Schema.String },
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export type Message = typeof Message.Type
|
|
24
|
+
|
|
25
|
+
// INIT
|
|
26
|
+
|
|
27
|
+
export const init = (): Model => ({
|
|
28
|
+
content: '',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// UPDATE
|
|
32
|
+
|
|
33
|
+
export const update = (model: Model, message: Message) =>
|
|
34
|
+
Message.match<Update.Return<Model, Message>>(message, {
|
|
35
|
+
UpdatedContent: ({ value }) => ({
|
|
36
|
+
model: evo(model, { content: () => value }),
|
|
37
|
+
}),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// VIEW
|
|
9
41
|
|
|
10
42
|
const MAX_COVER_LETTER_LENGTH = 2000
|
|
11
43
|
const WARNING_THRESHOLD_CHARS = 200
|
|
12
44
|
|
|
13
|
-
export const
|
|
14
|
-
CoverLetter.Model,
|
|
15
|
-
CoverLetter.Message
|
|
16
|
-
>((model, h): Html => {
|
|
45
|
+
export const view = Submodel.defineView<Model, Message>((model, h): Html => {
|
|
17
46
|
const remaining = MAX_COVER_LETTER_LENGTH - model.content.length
|
|
18
47
|
const isOverLimit = remaining < 0
|
|
19
48
|
const isWarning = !isOverLimit && remaining <= WARNING_THRESHOLD_CHARS
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Model, Message, init, update, view } from './coverLetter'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { expect, given, label, scene, text, type } from 'foldkit/scene'
|
|
2
|
+
import { describe, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { init, update, view } from './coverLetter'
|
|
5
|
+
|
|
6
|
+
describe('coverLetter', () => {
|
|
7
|
+
test('renders the cover letter field', () => {
|
|
8
|
+
scene(
|
|
9
|
+
{ update, view },
|
|
10
|
+
given(init()),
|
|
11
|
+
expect(label('Cover Letter')).toExist(),
|
|
12
|
+
expect(text('2000 characters remaining')).toExist(),
|
|
13
|
+
)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('typing updates the remaining character count', () => {
|
|
17
|
+
scene(
|
|
18
|
+
{ update, view },
|
|
19
|
+
given(init()),
|
|
20
|
+
type(label('Cover Letter'), 'Hello'),
|
|
21
|
+
expect(text('1995 characters remaining')).toExist(),
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
})
|