@fiscozen/input 0.1.17 → 1.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,720 @@
1
1
  # @fiscozen/input
2
2
 
3
- TODO
3
+ Flexible input component library for Vue 3 applications, featuring icon support, validation states, multiple variants, and specialized currency input with number formatting.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Input Types**: text, password, email, number, tel, url
8
+ - **Icon Support**: Left and right icons (static or clickable buttons)
9
+ - **Validation States**: Error and valid states with visual feedback
10
+ - **Two Variants**: Normal and floating-label presentation
11
+ - **Two Environments**: frontoffice and backoffice (different heights and styling)
12
+ - **Currency Input**: Specialized component with locale-aware number formatting
13
+ - **Full Accessibility**: ARIA attributes, keyboard navigation, screen reader support
14
+ - **Customizable**: Slots for label, icons, help text, and error messages
15
+ - **TypeScript**: Full type safety with TypeScript definitions
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @fiscozen/input
21
+ ```
22
+
23
+ ## Components
24
+
25
+ This package exports two components:
26
+
27
+ - `FzInput` - Standard input with label, icons, and validation states
28
+ - `FzCurrencyInput` - Specialized currency input with number formatting and validation
29
+
30
+ ## Basic Usage
31
+
32
+ ### FzInput
33
+
34
+ ```vue
35
+ <script setup lang="ts">
36
+ import { FzInput } from '@fiscozen/input'
37
+ import { ref } from 'vue'
38
+
39
+ const email = ref('')
40
+ </script>
41
+
42
+ <template>
43
+ <FzInput label="Email" type="email" v-model="email" />
44
+ </template>
45
+ ```
46
+
47
+ ### FzCurrencyInput
48
+
49
+ ```vue
50
+ <script setup lang="ts">
51
+ import { FzCurrencyInput } from '@fiscozen/input'
52
+ import { ref } from 'vue'
53
+
54
+ const amount = ref<number | undefined>(undefined)
55
+ </script>
56
+
57
+ <template>
58
+ <FzCurrencyInput label="Amount" v-model="amount" />
59
+ </template>
60
+ ```
61
+
62
+ ## Props
63
+
64
+ ### FzInput Props
65
+
66
+ | Prop | Type | Default | Description |
67
+ |------|------|---------|-------------|
68
+ | `label` | `string` | - | Text label displayed above the input field. Overridden by label slot if provided. |
69
+ | `environment` | `'backoffice' \| 'frontoffice'` | `'frontoffice'` | Environment determining input size and styling |
70
+ | `size` | `'sm' \| 'md' \| 'lg'` | - | **Deprecated**: Use `environment` prop instead. Size values map to environments: sm/md → backoffice, lg → frontoffice |
71
+ | `placeholder` | `string` | - | Placeholder text shown when input is empty. Behavior differs based on variant. |
72
+ | `required` | `boolean` | `false` | Marks input as required. Adds asterisk to label and sets native required attribute. |
73
+ | `disabled` | `boolean` | `false` | Disables input interaction and applies disabled styling |
74
+ | `error` | `boolean` | `false` | Shows error state with red border and enables errorMessage slot display |
75
+ | `valid` | `boolean` | `false` | Shows success checkmark icon on the right when true. Takes precedence over rightIcon |
76
+ | `variant` | `'normal' \| 'floating-label'` | `'normal'` | Visual presentation style. 'floating-label' moves placeholder above input when focused/filled |
77
+ | `type` | `'text' \| 'password' \| 'email' \| 'number' \| 'tel' \| 'url'` | `'text'` | Native HTML input type. Determines keyboard layout and validation behavior |
78
+ | `leftIcon` | `string` | - | Font Awesome icon name displayed on the left side of input |
79
+ | `leftIconVariant` | `IconVariant` | - | Visual style variant for left icon (solid, regular, light, etc.) |
80
+ | `leftIconButtonVariant` | `IconButtonVariant` | - | Button variant for left icon when rendered as clickable button |
81
+ | `leftIconAriaLabel` | `string` | - | Accessible label for left icon when clickable. Required for screen reader accessibility |
82
+ | `rightIcon` | `string` | - | Font Awesome icon name displayed on the right side of input |
83
+ | `rightIconSize` | `IconSize` | - | **Deprecated**: Size override for right icon. Icons now have a fixed size of "md". This prop is ignored. |
84
+ | `rightIconVariant` | `IconVariant` | - | Visual style variant for right icon (solid, regular, light, etc.) |
85
+ | `rightIconButton` | `boolean` | `false` | Renders right icon as clickable button instead of static icon |
86
+ | `rightIconButtonVariant` | `IconButtonVariant` | `'invisible'` | Button variant for right icon when rightIconButton is true |
87
+ | `rightIconAriaLabel` | `string` | - | Accessible label for right icon when clickable. Required for screen reader accessibility |
88
+ | `secondRightIcon` | `string` | - | Font Awesome icon name displayed as second icon on the right side of input. Order: secondRightIcon > rightIcon > valid (all can be present simultaneously) |
89
+ | `secondRightIconClass` | `string` | - | Additional CSS classes applied to second right icon container |
90
+ | `secondRightIconVariant` | `IconVariant` | - | Visual style variant for second right icon (solid, regular, light, etc.) |
91
+ | `secondRightIconButton` | `boolean` | `false` | Renders second right icon as clickable button instead of static icon |
92
+ | `secondRightIconButtonVariant` | `IconButtonVariant` | `'invisible'` | Button variant for second right icon when secondRightIconButton is true |
93
+ | `secondRightIconAriaLabel` | `string` | - | Accessible label for second right icon when clickable. Required for screen reader accessibility |
94
+ | `pattern` | `string` | - | HTML5 pattern attribute for native browser validation |
95
+ | `name` | `string` | - | Native name attribute for form submission and identification |
96
+ | `readonly` | `boolean` | `false` | Native readonly attribute. Prevents user input while keeping field focusable |
97
+ | `maxlength` | `number` | - | Native maxlength attribute. Limits maximum number of characters |
98
+ | `rightIconClass` | `string` | - | Additional CSS classes applied to right icon container |
99
+ | `leftIconClass` | `string` | - | Additional CSS classes applied to left icon container |
100
+
101
+ ### FzCurrencyInput Props
102
+
103
+ FzCurrencyInput extends FzInput props (except `type`, `modelValue`, `rightIcon`, `rightIconSize`, `rightIconVariant`, `rightIconButton`, `rightIconButtonVariant`, `rightIconAriaLabel`, `rightIconClass`, `secondRightIcon`, `secondRightIconClass`, `secondRightIconVariant`, `secondRightIconButton`, `secondRightIconButtonVariant`, `secondRightIconAriaLabel`) and adds:
104
+
105
+ | Prop | Type | Default | Description |
106
+ |------|------|---------|-------------|
107
+ | `modelValue` | `number \| string \| undefined` | - | **Type assertion**: Accepts `number \| string \| undefined` as input, but **always emits** `number \| undefined` (never `string`). Strings are deprecated and trigger a console warning. See [v-model Type Behavior](#v-model-type-behavior) for details. |
108
+ | `nullOnEmpty` | `boolean` | `false` | Converts empty input to null instead of 0 |
109
+ | `minimumFractionDigits` | `number` | `2` | Minimum decimal places in formatted output |
110
+ | `maximumFractionDigits` | `number` | `2` | Maximum decimal places in formatted output |
111
+ | `min` | `number` | - | Minimum allowed value. Values below this are clamped to min |
112
+ | `max` | `number` | - | Maximum allowed value. Values above this are clamped to max |
113
+ | `step` | `number` | `1` | Step increment for arrow buttons. When forceStep is true, values are rounded to nearest step multiple |
114
+ | `forceStep` | `boolean` | `false` | Enforces quantization: values are automatically rounded to nearest step multiple |
115
+ | `stepUpAriaLabel` | `string` | - | Custom accessible label for step up button. If not provided, uses default label |
116
+ | `stepDownAriaLabel` | `string` | - | Custom accessible label for step down button. If not provided, uses default label |
117
+
118
+ **Note**: `rightIcon` and `secondRightIcon` props are not available in FzCurrencyInput. Only `valid` checkmark icon can be displayed alongside step controls.
119
+
120
+ ## Slots
121
+
122
+ | Slot | Description |
123
+ |------|-------------|
124
+ | `label` | Custom label content (overrides `label` prop) |
125
+ | `left-icon` | Custom left icon content (overrides `leftIcon` prop) |
126
+ | `right-icon` | Custom right icon content (overrides `rightIcon` prop and `valid` checkmark) |
127
+ | `helpText` | Help text displayed below input when no error is present |
128
+ | `errorMessage` | Error message displayed below input when `error` is true |
129
+
130
+ ## Variants
131
+
132
+ ### Normal (Default)
133
+
134
+ Standard input with label above and placeholder inside the input field.
135
+
136
+ ```vue
137
+ <FzInput label="Email" type="email" placeholder="Enter your email" v-model="email" />
138
+ ```
139
+
140
+ ### Floating Label
141
+
142
+ Floating label variant moves placeholder above input when focused or when input has a value.
143
+
144
+ ```vue
145
+ <FzInput
146
+ label="Email"
147
+ variant="floating-label"
148
+ placeholder="Enter your email"
149
+ v-model="email"
150
+ />
151
+ ```
152
+
153
+ ## Environments
154
+
155
+ Inputs support two environments that determine their height and styling:
156
+
157
+ - **frontoffice** (default): larger spacing
158
+ - **backoffice**: compact spacing
159
+
160
+ ```vue
161
+ <template>
162
+ <FzInput label="Frontoffice (Default)" environment="frontoffice" v-model="value1" />
163
+ <FzInput label="Backoffice" environment="backoffice" v-model="value2" />
164
+ </template>
165
+ ```
166
+
167
+ **Note**: The `size` prop is deprecated. Use `environment` instead.
168
+
169
+ ## Input Types
170
+
171
+ ### Text
172
+
173
+ ```vue
174
+ <FzInput label="Name" type="text" v-model="name" />
175
+ ```
176
+
177
+ ### Password
178
+
179
+ ```vue
180
+ <FzInput label="Password" type="password" v-model="password" />
181
+ ```
182
+
183
+ ### Email
184
+
185
+ ```vue
186
+ <FzInput label="Email" type="email" required v-model="email" />
187
+ ```
188
+
189
+ ### Number
190
+
191
+ ```vue
192
+ <FzInput label="Age" type="number" v-model="age" />
193
+ ```
194
+
195
+ ### Telephone
196
+
197
+ ```vue
198
+ <FzInput
199
+ label="Phone"
200
+ type="tel"
201
+ pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
202
+ v-model="phone"
203
+ />
204
+ ```
205
+
206
+ ### URL
207
+
208
+ ```vue
209
+ <FzInput label="Website" type="url" v-model="website" />
210
+ ```
211
+
212
+ ## States
213
+
214
+ ### Required
215
+
216
+ Required inputs show an asterisk (*) next to the label and set the native required attribute.
217
+
218
+ ```vue
219
+ <FzInput label="Email" type="email" required v-model="email" />
220
+ ```
221
+
222
+ ### Disabled
223
+
224
+ Disabled inputs prevent user interaction and apply muted styling.
225
+
226
+ ```vue
227
+ <FzInput label="Disabled Input" disabled v-model="value" />
228
+ ```
229
+
230
+ ### Error
231
+
232
+ Error state shows red border and displays error message via errorMessage slot.
233
+
234
+ ```vue
235
+ <FzInput label="Email" type="email" :error="hasError" v-model="email">
236
+ <template #errorMessage>Please enter a valid email address</template>
237
+ </FzInput>
238
+ ```
239
+
240
+ ### Valid
241
+
242
+ Valid state shows a green checkmark icon on the right side.
243
+
244
+ ```vue
245
+ <FzInput label="Email" type="email" :valid="isValid" v-model="email" />
246
+ ```
247
+
248
+ ### Readonly
249
+
250
+ Readonly inputs prevent editing but remain focusable and visible.
251
+
252
+ ```vue
253
+ <FzInput label="Read-only Value" readonly v-model="readonlyValue" />
254
+ ```
255
+
256
+ ## With Icons
257
+
258
+ ### Left Icon
259
+
260
+ ```vue
261
+ <FzInput
262
+ label="Date"
263
+ leftIcon="calendar-lines"
264
+ v-model="date"
265
+ />
266
+ ```
267
+
268
+ ### Right Icon
269
+
270
+ ```vue
271
+ <FzInput
272
+ label="Search"
273
+ rightIcon="magnifying-glass"
274
+ v-model="search"
275
+ />
276
+ ```
277
+
278
+ ### Right Icon as Button
279
+
280
+ Right icon can be rendered as a clickable button (useful for password visibility toggle, clear button, etc.).
281
+
282
+ ```vue
283
+ <template>
284
+ <FzInput
285
+ label="Password"
286
+ :type="passwordType"
287
+ rightIcon="eye"
288
+ :rightIconButton="true"
289
+ rightIconButtonVariant="secondary"
290
+ rightIconAriaLabel="Toggle password visibility"
291
+ @fzinput:right-icon-click="togglePasswordVisibility"
292
+ v-model="password"
293
+ />
294
+ </template>
295
+
296
+ <script setup>
297
+ import { ref } from 'vue'
298
+
299
+ const passwordType = ref('password')
300
+ const password = ref('')
301
+
302
+ const togglePasswordVisibility = () => {
303
+ passwordType.value = passwordType.value === 'password' ? 'text' : 'password'
304
+ }
305
+ </script>
306
+ ```
307
+
308
+ ### Clickable Icons with Accessibility
309
+
310
+ When icons are clickable, provide `leftIconAriaLabel` or `rightIconAriaLabel` for keyboard accessibility:
311
+
312
+ ```vue
313
+ <FzInput
314
+ label="Search"
315
+ leftIcon="magnifying-glass"
316
+ leftIconAriaLabel="Open search options"
317
+ @fzinput:left-icon-click="openSearchOptions"
318
+ v-model="search"
319
+ />
320
+ ```
321
+
322
+ ### Both Icons
323
+
324
+ ```vue
325
+ <FzInput
326
+ label="Amount"
327
+ leftIcon="dollar-sign"
328
+ rightIcon="credit-card"
329
+ v-model="amount"
330
+ />
331
+ ```
332
+
333
+ ## Help Text and Error Messages
334
+
335
+ ### Help Text
336
+
337
+ Help text is displayed below the input when no error is present.
338
+
339
+ ```vue
340
+ <FzInput label="Username" v-model="username">
341
+ <template #helpText>
342
+ Username must be between 3 and 20 characters
343
+ </template>
344
+ </FzInput>
345
+ ```
346
+
347
+ ### Error Message
348
+
349
+ Error message is displayed below the input when error prop is true.
350
+
351
+ ```vue
352
+ <FzInput label="Email" type="email" :error="hasError" v-model="email">
353
+ <template #errorMessage>
354
+ Please enter a valid email address
355
+ </template>
356
+ </FzInput>
357
+ ```
358
+
359
+ ## Custom Label
360
+
361
+ You can provide custom label content using the label slot.
362
+
363
+ ```vue
364
+ <FzInput v-model="value">
365
+ <template #label>
366
+ <strong>Custom Label</strong> with <em>formatting</em>
367
+ </template>
368
+ </FzInput>
369
+ ```
370
+
371
+ ## Examples
372
+
373
+ ### Form with Validation
374
+
375
+ ```vue
376
+ <template>
377
+ <form @submit.prevent="handleSubmit">
378
+ <FzInput
379
+ label="Email"
380
+ type="email"
381
+ required
382
+ :error="errors.email"
383
+ v-model="form.email"
384
+ >
385
+ <template #errorMessage v-if="errors.email">
386
+ {{ errors.email }}
387
+ </template>
388
+ </FzInput>
389
+
390
+ <FzInput
391
+ label="Password"
392
+ type="password"
393
+ required
394
+ :error="errors.password"
395
+ v-model="form.password"
396
+ >
397
+ <template #errorMessage v-if="errors.password">
398
+ {{ errors.password }}
399
+ </template>
400
+ </FzInput>
401
+
402
+ <button type="submit">Submit</button>
403
+ </form>
404
+ </template>
405
+
406
+ <script setup>
407
+ import { ref } from 'vue'
408
+
409
+ const form = ref({
410
+ email: '',
411
+ password: ''
412
+ })
413
+
414
+ const errors = ref({
415
+ email: null,
416
+ password: null
417
+ })
418
+
419
+ const handleSubmit = () => {
420
+ // Validation logic
421
+ }
422
+ </script>
423
+ ```
424
+
425
+ ### Password with Visibility Toggle
426
+
427
+ ```vue
428
+ <template>
429
+ <FzInput
430
+ label="Password"
431
+ :type="showPassword ? 'text' : 'password'"
432
+ rightIcon="eye"
433
+ :rightIconButton="true"
434
+ @fzinput:right-icon-click="togglePassword"
435
+ v-model="password"
436
+ />
437
+ </template>
438
+
439
+ <script setup>
440
+ import { ref } from 'vue'
441
+
442
+ const password = ref('')
443
+ const showPassword = ref(false)
444
+
445
+ const togglePassword = () => {
446
+ showPassword.value = !showPassword.value
447
+ }
448
+ </script>
449
+ ```
450
+
451
+ ### Search Input
452
+
453
+ ```vue
454
+ <FzInput
455
+ label="Search"
456
+ leftIcon="magnifying-glass"
457
+ rightIcon="xmark"
458
+ :rightIconButton="true"
459
+ @fzinput:right-icon-click="clearSearch"
460
+ v-model="searchQuery"
461
+ />
462
+ ```
463
+
464
+ ### Input with Max Length
465
+
466
+ ```vue
467
+ <FzInput
468
+ label="PIN"
469
+ type="text"
470
+ maxlength="4"
471
+ pattern="[0-9]{4}"
472
+ v-model="pin"
473
+ />
474
+ ```
475
+
476
+ ## FzCurrencyInput
477
+
478
+ Specialized currency input component built on FzInput with number formatting, validation, and step controls. Formats values using Intl.NumberFormat with locale-aware separators.
479
+
480
+ ### Features
481
+
482
+ - Locale-aware number formatting (decimal and thousand separators)
483
+ - Min/max value constraints
484
+ - Step quantization with arrow buttons
485
+ - Intelligent paste parsing (detects decimal/thousand separators automatically)
486
+ - Configurable decimal places
487
+
488
+ ### Basic Usage
489
+
490
+ ```vue
491
+ <script setup lang="ts">
492
+ import { FzCurrencyInput } from '@fiscozen/input'
493
+ import { ref } from 'vue'
494
+
495
+ const amount = ref<number | undefined>(undefined)
496
+ </script>
497
+
498
+ <template>
499
+ <FzCurrencyInput
500
+ label="Amount"
501
+ v-model="amount"
502
+ :min="0"
503
+ :max="1000"
504
+ />
505
+ </template>
506
+ ```
507
+
508
+ ### With Step Controls
509
+
510
+ Step controls (arrow buttons) are always visible in FzCurrencyInput. Default step is 1, but can be customized.
511
+
512
+ ```vue
513
+ <FzCurrencyInput
514
+ label="Quantity"
515
+ v-model="quantity"
516
+ :step="5"
517
+ />
518
+ ```
519
+
520
+ ### With Step Quantization
521
+
522
+ When `forceStep` is true, values are automatically rounded to the nearest step multiple.
523
+
524
+ ```vue
525
+ <FzCurrencyInput
526
+ label="Quantity"
527
+ v-model="quantity"
528
+ :step="4"
529
+ :forceStep="true"
530
+ />
531
+ ```
532
+
533
+ ### With Min/Max Constraints
534
+
535
+ ```vue
536
+ <FzCurrencyInput
537
+ label="Price"
538
+ v-model="price"
539
+ :min="0"
540
+ :max="9999.99"
541
+ :minimumFractionDigits="2"
542
+ :maximumFractionDigits="2"
543
+ />
544
+ ```
545
+
546
+ ### With Valid State
547
+
548
+ Valid checkmark icon is displayed alongside step controls.
549
+
550
+ ```vue
551
+ <FzCurrencyInput
552
+ label="Amount"
553
+ v-model="amount"
554
+ :valid="isValid"
555
+ />
556
+ ```
557
+
558
+ ### Currency Input Behavior
559
+
560
+ - **Formatting**: Values are formatted using Intl.NumberFormat with locale settings (default: 'it-IT')
561
+ - **Paste Handling**: Automatically detects and parses various number formats (e.g., "1.234,56", "1,234.56")
562
+ - **Step Controls**: Arrow buttons are always visible (default step = 1). Support keyboard accessibility (Enter/Space to activate)
563
+ - **Step Quantization**: When `forceStep` is true, values are automatically rounded to nearest step multiple
564
+ - **Empty Values**: When `nullOnEmpty` is true, empty input converts to null instead of 0
565
+ - **Valid State**: Valid checkmark icon can be displayed alongside step controls
566
+ - **Right Icons**: `rightIcon` and `secondRightIcon` props are not available. Only `valid` icon is supported.
567
+
568
+ ### v-model Type Behavior
569
+
570
+ FzCurrencyInput uses a **type assertion** pattern for its v-model:
571
+
572
+ - **Accepts**: `number | string | undefined` as input
573
+ - **Emits**: Always `number | undefined` (never `string`)
574
+
575
+ #### Type Assertion
576
+
577
+ The component accepts `number | string | undefined` as input, but **always emits** `number | undefined`. Strings are automatically parsed (Italian format: "1.234,56" → 1234.56) and converted to numbers internally.
578
+
579
+ #### Deprecation Warning
580
+
581
+ **String values are deprecated** and will be removed in a future version. A console warning is shown when strings are used. Please use `number | undefined` instead for type safety and future compatibility.
582
+
583
+ #### Recommended Usage
584
+
585
+ ```vue
586
+ <script setup lang="ts">
587
+ import { ref } from 'vue'
588
+
589
+ // ✅ Recommended: number | undefined (future-proof)
590
+ const amount = ref<number | undefined>(undefined)
591
+ </script>
592
+
593
+ <template>
594
+ <FzCurrencyInput label="Amount" v-model="amount" />
595
+ </template>
596
+ ```
597
+
598
+ #### Deprecated Usage (Still Works)
599
+
600
+ ```vue
601
+ <script setup lang="ts">
602
+ import { ref } from 'vue'
603
+
604
+ // ⚠️ Deprecated: string (still works but shows warning)
605
+ // Strings are parsed and converted to numbers internally
606
+ const amount = ref<string>("1234,56")
607
+ </script>
608
+
609
+ <template>
610
+ <FzCurrencyInput label="Amount" v-model="amount" />
611
+ </template>
612
+ ```
613
+
614
+ **Note**: When using strings, the component will:
615
+ 1. Show a console warning about deprecation
616
+ 2. Parse the string using Italian format (points = thousands, comma = decimal)
617
+ 3. Convert it to a number internally
618
+ 4. Always emit a `number | undefined` value
619
+
620
+ #### Migration Guide
621
+
622
+ If you're currently using strings, migrate to numbers:
623
+
624
+ ```vue
625
+ <!-- Before (deprecated) -->
626
+ <script setup lang="ts">
627
+ const amount = ref<string>("1234,56")
628
+ </script>
629
+
630
+ <!-- After (recommended) -->
631
+ <script setup lang="ts">
632
+ const amount = ref<number | undefined>(1234.56)
633
+ // or
634
+ const amount = ref<number | undefined>(undefined)
635
+ </script>
636
+ ```
637
+
638
+ ## Accessibility
639
+
640
+ FzInput and FzCurrencyInput are fully accessible and meet WCAG 2.1 AA standards:
641
+
642
+ - **ARIA Attributes**: Proper aria-required, aria-invalid, aria-disabled, aria-labelledby, aria-describedby
643
+ - **Keyboard Navigation**: Full keyboard support (Tab, Enter, Space, Arrow keys)
644
+ - **Screen Readers**: Error messages and help text are properly associated with inputs
645
+ - **Focus Management**: Visible focus indicators with proper contrast
646
+ - **Semantic HTML**: Native input elements with proper label associations
647
+ - **Error Announcements**: Error messages use role="alert" for immediate screen reader announcements
648
+
649
+ ### ARIA Attributes
650
+
651
+ - **aria-required**: Set to "true" or "false" (string values for Vue 3 compatibility)
652
+ - **aria-invalid**: Set to "true" when error prop is true
653
+ - **aria-disabled**: Set to "true" when disabled prop is true
654
+ - **aria-labelledby**: Links to label element when label prop is provided
655
+ - **aria-describedby**: Links to help text or error message when present
656
+
657
+ ### Error Message Accessibility
658
+
659
+ Error messages are rendered with `role="alert"` to ensure screen readers announce them immediately when displayed.
660
+
661
+ ## Behavior & Concepts
662
+
663
+ ### Label vs Slot Priority
664
+
665
+ When both `label` prop and label slot are provided, the slot takes precedence:
666
+
667
+ ```vue
668
+ <FzInput label="This won't show">
669
+ <template #label>This will show instead</template>
670
+ </FzInput>
671
+ ```
672
+
673
+ ### Right Icons Display Order
674
+
675
+ The component supports three types of right-side icons that can all be displayed simultaneously:
676
+ 1. **`valid` checkmark** (when `valid` prop is `true`) - displayed first
677
+ 2. **`secondRightIcon`** (when provided) - displayed second
678
+ 3. **`rightIcon`** (when provided) - displayed third
679
+
680
+ All three icons can be visible at the same time, appearing in this order from left to right.
681
+
682
+ ### Floating Label Placeholder Behavior
683
+
684
+ In floating-label variant:
685
+ - When input is empty and not focused: placeholder shows above input as floating text
686
+ - When input has value or is focused: placeholder moves above input permanently
687
+ - Normal placeholder attribute is hidden in this mode
688
+
689
+ ### Icon Button Size Mapping
690
+
691
+ Right icon buttons use a smaller size scale than inputs to maintain visual balance:
692
+ - Input size `sm` → Icon button size `xs`
693
+ - Input size `md` → Icon button size `sm`
694
+ - Input size `lg` → Icon button size `md`
695
+
696
+ ## Notes
697
+
698
+ ### Input Type Behavior
699
+
700
+ Different input types provide different keyboard layouts and validation:
701
+ - **email**: Triggers email keyboard on mobile devices
702
+ - **tel**: Triggers numeric keyboard on mobile devices
703
+ - **number**: Allows numeric input with spinner controls
704
+ - **password**: Masks input characters
705
+ - **url**: Validates URL format
706
+
707
+ ### Currency Input Formatting
708
+
709
+ FzCurrencyInput uses Intl.NumberFormat for locale-aware formatting. The formatting respects:
710
+ - Browser locale settings
711
+ - Minimum and maximum fraction digits
712
+ - Decimal and thousand separator conventions
713
+
714
+ ### Paste Handling in Currency Input
715
+
716
+ The currency input uses intelligent heuristics to parse pasted values:
717
+ - Multiple different separators: rightmost is decimal separator
718
+ - Multiple same separators: thousand separator
719
+ - Single separator with <3 digits after: decimal separator
720
+ - Single separator with 3+ digits after: ambiguous, uses default formatting