@fiscozen/input 3.0.3 → 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.
@@ -1,258 +1,67 @@
1
1
  import { Ref } from 'vue';
2
- import { InputEnvironment } from './types';
3
-
4
- declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<import('vue').ExtractPropTypes<{
5
- modelValue: import('vue').PropType<string>;
6
- error: {
7
- type: import('vue').PropType<boolean>;
8
- default: boolean;
9
- };
10
- name: {
11
- type: import('vue').PropType<string>;
12
- };
13
- size: {
14
- type: import('vue').PropType<"sm" | "md" | "lg">;
15
- };
16
- variant: {
17
- type: import('vue').PropType<"normal" | "floating-label">;
18
- default: string;
19
- };
20
- type: {
21
- type: import('vue').PropType<"number" | "text" | "password" | "email" | "tel" | "url">;
22
- default: string;
23
- };
24
- required: {
25
- type: import('vue').PropType<boolean>;
26
- };
27
- disabled: {
28
- type: import('vue').PropType<boolean>;
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
- blur: (event: FocusEvent) => void;
117
- focus: (event: FocusEvent) => void;
118
- "fzinput:left-icon-click": () => void;
119
- "fzinput:right-icon-click": () => void;
120
- "fzinput:second-right-icon-click": () => void;
121
- }, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
122
- modelValue: import('vue').PropType<string>;
123
- error: {
124
- type: import('vue').PropType<boolean>;
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: "number" | "text" | "password" | "email" | "tel" | "url";
52
+ type: "text" | "password" | "email" | "number" | "tel" | "url";
239
53
  environment: InputEnvironment;
240
- rightIconButtonVariant: import('@fiscozen/button').CommonButtonVariant;
241
- secondRightIconButtonVariant: import('@fiscozen/button').CommonButtonVariant;
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, true, {}, any>, Readonly<{
244
- label?: (() => unknown) | undefined;
245
- "left-icon"?: (() => unknown) | undefined;
246
- "right-icon"?: (() => unknown) | undefined;
247
- errorMessage?: (() => unknown) | undefined;
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 (): {
@@ -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?: 'normal' | 'floating-label';
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<(string | undefined)[]>;
18
+ computedContainerClass: ComputedRef<string[]>;
20
19
  computedLabelClass: ComputedRef<string[]>;
21
20
  staticInputClass: string;
22
21
  computedInputClass: ComputedRef<string[]>;
@@ -1,5 +1,4 @@
1
1
  import { InputEnvironment } from './types';
2
-
3
2
  type InputSize = "sm" | "md" | "lg";
4
3
  /**
5
4
  * Maps deprecated InputSize to InputEnvironment
package/package.json CHANGED
@@ -1,37 +1,37 @@
1
1
  {
2
2
  "name": "@fiscozen/input",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "description": "Design System Input component",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
7
7
  "keywords": [],
8
8
  "author": "Cristian Barraco",
9
9
  "dependencies": {
10
- "@fiscozen/button": "3.0.0",
10
+ "@fiscozen/alert": "3.0.0",
11
11
  "@fiscozen/composables": "1.0.3",
12
- "@fiscozen/alert": "3.0.0"
12
+ "@fiscozen/button": "3.0.0"
13
13
  },
14
14
  "peerDependencies": {
15
15
  "tailwindcss": "^3.4.1",
16
16
  "vue": "^3.4.13",
17
- "@fiscozen/icons": "^1.0.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": "^4.5.2",
24
- "@vitest/coverage-v8": "^1.2.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.3.0",
31
- "vite": "^5.0.10",
32
- "vite-plugin-dts": "^3.8.3",
33
- "vitest": "^1.2.0",
34
- "vue-tsc": "^1.8.25",
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/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
- toRefs(props),
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;