srcdev-nuxt-components 9.3.7 → 9.3.8

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 (108) hide show
  1. package/.claude/hooks/check-component-update.sh +30 -1
  2. package/.claude/skills/components/breadcrumb.md +49 -0
  3. package/.claude/skills/components/content-docs.md +1 -1
  4. package/.claude/skills/components/service-detail.md +156 -0
  5. package/.claude/skills/components/services-card-grid.md +9 -9
  6. package/.claude/skills/components/services-card.md +82 -30
  7. package/.claude/skills/index.md +3 -1
  8. package/.claude/skills/qa-panel.md +2 -2
  9. package/.vscode/srcdev-component-breadcrumb.code-snippets +14 -0
  10. package/.vscode/srcdev-component-service-detail.code-snippets +49 -0
  11. package/.vscode/srcdev-component-services-card.code-snippets +108 -0
  12. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +94 -0
  13. package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +32 -0
  14. package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +87 -0
  15. package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +87 -0
  16. package/app/components/01.atoms/navigation/breadcrumb/tests/__snapshots__/Breadcrumb.spec.ts.snap +13 -0
  17. package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +181 -0
  18. package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +480 -0
  19. package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +282 -0
  20. package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +270 -0
  21. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +99 -0
  22. package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +129 -0
  23. package/app/components/03.organisms/services/services-card/ServicesCard.vue +153 -39
  24. package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +65 -0
  25. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +82 -0
  26. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +13 -5
  27. package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +3 -7
  28. package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +126 -2
  29. package/app/components/03.organisms/services/services-grids/stories/ServicesSectionGrid.stories.ts +6 -0
  30. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +51 -27
  31. package/app/types/components/breadcrumb.ts +4 -0
  32. package/app/types/components/index.ts +1 -0
  33. package/package.json +2 -2
  34. package/app/pages/auto-grid.vue +0 -328
  35. package/app/pages/banner-video.vue +0 -238
  36. package/app/pages/forms/examples/buttons/index.vue +0 -259
  37. package/app/pages/forms/examples/material/checkbox-radio-panels.vue +0 -244
  38. package/app/pages/forms/examples/material/cssbattle.vue +0 -60
  39. package/app/pages/forms/examples/material/text-fields.vue +0 -910
  40. package/app/pages/grid-stack.vue +0 -252
  41. package/app/pages/index.vue +0 -127
  42. package/app/pages/page-hero-highlights.vue +0 -204
  43. package/app/pages/samaritan.vue +0 -327
  44. package/app/pages/typography/hero-text.vue +0 -97
  45. package/app/pages/typography/index.vue +0 -39
  46. package/app/pages/typography/page-body.vue +0 -171
  47. package/app/pages/typography/page-heading.vue +0 -76
  48. package/app/pages/typography/page-link.vue +0 -103
  49. package/app/pages/ui/accordian.vue +0 -211
  50. package/app/pages/ui/anchor-scroll-tab-navigation.vue +0 -191
  51. package/app/pages/ui/anchor-scroll.vue +0 -367
  52. package/app/pages/ui/animated-svg-text.vue +0 -85
  53. package/app/pages/ui/block-decorators.vue +0 -142
  54. package/app/pages/ui/card-core.vue +0 -82
  55. package/app/pages/ui/carousel-basic.vue +0 -292
  56. package/app/pages/ui/carousel-flip.vue +0 -449
  57. package/app/pages/ui/carousel-infinite.vue +0 -260
  58. package/app/pages/ui/clipped-panels.vue +0 -101
  59. package/app/pages/ui/contact-section.vue +0 -323
  60. package/app/pages/ui/container-glow.vue +0 -106
  61. package/app/pages/ui/content-width.vue +0 -126
  62. package/app/pages/ui/deep-expanding-menu.vue +0 -138
  63. package/app/pages/ui/display-avatar.vue +0 -253
  64. package/app/pages/ui/display-banner.vue +0 -78
  65. package/app/pages/ui/display-chip.vue +0 -367
  66. package/app/pages/ui/display-details.vue +0 -171
  67. package/app/pages/ui/display-dialog.vue +0 -344
  68. package/app/pages/ui/display-pill.vue +0 -396
  69. package/app/pages/ui/display-prompt.vue +0 -284
  70. package/app/pages/ui/display-toast.vue +0 -311
  71. package/app/pages/ui/expanding-panel.vue +0 -326
  72. package/app/pages/ui/glowing-border.vue +0 -111
  73. package/app/pages/ui/layout-content-docs.vue +0 -72
  74. package/app/pages/ui/layout-grid-a.vue +0 -96
  75. package/app/pages/ui/layout-grid-b.vue +0 -132
  76. package/app/pages/ui/magnetic-navigation.vue +0 -50
  77. package/app/pages/ui/marquee-scroller.vue +0 -99
  78. package/app/pages/ui/mask-element.vue +0 -104
  79. package/app/pages/ui/masonry-columns.vue +0 -55
  80. package/app/pages/ui/masonry-grid-ordered-experiment.vue +0 -79
  81. package/app/pages/ui/masonry-grid-ordered.vue +0 -56
  82. package/app/pages/ui/masonry-grid-sorted.vue +0 -55
  83. package/app/pages/ui/masonry-grid.vue +0 -55
  84. package/app/pages/ui/navigation/navigation-horizontal.vue +0 -484
  85. package/app/pages/ui/navigation/site-navigation/about.vue +0 -36
  86. package/app/pages/ui/navigation/site-navigation/contact.vue +0 -39
  87. package/app/pages/ui/navigation/site-navigation/index.vue +0 -38
  88. package/app/pages/ui/navigation/site-navigation/portfolio.vue +0 -43
  89. package/app/pages/ui/navigation/site-navigation/services.vue +0 -42
  90. package/app/pages/ui/navigation/tab-navigation.vue +0 -493
  91. package/app/pages/ui/page-row.vue +0 -127
  92. package/app/pages/ui/price-list.vue +0 -65
  93. package/app/pages/ui/profile-section.vue +0 -142
  94. package/app/pages/ui/qr-code/[componentName].vue +0 -218
  95. package/app/pages/ui/rotating-carousel.vue +0 -154
  96. package/app/pages/ui/scroll-reveal-image.vue +0 -483
  97. package/app/pages/ui/section-parallax.vue +0 -54
  98. package/app/pages/ui/services/colour-finder.vue +0 -43
  99. package/app/pages/ui/services/services-cards.vue +0 -57
  100. package/app/pages/ui/services/services-section/[slug].vue +0 -61
  101. package/app/pages/ui/services/services-sections-compact.vue +0 -64
  102. package/app/pages/ui/settings.vue +0 -54
  103. package/app/pages/ui/simple-grid.vue +0 -55
  104. package/app/pages/ui/slider-gallery.vue +0 -261
  105. package/app/pages/ui/tabs-y.vue +0 -107
  106. package/app/pages/ui/tabs.vue +0 -189
  107. package/app/pages/ui/tooltips.vue +0 -104
  108. package/app/pages/ui/wipe-away-vertical.vue +0 -129
@@ -1,910 +0,0 @@
1
- <template>
2
- <div>
3
- <NuxtLayout name="default">
4
- <template #layout-content>
5
- <LayoutGridByCols>
6
- <template #item-0>
7
- <div>
8
- <h1 class="header-1">Example form</h1>
9
- <p class="body-normal">Use 'test@test.com' to trigger server errors</p>
10
- </div>
11
- </template>
12
- </LayoutGridByCols>
13
- <LayoutGridByCols>
14
- <template #item-0>
15
- <div>
16
- <p>Themes switcher</p>
17
- <ul class="flex-group">
18
- <li>
19
- <InputButtonCore
20
- type="button"
21
- :is-pending="false"
22
- button-text="Default"
23
- theme="default"
24
- @click.stop.prevent="swapTheme('default')"
25
- />
26
- </li>
27
- <li>
28
- <InputButtonCore
29
- type="button"
30
- :is-pending="false"
31
- button-text="Success"
32
- theme="success"
33
- @click.stop.prevent="swapTheme('success')"
34
- />
35
- </li>
36
- <li>
37
- <InputButtonCore
38
- type="button"
39
- :is-pending="false"
40
- button-text="Error"
41
- theme="error"
42
- @click.stop.prevent="swapTheme('error')"
43
- />
44
- </li>
45
- <li>
46
- <InputButtonCore
47
- type="button"
48
- :is-pending="false"
49
- button-text="Warning"
50
- theme="warning"
51
- @click.stop.prevent="swapTheme('warning')"
52
- />
53
- </li>
54
- </ul>
55
-
56
- <p>Select type of text input to display</p>
57
- <ul class="flex-group">
58
- <li>
59
- <InputButtonCore
60
- type="button"
61
- button-text="Normal"
62
- theme="default"
63
- @click.stop.prevent="inputVariant = 'normal'"
64
- />
65
- </li>
66
- <li>
67
- <InputButtonCore
68
- type="button"
69
- button-text="Underlined"
70
- theme="default"
71
- @click.stop.prevent="inputVariant = 'underlined'"
72
- />
73
- </li>
74
- </ul>
75
-
76
- <PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
77
- <h1 class="page-heading-1 mbe-18">Settings</h1>
78
- <h2 class="page-heading-2 mbe-18">Theme Switcher</h2>
79
- <DisplayThemeSwitch />
80
- </PageRow>
81
-
82
- <FormWrapper width="medium">
83
- <template #default>
84
- <ClientOnly>
85
- <form ref="formRef" class="form-wrapper" @submit.stop.prevent="submitForm()">
86
- <div id="aria-live-message" aria-live="assertive"></div>
87
-
88
- <FormField v-if="countriesData !== null" width="wide" :has-gutter="false">
89
- <template #default>
90
- <InputSelectWithLabel
91
- id="countrySelect"
92
- v-model="state.countrySelect"
93
- v-model:field-data="countriesData"
94
- name="countrySelect"
95
- :required="true"
96
- label="Where are you from?"
97
- placeholder="Please select a country"
98
- :error-message="formErrors?.countrySelect?._errors[0] ?? ''"
99
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.countrySelect)"
100
- :theme
101
- :input-variant
102
- >
103
- <template #descriptionText>This is a descriptionText for the message input.</template>
104
- </InputSelectWithLabel>
105
- </template>
106
- </FormField>
107
-
108
- <FormField width="wide" :has-gutter="false">
109
- <template #default>
110
- <InputTextWithLabel
111
- id="emailAddress"
112
- v-model="state.emailAddress"
113
- type="email"
114
- inputmode="email"
115
- :maxlength="fieldMaxLength('email')"
116
- name="emailAddress"
117
- placeholder="eg. name@domain.com"
118
- label="Email address"
119
- :error-message="formErrors?.emailAddress?._errors[0] ?? ''"
120
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.emailAddress)"
121
- :required="true"
122
- :style-class-passthrough="['style-1', 'style-2']"
123
- :theme
124
- :input-variant
125
- >
126
- <template #descriptionText>This is a descriptionText for the email input.</template>
127
- <template #left>
128
- <Icon name="radix-icons:envelope-closed" class="icon" />
129
- </template>
130
- <template #right>
131
- <Icon name="radix-icons:envelope-closed" class="icon" />
132
- </template>
133
- </InputTextWithLabel>
134
- </template>
135
- </FormField>
136
-
137
- <FormField width="wide" :has-gutter="false">
138
- <template #default>
139
- <InputTextWithLabel
140
- id="username"
141
- v-model="state.username"
142
- type="text"
143
- :maxlength="fieldMaxLength('username')"
144
- name="username"
145
- placeholder="eg. JoeBloggs1"
146
- label="Username"
147
- :error-message="formErrors?.username?._errors[0] ?? ''"
148
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.username)"
149
- :required="true"
150
- :style-class-passthrough="['style-1', 'style-2']"
151
- :theme
152
- :input-variant
153
- >
154
- <template #descriptionHtml>
155
- <p class="mbs-4 mbe-8" style="font-weight: 900">
156
- This is a descriptionHtml for the email input.
157
- </p>
158
- </template>
159
- <template #left>
160
- <Icon name="radix-icons:person" class="icon" />
161
- </template>
162
- </InputTextWithLabel>
163
- </template>
164
- </FormField>
165
-
166
- <FormField width="wide" :has-gutter="false">
167
- <template #default>
168
- <InputPasswordWithLabel
169
- id="password"
170
- v-model="state.password"
171
- :maxlength="fieldMaxLength('password')"
172
- name="password"
173
- placeholder="eg. a mixure of numbers and letters"
174
- label="Password"
175
- :error-message="formErrors?.password?._errors[0] ?? ''"
176
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.password)"
177
- :required="true"
178
- :style-class-passthrough="['style-1', 'style-2']"
179
- :theme
180
- :input-variant
181
- >
182
- <template #descriptionText>This is a descriptionText for the password input.</template>
183
- <template #right>
184
- <Icon name="radix-icons:eye-open" class="icon" />
185
- </template>
186
- </InputPasswordWithLabel>
187
- </template>
188
- </FormField>
189
-
190
- <FormField width="wide" :has-gutter="false">
191
- <template #default>
192
- <InputTextareaWithLabel
193
- v-model="state.message"
194
- :maxlength="fieldMaxLength('message')"
195
- name="message"
196
- placeholder="Type your message here"
197
- label="Your message"
198
- :error-message="formErrors?.message?._errors[0] ?? ''"
199
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.message)"
200
- :required="true"
201
- :style-class-passthrough="['style-1', 'style-2']"
202
- :theme
203
- :input-variant
204
- >
205
- <template #descriptionText>This is a descriptionText for the message input.</template>
206
- </InputTextareaWithLabel>
207
- </template>
208
- </FormField>
209
-
210
- <FormField v-if="tagsData !== null" width="wide" :has-gutter="false">
211
- <template #default>
212
- <MultipleCheckboxes
213
- v-model="state.tags"
214
- v-model:field-data="tagsData"
215
- name="tags"
216
- legend="Choose tags (MultipleCheckboxes)"
217
- :required="true"
218
- label="Check between 3 and 8 tags"
219
- placeholder="eg. Type something here"
220
- :is-button="true"
221
- :is-pill="false"
222
- :error-message="formErrors?.tags?._errors[0] ?? ''"
223
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.tags)"
224
- options-layout="inline"
225
- :theme
226
- :display-as-disc="false"
227
- >
228
- <template #descriptionText>descriptionText slot</template>
229
- <template #itemIcon>
230
- <Icon name="material-symbols:bookmark-add-outline" class="icon" />
231
- </template>
232
- </MultipleCheckboxes>
233
- </template>
234
- </FormField>
235
-
236
- <FormField width="wide" :has-gutter="false">
237
- <template #default>
238
- <ToggleSwitchWithLabel
239
- v-model="state.darkMode"
240
- name="darkMode"
241
- label="Toggle Dark mode"
242
- true-value="dark"
243
- false-value="light"
244
- :theme
245
- >
246
- <template #description>
247
- <p class="label-description">Light or Dark mode?</p>
248
- </template>
249
- <template #iconOn>
250
- <Icon name="radix-icons:sun" class="icon" />
251
- </template>
252
- <template #iconOff>
253
- <Icon name="radix-icons:moon" class="icon" />
254
- </template>
255
- </ToggleSwitchWithLabel>
256
- </template>
257
- </FormField>
258
-
259
- <FormField width="wide" :has-gutter="false">
260
- <template #default>
261
- <ToggleSwitchWithLabelInline
262
- v-model="state.anotherToggle"
263
- name="anotherToggle"
264
- label="Toggle Dark mode (inline)"
265
- :theme
266
- >
267
- <template #iconOn>
268
- <Icon name="radix-icons:moon" class="icon" />
269
- </template>
270
- <template #iconOff>
271
- <Icon name="radix-icons:sun" class="icon" />
272
- </template>
273
- </ToggleSwitchWithLabelInline>
274
- </template>
275
- </FormField>
276
-
277
- <FormField width="wide" :has-gutter="false">
278
- <template #default>
279
- <ToggleSwitchWithLabel
280
- v-model="state.toggleBoolean"
281
- :error-message="formErrors?.toggleBoolean?._errors[0] ?? ''"
282
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.toggleBoolean)"
283
- name="toggleBoolean"
284
- label="Toggle Dark mode"
285
- :theme
286
- >
287
- <template #descriptionHtml>
288
- <p class="label-description">Toggle some value</p>
289
- </template>
290
- </ToggleSwitchWithLabel>
291
- </template>
292
- </FormField>
293
-
294
- <FormField width="wide" :has-gutter="false">
295
- <template #default>
296
- <InputTextAsNumberWithLabel
297
- v-model.number="state.count2"
298
- :maxlength="fieldMaxLength('count2')"
299
- :min="25"
300
- :max="75"
301
- :step="5"
302
- name="count2"
303
- placeholder="eg. 10"
304
- label="How many things? Between 25 & 75"
305
- :error-message="formErrors?.count2?._errors[0] ?? ''"
306
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.count2)"
307
- :required="true"
308
- :style-class-passthrough="['style-1', 'style-2']"
309
- :theme
310
- >
311
- <template #descriptionHtml>
312
- <p class="label-description">Input type="text" inputmode="numeric"</p>
313
- </template>
314
- <template #left>
315
- <Icon name="gridicons:minus-small" class="icon" />
316
- </template>
317
- <template #right>
318
- <Icon name="gridicons:plus-small" class="icon" />
319
- </template>
320
- </InputTextAsNumberWithLabel>
321
- </template>
322
- </FormField>
323
-
324
- <FormField width="wide" :has-gutter="false">
325
- <template #default>
326
- <InputNumberDefault
327
- v-model.number="state.count"
328
- name="count"
329
- label="How many things? Between 25 & 75 , step 5"
330
- :min="25"
331
- :max="75"
332
- :step="5"
333
- placeholder="eg. What\'s your count?"
334
- :error-message="formErrors?.count?._errors[0] ?? ''"
335
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.count)"
336
- :required="true"
337
- :style-class-passthrough="['count-1', 'count-2']"
338
- :theme
339
- >
340
- <template #descriptionHtml>
341
- <p class="label-description">Input type="number" inputmode="numeric"</p>
342
- </template>
343
- <template #left>
344
- <Icon name="gridicons:minus-small" class="icon" />
345
- </template>
346
- <template #right>
347
- <Icon name="gridicons:plus-small" class="icon" />
348
- </template>
349
- </InputNumberDefault>
350
- </template>
351
- </FormField>
352
-
353
- <FormField v-if="tagsData !== null" width="wide" :has-gutter="false">
354
- <template #default>
355
- <MultipleRadiobuttons
356
- v-model="state.tagsRadio"
357
- v-model:field-data="tagsData"
358
- name="tagsRadio"
359
- legend="Choose tags (as radiobuttons)"
360
- :required="true"
361
- label="Check between 3 and 8 tags"
362
- placeholder="eg. Type something here"
363
- :is-button="true"
364
- :is-pill="true"
365
- :error-message="formErrors?.tagsRadio?._errors[0] ?? ''"
366
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.tagsRadio)"
367
- options-layout="inline"
368
- :theme
369
- direction="row-reverse"
370
- >
371
- <template #description>
372
- <p class="label-description">This is description: optionsLayout = 'inline'</p>
373
- </template>
374
- </MultipleRadiobuttons>
375
- </template>
376
- </FormField>
377
-
378
- <FormField
379
- width="wide"
380
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.score)"
381
- :has-gutter="false"
382
- >
383
- <template #default>
384
- <InputRangeDefault
385
- v-model.number="state.score"
386
- name="score"
387
- label="Score between 0 & 100"
388
- :min="0"
389
- :max="100"
390
- :step="10"
391
- placeholder="eg. What\'s your score?"
392
- :error-message="formErrors?.score?._errors[0] ?? ''"
393
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.score)"
394
- :required="true"
395
- :style-class-passthrough="['style-1', 'style-2']"
396
- :theme
397
- >
398
- <template #descriptionText>
399
- <p class="label-description">This is a description of what the user is required to do</p>
400
- </template>
401
- <template #datalist>
402
- <datalist id="score-datalist" class="input-range-datalist">
403
- <option value="0" label="Rubbish!"></option>
404
- <option value="25" label="Below par"></option>
405
- <option value="50" label="Average"></option>
406
- <option value="75" label="Quite good"></option>
407
- <option value="100" label="Excellent"></option>
408
- </datalist>
409
- </template>
410
- <template #left>
411
- <Icon name="gridicons:minus-small" class="icon" />
412
- </template>
413
- <template #right>
414
- <Icon name="gridicons:plus-small" class="icon" />
415
- </template>
416
- </InputRangeDefault>
417
- </template>
418
- </FormField>
419
-
420
- <FormField v-if="titleData !== null" width="wide" :has-gutter="false">
421
- <template #default>
422
- <MultipleRadiobuttons
423
- v-model="state.title"
424
- v-model:field-data="titleData"
425
- name="title"
426
- legend="What is your title"
427
- :required="true"
428
- label="Check one"
429
- placeholder="eg. Type something here"
430
- :error-message="formErrors?.title?._errors[0] ?? ''"
431
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.title)"
432
- options-layout="inline"
433
- :theme
434
- >
435
- <template #checkedIcon>
436
- <Icon name="material-symbols:check-small" class="input-checked-icon" />
437
- </template>
438
- <template #descriptionHtml>
439
- <p class="label-description">
440
- NOTE: Custom check icon set via slot
441
- <br />
442
- This is description: optionsLayout = 'inline'
443
- </p>
444
- </template>
445
- </MultipleRadiobuttons>
446
- </template>
447
- </FormField>
448
-
449
- <FormField v-if="titleData !== null" width="wide" :has-gutter="false">
450
- <template #default>
451
- <MultipleRadiobuttons
452
- v-model="state.otherTitle"
453
- v-model:field-data="titleData"
454
- name="otherTitle"
455
- legend="What is your title"
456
- :required="true"
457
- label="Check one"
458
- placeholder="eg. Type something here"
459
- :error-message="formErrors?.otherTitle?._errors[0] ?? ''"
460
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.otherTitle)"
461
- options-layout="equal-widths"
462
- :theme
463
- >
464
- <template #descriptionHtml>
465
- <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
466
- </template>
467
- </MultipleRadiobuttons>
468
- </template>
469
- </FormField>
470
-
471
- <FormField v-if="citiesData !== null" width="wide" :has-gutter="false">
472
- <template #default>
473
- <MultipleCheckboxes
474
- v-model="state.cities"
475
- v-model:field-data="citiesData"
476
- name="cities"
477
- legend="Choose a location"
478
- :required="true"
479
- label="Check all Cities you like"
480
- placeholder="eg. Type something here"
481
- :error-message="formErrors?.cities?._errors[0] ?? ''"
482
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.cities)"
483
- options-layout="inline"
484
- :theme
485
- >
486
- <template #description>
487
- <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
488
- </template>
489
- </MultipleCheckboxes>
490
- </template>
491
- </FormField>
492
-
493
- <FormField v-if="countriesData !== null" width="wide" :has-gutter="false">
494
- <template #default>
495
- <MultipleCheckboxes
496
- v-model="state.countries"
497
- v-model:field-data="countriesData"
498
- name="countries"
499
- legend="Choose a country"
500
- :required="true"
501
- label="Check all Countries you like"
502
- placeholder="eg. Type something here"
503
- :error-message="formErrors?.countries?._errors[0] ?? ''"
504
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.countries)"
505
- options-layout="equal-widths"
506
- :theme
507
- >
508
- <template #checkedIcon>
509
- <Icon name="material-symbols:circle" class="input-checked-icon" />
510
- </template>
511
- <template #descriptionHtml>
512
- <p class="label-description">
513
- NOTE: Custom chefck icon set via slot
514
- <br />
515
- This is description: optionsLayout = 'inline'
516
- </p>
517
- </template>
518
- </MultipleCheckboxes>
519
- </template>
520
- </FormField>
521
-
522
- <FormField width="wide" :has-gutter="false">
523
- <template #default>
524
- <SingleCheckbox
525
- v-model="state.agreed"
526
- name="agreed"
527
- legend="I agree (label with description)"
528
- label="Click to agree to something"
529
- :required="true"
530
- :error-message="formErrors?.agreed?._errors[0] ?? ''"
531
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.agreed)"
532
- :theme
533
- >
534
- <template #description>
535
- <p class="label-description">
536
- You must
537
- <strong>agree</strong>
538
- to continue
539
- </p>
540
- </template>
541
- </SingleCheckbox>
542
- </template>
543
- </FormField>
544
-
545
- <FormField width="wide" :has-gutter="false">
546
- <template #default>
547
- <SingleCheckbox
548
- v-model="state.agree"
549
- name="agree"
550
- legend="I agree (label no description)"
551
- label="Click to agree to something"
552
- :required="true"
553
- :error-message="formErrors?.agree?._errors[0] ?? ''"
554
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.agree)"
555
- :theme
556
- />
557
- </template>
558
- </FormField>
559
-
560
- <FormField width="wide" :has-gutter="false">
561
- <template #default>
562
- <SingleCheckbox
563
- v-model="state.terms"
564
- name="terms"
565
- legend="Terms and conditions"
566
- :required="true"
567
- :error-message="formErrors?.terms?._errors[0] ?? ''"
568
- :field-has-error="Boolean(zodFormControl.submitAttempted && formErrors?.terms)"
569
- :theme
570
- >
571
- <template #labelContent>
572
- <span class="body-normal">
573
- You must agree to our
574
- <NuxtLink to="/typography" class="link-normal">terms and conditions</NuxtLink>
575
- to continue
576
- </span>
577
- </template>
578
- </SingleCheckbox>
579
- </template>
580
- </FormField>
581
-
582
- <FormField width="wide" :has-gutter="false">
583
- <template #default>
584
- <InputButtonCore
585
- type="button"
586
- :is-pending="false"
587
- :readonly="zodFormControl.submitDisabled"
588
- button-text="Submit"
589
- :theme
590
- @click.stop.prevent="submitForm()"
591
- />
592
- </template>
593
- </FormField>
594
- </form>
595
- </ClientOnly>
596
- </template>
597
- </FormWrapper>
598
- </div>
599
- </template>
600
- <template #item-1>
601
- <div>
602
- <ClientOnly>
603
- <p>Client only state</p>
604
- <pre>
605
- {{ state }}
606
- </pre
607
- >
608
- <p>Client only zodFormControl</p>
609
- <pre>
610
- {{ zodFormControl }}
611
- </pre
612
- >
613
- <p>Client only formErrors</p>
614
-
615
- <pre>
616
- {{ formErrors }}
617
- </pre
618
- >
619
- </ClientOnly>
620
- </div>
621
- </template>
622
- </LayoutGridByCols>
623
- </template>
624
- </NuxtLayout>
625
- </div>
626
- </template>
627
-
628
- <script setup lang="ts">
629
- import { z } from "zod";
630
- import type { IFormMultipleOptions, FormUiTheme, InputUiVariant } from "~/types/forms/types.forms";
631
- // Inline fallback for ReturnTypeUseZodValidation if not exported
632
- type ReturnTypeUseZodValidation = ReturnType<typeof useZodValidation>;
633
-
634
- definePageMeta({
635
- layout: false,
636
- });
637
-
638
- useHead({
639
- title: "Text Field Example",
640
- meta: [{ name: "description", content: "Homepage" }],
641
- bodyAttrs: {
642
- class: "text-fields-page",
643
- },
644
- });
645
-
646
- const inputVariant = ref<InputUiVariant>("normal");
647
- // const inputVariantData = ref<IFormMultipleOptions>({
648
- // data: [
649
- // {
650
- // id: 'normal',
651
- // name: 'normal',
652
- // value: 'normal',
653
- // label: 'Normal',
654
- // },
655
- // {
656
- // id: 'outlined',
657
- // name: 'outlined',
658
- // value: 'outlined',
659
- // label: 'Material - Outlined',
660
- // },
661
- // {
662
- // id: 'underlined',
663
- // name: 'underlined',
664
- // value: 'underlined',
665
- // label: 'Material - Underlined',
666
- // },
667
- // ],
668
- // total: 3,
669
- // skip: 0,
670
- // limit: 3,
671
- // });
672
- const theme = ref<FormUiTheme>("default");
673
- const swapTheme = (newTheme: FormUiTheme) => {
674
- theme.value = newTheme;
675
- };
676
-
677
- // const variant = ref<"primary" | "secondary" | "tertiary">("primary");
678
- // const swapVariant = (newVariant: "primary" | "secondary" | "tertiary") => {
679
- // variant.value = newVariant;
680
- // };
681
-
682
- /*
683
- * Fetch some sample data
684
- **/
685
- const { data: citiesData } = await useFetch<IFormMultipleOptions>("/api/places/list?category=cities");
686
- const { data: countriesData } = await useFetch<IFormMultipleOptions>("/api/places/list?category=countries");
687
- const { data: titleData } = await useFetch<IFormMultipleOptions>("/api/utils?category=title");
688
- const { data: tagsData } = await useFetch<IFormMultipleOptions>("/api/recipes/tags");
689
-
690
- /*
691
- * Setup forms
692
- */
693
- const formSchema = z
694
- .object({
695
- emailAddress: z
696
- .string({
697
- error: (issue) => (issue.input === undefined ? "Email address is required" : "Email address must be a string"),
698
- })
699
- .email({ error: "Invalid email address" })
700
- .refine(
701
- (email) => {
702
- // Defensive: previousState may be undefined on first run
703
- return email !== (zodFormControl?.previousState?.emailAddress?.value ?? "");
704
- },
705
- {
706
- error: "This email address has already been used",
707
- }
708
- ),
709
- username: z
710
- .string({
711
- error: (issue) => (issue.input === undefined ? "Username is required" : "Username must be a string"),
712
- })
713
- .trim()
714
- .min(2, "Username is too short")
715
- .max(25, "Username is too long")
716
- .refine(
717
- (username) => {
718
- return username !== (zodFormControl?.previousState?.username?.value ?? "");
719
- },
720
- {
721
- error: "This username has already been used",
722
- }
723
- ),
724
- password: z
725
- .string()
726
- .trim()
727
- .min(8, "Password is too short")
728
- .max(25, "Password is too long")
729
- .refine(
730
- (password) => {
731
- return password !== (zodFormControl?.previousState?.password?.value ?? "");
732
- },
733
- {
734
- error: "You've already used this password",
735
- }
736
- ),
737
- message: z.string().trim().min(2, "Message is too short").max(255, "Message is too long"),
738
- count: z
739
- .number({
740
- error: (issue) => (issue.input === undefined ? "Count is required" : "Count must be a number"),
741
- })
742
- .int({ error: "Count must be a whole number" })
743
- .gte(25, "Count must be between 25 and 75")
744
- .lte(75, "Count must be between 25 and 75")
745
- .multipleOf(5, "Count must be a multiple of 5"),
746
- count2: z
747
- .number({
748
- error: (issue) => (issue.input === undefined ? "Count is required" : "Count must be a number"),
749
- })
750
- .int({ error: "Count must be a whole number" })
751
- .gte(25, "Count must be between 25 and 75")
752
- .lte(75, "Count must be between 25 and 75"),
753
- score: z
754
- .number({
755
- error: (issue) => (issue.input === undefined ? "Score is required" : "Score must be a number"),
756
- })
757
- .gte(0)
758
- .lte(100),
759
- cities: z.array(z.string()).min(1, "Please select at least one city"),
760
- countries: z
761
- .array(z.string())
762
- .min(2, "Please select at least 2 countries")
763
- .max(5, "Please select no more than 5 countries"),
764
- countrySelect: z.string().min(1, { error: "Please select a country" }),
765
- tags: z.array(z.string()).min(3, "Please select at least 3 tags").max(8, "Please select no more than 8 tags"),
766
- tagsRadio: z.string().min(1, { error: "Please choose a tag" }),
767
- darkMode: z.string().min(1, { error: "Please choose a mode" }),
768
- title: z.string().min(1, { error: "Title is required" }),
769
- otherTitle: z.string().min(1, { error: "Title is required" }),
770
- toggleBoolean: z.boolean().refine((val) => val === true, { error: "You must tick this box" }),
771
- agreed: z.boolean().refine((val) => val === true, { error: "You must tick this box" }),
772
- agree: z.boolean().refine((val) => val === true, { error: "You must tick this box" }),
773
- terms: z.boolean().refine((val) => val === true, { error: "You must accept our terms" }),
774
- })
775
- .required({
776
- emailAddress: true,
777
- username: true,
778
- password: true,
779
- message: true,
780
- count: true,
781
- count2: true,
782
- score: true,
783
- cities: true,
784
- countries: true,
785
- countrySelect: true,
786
- tags: true,
787
- tagsRadio: true,
788
- title: true,
789
- otherTitle: true,
790
- toggleBoolean: true,
791
- agreed: true,
792
- agree: true,
793
- terms: true,
794
- });
795
-
796
- type FormSchema = z.infer<typeof formSchema>;
797
- const formErrors = computed<z.ZodFormattedError<FormSchema> | null>(() => zodErrorObj.value);
798
-
799
- const state = reactive({
800
- emailAddress: "",
801
- username: "",
802
- password: "",
803
- message: "",
804
- count: 25,
805
- count2: 25,
806
- score: 50,
807
- cities: [],
808
- countries: [],
809
- countrySelect: "",
810
- tags: [],
811
- tagsRadio: [],
812
- title: "",
813
- otherTitle: "",
814
- darkMode: "light",
815
- toggleBoolean: false,
816
- anotherToggle: false,
817
- agreed: false,
818
- agree: false,
819
- terms: false,
820
- });
821
-
822
- const formRef = ref<HTMLFormElement | null>(null);
823
-
824
- // If you don't have this type, you can define it inline as:
825
- // type ReturnTypeUseZodValidation = ReturnType<typeof useZodValidation>;
826
-
827
- const {
828
- initZodForm,
829
- zodFormControl,
830
- zodErrorObj,
831
- pushCustomErrors,
832
- doZodValidate,
833
- fieldMaxLength,
834
- scrollToFirstError,
835
- } = useZodValidation<typeof formSchema>(formSchema, formRef) as ReturnTypeUseZodValidation;
836
-
837
- initZodForm();
838
-
839
- const submitForm = async () => {
840
- zodFormControl.submitAttempted = true;
841
- if (!(await doZodValidate(state))) {
842
- scrollToFirstError();
843
- return;
844
- }
845
- zodFormControl.displayLoader = true;
846
- try {
847
- console.log("Form valid - post it");
848
- const data = await $fetch("/api/textFields", {
849
- method: "post",
850
- body: state,
851
- async onResponse({ response }) {
852
- if (response.status === 400) {
853
- console.log("onResponse", response);
854
- console.log(response.status);
855
-
856
- // useApiErrors(response._data.data.errors);
857
- // for (const [key, message] of Object.entries(response._data.data.errors)) {
858
- // console.log(`${key}: ${message}`);
859
- // updateErrorMessages(key, message);
860
- // }
861
-
862
- // if (error instanceof Error) {
863
- await pushCustomErrors(response._data, state);
864
- // zodFormControl.formIsValid = false;
865
- // }
866
- // zodFormControl.submitAttempted = false;
867
- }
868
- if (response.status === 200) {
869
- zodFormControl.submitSuccessful = true;
870
- }
871
- },
872
- });
873
- console.log("3: Finished data", data);
874
- // return data;
875
- } catch (error) {
876
- console.warn("2: An error occured posting form data", error);
877
- } finally {
878
- zodFormControl.displayLoader = false;
879
- }
880
- };
881
-
882
- watch(
883
- () => state,
884
- () => {
885
- // console.log('Watching state');
886
- doZodValidate(state);
887
- },
888
- { deep: true }
889
- );
890
- </script>
891
-
892
- <style lang="css">
893
- .flex-group {
894
- align-items: flex-start;
895
- display: flex;
896
- flex-wrap: wrap;
897
- gap: 2.4rem;
898
- margin-bottom: 3.2rem;
899
- }
900
-
901
- ul.flex-group {
902
- list-style-type: none;
903
- padding: 0;
904
- }
905
-
906
- .header-1 {
907
- font-family: var(--font-family);
908
- color: var(--brand-success-text-text);
909
- }
910
- </style>