@fiscozen/input 3.0.2 → 3.1.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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +1 -0
- package/dist/input.css +2 -0
- package/dist/input.js +8359 -8345
- package/dist/input.umd.cjs +5 -9
- package/dist/src/FzCurrencyInput.vue.d.ts +345 -197
- package/dist/src/FzInput.vue.d.ts +52 -243
- package/dist/src/types.d.ts +17 -2
- package/dist/src/useInputStyle.d.ts +1 -2
- package/dist/src/utils.d.ts +0 -1
- package/package.json +9 -9
- package/src/FzCurrencyInput.vue +13 -13
- package/src/FzInput.vue +67 -1
- package/src/__tests__/FzCurrencyInput.spec.ts +104 -0
- package/src/__tests__/FzInput.spec.ts +1332 -614
- package/src/types.ts +42 -27
- package/src/useInputStyle.ts +42 -23
- package/tsconfig.tsbuildinfo +1 -1
- package/coverage/FzCurrencyInput.vue.html +0 -640
- package/coverage/FzInput.vue.html +0 -709
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -494
- package/coverage/coverage-final.json +0 -4
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -146
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/coverage/useInputStyle.ts.html +0 -343
- package/dist/style.css +0 -1
|
@@ -1,258 +1,67 @@
|
|
|
1
1
|
import { Ref } from 'vue';
|
|
2
|
-
import { InputEnvironment } from './types';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
modelValue
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
label: {
|
|
31
|
-
type: import('vue').PropType<string>;
|
|
32
|
-
};
|
|
33
|
-
pattern: {
|
|
34
|
-
type: import('vue').PropType<string>;
|
|
35
|
-
};
|
|
36
|
-
placeholder: {
|
|
37
|
-
type: import('vue').PropType<string>;
|
|
38
|
-
};
|
|
39
|
-
environment: {
|
|
40
|
-
type: import('vue').PropType<InputEnvironment>;
|
|
41
|
-
default: string;
|
|
42
|
-
};
|
|
43
|
-
rightIcon: {
|
|
44
|
-
type: import('vue').PropType<string>;
|
|
45
|
-
};
|
|
46
|
-
rightIconSize: {
|
|
47
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconSize>;
|
|
48
|
-
};
|
|
49
|
-
rightIconVariant: {
|
|
50
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
51
|
-
};
|
|
52
|
-
rightIconButton: {
|
|
53
|
-
type: import('vue').PropType<boolean>;
|
|
54
|
-
};
|
|
55
|
-
rightIconButtonVariant: {
|
|
56
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
57
|
-
default: string;
|
|
58
|
-
};
|
|
59
|
-
rightIconAriaLabel: {
|
|
60
|
-
type: import('vue').PropType<string>;
|
|
61
|
-
};
|
|
62
|
-
rightIconClass: {
|
|
63
|
-
type: import('vue').PropType<string>;
|
|
64
|
-
};
|
|
65
|
-
secondRightIcon: {
|
|
66
|
-
type: import('vue').PropType<string>;
|
|
67
|
-
};
|
|
68
|
-
secondRightIconClass: {
|
|
69
|
-
type: import('vue').PropType<string>;
|
|
70
|
-
};
|
|
71
|
-
secondRightIconVariant: {
|
|
72
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
73
|
-
};
|
|
74
|
-
secondRightIconButton: {
|
|
75
|
-
type: import('vue').PropType<boolean>;
|
|
76
|
-
};
|
|
77
|
-
secondRightIconButtonVariant: {
|
|
78
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
79
|
-
default: string;
|
|
80
|
-
};
|
|
81
|
-
secondRightIconAriaLabel: {
|
|
82
|
-
type: import('vue').PropType<string>;
|
|
83
|
-
};
|
|
84
|
-
leftIcon: {
|
|
85
|
-
type: import('vue').PropType<string>;
|
|
86
|
-
};
|
|
87
|
-
leftIconVariant: {
|
|
88
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
89
|
-
};
|
|
90
|
-
leftIconButtonVariant: {
|
|
91
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
92
|
-
};
|
|
93
|
-
leftIconAriaLabel: {
|
|
94
|
-
type: import('vue').PropType<string>;
|
|
95
|
-
};
|
|
96
|
-
valid: {
|
|
97
|
-
type: import('vue').PropType<boolean>;
|
|
98
|
-
};
|
|
99
|
-
readonly: {
|
|
100
|
-
type: import('vue').PropType<boolean>;
|
|
101
|
-
};
|
|
102
|
-
maxlength: {
|
|
103
|
-
type: import('vue').PropType<number>;
|
|
104
|
-
};
|
|
105
|
-
autocomplete: {
|
|
106
|
-
type: import('vue').PropType<boolean>;
|
|
107
|
-
default: boolean;
|
|
108
|
-
};
|
|
109
|
-
leftIconClass: {
|
|
110
|
-
type: import('vue').PropType<string>;
|
|
111
|
-
};
|
|
112
|
-
}>, {
|
|
2
|
+
import { FzInputProps, InputEnvironment } from './types';
|
|
3
|
+
type __VLS_Props = FzInputProps;
|
|
4
|
+
type __VLS_PublicProps = {
|
|
5
|
+
modelValue?: string;
|
|
6
|
+
} & __VLS_Props;
|
|
7
|
+
declare function __VLS_template(): {
|
|
8
|
+
attrs: Partial<{}>;
|
|
9
|
+
slots: Readonly<{
|
|
10
|
+
label?: () => unknown;
|
|
11
|
+
"left-icon"?: () => unknown;
|
|
12
|
+
"right-icon"?: () => unknown;
|
|
13
|
+
errorMessage?: () => unknown;
|
|
14
|
+
helpText?: () => unknown;
|
|
15
|
+
}> & {
|
|
16
|
+
label?: () => unknown;
|
|
17
|
+
"left-icon"?: () => unknown;
|
|
18
|
+
"right-icon"?: () => unknown;
|
|
19
|
+
errorMessage?: () => unknown;
|
|
20
|
+
helpText?: () => unknown;
|
|
21
|
+
};
|
|
22
|
+
refs: {
|
|
23
|
+
containerRef: HTMLDivElement;
|
|
24
|
+
inputRef: HTMLInputElement;
|
|
25
|
+
};
|
|
26
|
+
rootEl: any;
|
|
27
|
+
};
|
|
28
|
+
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
29
|
+
declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
113
30
|
inputRef: Ref<HTMLInputElement | null, HTMLInputElement | null>;
|
|
114
31
|
containerRef: Ref<HTMLElement | null, HTMLElement | null>;
|
|
115
32
|
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"fzinput:left-icon-click": () =>
|
|
119
|
-
"fzinput:right-icon-click": () =>
|
|
120
|
-
"fzinput:second-right-icon-click": () =>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
default: boolean;
|
|
126
|
-
};
|
|
127
|
-
name: {
|
|
128
|
-
type: import('vue').PropType<string>;
|
|
129
|
-
};
|
|
130
|
-
size: {
|
|
131
|
-
type: import('vue').PropType<"sm" | "md" | "lg">;
|
|
132
|
-
};
|
|
133
|
-
variant: {
|
|
134
|
-
type: import('vue').PropType<"normal" | "floating-label">;
|
|
135
|
-
default: string;
|
|
136
|
-
};
|
|
137
|
-
type: {
|
|
138
|
-
type: import('vue').PropType<"number" | "text" | "password" | "email" | "tel" | "url">;
|
|
139
|
-
default: string;
|
|
140
|
-
};
|
|
141
|
-
required: {
|
|
142
|
-
type: import('vue').PropType<boolean>;
|
|
143
|
-
};
|
|
144
|
-
disabled: {
|
|
145
|
-
type: import('vue').PropType<boolean>;
|
|
146
|
-
};
|
|
147
|
-
label: {
|
|
148
|
-
type: import('vue').PropType<string>;
|
|
149
|
-
};
|
|
150
|
-
pattern: {
|
|
151
|
-
type: import('vue').PropType<string>;
|
|
152
|
-
};
|
|
153
|
-
placeholder: {
|
|
154
|
-
type: import('vue').PropType<string>;
|
|
155
|
-
};
|
|
156
|
-
environment: {
|
|
157
|
-
type: import('vue').PropType<InputEnvironment>;
|
|
158
|
-
default: string;
|
|
159
|
-
};
|
|
160
|
-
rightIcon: {
|
|
161
|
-
type: import('vue').PropType<string>;
|
|
162
|
-
};
|
|
163
|
-
rightIconSize: {
|
|
164
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconSize>;
|
|
165
|
-
};
|
|
166
|
-
rightIconVariant: {
|
|
167
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
168
|
-
};
|
|
169
|
-
rightIconButton: {
|
|
170
|
-
type: import('vue').PropType<boolean>;
|
|
171
|
-
};
|
|
172
|
-
rightIconButtonVariant: {
|
|
173
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
174
|
-
default: string;
|
|
175
|
-
};
|
|
176
|
-
rightIconAriaLabel: {
|
|
177
|
-
type: import('vue').PropType<string>;
|
|
178
|
-
};
|
|
179
|
-
rightIconClass: {
|
|
180
|
-
type: import('vue').PropType<string>;
|
|
181
|
-
};
|
|
182
|
-
secondRightIcon: {
|
|
183
|
-
type: import('vue').PropType<string>;
|
|
184
|
-
};
|
|
185
|
-
secondRightIconClass: {
|
|
186
|
-
type: import('vue').PropType<string>;
|
|
187
|
-
};
|
|
188
|
-
secondRightIconVariant: {
|
|
189
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
190
|
-
};
|
|
191
|
-
secondRightIconButton: {
|
|
192
|
-
type: import('vue').PropType<boolean>;
|
|
193
|
-
};
|
|
194
|
-
secondRightIconButtonVariant: {
|
|
195
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
196
|
-
default: string;
|
|
197
|
-
};
|
|
198
|
-
secondRightIconAriaLabel: {
|
|
199
|
-
type: import('vue').PropType<string>;
|
|
200
|
-
};
|
|
201
|
-
leftIcon: {
|
|
202
|
-
type: import('vue').PropType<string>;
|
|
203
|
-
};
|
|
204
|
-
leftIconVariant: {
|
|
205
|
-
type: import('vue').PropType<import('@fiscozen/icons/src/types').IconVariant>;
|
|
206
|
-
};
|
|
207
|
-
leftIconButtonVariant: {
|
|
208
|
-
type: import('vue').PropType<import('@fiscozen/button').CommonButtonVariant>;
|
|
209
|
-
};
|
|
210
|
-
leftIconAriaLabel: {
|
|
211
|
-
type: import('vue').PropType<string>;
|
|
212
|
-
};
|
|
213
|
-
valid: {
|
|
214
|
-
type: import('vue').PropType<boolean>;
|
|
215
|
-
};
|
|
216
|
-
readonly: {
|
|
217
|
-
type: import('vue').PropType<boolean>;
|
|
218
|
-
};
|
|
219
|
-
maxlength: {
|
|
220
|
-
type: import('vue').PropType<number>;
|
|
221
|
-
};
|
|
222
|
-
autocomplete: {
|
|
223
|
-
type: import('vue').PropType<boolean>;
|
|
224
|
-
default: boolean;
|
|
225
|
-
};
|
|
226
|
-
leftIconClass: {
|
|
227
|
-
type: import('vue').PropType<string>;
|
|
228
|
-
};
|
|
229
|
-
}>> & Readonly<{
|
|
230
|
-
onBlur?: ((event: FocusEvent) => any) | undefined;
|
|
33
|
+
focus: (event: FocusEvent) => any;
|
|
34
|
+
blur: (event: FocusEvent) => any;
|
|
35
|
+
"fzinput:left-icon-click": () => any;
|
|
36
|
+
"fzinput:right-icon-click": () => any;
|
|
37
|
+
"fzinput:second-right-icon-click": () => any;
|
|
38
|
+
"update:highlighted": (value: boolean) => any;
|
|
39
|
+
"update:aiReasoning": (value: boolean) => any;
|
|
40
|
+
"update:modelValue": (value: string) => any;
|
|
41
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
231
42
|
onFocus?: ((event: FocusEvent) => any) | undefined;
|
|
43
|
+
onBlur?: ((event: FocusEvent) => any) | undefined;
|
|
232
44
|
"onFzinput:left-icon-click"?: (() => any) | undefined;
|
|
233
45
|
"onFzinput:right-icon-click"?: (() => any) | undefined;
|
|
234
46
|
"onFzinput:second-right-icon-click"?: (() => any) | undefined;
|
|
47
|
+
"onUpdate:highlighted"?: ((value: boolean) => any) | undefined;
|
|
48
|
+
"onUpdate:aiReasoning"?: ((value: boolean) => any) | undefined;
|
|
49
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
235
50
|
}>, {
|
|
236
|
-
error: boolean;
|
|
237
51
|
variant: "normal" | "floating-label";
|
|
238
|
-
type: "
|
|
52
|
+
type: "text" | "password" | "email" | "number" | "tel" | "url";
|
|
239
53
|
environment: InputEnvironment;
|
|
240
|
-
rightIconButtonVariant: import('@fiscozen/button').
|
|
241
|
-
secondRightIconButtonVariant: import('@fiscozen/button').
|
|
54
|
+
rightIconButtonVariant: import('@fiscozen/button').IconButtonVariant;
|
|
55
|
+
secondRightIconButtonVariant: import('@fiscozen/button').IconButtonVariant;
|
|
56
|
+
error: boolean;
|
|
57
|
+
highlighted: boolean;
|
|
58
|
+
aiReasoning: boolean;
|
|
242
59
|
autocomplete: boolean;
|
|
243
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
helpText?: (() => unknown) | undefined;
|
|
249
|
-
}> & {
|
|
250
|
-
label?: (() => unknown) | undefined;
|
|
251
|
-
"left-icon"?: (() => unknown) | undefined;
|
|
252
|
-
"right-icon"?: (() => unknown) | undefined;
|
|
253
|
-
errorMessage?: (() => unknown) | undefined;
|
|
254
|
-
helpText?: (() => unknown) | undefined;
|
|
255
|
-
}>;
|
|
60
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
61
|
+
containerRef: HTMLDivElement;
|
|
62
|
+
inputRef: HTMLInputElement;
|
|
63
|
+
}, any>;
|
|
64
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
256
65
|
export default _default;
|
|
257
66
|
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
258
67
|
new (): {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { IconButtonVariant } from '@fiscozen/button';
|
|
2
2
|
import { IconSize, IconVariant } from '@fiscozen/icons';
|
|
3
|
-
|
|
4
3
|
export type InputEnvironment = "backoffice" | "frontoffice";
|
|
5
4
|
type FzInputProps = {
|
|
6
5
|
/**
|
|
@@ -127,7 +126,7 @@ type FzInputProps = {
|
|
|
127
126
|
* Visual presentation style. 'floating-label' moves placeholder above input when focused/filled
|
|
128
127
|
* @default 'normal'
|
|
129
128
|
*/
|
|
130
|
-
variant?:
|
|
129
|
+
variant?: "normal" | "floating-label";
|
|
131
130
|
/**
|
|
132
131
|
* HTML5 pattern attribute for native browser validation
|
|
133
132
|
*/
|
|
@@ -141,6 +140,22 @@ type FzInputProps = {
|
|
|
141
140
|
* @default false
|
|
142
141
|
*/
|
|
143
142
|
readonly?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Shows highlighted state with warning colors (orange border, warm background, glow ring).
|
|
145
|
+
* Overridden by error, disabled, and readonly states.
|
|
146
|
+
* If both highlighted and aiReasoning are true, highlighted takes priority.
|
|
147
|
+
* @default false
|
|
148
|
+
*/
|
|
149
|
+
highlighted?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Shows AI reasoning state with purple colors (purple border, light purple background, glow ring).
|
|
152
|
+
* Auto-renders a sparkles icon unless leftIcon prop or left-icon slot is provided.
|
|
153
|
+
* Overridden by error, disabled, readonly, and highlighted states.
|
|
154
|
+
* @note When both aiReasoning and leftIcon are provided, leftIcon takes visual precedence
|
|
155
|
+
* and no sparkles icon is rendered.
|
|
156
|
+
* @default false
|
|
157
|
+
*/
|
|
158
|
+
aiReasoning?: boolean;
|
|
144
159
|
/**
|
|
145
160
|
* Native maxlength attribute. Limits maximum number of characters
|
|
146
161
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ToRefs, Ref, ComputedRef } from 'vue';
|
|
2
2
|
import { FzInputProps, InputEnvironment } from './types';
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* Composable for managing FzInput component styles and computed classes
|
|
6
5
|
*
|
|
@@ -16,7 +15,7 @@ import { FzInputProps, InputEnvironment } from './types';
|
|
|
16
15
|
*/
|
|
17
16
|
export default function useInputStyle(props: ToRefs<FzInputProps>, container: Ref<HTMLElement | null>, model: Ref<string | undefined>, effectiveEnvironment: ComputedRef<InputEnvironment>, isFocused: Ref<boolean>): {
|
|
18
17
|
staticContainerClass: string;
|
|
19
|
-
computedContainerClass: ComputedRef<
|
|
18
|
+
computedContainerClass: ComputedRef<string[]>;
|
|
20
19
|
computedLabelClass: ComputedRef<string[]>;
|
|
21
20
|
staticInputClass: string;
|
|
22
21
|
computedInputClass: ComputedRef<string[]>;
|
package/dist/src/utils.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiscozen/input",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Design System Input component",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -14,24 +14,24 @@
|
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"tailwindcss": "^3.4.1",
|
|
16
16
|
"vue": "^3.4.13",
|
|
17
|
-
"@fiscozen/icons": "^1.0.
|
|
17
|
+
"@fiscozen/icons": "^1.0.2"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@rushstack/eslint-patch": "^1.3.3",
|
|
21
21
|
"@types/jsdom": "^21.1.6",
|
|
22
22
|
"@types/node": "^18.19.3",
|
|
23
|
-
"@vitejs/plugin-vue": "^
|
|
24
|
-
"@vitest/coverage-v8": "^1.
|
|
23
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
24
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
25
25
|
"@vue/test-utils": "^2.4.3",
|
|
26
26
|
"@vue/tsconfig": "^0.5.0",
|
|
27
27
|
"eslint": "^8.49.0",
|
|
28
28
|
"jsdom": "^23.0.1",
|
|
29
29
|
"prettier": "^3.0.3",
|
|
30
|
-
"typescript": "~5.
|
|
31
|
-
"vite": "^
|
|
32
|
-
"vite-plugin-dts": "^
|
|
33
|
-
"vitest": "^1.
|
|
34
|
-
"vue-tsc": "^
|
|
30
|
+
"typescript": "~5.7.0",
|
|
31
|
+
"vite": "^8.0.0",
|
|
32
|
+
"vite-plugin-dts": "^4.5.0",
|
|
33
|
+
"vitest": "^4.1.1",
|
|
34
|
+
"vue-tsc": "^2.2.12",
|
|
35
35
|
"@fiscozen/eslint-config": "^0.1.0",
|
|
36
36
|
"@fiscozen/prettier-config": "^0.1.0",
|
|
37
37
|
"@fiscozen/tsconfig": "^0.1.0"
|
package/src/FzCurrencyInput.vue
CHANGED
|
@@ -78,7 +78,7 @@ const getEmptyValue = (): number | null | undefined => {
|
|
|
78
78
|
*/
|
|
79
79
|
const getEmptyDisplayValue = (
|
|
80
80
|
isEmptyValueZero: boolean,
|
|
81
|
-
isCurrentlyFocused: boolean
|
|
81
|
+
isCurrentlyFocused: boolean,
|
|
82
82
|
): string => {
|
|
83
83
|
if (isEmptyValueZero) {
|
|
84
84
|
// During typing, show empty string. Formatting happens on blur
|
|
@@ -327,7 +327,7 @@ const handleBlur = () => {
|
|
|
327
327
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
328
328
|
fzInputModel.value = getEmptyDisplayValue(
|
|
329
329
|
expectedEmptyValue === 0,
|
|
330
|
-
false // Not focused during blur
|
|
330
|
+
false, // Not focused during blur
|
|
331
331
|
);
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
@@ -395,7 +395,7 @@ const handleFocus = () => {
|
|
|
395
395
|
* @returns Normalized number value, undefined, or null
|
|
396
396
|
*/
|
|
397
397
|
const normalizeModelValue = (
|
|
398
|
-
value: number | string | undefined | null
|
|
398
|
+
value: number | string | undefined | null,
|
|
399
399
|
): number | undefined | null => {
|
|
400
400
|
if (value === undefined || value === null || value === "") {
|
|
401
401
|
return value === null ? null : undefined;
|
|
@@ -406,7 +406,7 @@ const normalizeModelValue = (
|
|
|
406
406
|
if (typeof value === "string") {
|
|
407
407
|
console.warn(
|
|
408
408
|
"[FzCurrencyInput] String values in v-model are deprecated. Please use number instead. " +
|
|
409
|
-
`Received: "${value}". This will be parsed to a number for retrocompatibility, but string support may be removed in a future version
|
|
409
|
+
`Received: "${value}". This will be parsed to a number for retrocompatibility, but string support may be removed in a future version.`,
|
|
410
410
|
);
|
|
411
411
|
const parsed = parse(value);
|
|
412
412
|
return isNaN(parsed) ? undefined : parsed;
|
|
@@ -512,7 +512,7 @@ onMounted(() => {
|
|
|
512
512
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
513
513
|
fzInputModel.value = getEmptyDisplayValue(
|
|
514
514
|
expectedEmptyValue === 0,
|
|
515
|
-
false // Not focused during mount
|
|
515
|
+
false, // Not focused during mount
|
|
516
516
|
);
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
@@ -585,7 +585,7 @@ onMounted(() => {
|
|
|
585
585
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
586
586
|
fzInputModel.value = getEmptyDisplayValue(
|
|
587
587
|
emptyValue === 0,
|
|
588
|
-
false // Not focused during mount
|
|
588
|
+
false, // Not focused during mount
|
|
589
589
|
);
|
|
590
590
|
}
|
|
591
591
|
return;
|
|
@@ -621,7 +621,7 @@ watch(
|
|
|
621
621
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
622
622
|
fzInputModel.value = getEmptyDisplayValue(
|
|
623
623
|
expectedEmptyValue === 0,
|
|
624
|
-
isFocused.value
|
|
624
|
+
isFocused.value,
|
|
625
625
|
);
|
|
626
626
|
return;
|
|
627
627
|
}
|
|
@@ -705,12 +705,12 @@ watch(
|
|
|
705
705
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
706
706
|
fzInputModel.value = getEmptyDisplayValue(
|
|
707
707
|
emptyValue === 0,
|
|
708
|
-
isFocused.value
|
|
708
|
+
isFocused.value,
|
|
709
709
|
);
|
|
710
710
|
}
|
|
711
711
|
return;
|
|
712
712
|
}
|
|
713
|
-
}
|
|
713
|
+
},
|
|
714
714
|
);
|
|
715
715
|
|
|
716
716
|
defineExpose({
|
|
@@ -731,10 +731,10 @@ defineExpose({
|
|
|
731
731
|
@blur="handleBlur"
|
|
732
732
|
@paste="handlePaste"
|
|
733
733
|
>
|
|
734
|
-
<template #label>
|
|
734
|
+
<template v-if="$slots.label" #label>
|
|
735
735
|
<slot name="label"></slot>
|
|
736
736
|
</template>
|
|
737
|
-
<template #left-icon>
|
|
737
|
+
<template v-if="$slots['left-icon']" #left-icon>
|
|
738
738
|
<slot name="left-icon"></slot>
|
|
739
739
|
</template>
|
|
740
740
|
<template #right-icon>
|
|
@@ -786,10 +786,10 @@ defineExpose({
|
|
|
786
786
|
</div>
|
|
787
787
|
</div>
|
|
788
788
|
</template>
|
|
789
|
-
<template #helpText>
|
|
789
|
+
<template v-if="$slots.helpText" #helpText>
|
|
790
790
|
<slot name="helpText"></slot>
|
|
791
791
|
</template>
|
|
792
|
-
<template #errorMessage>
|
|
792
|
+
<template v-if="$slots.errorMessage" #errorMessage>
|
|
793
793
|
<slot name="errorMessage"></slot>
|
|
794
794
|
</template>
|
|
795
795
|
</FzInput>
|
package/src/FzInput.vue
CHANGED
|
@@ -27,6 +27,8 @@ const props = withDefaults(defineProps<FzInputProps>(), {
|
|
|
27
27
|
variant: "normal",
|
|
28
28
|
environment: "frontoffice",
|
|
29
29
|
autocomplete: false,
|
|
30
|
+
highlighted: false,
|
|
31
|
+
aiReasoning: false,
|
|
30
32
|
});
|
|
31
33
|
|
|
32
34
|
defineOptions({
|
|
@@ -117,6 +119,46 @@ const inputRef: Ref<HTMLInputElement | null> = ref(null);
|
|
|
117
119
|
const uniqueId = generateInputId();
|
|
118
120
|
const isFocused = ref(false);
|
|
119
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Internal visual state for emphasis props.
|
|
124
|
+
* These track the effective visual state which can differ from props when
|
|
125
|
+
* the user types into a highlighted/aiReasoning input (user input resets emphasis).
|
|
126
|
+
* Programmatic value changes (via v-model) do not affect these states.
|
|
127
|
+
*/
|
|
128
|
+
const effectiveHighlighted = ref(props.highlighted);
|
|
129
|
+
const effectiveAiReasoning = ref(props.aiReasoning);
|
|
130
|
+
|
|
131
|
+
watch(
|
|
132
|
+
() => props.highlighted,
|
|
133
|
+
(val) => {
|
|
134
|
+
effectiveHighlighted.value = val;
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
watch(
|
|
138
|
+
() => props.aiReasoning,
|
|
139
|
+
(val) => {
|
|
140
|
+
effectiveAiReasoning.value = val;
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resets visual emphasis (highlighted/aiReasoning) when user physically types.
|
|
146
|
+
* Only triggered by native input events (user interaction), not programmatic v-model updates.
|
|
147
|
+
* Emits update events so parents using v-model:highlighted / v-model:aiReasoning stay in sync.
|
|
148
|
+
*/
|
|
149
|
+
const handleUserInput = () => {
|
|
150
|
+
if (effectiveHighlighted.value) {
|
|
151
|
+
effectiveHighlighted.value = false;
|
|
152
|
+
emit("update:highlighted", false);
|
|
153
|
+
}
|
|
154
|
+
if (effectiveAiReasoning.value) {
|
|
155
|
+
effectiveAiReasoning.value = false;
|
|
156
|
+
emit("update:aiReasoning", false);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const propsRefs = toRefs(props);
|
|
161
|
+
|
|
120
162
|
const {
|
|
121
163
|
staticContainerClass,
|
|
122
164
|
computedContainerClass,
|
|
@@ -127,7 +169,11 @@ const {
|
|
|
127
169
|
containerWidth,
|
|
128
170
|
showNormalPlaceholder,
|
|
129
171
|
} = useInputStyle(
|
|
130
|
-
|
|
172
|
+
{
|
|
173
|
+
...propsRefs,
|
|
174
|
+
highlighted: effectiveHighlighted,
|
|
175
|
+
aiReasoning: effectiveAiReasoning,
|
|
176
|
+
},
|
|
131
177
|
containerRef,
|
|
132
178
|
model,
|
|
133
179
|
effectiveEnvironment,
|
|
@@ -185,6 +231,8 @@ const emit = defineEmits<{
|
|
|
185
231
|
"fzinput:left-icon-click": [];
|
|
186
232
|
"fzinput:right-icon-click": [];
|
|
187
233
|
"fzinput:second-right-icon-click": [];
|
|
234
|
+
"update:highlighted": [value: boolean];
|
|
235
|
+
"update:aiReasoning": [value: boolean];
|
|
188
236
|
}>();
|
|
189
237
|
|
|
190
238
|
/**
|
|
@@ -305,6 +353,15 @@ const isReadonlyOrDisabled = computed(
|
|
|
305
353
|
() => !!props.disabled || !!props.readonly,
|
|
306
354
|
);
|
|
307
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Computed class for the auto-rendered AI sparkles icon.
|
|
358
|
+
* Muted when the input is in error, disabled, or readonly state.
|
|
359
|
+
*/
|
|
360
|
+
const aiIconClass = computed(() => {
|
|
361
|
+
if (isReadonlyOrDisabled.value || props.error) return "text-grey-300";
|
|
362
|
+
return "text-purple-600";
|
|
363
|
+
});
|
|
364
|
+
|
|
308
365
|
/**
|
|
309
366
|
* Determines if right icon is clickable (not rendered as button)
|
|
310
367
|
*/
|
|
@@ -385,6 +442,14 @@ defineExpose({
|
|
|
385
442
|
: undefined
|
|
386
443
|
"
|
|
387
444
|
/>
|
|
445
|
+
<FzIcon
|
|
446
|
+
v-else-if="effectiveAiReasoning"
|
|
447
|
+
name="sparkles"
|
|
448
|
+
variant="fas"
|
|
449
|
+
size="md"
|
|
450
|
+
aria-hidden="true"
|
|
451
|
+
:class="aiIconClass"
|
|
452
|
+
/>
|
|
388
453
|
</slot>
|
|
389
454
|
<div class="flex flex-col justify-around min-w-0 grow">
|
|
390
455
|
<span
|
|
@@ -412,6 +477,7 @@ defineExpose({
|
|
|
412
477
|
:aria-labelledby="ariaLabelledBy"
|
|
413
478
|
:aria-describedby="ariaDescribedBy"
|
|
414
479
|
v-bind="inputAttrs"
|
|
480
|
+
@input="handleUserInput"
|
|
415
481
|
@blur="
|
|
416
482
|
(e) => {
|
|
417
483
|
isFocused = false;
|