create-foldkit-app 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/templates/base/FOLDKIT.md +3 -1
  2. package/dist/templates/examples/canvas-art/src/main.ts +1 -1
  3. package/dist/templates/examples/charting/src/chartHost.ts +3 -4
  4. package/dist/templates/examples/charting/src/entry.ts +0 -2
  5. package/dist/templates/examples/charting/src/view/chart.ts +101 -36
  6. package/dist/templates/examples/embedding/src/main.ts +3 -3
  7. package/dist/templates/examples/generative-art/src/subscription.ts +1 -1
  8. package/dist/templates/examples/job-application/src/step/education/education.ts +14 -16
  9. package/dist/templates/examples/job-application/src/step/skills/skills.ts +14 -16
  10. package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +14 -16
  11. package/dist/templates/examples/map/src/entry.ts +1 -2
  12. package/dist/templates/examples/map/src/main.ts +123 -145
  13. package/dist/templates/examples/map/src/mapHost.ts +10 -12
  14. package/dist/templates/examples/map/src/mount.test.ts +93 -68
  15. package/dist/templates/examples/pixel-art/src/subscription.ts +5 -5
  16. package/dist/templates/examples/routing/src/main.ts +3 -3
  17. package/dist/templates/examples/slow-warnings/src/main.ts +4 -4
  18. package/dist/templates/examples/snake/src/main.ts +4 -3
  19. package/dist/templates/examples/ssg/src/entry.server.ts +2 -0
  20. package/dist/templates/examples/ssg/src/entry.ts +1 -0
  21. package/dist/templates/examples/ssr/package.json +1 -1
  22. package/dist/templates/examples/ssr/src/entry.server.ts +2 -0
  23. package/dist/templates/examples/ssr/src/entry.ts +1 -0
  24. package/dist/templates/examples/stopwatch/src/main.ts +1 -0
  25. package/dist/templates/examples/ui-showcase/src/main.ts +49 -37
  26. package/dist/templates/examples/ui-showcase/src/ui/subscriptions.ts +1 -18
  27. package/dist/templates/examples/ui-showcase/src/ui/update.ts +2 -12
  28. package/dist/templates/examples/ui-showcase/src/ui/view/calendar.ts +9 -7
  29. package/dist/templates/examples/ui-showcase/src/ui/view/checkbox.ts +80 -65
  30. package/dist/templates/examples/ui-showcase/src/ui/view/dialog.ts +102 -123
  31. package/dist/templates/examples/ui-showcase/src/ui/view/disclosure.ts +128 -120
  32. package/dist/templates/examples/ui-showcase/src/ui/view/fieldset.ts +173 -144
  33. package/dist/templates/examples/ui-showcase/src/ui/view/fileDrop.ts +17 -15
  34. package/dist/templates/examples/ui-showcase/src/ui/view/hoverIntent.ts +64 -56
  35. package/dist/templates/examples/ui-showcase/src/ui/view/input.ts +32 -33
  36. package/dist/templates/examples/ui-showcase/src/ui/view/meter.ts +82 -73
  37. package/dist/templates/examples/ui-showcase/src/ui/view/popover.ts +100 -81
  38. package/dist/templates/examples/ui-showcase/src/ui/view/progress.ts +83 -68
  39. package/dist/templates/examples/ui-showcase/src/ui/view/radioGroup.ts +79 -85
  40. package/dist/templates/examples/ui-showcase/src/ui/view/select.ts +63 -67
  41. package/dist/templates/examples/ui-showcase/src/ui/view/slider.ts +72 -72
  42. package/dist/templates/examples/ui-showcase/src/ui/view/switch.ts +25 -26
  43. package/dist/templates/examples/ui-showcase/src/ui/view/tabs.ts +58 -46
  44. package/dist/templates/examples/ui-showcase/src/ui/view/textarea.ts +32 -39
  45. package/dist/templates/examples/ui-showcase/src/ui/view/tooltip.ts +48 -36
  46. package/dist/templates/examples/websocket-chat/src/main.ts +44 -18
  47. package/dist/templates/examples/websocket-chat/src/story.test.ts +33 -6
  48. package/dist/templates/release.json +12 -11
  49. package/dist/templates/rendering/ssg/src/entry.server.ts +2 -0
  50. package/dist/templates/rendering/ssg/src/entry.ts +1 -0
  51. package/dist/templates/rendering/ssg/vite.config.ts +1 -0
  52. package/dist/templates/rendering/ssr/scripts/serve.ts +4 -145
  53. package/dist/templates/rendering/ssr/src/entry.server.ts +2 -0
  54. package/dist/templates/rendering/ssr/src/entry.ts +1 -0
  55. package/dist/templates/rendering/ssr/vite.config.ts +1 -0
  56. package/dist/utils/files.js +2 -0
  57. package/package.json +1 -1
  58. package/dist/templates/examples/charting/src/subscription.ts +0 -70
@@ -13,77 +13,86 @@ const valueClassName = 'tabular-nums text-gray-600'
13
13
  const trackClassName = 'h-3 w-full overflow-hidden rounded-full bg-gray-200'
14
14
  const fillClassName = 'h-full rounded-full bg-emerald-600'
15
15
 
16
- export const view = Submodel.defineView<UiModel, UiMessage>((_model, h): Html =>
17
- h.div(
18
- [],
19
- [
20
- h.h2([h.Class('text-2xl font-bold text-gray-900 mb-6')], ['Meter']),
21
- h.h3(
22
- [h.Class('text-lg font-semibold text-gray-900 mb-4')],
23
- ['Scalar value'],
24
- ),
25
- Meter.view(
26
- {
27
- id: 'health-meter',
28
- value: 75,
29
- valueText: '75 of 100 health',
30
- toView: ({ meter, label, fill }) =>
31
- h.div(
32
- [h.Class(rowClassName)],
33
- [
34
- h.div(
35
- [h.Class(headerClassName)],
36
- [
37
- h.span([...label, h.Class(labelClassName)], ['Health']),
38
- h.span([h.Class(valueClassName)], ['75 / 100']),
39
- ],
40
- ),
41
- h.div(
42
- [...meter, h.Class(trackClassName)],
43
- [h.div([...fill, h.Class(fillClassName)])],
44
- ),
45
- ],
46
- ),
47
- },
48
- h,
49
- ),
50
- h.h3(
51
- [h.Class('text-lg font-semibold text-gray-900 mt-8 mb-4')],
52
- ['Thresholds'],
53
- ),
54
- Meter.view(
55
- {
56
- id: 'storage-meter',
57
- value: 82,
58
- low: 30,
59
- high: 80,
60
- optimum: 20,
61
- valueText: '82 percent used',
62
- toView: ({ meter, label, fill }) =>
63
- h.div(
64
- [h.Class(rowClassName)],
65
- [
66
- h.div(
67
- [h.Class(headerClassName)],
68
- [
69
- h.span([...label, h.Class(labelClassName)], ['Storage']),
70
- h.span([h.Class(valueClassName)], ['82%']),
71
- ],
72
- ),
73
- h.div(
74
- [...meter, h.Class(trackClassName)],
75
- [
76
- h.div([
77
- ...fill,
78
- h.Class('h-full rounded-full bg-amber-500'),
79
- ]),
80
- ],
81
- ),
82
- ],
83
- ),
84
- },
85
- h,
86
- ),
87
- ],
88
- ),
16
+ export const view = Submodel.defineView<UiModel, UiMessage>(
17
+ (_model, h): Html => {
18
+ const healthMeterView = ({
19
+ meter,
20
+ label,
21
+ fill,
22
+ }: Meter.MeterAttributes<UiMessage>): Html =>
23
+ h.div(
24
+ [h.Class(rowClassName)],
25
+ [
26
+ h.div(
27
+ [h.Class(headerClassName)],
28
+ [
29
+ h.span([...label, h.Class(labelClassName)], ['Health']),
30
+ h.span([h.Class(valueClassName)], ['75 / 100']),
31
+ ],
32
+ ),
33
+ h.div(
34
+ [...meter, h.Class(trackClassName)],
35
+ [h.div([...fill, h.Class(fillClassName)])],
36
+ ),
37
+ ],
38
+ )
39
+
40
+ const storageMeterView = ({
41
+ meter,
42
+ label,
43
+ fill,
44
+ }: Meter.MeterAttributes<UiMessage>): Html =>
45
+ h.div(
46
+ [h.Class(rowClassName)],
47
+ [
48
+ h.div(
49
+ [h.Class(headerClassName)],
50
+ [
51
+ h.span([...label, h.Class(labelClassName)], ['Storage']),
52
+ h.span([h.Class(valueClassName)], ['82%']),
53
+ ],
54
+ ),
55
+ h.div(
56
+ [...meter, h.Class(trackClassName)],
57
+ [h.div([...fill, h.Class('h-full rounded-full bg-amber-500')])],
58
+ ),
59
+ ],
60
+ )
61
+
62
+ return h.div(
63
+ [],
64
+ [
65
+ h.h2([h.Class('text-2xl font-bold text-gray-900 mb-6')], ['Meter']),
66
+ h.h3(
67
+ [h.Class('text-lg font-semibold text-gray-900 mb-4')],
68
+ ['Scalar value'],
69
+ ),
70
+ Meter.view(
71
+ {
72
+ id: 'health-meter',
73
+ value: 75,
74
+ valueText: '75 of 100 health',
75
+ toView: healthMeterView,
76
+ },
77
+ h,
78
+ ),
79
+ h.h3(
80
+ [h.Class('text-lg font-semibold text-gray-900 mt-8 mb-4')],
81
+ ['Thresholds'],
82
+ ),
83
+ Meter.view(
84
+ {
85
+ id: 'storage-meter',
86
+ value: 82,
87
+ low: 30,
88
+ high: 80,
89
+ optimum: 20,
90
+ valueText: '82 percent used',
91
+ toView: storageMeterView,
92
+ },
93
+ h,
94
+ ),
95
+ ],
96
+ )
97
+ },
89
98
  )
@@ -45,31 +45,38 @@ const popoverDemo = (
45
45
  panelClassNameValue: string,
46
46
  h: HtmlBuilder<UiMessage>,
47
47
  ): Html => {
48
+ const popoverView = ({
49
+ button,
50
+ panel,
51
+ backdrop,
52
+ isVisible,
53
+ }: Popover.RenderInfo): Html =>
54
+ h.div(
55
+ [h.Class(wrapperClassName)],
56
+ [
57
+ h.button(
58
+ [...button, h.Class(triggerClassName)],
59
+ [h.span([], ['Solutions'])],
60
+ ),
61
+ ...(isVisible
62
+ ? [
63
+ h.div([...backdrop, h.Class(backdropClassName)]),
64
+ h.div(
65
+ [...panel, h.Class(panelClassNameValue)],
66
+ [popoverPanelContent(h)],
67
+ ),
68
+ ]
69
+ : []),
70
+ ],
71
+ )
72
+
48
73
  return h.submodel({
49
74
  slotId: id,
50
75
  model: popoverModel,
51
76
  view: Popover.view,
52
77
  viewInputs: {
53
78
  anchor: POPOVER_ANCHOR,
54
- toView: ({ button, panel, backdrop, isVisible }) =>
55
- h.div(
56
- [h.Class(wrapperClassName)],
57
- [
58
- h.button(
59
- [...button, h.Class(triggerClassName)],
60
- [h.span([], ['Solutions'])],
61
- ),
62
- ...(isVisible
63
- ? [
64
- h.div([...backdrop, h.Class(backdropClassName)]),
65
- h.div(
66
- [...panel, h.Class(panelClassNameValue)],
67
- [popoverPanelContent(h)],
68
- ),
69
- ]
70
- : []),
71
- ],
72
- ),
79
+ toView: popoverView,
73
80
  },
74
81
  toParentMessage,
75
82
  })
@@ -87,6 +94,42 @@ const nestedChildPopover = (
87
94
  childPopoverModel: Popover.Model,
88
95
  h: HtmlBuilder<UiMessage>,
89
96
  ): Html => {
97
+ const childPopoverView = ({
98
+ button,
99
+ panel,
100
+ backdrop,
101
+ isVisible,
102
+ }: Popover.RenderInfo): Html =>
103
+ h.div(
104
+ [h.Class(wrapperClassName)],
105
+ [
106
+ h.button(
107
+ [...button, h.Class(triggerClassName)],
108
+ [h.span([], ['Advanced settings'])],
109
+ ),
110
+ ...(isVisible
111
+ ? [
112
+ h.div([...backdrop, h.Class(backdropClassName)]),
113
+ h.div(
114
+ [...panel, h.Class(basicPanelClassName)],
115
+ [
116
+ h.p(
117
+ [h.Class('text-sm font-semibold text-gray-900 mb-2')],
118
+ ['Permissions'],
119
+ ),
120
+ h.p(
121
+ [h.Class('text-sm text-gray-600')],
122
+ [
123
+ 'Review who can change billing, members, and integrations.',
124
+ ],
125
+ ),
126
+ ],
127
+ ),
128
+ ]
129
+ : []),
130
+ ],
131
+ )
132
+
90
133
  return h.submodel({
91
134
  slotId: childPopoverModel.id,
92
135
  model: childPopoverModel,
@@ -94,36 +137,7 @@ const nestedChildPopover = (
94
137
  viewInputs: {
95
138
  ariaLabel: 'Advanced settings',
96
139
  anchor: NESTED_POPOVER_ANCHOR,
97
- toView: ({ button, panel, backdrop, isVisible }) =>
98
- h.div(
99
- [h.Class(wrapperClassName)],
100
- [
101
- h.button(
102
- [...button, h.Class(triggerClassName)],
103
- [h.span([], ['Advanced settings'])],
104
- ),
105
- ...(isVisible
106
- ? [
107
- h.div([...backdrop, h.Class(backdropClassName)]),
108
- h.div(
109
- [...panel, h.Class(basicPanelClassName)],
110
- [
111
- h.p(
112
- [h.Class('text-sm font-semibold text-gray-900 mb-2')],
113
- ['Permissions'],
114
- ),
115
- h.p(
116
- [h.Class('text-sm text-gray-600')],
117
- [
118
- 'Review who can change billing, members, and integrations.',
119
- ],
120
- ),
121
- ],
122
- ),
123
- ]
124
- : []),
125
- ],
126
- ),
140
+ toView: childPopoverView,
127
141
  },
128
142
  toParentMessage: message =>
129
143
  UiMessage.GotPopoverNestedChildDemoMessage({ message }),
@@ -135,6 +149,42 @@ const nestedDemo = (
135
149
  childPopoverModel: Popover.Model,
136
150
  h: HtmlBuilder<UiMessage>,
137
151
  ): Html => {
152
+ const parentPopoverView = ({
153
+ button,
154
+ panel,
155
+ backdrop,
156
+ isVisible,
157
+ }: Popover.RenderInfo): Html =>
158
+ h.div(
159
+ [h.Class(wrapperClassName)],
160
+ [
161
+ h.button(
162
+ [...button, h.Class(triggerClassName)],
163
+ [h.span([], ['Account'])],
164
+ ),
165
+ ...(isVisible
166
+ ? [
167
+ h.div([...backdrop, h.Class(backdropClassName)]),
168
+ h.div(
169
+ [...panel, h.Class(basicPanelClassName)],
170
+ [
171
+ h.div(
172
+ [h.Class('flex flex-col gap-4')],
173
+ [
174
+ h.p(
175
+ [h.Class('text-sm text-gray-600')],
176
+ ['Manage account settings without leaving this panel.'],
177
+ ),
178
+ nestedChildPopover(childPopoverModel, h),
179
+ ],
180
+ ),
181
+ ],
182
+ ),
183
+ ]
184
+ : []),
185
+ ],
186
+ )
187
+
138
188
  return h.div(
139
189
  [h.Class('relative')],
140
190
  [
@@ -145,38 +195,7 @@ const nestedDemo = (
145
195
  viewInputs: {
146
196
  anchor: POPOVER_ANCHOR,
147
197
  focusSelector: nestedChildButtonSelector,
148
- toView: ({ button, panel, backdrop, isVisible }) =>
149
- h.div(
150
- [h.Class(wrapperClassName)],
151
- [
152
- h.button(
153
- [...button, h.Class(triggerClassName)],
154
- [h.span([], ['Account'])],
155
- ),
156
- ...(isVisible
157
- ? [
158
- h.div([...backdrop, h.Class(backdropClassName)]),
159
- h.div(
160
- [...panel, h.Class(basicPanelClassName)],
161
- [
162
- h.div(
163
- [h.Class('flex flex-col gap-4')],
164
- [
165
- h.p(
166
- [h.Class('text-sm text-gray-600')],
167
- [
168
- 'Manage account settings without leaving this panel.',
169
- ],
170
- ),
171
- nestedChildPopover(childPopoverModel, h),
172
- ],
173
- ),
174
- ],
175
- ),
176
- ]
177
- : []),
178
- ],
179
- ),
198
+ toView: parentPopoverView,
180
199
  },
181
200
  toParentMessage: message =>
182
201
  UiMessage.GotPopoverNestedParentDemoMessage({ message }),
@@ -13,72 +13,87 @@ const valueClassName = 'tabular-nums text-gray-600'
13
13
  const trackClassName = 'h-3 w-full overflow-hidden rounded-full bg-gray-200'
14
14
  const indicatorClassName = 'h-full rounded-full bg-accent-600'
15
15
 
16
- export const view = Submodel.defineView<UiModel, UiMessage>((_model, h): Html =>
17
- h.div(
18
- [],
19
- [
20
- h.h2([h.Class('text-2xl font-bold text-gray-900 mb-6')], ['Progress']),
21
- h.h3(
22
- [h.Class('text-lg font-semibold text-gray-900 mb-4')],
23
- ['Determinate'],
24
- ),
25
- Progress.view(
26
- {
27
- id: 'upload-progress',
28
- value: 42,
29
- valueText: '42 percent',
30
- toView: ({ progress, label, track, indicator }) =>
31
- h.div(
32
- [h.Class(rowClassName)],
33
- [
34
- h.div(
35
- [h.Class(headerClassName)],
36
- [
37
- h.span([...label, h.Class(labelClassName)], ['Upload']),
38
- h.span([h.Class(valueClassName)], ['42%']),
39
- ],
40
- ),
41
- h.div(
42
- [...progress, h.Class(trackClassName)],
43
- [
44
- h.div(
45
- [...track, h.Class('h-full w-full')],
46
- [h.div([...indicator, h.Class(indicatorClassName)])],
47
- ),
48
- ],
49
- ),
50
- ],
51
- ),
52
- },
53
- h,
54
- ),
55
- h.h3(
56
- [h.Class('text-lg font-semibold text-gray-900 mt-8 mb-4')],
57
- ['Indeterminate'],
58
- ),
59
- Progress.view(
60
- {
61
- id: 'sync-progress',
62
- valueText: 'Syncing files',
63
- toView: ({ progress, label, indicator }) =>
64
- h.div(
65
- [h.Class(rowClassName)],
66
- [
67
- h.span([...label, h.Class(labelClassName)], ['Syncing']),
68
- h.div(
69
- [...progress, h.Class(trackClassName)],
70
- [
71
- h.div([
72
- ...indicator,
73
- h.Class(`${indicatorClassName} w-1/3 animate-pulse`),
74
- ]),
75
- ],
76
- ),
77
- ],
78
- ),
79
- },
80
- h,
81
- ),
82
- ],
83
- ),
16
+ export const view = Submodel.defineView<UiModel, UiMessage>(
17
+ (_model, h): Html => {
18
+ const uploadProgressView = ({
19
+ progress,
20
+ label,
21
+ track,
22
+ indicator,
23
+ }: Progress.ProgressAttributes<UiMessage>): Html =>
24
+ h.div(
25
+ [h.Class(rowClassName)],
26
+ [
27
+ h.div(
28
+ [h.Class(headerClassName)],
29
+ [
30
+ h.span([...label, h.Class(labelClassName)], ['Upload']),
31
+ h.span([h.Class(valueClassName)], ['42%']),
32
+ ],
33
+ ),
34
+ h.div(
35
+ [...progress, h.Class(trackClassName)],
36
+ [
37
+ h.div(
38
+ [...track, h.Class('h-full w-full')],
39
+ [h.div([...indicator, h.Class(indicatorClassName)])],
40
+ ),
41
+ ],
42
+ ),
43
+ ],
44
+ )
45
+
46
+ const syncingProgressView = ({
47
+ progress,
48
+ label,
49
+ indicator,
50
+ }: Progress.ProgressAttributes<UiMessage>): Html =>
51
+ h.div(
52
+ [h.Class(rowClassName)],
53
+ [
54
+ h.span([...label, h.Class(labelClassName)], ['Syncing']),
55
+ h.div(
56
+ [...progress, h.Class(trackClassName)],
57
+ [
58
+ h.div([
59
+ ...indicator,
60
+ h.Class(`${indicatorClassName} w-1/3 animate-pulse`),
61
+ ]),
62
+ ],
63
+ ),
64
+ ],
65
+ )
66
+
67
+ return h.div(
68
+ [],
69
+ [
70
+ h.h2([h.Class('text-2xl font-bold text-gray-900 mb-6')], ['Progress']),
71
+ h.h3(
72
+ [h.Class('text-lg font-semibold text-gray-900 mb-4')],
73
+ ['Determinate'],
74
+ ),
75
+ Progress.view(
76
+ {
77
+ id: 'upload-progress',
78
+ value: 42,
79
+ valueText: '42 percent',
80
+ toView: uploadProgressView,
81
+ },
82
+ h,
83
+ ),
84
+ h.h3(
85
+ [h.Class('text-lg font-semibold text-gray-900 mt-8 mb-4')],
86
+ ['Indeterminate'],
87
+ ),
88
+ Progress.view(
89
+ {
90
+ id: 'sync-progress',
91
+ valueText: 'Syncing files',
92
+ toView: syncingProgressView,
93
+ },
94
+ h,
95
+ ),
96
+ ],
97
+ )
98
+ },
84
99
  )