srcdev-nuxt-components 9.6.8 → 9.6.9

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/skills/component-compliance-checklist.md +12 -1
  2. package/.claude/skills/component-inline-action-button.md +1 -1
  3. package/.claude/skills/component-local-style-override.md +1 -1
  4. package/.claude/skills/component-naming.md +11 -11
  5. package/.claude/skills/components/container-glow.md +2 -2
  6. package/.claude/skills/components/form-field.md +2 -2
  7. package/.claude/skills/components/form-wrapper.md +1 -1
  8. package/.claude/skills/components/glowing-border.md +1 -1
  9. package/.claude/skills/components/input-description.md +7 -7
  10. package/.claude/skills/components/input-error.md +3 -3
  11. package/.claude/skills/components/input-label.md +7 -8
  12. package/.claude/skills/components/input-number.md +17 -17
  13. package/.claude/skills/components/input-select.md +153 -0
  14. package/.claude/skills/components/input-text.md +220 -0
  15. package/.claude/skills/components/input-textarea.md +147 -0
  16. package/.claude/skills/components/select-menu.md +4 -4
  17. package/.claude/skills/components/toggle-switch.md +143 -0
  18. package/.claude/skills/composable-zod-validation.md +1 -1
  19. package/.claude/skills/icon-sets.md +1 -1
  20. package/.claude/skills/icon-sizing.md +1 -1
  21. package/.claude/skills/index.md +5 -5
  22. package/.claude/skills/storybook-add-story.md +1 -1
  23. package/.claude/skills/theming-component-token-pattern.md +8 -8
  24. package/.claude/skills/theming-form-geometry-tokens.md +1 -1
  25. package/.vscode/srcdev-component-input-select.code-snippets +20 -14
  26. package/.vscode/srcdev-component-input-text.code-snippets +46 -40
  27. package/.vscode/srcdev-component-input-textarea.code-snippets +19 -14
  28. package/.vscode/srcdev-component-toggle-switch.code-snippets +19 -29
  29. package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +2 -2
  30. package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
  31. package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +2 -2
  32. package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +4 -4
  33. package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +4 -4
  34. package/app/components/05.forms/form-errors/CONSUMER-STYLING.md +1 -1
  35. package/app/components/05.forms/form-field/CONSUMER-STYLING.md +2 -2
  36. package/app/components/05.forms/input-button/stories/InputButton.stories.ts +3 -3
  37. package/app/components/05.forms/input-description/CONSUMER-STYLING.md +6 -6
  38. package/app/components/05.forms/input-label/CONSUMER-STYLING.md +5 -5
  39. package/app/components/05.forms/input-number/CONSUMER-STYLING.md +9 -5
  40. package/app/components/05.forms/input-number/InputNumber.vue +2 -2
  41. package/app/components/05.forms/input-select/CONSUMER-STYLING.md +80 -10
  42. package/app/components/05.forms/input-select/InputSelect.vue +198 -0
  43. package/app/components/05.forms/input-select/InputSelectField.vue +130 -0
  44. package/app/components/05.forms/input-select/stories/InputSelect.stories.ts +184 -0
  45. package/app/components/05.forms/input-select/stories/InputSelectField.stories.ts +187 -0
  46. package/app/components/05.forms/input-select/tests/{InputSelectCore.spec.ts → InputSelect.spec.ts} +75 -12
  47. package/app/components/05.forms/input-select/tests/InputSelectField.spec.ts +121 -0
  48. package/app/components/05.forms/input-text/CONSUMER-STYLING.md +85 -15
  49. package/app/components/05.forms/input-text/{InputPasswordWithLabel.vue → InputPasswordField.vue} +16 -19
  50. package/app/components/05.forms/input-text/{InputTextCore.vue → InputText.vue} +42 -78
  51. package/app/components/05.forms/input-text/{InputTextAsNumberWithLabel.vue → InputTextAsNumberField.vue} +66 -62
  52. package/app/components/05.forms/input-text/InputTextField.vue +168 -0
  53. package/app/components/05.forms/input-text/stories/InputPasswordField.stories.ts +163 -0
  54. package/app/components/05.forms/input-text/stories/InputText.stories.ts +194 -0
  55. package/app/components/05.forms/input-text/stories/InputTextAsNumberField.stories.ts +182 -0
  56. package/app/components/05.forms/input-text/stories/InputTextField.stories.ts +206 -0
  57. package/app/components/05.forms/input-text/tests/{InputPasswordWithLabel.spec.ts → InputPasswordField.spec.ts} +42 -3
  58. package/app/components/05.forms/input-text/tests/InputText.spec.ts +166 -0
  59. package/app/components/05.forms/input-text/tests/InputTextAsNumberField.spec.ts +135 -0
  60. package/app/components/05.forms/input-text/tests/InputTextField.spec.ts +130 -0
  61. package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +94 -12
  62. package/app/components/05.forms/input-textarea/InputTextarea.vue +165 -0
  63. package/app/components/05.forms/input-textarea/InputTextareaField.vue +159 -0
  64. package/app/components/05.forms/input-textarea/stories/InputTextarea.stories.ts +198 -0
  65. package/app/components/05.forms/input-textarea/stories/InputTextareaField.stories.ts +161 -0
  66. package/app/components/05.forms/input-textarea/tests/InputTextarea.spec.ts +149 -0
  67. package/app/components/05.forms/input-textarea/tests/InputTextareaField.spec.ts +122 -0
  68. package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +13 -13
  69. package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +6 -6
  70. package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +30 -32
  71. package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +71 -50
  72. package/app/components/05.forms/toggle-switch/{ToggleSwitchCore.vue → ToggleSwitch.vue} +58 -73
  73. package/app/components/05.forms/toggle-switch/{ToggleSwitchWithLabel.vue → ToggleSwitchField.vue} +65 -19
  74. package/app/components/05.forms/toggle-switch/stories/ToggleSwitch.stories.ts +148 -0
  75. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchField.stories.ts +176 -0
  76. package/app/components/05.forms/toggle-switch/tests/ToggleSwitch.spec.ts +145 -0
  77. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchField.spec.ts +155 -0
  78. package/package.json +1 -1
  79. package/.claude/skills/components/input-select-core.md +0 -151
  80. package/.claude/skills/components/input-text-core.md +0 -204
  81. package/.claude/skills/components/input-textarea-core.md +0 -154
  82. package/.claude/skills/components/toggle-switch-core.md +0 -169
  83. package/app/components/05.forms/input-select/InputSelectCore.vue +0 -188
  84. package/app/components/05.forms/input-select/InputSelectWithLabel.vue +0 -118
  85. package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +0 -69
  86. package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +0 -115
  87. package/app/components/05.forms/input-select/tests/InputSelectWithLabel.spec.ts +0 -87
  88. package/app/components/05.forms/input-text/InputTextWithLabel.vue +0 -154
  89. package/app/components/05.forms/input-text/stories/InputPasswordWithLabel.stories.ts +0 -368
  90. package/app/components/05.forms/input-text/stories/InputTextAsNumberWithLabel.stories.ts +0 -480
  91. package/app/components/05.forms/input-text/stories/InputTextCore.stories.ts +0 -267
  92. package/app/components/05.forms/input-text/stories/InputTextWithLabel.stories.ts +0 -319
  93. package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +0 -75
  94. package/app/components/05.forms/input-text/tests/InputTextCore.spec.ts +0 -60
  95. package/app/components/05.forms/input-text/tests/InputTextWithLabel.spec.ts +0 -71
  96. package/app/components/05.forms/input-textarea/InputTextareaCore.vue +0 -159
  97. package/app/components/05.forms/input-textarea/InputTextareaWithLabel.vue +0 -136
  98. package/app/components/05.forms/input-textarea/stories/InputTextareaCore.stories.ts +0 -247
  99. package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +0 -458
  100. package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +0 -119
  101. package/app/components/05.forms/input-textarea/tests/InputTextareaWithLabel.spec.ts +0 -93
  102. package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabelInline.vue +0 -81
  103. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +0 -251
  104. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +0 -251
  105. package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +0 -283
  106. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +0 -101
  107. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabel.spec.ts +0 -133
  108. package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabelInline.spec.ts +0 -66
@@ -1,154 +0,0 @@
1
- <template>
2
- <div>
3
- <div
4
- class="input-text-with-label"
5
- :data-theme="theme"
6
- :data-invalid="fieldHasError ? '' : null"
7
- :class="[elementClasses, inputVariant, { dirty: isDirty }, { active: isActive }]"
8
- >
9
- <InputLabel
10
- :id
11
- :required
12
- :input-variant
13
- :field-has-error
14
- :style-class-passthrough="['input-text-label']"
15
- >
16
- <template #textLabel>{{ label }}</template>
17
- </InputLabel>
18
-
19
- <InputDescription
20
- v-if="inputVariant !== 'outlined'"
21
- :description-id
22
- :input-variant
23
- :field-has-error="fieldHasError"
24
- :style-class-passthrough="['input-text-description']"
25
- >
26
- <template v-if="slots.descriptionHtml" #descriptionHtml>
27
- <slot name="descriptionHtml"></slot>
28
- </template>
29
- <template v-if="slots.descriptionText" #descriptionText>
30
- <slot name="descriptionText"></slot>
31
- </template>
32
- </InputDescription>
33
-
34
- <InputTextCore
35
- :id
36
- v-model="modelValue"
37
- v-model:is-dirty="isDirty"
38
- v-model:is-active="isActive"
39
- :type
40
- :inputmode
41
- :maxlength
42
- :min
43
- :max
44
- :name
45
- :placeholder
46
- :label
47
- :error-message
48
- :field-has-error
49
- :required
50
- :style-class-passthrough
51
- :theme
52
- :aria-describedby="ariaDescribedby()"
53
- :input-variant
54
- >
55
- <template v-if="slots.left" #left>
56
- <slot name="left"></slot>
57
- </template>
58
- <template v-if="slots.right" #right>
59
- <slot name="right"></slot>
60
- </template>
61
- </InputTextCore>
62
-
63
- <InputError :id="errorId" :error-message :show-error="fieldHasError" :is-detached="false" :input-variant />
64
- </div>
65
-
66
- <InputDescription
67
- v-if="inputVariant === 'outlined'"
68
- :description-id
69
- :input-variant
70
- :field-has-error="fieldHasError"
71
- :style-class-passthrough="['input-text-description']"
72
- >
73
- <template v-if="slots.descriptionHtml" #descriptionHtml>
74
- <slot name="descriptionHtml"></slot>
75
- </template>
76
- <template v-if="slots.descriptionText" #descriptionText>
77
- <slot name="descriptionText"></slot>
78
- </template>
79
- </InputDescription>
80
- </div>
81
- </template>
82
-
83
- <script setup lang="ts">
84
- import type { InputTypesText, FormUiTheme, InputUiVariant, InputMode } from "~/types/forms/types.forms";
85
-
86
- interface Props {
87
- maxlength?: number;
88
- /** Passed straight through to the native input — meaningful for type="date"/"number" etc. */
89
- min?: string | number;
90
- /** Passed straight through to the native input — meaningful for type="date"/"number" etc. */
91
- max?: string | number;
92
- type: InputTypesText;
93
- inputmode?: InputMode;
94
- name: string;
95
- placeholder?: string;
96
- label: string;
97
- errorMessage: object | string;
98
- fieldHasError?: boolean;
99
- required?: boolean;
100
- styleClassPassthrough?: string | string[];
101
- theme?: FormUiTheme;
102
- inputVariant?: InputUiVariant;
103
- }
104
-
105
- const props = withDefaults(defineProps<Props>(), {
106
- maxlength: 255,
107
- min: undefined,
108
- max: undefined,
109
- inputmode: "text",
110
- placeholder: "",
111
- fieldHasError: false,
112
- required: false,
113
- styleClassPassthrough: () => [],
114
- theme: "default",
115
- inputVariant: "normal",
116
- });
117
-
118
- const slots = useSlots();
119
-
120
- const FormUiTheme = computed(() => {
121
- return props.fieldHasError ? "error" : props.theme;
122
- });
123
-
124
- const { id, errorId, descriptionId, ariaDescribedby } = useAriaDescribedById(
125
- props.name,
126
- toRef(props, "fieldHasError"),
127
- slots
128
- );
129
-
130
- const modelValue = defineModel<string>({ required: true });
131
- const isActive = ref<boolean>(false);
132
- const isDirty = ref<boolean>(false);
133
-
134
- const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
135
-
136
- const testDirty = () => {
137
- const watchValue = modelValue.value ?? "";
138
-
139
- if (!isDirty.value && typeof watchValue === "string" && watchValue.length > 0) {
140
- isDirty.value = true;
141
- }
142
- };
143
-
144
- onMounted(() => {
145
- testDirty();
146
- });
147
-
148
- watch(
149
- () => modelValue.value,
150
- () => {
151
- testDirty();
152
- }
153
- );
154
- </script>
@@ -1,368 +0,0 @@
1
- import type { Meta, StoryFn } from "@nuxtjs/storybook";
2
- import { computed } from "vue";
3
- import StorybookComponent from "../InputPasswordWithLabel.vue";
4
- import type { FormUiTheme, InputUiVariant } from "~/types/forms/types.forms.d";
5
-
6
- interface InputPasswordWithLabelStoryArgs {
7
- modelValue: string;
8
- maxlength: number;
9
- name: string;
10
- placeholder: string;
11
- label: string;
12
- errorMessage: string;
13
- fieldHasError: boolean;
14
- required: boolean;
15
- theme: FormUiTheme;
16
- inputVariant: InputUiVariant;
17
- styleClassPassthrough: string[];
18
- useDescriptionSlot: boolean;
19
- useDescriptionHtmlSlot: boolean;
20
- descriptionContent: string;
21
- descriptionHtmlContent: string;
22
- showPasswordText: string;
23
- hidePasswordText: string;
24
- }
25
-
26
- export default {
27
- title: "Components/Forms/Input Text/InputPasswordWithLabel",
28
- component: StorybookComponent,
29
- argTypes: {
30
- // Model
31
- modelValue: {
32
- control: "text",
33
- description: "The password value",
34
- table: {
35
- category: "Model",
36
- },
37
- },
38
-
39
- // Basic Configuration
40
- maxlength: {
41
- control: { type: "number", min: 1, max: 1000 },
42
- description: "Maximum length of password",
43
- table: {
44
- category: "Basic",
45
- },
46
- },
47
- name: {
48
- control: "text",
49
- description: "Input name attribute",
50
- table: {
51
- category: "Basic",
52
- },
53
- },
54
- placeholder: {
55
- control: "text",
56
- description: "Placeholder text",
57
- table: {
58
- category: "Basic",
59
- },
60
- },
61
- label: {
62
- control: "text",
63
- description: "Input label",
64
- table: {
65
- category: "Basic",
66
- },
67
- },
68
-
69
- // Validation
70
- errorMessage: {
71
- control: "text",
72
- description: "Error message to display",
73
- table: {
74
- category: "Validation",
75
- },
76
- },
77
- fieldHasError: {
78
- control: "boolean",
79
- description: "Whether field has validation error",
80
- table: {
81
- category: "Validation",
82
- },
83
- },
84
- required: {
85
- control: "boolean",
86
- description: "Whether field is required",
87
- table: {
88
- category: "Validation",
89
- },
90
- },
91
-
92
- // Styling
93
- theme: {
94
- control: { type: "select" },
95
- options: ["primary", "secondary", "tertiary", "ghost", "error", "success", "warning"],
96
- description: "Theme for styling",
97
- table: {
98
- category: "Styling",
99
- },
100
- },
101
- inputVariant: {
102
- control: { type: "select" },
103
- options: ["normal", "outlined", "underlined"],
104
- description: "Input variant style",
105
- table: {
106
- category: "Styling",
107
- },
108
- },
109
- styleClassPassthrough: {
110
- control: "object",
111
- description: "Additional CSS classes",
112
- table: {
113
- category: "Styling",
114
- },
115
- },
116
-
117
- // Slots
118
- useDescriptionSlot: {
119
- control: "boolean",
120
- description: "Use description slot",
121
- table: {
122
- category: "Slots",
123
- },
124
- },
125
- useDescriptionHtmlSlot: {
126
- control: "boolean",
127
- description: "Use description HTML slot",
128
- table: {
129
- category: "Slots",
130
- },
131
- },
132
- descriptionContent: {
133
- control: "text",
134
- description: "Content for description slot",
135
- table: {
136
- category: "Slots",
137
- },
138
- },
139
- descriptionHtmlContent: {
140
- control: "text",
141
- description: "HTML content for description slot",
142
- table: {
143
- category: "Slots",
144
- },
145
- },
146
-
147
- // Localisation
148
- showPasswordText: {
149
- control: "text",
150
- description: "Accessible name for the toggle button while the password is hidden",
151
- table: {
152
- category: "Localisation",
153
- },
154
- },
155
- hidePasswordText: {
156
- control: "text",
157
- description: "Accessible name for the toggle button while the password is visible",
158
- table: {
159
- category: "Localisation",
160
- },
161
- },
162
- },
163
- args: {
164
- modelValue: "",
165
- maxlength: 255,
166
- name: "password-input",
167
- placeholder: "Enter your password...",
168
- label: "Password",
169
- errorMessage: "",
170
- fieldHasError: false,
171
- required: false,
172
- theme: "default",
173
- inputVariant: "normal",
174
- styleClassPassthrough: [],
175
- useDescriptionSlot: false,
176
- useDescriptionHtmlSlot: false,
177
- descriptionContent: "Password should be at least 8 characters long",
178
- descriptionHtmlContent:
179
- "Password must contain: <strong>8+ characters</strong>, <em>uppercase</em>, <em>lowercase</em>, <em>numbers</em>",
180
- showPasswordText: "Show password",
181
- hidePasswordText: "Hide password",
182
- },
183
- } as Meta<typeof StorybookComponent>;
184
-
185
- // `args` is Storybook's own reactive object — bind to it directly (`args.x`) rather than
186
- // destructuring it into local variables/refs, which would snapshot the values once at setup()
187
- // and stop reflecting later Controls-panel changes. `componentArgs` strips the non-prop
188
- // slot-toggle args so they don't leak onto the component as unknown attributes.
189
- const Template: StoryFn<InputPasswordWithLabelStoryArgs> = (args) => ({
190
- components: { StorybookComponent },
191
- setup() {
192
- const componentArgs = computed(() => {
193
- const { useDescriptionSlot, useDescriptionHtmlSlot, descriptionContent, descriptionHtmlContent, ...rest } = args;
194
- return rest;
195
- });
196
-
197
- return { args, componentArgs };
198
- },
199
- template: `
200
- <StorybookComponent
201
- v-model="args.modelValue"
202
- v-bind="componentArgs"
203
- >
204
- <template v-if="args.useDescriptionSlot" #description>{{ args.descriptionContent }}</template>
205
- <template v-if="args.useDescriptionHtmlSlot" #descriptionHtml v-html="args.descriptionHtmlContent"></template>
206
- </StorybookComponent>
207
- <div class="mt-4 text-sm text-gray-600">
208
- Current value: {{ args.modelValue ? '•'.repeat(args.modelValue.length) : 'Empty' }}
209
- </div>
210
- `,
211
- });
212
-
213
- export const Default = Template.bind({});
214
- Default.args = {};
215
-
216
- export const WithDescription = Template.bind({});
217
- WithDescription.args = {
218
- useDescriptionSlot: true,
219
- label: "Account Password",
220
- placeholder: "Create a secure password",
221
- };
222
-
223
- export const WithHtmlDescription = Template.bind({});
224
- WithHtmlDescription.args = {
225
- useDescriptionHtmlSlot: true,
226
- label: "New Password",
227
- placeholder: "Enter new password",
228
- };
229
-
230
- export const Required = Template.bind({});
231
- Required.args = {
232
- required: true,
233
- label: "Login Password",
234
- placeholder: "Enter your password",
235
- useDescriptionSlot: true,
236
- descriptionContent: "This field is required for login",
237
- };
238
-
239
- export const WithError = Template.bind({});
240
- WithError.args = {
241
- fieldHasError: true,
242
- errorMessage: "Password must be at least 8 characters long",
243
- theme: "error",
244
- label: "Password",
245
- placeholder: "Enter password",
246
- modelValue: "123", // Short password to show error
247
- };
248
-
249
- export const PasswordTooShort = Template.bind({});
250
- PasswordTooShort.args = {
251
- fieldHasError: true,
252
- errorMessage: "Password is too weak",
253
- theme: "error",
254
- label: "Strong Password",
255
- placeholder: "Enter a strong password",
256
- modelValue: "weak",
257
- useDescriptionHtmlSlot: true,
258
- };
259
-
260
- export const Outlined = Template.bind({});
261
- Outlined.args = {
262
- inputVariant: "outlined",
263
- label: "Confirm Password",
264
- placeholder: "Confirm your password",
265
- };
266
-
267
- export const Underlined = Template.bind({});
268
- Underlined.args = {
269
- inputVariant: "underlined",
270
- label: "Master Password",
271
- placeholder: "Enter master password",
272
- };
273
-
274
- export const LocalisedToggleText = Template.bind({});
275
- LocalisedToggleText.storyName = "Localised Toggle Text";
276
- LocalisedToggleText.args = {
277
- label: "Mot de passe",
278
- placeholder: "Saisissez votre mot de passe",
279
- showPasswordText: "Afficher le mot de passe",
280
- hidePasswordText: "Masquer le mot de passe",
281
- };
282
-
283
- export const AllVariants = Template.bind({});
284
- AllVariants.storyName = "All Input Variants";
285
- AllVariants.render = (args) => ({
286
- components: { StorybookComponent },
287
- setup() {
288
- const variants: InputUiVariant[] = ["normal", "outlined", "underlined"];
289
- const passwordValues = reactive(
290
- variants.reduce(
291
- (acc, variant) => ({
292
- ...acc,
293
- [variant]: "",
294
- }),
295
- {} as Record<string, string>
296
- )
297
- );
298
-
299
- return {
300
- variants,
301
- passwordValues,
302
- args,
303
- };
304
- },
305
- template: `
306
- <div class="space-y-6">
307
- <div v-for="variant in variants" :key="variant" class="space-y-2">
308
- <h3 class="text-lg font-semibold capitalize">{{ variant }} Variant</h3>
309
- <StorybookComponent
310
- v-model="passwordValues[variant]"
311
- v-bind="{ ...args, inputVariant: variant, label: variant + ' Password', name: variant + '-password' }"
312
- />
313
- </div>
314
- </div>
315
- `,
316
- });
317
-
318
- export const SecurityFeatures = Template.bind({});
319
- SecurityFeatures.storyName = "Password Security Demo";
320
- SecurityFeatures.render = (args) => ({
321
- components: { StorybookComponent },
322
- setup() {
323
- const passwords = reactive({
324
- weak: "123",
325
- medium: "password123",
326
- strong: "MyStr0ng!Pass",
327
- });
328
-
329
- const getStrengthColor = (password: string) => {
330
- if (password.length < 6) return "error";
331
- if (password.length < 10) return "warning";
332
- return "success";
333
- };
334
-
335
- return {
336
- passwords,
337
- getStrengthColor,
338
- args,
339
- };
340
- },
341
- template: `
342
- <div class="space-y-6">
343
- <div class="space-y-2">
344
- <h3 class="text-lg font-semibold">Weak Password (Error State)</h3>
345
- <StorybookComponent
346
- v-model="passwords.weak"
347
- v-bind="{ ...args, theme: getStrengthColor(passwords.weak), fieldHasError: passwords.weak.length < 6, errorMessage: 'Password too short', label: 'Weak Password', name: 'weak-password' }"
348
- />
349
- </div>
350
-
351
- <div class="space-y-2">
352
- <h3 class="text-lg font-semibold">Medium Password (Warning State)</h3>
353
- <StorybookComponent
354
- v-model="passwords.medium"
355
- v-bind="{ ...args, theme: getStrengthColor(passwords.medium), label: 'Medium Password', name: 'medium-password' }"
356
- />
357
- </div>
358
-
359
- <div class="space-y-2">
360
- <h3 class="text-lg font-semibold">Strong Password (Success State)</h3>
361
- <StorybookComponent
362
- v-model="passwords.strong"
363
- v-bind="{ ...args, theme: getStrengthColor(passwords.strong), label: 'Strong Password', name: 'strong-password' }"
364
- />
365
- </div>
366
- </div>
367
- `,
368
- });