srcdev-nuxt-components 9.3.6 → 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 (126) hide show
  1. package/.claude/hooks/check-component-update.sh +30 -1
  2. package/.claude/skills/components/accordian-core.md +7 -7
  3. package/.claude/skills/components/breadcrumb.md +49 -0
  4. package/.claude/skills/components/content-docs.md +2 -1
  5. package/.claude/skills/components/expanding-panel.md +5 -5
  6. package/.claude/skills/components/navigation-items.md +1 -0
  7. package/.claude/skills/components/responsive-header.md +1 -0
  8. package/.claude/skills/components/service-detail.md +156 -0
  9. package/.claude/skills/components/services-card-grid.md +9 -9
  10. package/.claude/skills/components/services-card.md +82 -30
  11. package/.claude/skills/index.md +3 -1
  12. package/.claude/skills/qa-panel.md +2 -2
  13. package/.vscode/srcdev-component-breadcrumb.code-snippets +14 -0
  14. package/.vscode/srcdev-component-service-detail.code-snippets +49 -0
  15. package/.vscode/srcdev-component-services-card.code-snippets +108 -0
  16. package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +22 -16
  17. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +94 -0
  18. package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +32 -0
  19. package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +87 -0
  20. package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +87 -0
  21. package/app/components/01.atoms/navigation/breadcrumb/tests/__snapshots__/Breadcrumb.spec.ts.snap +13 -0
  22. package/app/components/02.molecules/expandable/accordian/AccordianCore.vue +5 -1
  23. package/app/components/02.molecules/expandable/accordian/tests/AccordianCore.spec.ts +22 -14
  24. package/app/components/02.molecules/expandable/accordian/tests/__snapshots__/AccordianCore.spec.ts.snap +42 -28
  25. package/app/components/02.molecules/expandable/expanding-panel/CONSUMER-STYLING.md +8 -10
  26. package/app/components/02.molecules/expandable/expanding-panel/ExpandingPanel.vue +15 -47
  27. package/app/components/02.molecules/expandable/expanding-panel/tests/__snapshots__/ExpandingPanel.spec.ts.snap +4 -12
  28. package/app/components/03.organisms/responsive-header/NavigationItems.vue +91 -4
  29. package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +5 -1
  30. package/app/components/03.organisms/responsive-header/tests/NavigationItems.spec.ts +2 -2
  31. package/app/components/03.organisms/responsive-header/tests/__snapshots__/NavigationItems.spec.ts.snap +13 -13
  32. package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +13 -13
  33. package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +181 -0
  34. package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +480 -0
  35. package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +282 -0
  36. package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +270 -0
  37. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +99 -0
  38. package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +129 -0
  39. package/app/components/03.organisms/services/services-card/ServicesCard.vue +153 -39
  40. package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +65 -0
  41. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +82 -0
  42. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +13 -5
  43. package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +3 -7
  44. package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +126 -2
  45. package/app/components/03.organisms/services/services-grids/stories/ServicesSectionGrid.stories.ts +6 -0
  46. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +51 -27
  47. package/app/components/03.organisms/services/services-section/ServicesSection.vue +19 -0
  48. package/app/components/03.organisms/services/services-section/tests/__snapshots__/ServicesSection.spec.ts.snap +8 -8
  49. package/app/types/components/breadcrumb.ts +4 -0
  50. package/app/types/components/index.ts +1 -0
  51. package/package.json +2 -2
  52. package/app/pages/auto-grid.vue +0 -328
  53. package/app/pages/banner-video.vue +0 -238
  54. package/app/pages/forms/examples/buttons/index.vue +0 -259
  55. package/app/pages/forms/examples/material/checkbox-radio-panels.vue +0 -244
  56. package/app/pages/forms/examples/material/cssbattle.vue +0 -60
  57. package/app/pages/forms/examples/material/text-fields.vue +0 -910
  58. package/app/pages/grid-stack.vue +0 -252
  59. package/app/pages/index.vue +0 -127
  60. package/app/pages/page-hero-highlights.vue +0 -204
  61. package/app/pages/samaritan.vue +0 -327
  62. package/app/pages/typography/hero-text.vue +0 -97
  63. package/app/pages/typography/index.vue +0 -39
  64. package/app/pages/typography/page-body.vue +0 -171
  65. package/app/pages/typography/page-heading.vue +0 -76
  66. package/app/pages/typography/page-link.vue +0 -103
  67. package/app/pages/ui/accordian.vue +0 -211
  68. package/app/pages/ui/anchor-scroll-tab-navigation.vue +0 -191
  69. package/app/pages/ui/anchor-scroll.vue +0 -367
  70. package/app/pages/ui/animated-svg-text.vue +0 -85
  71. package/app/pages/ui/block-decorators.vue +0 -142
  72. package/app/pages/ui/card-core.vue +0 -82
  73. package/app/pages/ui/carousel-basic.vue +0 -292
  74. package/app/pages/ui/carousel-flip.vue +0 -449
  75. package/app/pages/ui/carousel-infinite.vue +0 -260
  76. package/app/pages/ui/clipped-panels.vue +0 -101
  77. package/app/pages/ui/contact-section.vue +0 -323
  78. package/app/pages/ui/container-glow.vue +0 -106
  79. package/app/pages/ui/content-width.vue +0 -126
  80. package/app/pages/ui/deep-expanding-menu.vue +0 -138
  81. package/app/pages/ui/display-avatar.vue +0 -253
  82. package/app/pages/ui/display-banner.vue +0 -78
  83. package/app/pages/ui/display-chip.vue +0 -367
  84. package/app/pages/ui/display-details.vue +0 -171
  85. package/app/pages/ui/display-dialog.vue +0 -344
  86. package/app/pages/ui/display-pill.vue +0 -396
  87. package/app/pages/ui/display-prompt.vue +0 -284
  88. package/app/pages/ui/display-toast.vue +0 -311
  89. package/app/pages/ui/expanding-panel.vue +0 -326
  90. package/app/pages/ui/glowing-border.vue +0 -111
  91. package/app/pages/ui/layout-content-docs.vue +0 -72
  92. package/app/pages/ui/layout-grid-a.vue +0 -96
  93. package/app/pages/ui/layout-grid-b.vue +0 -132
  94. package/app/pages/ui/magnetic-navigation.vue +0 -50
  95. package/app/pages/ui/marquee-scroller.vue +0 -99
  96. package/app/pages/ui/mask-element.vue +0 -104
  97. package/app/pages/ui/masonry-columns.vue +0 -55
  98. package/app/pages/ui/masonry-grid-ordered-experiment.vue +0 -79
  99. package/app/pages/ui/masonry-grid-ordered.vue +0 -56
  100. package/app/pages/ui/masonry-grid-sorted.vue +0 -55
  101. package/app/pages/ui/masonry-grid.vue +0 -55
  102. package/app/pages/ui/navigation/navigation-horizontal.vue +0 -484
  103. package/app/pages/ui/navigation/site-navigation/about.vue +0 -36
  104. package/app/pages/ui/navigation/site-navigation/contact.vue +0 -39
  105. package/app/pages/ui/navigation/site-navigation/index.vue +0 -38
  106. package/app/pages/ui/navigation/site-navigation/portfolio.vue +0 -43
  107. package/app/pages/ui/navigation/site-navigation/services.vue +0 -42
  108. package/app/pages/ui/navigation/tab-navigation.vue +0 -493
  109. package/app/pages/ui/page-row.vue +0 -127
  110. package/app/pages/ui/price-list.vue +0 -65
  111. package/app/pages/ui/profile-section.vue +0 -142
  112. package/app/pages/ui/qr-code/[componentName].vue +0 -218
  113. package/app/pages/ui/rotating-carousel.vue +0 -154
  114. package/app/pages/ui/scroll-reveal-image.vue +0 -483
  115. package/app/pages/ui/section-parallax.vue +0 -54
  116. package/app/pages/ui/services/colour-finder.vue +0 -43
  117. package/app/pages/ui/services/services-cards.vue +0 -57
  118. package/app/pages/ui/services/services-section/[slug].vue +0 -61
  119. package/app/pages/ui/services/services-sections-compact.vue +0 -64
  120. package/app/pages/ui/settings.vue +0 -54
  121. package/app/pages/ui/simple-grid.vue +0 -55
  122. package/app/pages/ui/slider-gallery.vue +0 -261
  123. package/app/pages/ui/tabs-y.vue +0 -107
  124. package/app/pages/ui/tabs.vue +0 -189
  125. package/app/pages/ui/tooltips.vue +0 -104
  126. 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>