@lets-events/react 12.2.10 → 12.2.12

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 (90) hide show
  1. package/.eslintrc.json +2 -2
  2. package/.turbo/turbo-build.log +19 -21
  3. package/CHANGELOG.md +12 -0
  4. package/dist/index.css +171 -0
  5. package/dist/index.d.mts +377 -3
  6. package/dist/index.d.ts +377 -3
  7. package/dist/index.js +73 -27
  8. package/dist/index.mjs +72 -27
  9. package/package.json +1 -1
  10. package/src/components/Alert.tsx +303 -303
  11. package/src/components/Avatar.tsx +55 -55
  12. package/src/components/Badge.tsx +125 -125
  13. package/src/components/Box.tsx +3 -3
  14. package/src/components/Button/index.tsx +16 -16
  15. package/src/components/Button/styledComponents.ts +300 -300
  16. package/src/components/ButtonGroup.tsx +484 -484
  17. package/src/components/Calendar/index.tsx +148 -148
  18. package/src/components/Calendar/styledComponents.ts +250 -250
  19. package/src/components/Card.tsx +48 -48
  20. package/src/components/CheckboxGroup.tsx +176 -176
  21. package/src/components/Container.tsx +39 -39
  22. package/src/components/Divider.tsx +7 -0
  23. package/src/components/Drawer/index.tsx +148 -138
  24. package/src/components/Drawer/styledComponents.ts +52 -52
  25. package/src/components/Dropdown.tsx +302 -302
  26. package/src/components/Filter.tsx +164 -164
  27. package/src/components/Flex.tsx +118 -118
  28. package/src/components/FormFields/AddressFormFields/CityFormField.tsx +111 -111
  29. package/src/components/FormFields/AddressFormFields/CountryFormField.tsx +33 -33
  30. package/src/components/FormFields/AddressFormFields/PostalCodeFormField.tsx +39 -39
  31. package/src/components/FormFields/AddressFormFields/StateFormField.tsx +32 -32
  32. package/src/components/FormFields/AddressFormFields/index.tsx +141 -141
  33. package/src/components/FormFields/BirthDateFormField.tsx +84 -84
  34. package/src/components/FormFields/CNPJFormField.tsx +87 -87
  35. package/src/components/FormFields/CPFFormField.tsx +78 -78
  36. package/src/components/FormFields/CalendarFormField.tsx +95 -95
  37. package/src/components/FormFields/CheckboxGroupFormField.tsx +91 -91
  38. package/src/components/FormFields/EmailFormField.tsx +27 -27
  39. package/src/components/FormFields/Form.tsx +39 -39
  40. package/src/components/FormFields/IdentityDocumentNumberFormField.tsx +32 -32
  41. package/src/components/FormFields/MultiSelectFormField.tsx +64 -64
  42. package/src/components/FormFields/PhoneFormField.tsx +40 -40
  43. package/src/components/FormFields/RadioGroupFormField.tsx +84 -84
  44. package/src/components/FormFields/RichEditorFormField.tsx +103 -103
  45. package/src/components/FormFields/SelectFormField.tsx +93 -93
  46. package/src/components/FormFields/SwitchFormField.tsx +46 -46
  47. package/src/components/FormFields/TextAreaFormField.tsx +57 -57
  48. package/src/components/FormFields/TextFormField.tsx +112 -112
  49. package/src/components/FormFields/TimePickerFormField.tsx +88 -88
  50. package/src/components/FormFields/subComponents/ErrorFormMessage.tsx +36 -36
  51. package/src/components/FormFields/subComponents/FormLabel.tsx +29 -29
  52. package/src/components/FormFields/utils/validation.ts +23 -23
  53. package/src/components/Grid.tsx +137 -137
  54. package/src/components/Icon.tsx +47 -47
  55. package/src/components/MenuDropdown/index.tsx +38 -38
  56. package/src/components/MenuDropdown/styledComponents.ts +31 -31
  57. package/src/components/Modal.tsx +107 -90
  58. package/src/components/MultiSelect/index.tsx +243 -243
  59. package/src/components/MultiSelect/styledComponents.ts +160 -160
  60. package/src/components/RadioGroup.tsx +210 -210
  61. package/src/components/RichEditor/QuillComponent.tsx +457 -457
  62. package/src/components/RichEditor/RichEditor.tsx +49 -49
  63. package/src/components/RichEditor/index.ts +2 -2
  64. package/src/components/RichEditor/styledComponents.ts +1151 -1151
  65. package/src/components/Section.tsx +33 -33
  66. package/src/components/Step.tsx +164 -164
  67. package/src/components/Switch.tsx +108 -108
  68. package/src/components/Text.tsx +38 -38
  69. package/src/components/TextField.tsx +372 -372
  70. package/src/components/TextareaField.tsx +127 -127
  71. package/src/components/TimePicker.tsx +328 -328
  72. package/src/components/Toast/components/ToastItem.tsx +41 -41
  73. package/src/components/Toast/components/ToastProvider.tsx +63 -63
  74. package/src/components/Toast/hooks/useToast.ts +12 -12
  75. package/src/components/Toast/index.tsx +5 -5
  76. package/src/components/Toast/styles/index.ts +135 -135
  77. package/src/components/Toast/types/index.ts +46 -46
  78. package/src/components/Tooltip/index.tsx +66 -66
  79. package/src/components/Tooltip/styles.ts +77 -77
  80. package/src/hooks/useCountries.ts +41 -41
  81. package/src/hooks/useImageUpload.ts +139 -139
  82. package/src/hooks/useOnClickOutside.tsx +42 -42
  83. package/src/index.tsx +69 -68
  84. package/src/styles/index.ts +38 -38
  85. package/src/types/typographyValues.ts +178 -178
  86. package/src/utils/getNestedValue.ts +3 -3
  87. package/src/utils/states.ts +29 -29
  88. package/src/utils/uploadService.ts +180 -180
  89. package/tsconfig.json +3 -3
  90. package/tsup.config.ts +38 -38
@@ -1,372 +1,372 @@
1
- import React, { ComponentProps, ReactNode } from "react";
2
- import { styled } from "../styles";
3
- import { TextField as TextFieldRadix } from "@radix-ui/themes";
4
- import Icon from "./Icon";
5
- import { typographyValues } from "../types/typographyValues";
6
- import { TextStyle } from "./Text";
7
- import { Flex } from "./Flex";
8
- import { format, MaskOptions, useMask, unformat } from "@react-input/mask";
9
-
10
- export const TextFieldStyled = styled(TextFieldRadix.Root, {
11
- height: "$40",
12
- fontFamily: "$default",
13
- borderRadius: "$sm",
14
- boxSizing: "border-box",
15
- color: "$dark500",
16
- border: "1px solid $dark300",
17
- position: "relative",
18
- display: "flex",
19
- width: "100%",
20
- alignItems: "center",
21
- padding: "0 $14",
22
- gap: "$14",
23
- backgroundColor: "$dark50",
24
- input: {
25
- order: 1,
26
- outline: "none",
27
- border: "none",
28
- margin: 0,
29
- width: "100%",
30
- font: "inherit",
31
- textAlign: "left",
32
- },
33
-
34
- "&:has(input:focus)": {
35
- border: "2px solid $brand300",
36
- },
37
- "&:has(input:disabled)": {
38
- backgroundColor: "$dark100",
39
- color: "$dark400",
40
- border: "1px solid $dark200",
41
- cursor: "not-allowed",
42
- },
43
-
44
- variants: {
45
- color: {
46
- default: {
47
- color: "$dark400",
48
- border: "1px solid $dark300",
49
- "&:has(input:focus)": {
50
- border: "2px solid $brand300",
51
- },
52
- "&:has(input:disabled)": {
53
- backgroundColor: "$dark100",
54
- color: "$dark400",
55
- border: "1px solid $dark300",
56
- cursor: "not-allowed",
57
- },
58
- },
59
- error: {
60
- border: "1px solid $error400",
61
- color: "$error400",
62
- "input::placeholder": {
63
- color: "$error400",
64
- },
65
- "&:has(input:focus)": {
66
- border: "2px solid $error400",
67
- },
68
- "&:has(input:disabled)": {
69
- backgroundColor: "$error50",
70
- color: "$error300",
71
- border: "1px solid $error100",
72
- cursor: "not-allowed",
73
- },
74
- },
75
- },
76
-
77
- typography: typographyValues,
78
- fontWeight: {
79
- regular: { fontWeight: "$regular" },
80
- medium: { fontWeight: "$medium" },
81
- semibold: { fontWeight: "$semibold" },
82
- bold: { fontWeight: "$bold" },
83
- },
84
- textAlign: {
85
- left: { textAlign: "left" },
86
- center: { textAlign: "center" },
87
- right: { textAlign: "right" },
88
- },
89
- isValid: {
90
- true: {},
91
- false: {},
92
- },
93
- },
94
-
95
- compoundVariants: [
96
- {
97
- isValid: false,
98
- css: {
99
- border: "1px solid $error400",
100
- color: "$error400",
101
- backgroundColor: "$error50",
102
- input: {
103
- "&::placeholder": {
104
- color: "$error400",
105
- },
106
- backgroundColor: "$error50",
107
- },
108
- "&:has(input:focus)": {
109
- border: "2px solid $error400",
110
- },
111
- "&:has(input:disabled)": {
112
- input: {
113
- backgroundColor: "$error50",
114
- },
115
- backgroundColor: "$error50",
116
- color: "$error300",
117
- border: "1px solid $error100",
118
- cursor: "not-allowed",
119
- },
120
- },
121
- },
122
- {
123
- isValid: true,
124
- css: {
125
- "&:has(input:focus)": {
126
- border: "2px solid $success500",
127
- },
128
- "&:has(input:disabled)": {
129
- backgroundColor: "$dark100",
130
- color: "$dark400",
131
- border: "1px solid $dark200",
132
- cursor: "not-allowed",
133
- },
134
- },
135
- },
136
- ],
137
-
138
- defaultVariants: {
139
- typography: "labelSmall",
140
- },
141
- });
142
-
143
- export const TextFieldSlotStyled = styled(TextFieldRadix.Slot, {
144
- display: "flex",
145
- alignItems: "center",
146
- justifyContent: "center",
147
- padding: 4,
148
-
149
- variants: {
150
- typography: typographyValues,
151
- fontWeight: {
152
- regular: { fontWeight: "$regular" },
153
- medium: { fontWeight: "$medium" },
154
- semibold: { fontWeight: "$semibold" },
155
- bold: { fontWeight: "$bold" },
156
- },
157
- textAlign: {
158
- left: { textAlign: "left" },
159
- right: { textAlign: "right" },
160
- center: { textAlign: "center" },
161
- },
162
- },
163
- });
164
-
165
- export type TextFieldProps = ComponentProps<typeof TextFieldStyled> & {
166
- addon?: ReactNode;
167
- placeholder?: string;
168
- children?: React.ReactNode;
169
- isValid?: boolean;
170
- name?: string;
171
- maxLength?: number;
172
- typography?: string;
173
- fontWeight?: "regular" | "medium" | "semibold" | "bold";
174
- textAlign?: "left" | "right" | "center";
175
- mask?: MaskOptions;
176
- hasError?: boolean;
177
- };
178
-
179
- export type TextFieldSlotProps = Omit<
180
- ComponentProps<typeof TextFieldStyled>,
181
- "color"
182
- > & {
183
- placeholder?: string;
184
- children?: React.ReactNode;
185
- position?: "flex-start" | "flex-end";
186
- onClick?: () => void;
187
- color?: "error" | "success" | undefined;
188
- typography?: string;
189
- fontWeight?: "regular" | "medium" | "semibold" | "bold";
190
- textAlign?: "left" | "right" | "center";
191
- };
192
-
193
- const InputAddon = styled(TextStyle, {
194
- boxSizing: "border-box",
195
- border: "1px solid $dark300",
196
- height: "$40",
197
- padding: "0 $12",
198
- margin: "0 auto",
199
-
200
- color: "$dark600",
201
- whiteSpace: "nowrap",
202
- borderRadius: "$sm 0px 0px $sm",
203
- borderRightWidth: "0px",
204
- display: "flex",
205
- flexWrap: "nowrap",
206
- alignItems: "center",
207
- lineHeight: 1,
208
-
209
- variants: {
210
- color: {
211
- default: {
212
- borderColor: "$dark300",
213
- },
214
- error: {
215
- borderColor: "$error400",
216
- },
217
- },
218
- },
219
- });
220
- const TextFieldLimitIndicator = styled("div", {
221
- width: "fit-content",
222
- display: "flex",
223
- justifyContent: "flex-end",
224
- position: "absolute",
225
- top: "0.5rem",
226
- right: "1rem",
227
- span: {
228
- color: "$neutral700",
229
- borderRadius: "$2xs",
230
- padding: "$4",
231
- fontSize: "$labelSmall",
232
- backgroundColor: "$neutral200",
233
- fontFamily: "$default",
234
- },
235
- });
236
- export const maskFormat = format;
237
- export const maskUnformat = unformat;
238
-
239
- export const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
240
- (
241
- {
242
- children,
243
- isValid,
244
- name,
245
- color,
246
- typography,
247
- fontWeight,
248
- addon,
249
- textAlign = "left",
250
- mask,
251
- maxLength,
252
- hasError,
253
- ...props
254
- },
255
- fowardedRef
256
- ) => {
257
- const maskRef = mask ? useMask(mask) : undefined;
258
- const inputRef = React.useRef<HTMLInputElement>(null);
259
- const badgeRef = React.useRef<HTMLSpanElement>(null);
260
-
261
- const updateCharCountBadge = () => {
262
- if (!maxLength || !badgeRef.current) return;
263
- const currentLength = inputRef.current?.value.length ?? 0;
264
- badgeRef.current.textContent = String(maxLength - currentLength);
265
- };
266
-
267
- const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
268
- updateCharCountBadge();
269
- props.onInput?.(e);
270
- };
271
-
272
- return (
273
- <Flex direction="column" css={{ width: "100%", position: "relative" }}>
274
- <Flex gap={"0"} css={{ width: "100%" }}>
275
- {!!addon && (
276
- <InputAddon
277
- color={hasError ? "error" : color}
278
- typography="labelSmall"
279
- >
280
- {addon}
281
- </InputAddon>
282
- )}
283
-
284
- <TextFieldStyled
285
- color={hasError ? "error" : color || "default"}
286
- isValid={isValid}
287
- name={name}
288
- typography={typography}
289
- fontWeight={fontWeight}
290
- textAlign={textAlign}
291
- maxLength={maxLength}
292
- ref={(r) => {
293
- if (!r) return;
294
- inputRef.current = r;
295
- if (maskRef) maskRef.current = r;
296
-
297
- if (fowardedRef) {
298
- if (typeof fowardedRef === "function") fowardedRef(r);
299
- else fowardedRef.current = r;
300
- }
301
- }}
302
- onInput={handleInput}
303
- {...props}
304
- style={
305
- addon
306
- ? {
307
- borderTopLeftRadius: "0px",
308
- borderBottomLeftRadius: "0px",
309
- }
310
- : undefined
311
- }
312
- >
313
- {children}
314
- {isValid && (
315
- <TextFieldSlot
316
- position="flex-end"
317
- name={name}
318
- color={
319
- (hasError ? "error" : color) as TextFieldSlotProps["color"]
320
- }
321
- typography={typography}
322
- fontWeight={fontWeight}
323
- textAlign={textAlign}
324
- >
325
- <Icon name="check" />
326
- </TextFieldSlot>
327
- )}
328
- </TextFieldStyled>
329
- </Flex>
330
- {maxLength && (
331
- <TextFieldLimitIndicator>
332
- <span ref={badgeRef}>{maxLength}</span>
333
- </TextFieldLimitIndicator>
334
- )}
335
- </Flex>
336
- );
337
- }
338
- );
339
-
340
- export function TextFieldSlot({
341
- children,
342
- position = "flex-start",
343
- onClick,
344
- typography = "bodyXS",
345
- fontWeight = "regular",
346
- textAlign = "right",
347
- ...props
348
- }: TextFieldSlotProps) {
349
- const sharedStyles = {
350
- typography,
351
- fontWeight,
352
- textAlign,
353
- ...props,
354
- color: undefined,
355
- };
356
-
357
- return (
358
- <TextFieldSlotStyled
359
- {...sharedStyles}
360
- style={{
361
- float: position === "flex-start" ? "left" : "right",
362
- order: position === "flex-start" ? 0 : 2,
363
- textAlign,
364
- zIndex: onClick ? 2 : undefined,
365
- cursor: onClick ? "pointer" : undefined,
366
- }}
367
- onClick={onClick}
368
- >
369
- {children}
370
- </TextFieldSlotStyled>
371
- );
372
- }
1
+ import React, { ComponentProps, ReactNode } from "react";
2
+ import { styled } from "../styles";
3
+ import { TextField as TextFieldRadix } from "@radix-ui/themes";
4
+ import Icon from "./Icon";
5
+ import { typographyValues } from "../types/typographyValues";
6
+ import { TextStyle } from "./Text";
7
+ import { Flex } from "./Flex";
8
+ import { format, MaskOptions, useMask, unformat } from "@react-input/mask";
9
+
10
+ export const TextFieldStyled = styled(TextFieldRadix.Root, {
11
+ height: "$40",
12
+ fontFamily: "$default",
13
+ borderRadius: "$sm",
14
+ boxSizing: "border-box",
15
+ color: "$dark500",
16
+ border: "1px solid $dark300",
17
+ position: "relative",
18
+ display: "flex",
19
+ width: "100%",
20
+ alignItems: "center",
21
+ padding: "0 $14",
22
+ gap: "$14",
23
+ backgroundColor: "$dark50",
24
+ input: {
25
+ order: 1,
26
+ outline: "none",
27
+ border: "none",
28
+ margin: 0,
29
+ width: "100%",
30
+ font: "inherit",
31
+ textAlign: "left",
32
+ },
33
+
34
+ "&:has(input:focus)": {
35
+ border: "2px solid $brand300",
36
+ },
37
+ "&:has(input:disabled)": {
38
+ backgroundColor: "$dark100",
39
+ color: "$dark400",
40
+ border: "1px solid $dark200",
41
+ cursor: "not-allowed",
42
+ },
43
+
44
+ variants: {
45
+ color: {
46
+ default: {
47
+ color: "$dark400",
48
+ border: "1px solid $dark300",
49
+ "&:has(input:focus)": {
50
+ border: "2px solid $brand300",
51
+ },
52
+ "&:has(input:disabled)": {
53
+ backgroundColor: "$dark100",
54
+ color: "$dark400",
55
+ border: "1px solid $dark300",
56
+ cursor: "not-allowed",
57
+ },
58
+ },
59
+ error: {
60
+ border: "1px solid $error400",
61
+ color: "$error400",
62
+ "input::placeholder": {
63
+ color: "$error400",
64
+ },
65
+ "&:has(input:focus)": {
66
+ border: "2px solid $error400",
67
+ },
68
+ "&:has(input:disabled)": {
69
+ backgroundColor: "$error50",
70
+ color: "$error300",
71
+ border: "1px solid $error100",
72
+ cursor: "not-allowed",
73
+ },
74
+ },
75
+ },
76
+
77
+ typography: typographyValues,
78
+ fontWeight: {
79
+ regular: { fontWeight: "$regular" },
80
+ medium: { fontWeight: "$medium" },
81
+ semibold: { fontWeight: "$semibold" },
82
+ bold: { fontWeight: "$bold" },
83
+ },
84
+ textAlign: {
85
+ left: { textAlign: "left" },
86
+ center: { textAlign: "center" },
87
+ right: { textAlign: "right" },
88
+ },
89
+ isValid: {
90
+ true: {},
91
+ false: {},
92
+ },
93
+ },
94
+
95
+ compoundVariants: [
96
+ {
97
+ isValid: false,
98
+ css: {
99
+ border: "1px solid $error400",
100
+ color: "$error400",
101
+ backgroundColor: "$error50",
102
+ input: {
103
+ "&::placeholder": {
104
+ color: "$error400",
105
+ },
106
+ backgroundColor: "$error50",
107
+ },
108
+ "&:has(input:focus)": {
109
+ border: "2px solid $error400",
110
+ },
111
+ "&:has(input:disabled)": {
112
+ input: {
113
+ backgroundColor: "$error50",
114
+ },
115
+ backgroundColor: "$error50",
116
+ color: "$error300",
117
+ border: "1px solid $error100",
118
+ cursor: "not-allowed",
119
+ },
120
+ },
121
+ },
122
+ {
123
+ isValid: true,
124
+ css: {
125
+ "&:has(input:focus)": {
126
+ border: "2px solid $success500",
127
+ },
128
+ "&:has(input:disabled)": {
129
+ backgroundColor: "$dark100",
130
+ color: "$dark400",
131
+ border: "1px solid $dark200",
132
+ cursor: "not-allowed",
133
+ },
134
+ },
135
+ },
136
+ ],
137
+
138
+ defaultVariants: {
139
+ typography: "labelSmall",
140
+ },
141
+ });
142
+
143
+ export const TextFieldSlotStyled = styled(TextFieldRadix.Slot, {
144
+ display: "flex",
145
+ alignItems: "center",
146
+ justifyContent: "center",
147
+ padding: 4,
148
+
149
+ variants: {
150
+ typography: typographyValues,
151
+ fontWeight: {
152
+ regular: { fontWeight: "$regular" },
153
+ medium: { fontWeight: "$medium" },
154
+ semibold: { fontWeight: "$semibold" },
155
+ bold: { fontWeight: "$bold" },
156
+ },
157
+ textAlign: {
158
+ left: { textAlign: "left" },
159
+ right: { textAlign: "right" },
160
+ center: { textAlign: "center" },
161
+ },
162
+ },
163
+ });
164
+
165
+ export type TextFieldProps = ComponentProps<typeof TextFieldStyled> & {
166
+ addon?: ReactNode;
167
+ placeholder?: string;
168
+ children?: React.ReactNode;
169
+ isValid?: boolean;
170
+ name?: string;
171
+ maxLength?: number;
172
+ typography?: string;
173
+ fontWeight?: "regular" | "medium" | "semibold" | "bold";
174
+ textAlign?: "left" | "right" | "center";
175
+ mask?: MaskOptions;
176
+ hasError?: boolean;
177
+ };
178
+
179
+ export type TextFieldSlotProps = Omit<
180
+ ComponentProps<typeof TextFieldStyled>,
181
+ "color"
182
+ > & {
183
+ placeholder?: string;
184
+ children?: React.ReactNode;
185
+ position?: "flex-start" | "flex-end";
186
+ onClick?: () => void;
187
+ color?: "error" | "success" | undefined;
188
+ typography?: string;
189
+ fontWeight?: "regular" | "medium" | "semibold" | "bold";
190
+ textAlign?: "left" | "right" | "center";
191
+ };
192
+
193
+ const InputAddon = styled(TextStyle, {
194
+ boxSizing: "border-box",
195
+ border: "1px solid $dark300",
196
+ height: "$40",
197
+ padding: "0 $12",
198
+ margin: "0 auto",
199
+
200
+ color: "$dark600",
201
+ whiteSpace: "nowrap",
202
+ borderRadius: "$sm 0px 0px $sm",
203
+ borderRightWidth: "0px",
204
+ display: "flex",
205
+ flexWrap: "nowrap",
206
+ alignItems: "center",
207
+ lineHeight: 1,
208
+
209
+ variants: {
210
+ color: {
211
+ default: {
212
+ borderColor: "$dark300",
213
+ },
214
+ error: {
215
+ borderColor: "$error400",
216
+ },
217
+ },
218
+ },
219
+ });
220
+ const TextFieldLimitIndicator = styled("div", {
221
+ width: "fit-content",
222
+ display: "flex",
223
+ justifyContent: "flex-end",
224
+ position: "absolute",
225
+ top: "0.5rem",
226
+ right: "1rem",
227
+ span: {
228
+ color: "$neutral700",
229
+ borderRadius: "$2xs",
230
+ padding: "$4",
231
+ fontSize: "$labelSmall",
232
+ backgroundColor: "$neutral200",
233
+ fontFamily: "$default",
234
+ },
235
+ });
236
+ export const maskFormat = format;
237
+ export const maskUnformat = unformat;
238
+
239
+ export const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
240
+ (
241
+ {
242
+ children,
243
+ isValid,
244
+ name,
245
+ color,
246
+ typography,
247
+ fontWeight,
248
+ addon,
249
+ textAlign = "left",
250
+ mask,
251
+ maxLength,
252
+ hasError,
253
+ ...props
254
+ },
255
+ fowardedRef
256
+ ) => {
257
+ const maskRef = mask ? useMask(mask) : undefined;
258
+ const inputRef = React.useRef<HTMLInputElement>(null);
259
+ const badgeRef = React.useRef<HTMLSpanElement>(null);
260
+
261
+ const updateCharCountBadge = () => {
262
+ if (!maxLength || !badgeRef.current) return;
263
+ const currentLength = inputRef.current?.value.length ?? 0;
264
+ badgeRef.current.textContent = String(maxLength - currentLength);
265
+ };
266
+
267
+ const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
268
+ updateCharCountBadge();
269
+ props.onInput?.(e);
270
+ };
271
+
272
+ return (
273
+ <Flex direction="column" css={{ width: "100%", position: "relative" }}>
274
+ <Flex gap={"0"} css={{ width: "100%" }}>
275
+ {!!addon && (
276
+ <InputAddon
277
+ color={hasError ? "error" : color}
278
+ typography="labelSmall"
279
+ >
280
+ {addon}
281
+ </InputAddon>
282
+ )}
283
+
284
+ <TextFieldStyled
285
+ color={hasError ? "error" : color || "default"}
286
+ isValid={isValid}
287
+ name={name}
288
+ typography={typography}
289
+ fontWeight={fontWeight}
290
+ textAlign={textAlign}
291
+ maxLength={maxLength}
292
+ ref={(r) => {
293
+ if (!r) return;
294
+ inputRef.current = r;
295
+ if (maskRef) maskRef.current = r;
296
+
297
+ if (fowardedRef) {
298
+ if (typeof fowardedRef === "function") fowardedRef(r);
299
+ else fowardedRef.current = r;
300
+ }
301
+ }}
302
+ onInput={handleInput}
303
+ {...props}
304
+ style={
305
+ addon
306
+ ? {
307
+ borderTopLeftRadius: "0px",
308
+ borderBottomLeftRadius: "0px",
309
+ }
310
+ : undefined
311
+ }
312
+ >
313
+ {children}
314
+ {isValid && (
315
+ <TextFieldSlot
316
+ position="flex-end"
317
+ name={name}
318
+ color={
319
+ (hasError ? "error" : color) as TextFieldSlotProps["color"]
320
+ }
321
+ typography={typography}
322
+ fontWeight={fontWeight}
323
+ textAlign={textAlign}
324
+ >
325
+ <Icon name="check" />
326
+ </TextFieldSlot>
327
+ )}
328
+ </TextFieldStyled>
329
+ </Flex>
330
+ {maxLength && (
331
+ <TextFieldLimitIndicator>
332
+ <span ref={badgeRef}>{maxLength}</span>
333
+ </TextFieldLimitIndicator>
334
+ )}
335
+ </Flex>
336
+ );
337
+ }
338
+ );
339
+
340
+ export function TextFieldSlot({
341
+ children,
342
+ position = "flex-start",
343
+ onClick,
344
+ typography = "bodyXS",
345
+ fontWeight = "regular",
346
+ textAlign = "right",
347
+ ...props
348
+ }: TextFieldSlotProps) {
349
+ const sharedStyles = {
350
+ typography,
351
+ fontWeight,
352
+ textAlign,
353
+ ...props,
354
+ color: undefined,
355
+ };
356
+
357
+ return (
358
+ <TextFieldSlotStyled
359
+ {...sharedStyles}
360
+ style={{
361
+ float: position === "flex-start" ? "left" : "right",
362
+ order: position === "flex-start" ? 0 : 2,
363
+ textAlign,
364
+ zIndex: onClick ? 2 : undefined,
365
+ cursor: onClick ? "pointer" : undefined,
366
+ }}
367
+ onClick={onClick}
368
+ >
369
+ {children}
370
+ </TextFieldSlotStyled>
371
+ );
372
+ }