@udixio/ui-react 2.10.13 → 2.10.14

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 (173) hide show
  1. package/package.json +4 -1
  2. package/.eslintrc.mjs +0 -22
  3. package/.storybook/main.ts +0 -20
  4. package/.storybook/preview.ts +0 -1
  5. package/CHANGELOG.md +0 -1144
  6. package/postcss.config.mjs +0 -5
  7. package/src/index.css +0 -4
  8. package/src/index.ts +0 -1
  9. package/src/lib/components/AnchorPositioner.tsx +0 -185
  10. package/src/lib/components/Button.tsx +0 -208
  11. package/src/lib/components/Card.tsx +0 -47
  12. package/src/lib/components/Carousel.tsx +0 -437
  13. package/src/lib/components/CarouselItem.tsx +0 -61
  14. package/src/lib/components/Checkbox.tsx +0 -120
  15. package/src/lib/components/Chip.tsx +0 -341
  16. package/src/lib/components/Chips.tsx +0 -331
  17. package/src/lib/components/ContextMenu.tsx +0 -109
  18. package/src/lib/components/DatePicker.tsx +0 -432
  19. package/src/lib/components/Divider.tsx +0 -20
  20. package/src/lib/components/Fab.tsx +0 -127
  21. package/src/lib/components/FabMenu.tsx +0 -239
  22. package/src/lib/components/IconButton.tsx +0 -146
  23. package/src/lib/components/Menu.tsx +0 -88
  24. package/src/lib/components/MenuGroup.tsx +0 -34
  25. package/src/lib/components/MenuHeadline.tsx +0 -9
  26. package/src/lib/components/MenuItem.tsx +0 -215
  27. package/src/lib/components/NavigationRail.tsx +0 -186
  28. package/src/lib/components/NavigationRailItem.tsx +0 -227
  29. package/src/lib/components/ProgressIndicator.tsx +0 -214
  30. package/src/lib/components/SideSheet.tsx +0 -135
  31. package/src/lib/components/Slider.tsx +0 -374
  32. package/src/lib/components/Snackbar.tsx +0 -77
  33. package/src/lib/components/Switch.tsx +0 -107
  34. package/src/lib/components/Tab.tsx +0 -123
  35. package/src/lib/components/TabGroup.tsx +0 -66
  36. package/src/lib/components/TabGroupContext.tsx +0 -16
  37. package/src/lib/components/TabPanel.tsx +0 -27
  38. package/src/lib/components/TabPanels.tsx +0 -76
  39. package/src/lib/components/Tabs.tsx +0 -105
  40. package/src/lib/components/TextField.tsx +0 -586
  41. package/src/lib/components/Tooltip.tsx +0 -217
  42. package/src/lib/components/index.ts +0 -34
  43. package/src/lib/config/config.interface.ts +0 -9
  44. package/src/lib/config/define-config.ts +0 -16
  45. package/src/lib/config/index.ts +0 -2
  46. package/src/lib/effects/AnimateOnScroll.ts +0 -391
  47. package/src/lib/effects/State.tsx +0 -90
  48. package/src/lib/effects/SyncedFixedWrapper.tsx +0 -62
  49. package/src/lib/effects/ThemeProvider.tsx +0 -174
  50. package/src/lib/effects/block-scroll.effect.tsx +0 -313
  51. package/src/lib/effects/custom-scroll/custom-scroll.effect.tsx +0 -407
  52. package/src/lib/effects/custom-scroll/custom-scroll.interface.ts +0 -29
  53. package/src/lib/effects/custom-scroll/custom-scroll.style.ts +0 -32
  54. package/src/lib/effects/custom-scroll/index.ts +0 -3
  55. package/src/lib/effects/index.ts +0 -7
  56. package/src/lib/effects/ripple/RippleEffect.tsx +0 -116
  57. package/src/lib/effects/ripple/index.tsx +0 -1
  58. package/src/lib/effects/scrollDriven.ts +0 -239
  59. package/src/lib/effects/smooth-scroll.effect.tsx +0 -112
  60. package/src/lib/effects/theme.worker.ts +0 -97
  61. package/src/lib/hooks/index.ts +0 -10
  62. package/src/lib/hooks/useTooltipTrigger.ts +0 -270
  63. package/src/lib/icon/icon.tsx +0 -125
  64. package/src/lib/icon/index.ts +0 -1
  65. package/src/lib/index.ts +0 -8
  66. package/src/lib/interfaces/button.interface.ts +0 -65
  67. package/src/lib/interfaces/card.interface.ts +0 -11
  68. package/src/lib/interfaces/carousel-item.interface.ts +0 -12
  69. package/src/lib/interfaces/carousel.interface.ts +0 -41
  70. package/src/lib/interfaces/checkbox.interface.ts +0 -39
  71. package/src/lib/interfaces/chip.interface.ts +0 -97
  72. package/src/lib/interfaces/chips.interface.ts +0 -37
  73. package/src/lib/interfaces/date-picker.interface.ts +0 -79
  74. package/src/lib/interfaces/divider.interface.ts +0 -7
  75. package/src/lib/interfaces/fab-menu.interface.ts +0 -12
  76. package/src/lib/interfaces/fab.interface.ts +0 -27
  77. package/src/lib/interfaces/icon-button.interface.ts +0 -38
  78. package/src/lib/interfaces/index.ts +0 -26
  79. package/src/lib/interfaces/menu-group.interface.ts +0 -13
  80. package/src/lib/interfaces/menu-item.interface.ts +0 -29
  81. package/src/lib/interfaces/menu.interface.ts +0 -19
  82. package/src/lib/interfaces/navigation-rail-item.interface.ts +0 -39
  83. package/src/lib/interfaces/navigation-rail.interface.ts +0 -39
  84. package/src/lib/interfaces/progress-indicator.interface.ts +0 -41
  85. package/src/lib/interfaces/side-sheet.interface.tsx +0 -28
  86. package/src/lib/interfaces/slider.interface.ts +0 -27
  87. package/src/lib/interfaces/snackbar.interface.ts +0 -13
  88. package/src/lib/interfaces/switch.interface.ts +0 -14
  89. package/src/lib/interfaces/tab-group.interface.ts +0 -13
  90. package/src/lib/interfaces/tab-panels.interface.ts +0 -21
  91. package/src/lib/interfaces/tab.interface.ts +0 -31
  92. package/src/lib/interfaces/tabs.interface.ts +0 -22
  93. package/src/lib/interfaces/text-field.interface.ts +0 -61
  94. package/src/lib/interfaces/tooltip.interface.ts +0 -61
  95. package/src/lib/styles/button.style.ts +0 -136
  96. package/src/lib/styles/card.style.ts +0 -29
  97. package/src/lib/styles/carousel-item.style.ts +0 -24
  98. package/src/lib/styles/carousel.style.ts +0 -22
  99. package/src/lib/styles/checkbox.style.ts +0 -64
  100. package/src/lib/styles/chip.style.ts +0 -62
  101. package/src/lib/styles/chips.style.ts +0 -20
  102. package/src/lib/styles/date-picker.style.ts +0 -43
  103. package/src/lib/styles/divider.style.ts +0 -31
  104. package/src/lib/styles/fab-menu.style.ts +0 -29
  105. package/src/lib/styles/fab.style.ts +0 -49
  106. package/src/lib/styles/icon-button.style.ts +0 -168
  107. package/src/lib/styles/index.ts +0 -25
  108. package/src/lib/styles/menu-group.style.ts +0 -34
  109. package/src/lib/styles/menu-headline.style.ts +0 -20
  110. package/src/lib/styles/menu-item.style.ts +0 -45
  111. package/src/lib/styles/menu.style.ts +0 -32
  112. package/src/lib/styles/navigation-rail-item.style.ts +0 -56
  113. package/src/lib/styles/navigation-rail.style.ts +0 -36
  114. package/src/lib/styles/progress-indicator.style.ts +0 -72
  115. package/src/lib/styles/side-sheet.style.ts +0 -45
  116. package/src/lib/styles/slider.style.ts +0 -41
  117. package/src/lib/styles/snackbar.style.ts +0 -26
  118. package/src/lib/styles/switch.style.ts +0 -67
  119. package/src/lib/styles/tab-panels.style.ts +0 -35
  120. package/src/lib/styles/tab.style.ts +0 -78
  121. package/src/lib/styles/tabs.style.ts +0 -22
  122. package/src/lib/styles/text-field.style.ts +0 -115
  123. package/src/lib/styles/tooltip.style.ts +0 -48
  124. package/src/lib/utils/component-helper.ts +0 -134
  125. package/src/lib/utils/component.ts +0 -34
  126. package/src/lib/utils/index.ts +0 -7
  127. package/src/lib/utils/string.ts +0 -9
  128. package/src/lib/utils/styles/classnames.ts +0 -49
  129. package/src/lib/utils/styles/get-classname.ts +0 -96
  130. package/src/lib/utils/styles/index.ts +0 -4
  131. package/src/lib/utils/styles/use-classnames.ts +0 -25
  132. package/src/stories/action/button.stories.tsx +0 -86
  133. package/src/stories/action/fab.stories.tsx +0 -54
  134. package/src/stories/action/icon-button.stories.tsx +0 -134
  135. package/src/stories/assets/accessibility.png +0 -0
  136. package/src/stories/assets/accessibility.svg +0 -5
  137. package/src/stories/assets/addon-library.png +0 -0
  138. package/src/stories/assets/assets.png +0 -0
  139. package/src/stories/assets/context.png +0 -0
  140. package/src/stories/assets/discord.svg +0 -15
  141. package/src/stories/assets/docs.png +0 -0
  142. package/src/stories/assets/figma-plugin.png +0 -0
  143. package/src/stories/assets/github.svg +0 -3
  144. package/src/stories/assets/share.png +0 -0
  145. package/src/stories/assets/styling.png +0 -0
  146. package/src/stories/assets/testing.png +0 -0
  147. package/src/stories/assets/theming.png +0 -0
  148. package/src/stories/assets/tutorials.svg +0 -12
  149. package/src/stories/assets/youtube.svg +0 -4
  150. package/src/stories/communication/ProgressIndicator.stories.tsx +0 -57
  151. package/src/stories/communication/SnackBar.stories.tsx +0 -32
  152. package/src/stories/communication/tool-tip.stories.tsx +0 -133
  153. package/src/stories/containment/card.stories.tsx +0 -42
  154. package/src/stories/containment/carousel.stories.tsx +0 -65
  155. package/src/stories/containment/divider.stories.tsx +0 -35
  156. package/src/stories/containment/slide-sheet.stories.tsx +0 -45
  157. package/src/stories/effect/smooth-scroll.stories.tsx +0 -54
  158. package/src/stories/navigation/navigation-rail/navigation-rail-item.stories.tsx +0 -65
  159. package/src/stories/navigation/navigation-rail/navigation-rail.stories.tsx +0 -122
  160. package/src/stories/navigation/tabs/tab.stories.tsx +0 -57
  161. package/src/stories/navigation/tabs/tabs.stories.tsx +0 -102
  162. package/src/stories/selection/slider.stories.tsx +0 -85
  163. package/src/stories/selection/switch.stories.tsx +0 -46
  164. package/src/stories/text-inputs/text-field.stories.tsx +0 -135
  165. package/src/tests/Button.spec.tsx +0 -67
  166. package/src/tests/useClassNames.spec.tsx +0 -82
  167. package/src/udixio.css +0 -120
  168. package/theme.config.ts +0 -7
  169. package/tsconfig.json +0 -16
  170. package/tsconfig.lib.json +0 -51
  171. package/tsconfig.spec.json +0 -37
  172. package/tsconfig.storybook.json +0 -38
  173. package/vite.config.ts +0 -96
@@ -1,586 +0,0 @@
1
- import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
2
- import { Icon } from '../icon';
3
- import {
4
- faCalendarDays,
5
- faChevronDown,
6
- faChevronUp,
7
- faCircleExclamation,
8
- } from '@fortawesome/free-solid-svg-icons';
9
- import { motion } from 'motion/react';
10
- import { DatePicker } from './DatePicker';
11
- import { Button } from './Button';
12
- import { Menu } from './Menu';
13
- import { MenuItem } from './MenuItem';
14
- import { Divider } from './Divider';
15
- import { MenuHeadline } from './MenuHeadline';
16
-
17
- import TextareaAutosize from 'react-textarea-autosize';
18
- import { useTextFieldStyle } from '../styles/text-field.style';
19
- import { classNames } from '../utils';
20
- import { ReactProps } from '../utils/component';
21
- import { AnchorPositioner } from './AnchorPositioner';
22
- import { TextFieldInterface } from '../interfaces';
23
-
24
- /**
25
- * Text fields let users enter text into a UI
26
- * @status beta
27
- * @category Input
28
- * @devx
29
- * - Supports controlled (`value`) and uncontrolled (`defaultValue`) usage.
30
- * - `multiline` switches to textarea mode.
31
- * - `type="select" ` switches to select mode with `options`
32
- * @a11y
33
- * - `aria-describedby` links supporting text/error to input.
34
- */
35
- export const TextField = ({
36
- variant = 'filled',
37
- autoFocus,
38
- disabled = false,
39
- errorText,
40
- placeholder,
41
- suffix,
42
- name,
43
- label,
44
- className,
45
- supportingText,
46
- trailingIcon,
47
- leadingIcon,
48
- type = 'text',
49
- multiline = false,
50
- autoComplete = 'on',
51
- onChange,
52
- value: valueProp,
53
- defaultValue,
54
- showSupportingText,
55
- id: idProp,
56
- style,
57
- ref,
58
- onFocus,
59
- onBlur,
60
- options,
61
- children,
62
- ...restProps
63
- }: ReactProps<TextFieldInterface> & { children?: React.ReactNode }) => {
64
- const generatedId = useId();
65
- const id = idProp || generatedId;
66
- const helperTextId = `${id}-helper`;
67
-
68
- const isControlled = valueProp !== undefined;
69
- const [internalValue, setInternalValue] = useState(defaultValue ?? '');
70
- const value = isControlled ? valueProp : internalValue;
71
-
72
- const [isFocused, setIsFocused] = useState(false);
73
- const [showErrorIcon, setShowErrorIcon] = useState(!!errorText?.length);
74
-
75
- const internalRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
76
- const inputRef = (ref as any) || internalRef;
77
-
78
- const textFieldRef = useRef<HTMLDivElement>(null);
79
- const calendarTriggerRef = useRef<HTMLDivElement>(null);
80
- const datePickerRef = useRef<HTMLDivElement>(null);
81
-
82
- const hasSupportingText =
83
- showSupportingText ?? (!!errorText?.length || !!supportingText?.length);
84
-
85
- useEffect(() => {
86
- setShowErrorIcon(!!errorText?.length);
87
- }, [errorText]);
88
-
89
- const focusInput = () => {
90
- if (inputRef.current && !isFocused && !disabled) {
91
- if (type !== 'select') {
92
- inputRef.current.focus();
93
- }
94
- }
95
- };
96
-
97
- useEffect(() => {
98
- if (!autoFocus || disabled) return;
99
-
100
- if (type !== 'select') {
101
- const rafId = window.requestAnimationFrame(() => {
102
- focusInput();
103
- });
104
- return () => window.cancelAnimationFrame(rafId);
105
- }
106
- }, [autoFocus, disabled, inputRef, type]);
107
-
108
- useEffect(() => {
109
- if (isFocused) {
110
- setShowErrorIcon(false);
111
- onFocus?.();
112
- } else {
113
- if (errorText?.length) {
114
- setShowErrorIcon(true);
115
- }
116
- onBlur?.();
117
- }
118
- }, [isFocused]);
119
-
120
- const handleChange = (
121
- event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
122
- ) => {
123
- const newValue = event.target.value;
124
-
125
- if (!isControlled) {
126
- setInternalValue(newValue);
127
- }
128
-
129
- setShowErrorIcon(false);
130
-
131
- if (onChange) {
132
- onChange(event);
133
- }
134
- };
135
-
136
- // Date Picker Logic
137
- const isDateInput = type === 'date';
138
- const [showDatePicker, setShowDatePicker] = useState(false);
139
- const [tempDate, setTempDate] = useState<Date | null>(null);
140
-
141
- const initialDateValue = useMemo(() => {
142
- const val = String(value);
143
- if (!val) return null;
144
- const [y, m, d] = val.split('-').map(Number);
145
- if (y && m && d) return new Date(y, m - 1, d);
146
- return null;
147
- }, [value]);
148
-
149
- const handleDatePickerToggle = () => {
150
- if (disabled) return;
151
- if (showDatePicker) {
152
- setShowDatePicker(false);
153
- } else {
154
- setTempDate(initialDateValue);
155
- setShowDatePicker(true);
156
- }
157
- };
158
-
159
- useEffect(() => {
160
- if (showDatePicker) {
161
- setIsFocused(true);
162
- } else if (!isSelectInput || !showMenu) {
163
- setIsFocused(false);
164
- }
165
- }, [showDatePicker]);
166
-
167
- useEffect(() => {
168
- if (!showDatePicker) return;
169
-
170
- const isInside = (target: Node | null) => {
171
- if (!target) return false;
172
- return (
173
- textFieldRef.current?.contains(target) ||
174
- datePickerRef.current?.contains(target)
175
- );
176
- };
177
-
178
- const handlePointerDown = (e: PointerEvent) => {
179
- const target = e.target as Node;
180
- const inDatePicker = datePickerRef.current?.contains(target);
181
- const inCalendarTrigger = calendarTriggerRef.current?.contains(target);
182
- if (!inDatePicker && !inCalendarTrigger) {
183
- setShowDatePicker(false);
184
- }
185
- };
186
-
187
- const handleFocusIn = (e: FocusEvent) => {
188
- if (!isInside(e.target as Node)) {
189
- setShowDatePicker(false);
190
- }
191
- };
192
-
193
- const handleKeyDown = (e: KeyboardEvent) => {
194
- if (e.key === 'Escape') {
195
- setShowDatePicker(false);
196
- }
197
- };
198
-
199
- document.addEventListener('pointerdown', handlePointerDown);
200
- document.addEventListener('focusin', handleFocusIn);
201
- document.addEventListener('keydown', handleKeyDown);
202
-
203
- return () => {
204
- document.removeEventListener('pointerdown', handlePointerDown);
205
- document.removeEventListener('focusin', handleFocusIn);
206
- document.removeEventListener('keydown', handleKeyDown);
207
- };
208
- }, [showDatePicker]);
209
-
210
- const handleDateConfirm = () => {
211
- const newValue = tempDate ? tempDate.toLocaleDateString('en-CA') : '';
212
-
213
- if (!isControlled) {
214
- setInternalValue(newValue);
215
- }
216
-
217
- if (onChange) {
218
- const event = {
219
- target: {
220
- value: newValue,
221
- name,
222
- type,
223
- },
224
- } as React.ChangeEvent<HTMLInputElement>;
225
- onChange(event);
226
- }
227
- setShowDatePicker(false);
228
- };
229
-
230
- // Select Logic
231
- const isSelectInput = type === 'select';
232
- const [showMenu, setShowMenu] = useState(false);
233
- const menuRef = useRef<HTMLDivElement>(null);
234
-
235
- const displayValue = useMemo(() => {
236
- if (isSelectInput && options) {
237
- const selectedOption = options.find(
238
- (o) => String(o.value) === String(value),
239
- );
240
- return selectedOption ? selectedOption.label : value;
241
- }
242
- return value;
243
- }, [value, isSelectInput, options]);
244
-
245
- const handleSelectToggle = () => {
246
- if (disabled) return;
247
- setShowMenu(!showMenu);
248
- setIsFocused(!showMenu);
249
- };
250
-
251
- const handleSelectOption = (optionValue: string | number) => {
252
- if (!isControlled) {
253
- setInternalValue(String(optionValue));
254
- }
255
-
256
- if (onChange) {
257
- const event = {
258
- target: {
259
- value: String(optionValue),
260
- name,
261
- type,
262
- },
263
- } as React.ChangeEvent<HTMLInputElement>;
264
- onChange(event);
265
- }
266
- setShowMenu(false);
267
- setIsFocused(false);
268
- };
269
-
270
- // Close menu on outside click
271
- useEffect(() => {
272
- if (!showMenu) return;
273
- const handleClickOutside = (event: MouseEvent) => {
274
- if (
275
- textFieldRef.current &&
276
- !textFieldRef.current.contains(event.target as Node) &&
277
- menuRef.current &&
278
- !menuRef.current.contains(event.target as Node)
279
- ) {
280
- setShowMenu(false);
281
- setIsFocused(false);
282
- }
283
- };
284
- document.addEventListener('mousedown', handleClickOutside);
285
- return () => {
286
- document.removeEventListener('mousedown', handleClickOutside);
287
- };
288
- }, [showMenu]);
289
-
290
- const effectiveTrailingIcon = useMemo(() => {
291
- if (trailingIcon) return trailingIcon;
292
- if (isDateInput) return faCalendarDays;
293
- if (isSelectInput) return showMenu ? faChevronUp : faChevronDown;
294
- return undefined;
295
- }, [trailingIcon, isDateInput, isSelectInput, showMenu]);
296
-
297
- // Enhance styles for date input or select
298
- const inputSpecialClass =
299
- isDateInput || isSelectInput
300
- ? '[&::-webkit-calendar-picker-indicator]:hidden cursor-pointer selection:bg-transparent'
301
- : '';
302
-
303
- const styles = useTextFieldStyle({
304
- showSupportingText: hasSupportingText,
305
- isFocused,
306
- showErrorIcon,
307
- disabled,
308
- name,
309
- label,
310
- autoComplete,
311
- className,
312
- onChange,
313
- placeholder,
314
- supportingText,
315
- type,
316
- leadingIcon,
317
- trailingIcon: effectiveTrailingIcon,
318
- variant,
319
- errorText,
320
- value: String(displayValue),
321
- suffix,
322
- multiline,
323
- });
324
-
325
- const TextComponent = multiline ? TextareaAutosize : 'input';
326
- // For select, we want the input to be readOnly but still focusable?
327
- // Actually, for better UX, standard select inputs are often readOnly text fields.
328
- const textComponentProps = multiline
329
- ? {}
330
- : {
331
- type: isSelectInput ? 'text' : type,
332
- readOnly: isSelectInput,
333
- };
334
-
335
- const isFloating =
336
- isFocused ||
337
- (typeof value === 'string' && value.length > 0) ||
338
- type == 'date' ||
339
- (isSelectInput && showMenu);
340
-
341
- const showLegend = isFloating && variant === 'outlined';
342
- const showLabel = !showLegend;
343
-
344
- return (
345
- <div ref={textFieldRef} className={styles.textField} style={style}>
346
- <fieldset
347
- onClick={() => {
348
- if (isSelectInput) handleSelectToggle();
349
- else focusInput();
350
- }}
351
- className={styles.content}
352
- role="presentation"
353
- >
354
- <div className={styles.stateLayer}></div>
355
- {leadingIcon && (
356
- <div className={styles.leadingIcon}>
357
- {React.isValidElement(leadingIcon) ? (
358
- leadingIcon
359
- ) : (
360
- <Icon className={'w-5 h-5'} icon={leadingIcon}></Icon>
361
- )}
362
- </div>
363
- )}
364
-
365
- <motion.legend
366
- aria-hidden="true"
367
- variants={{
368
- hidden: { width: 0, padding: 0 },
369
- visible: { width: 'auto', padding: '0 8px' },
370
- }}
371
- initial={showLegend ? 'visible' : 'hidden'}
372
- animate={showLegend ? 'visible' : 'hidden'}
373
- className={
374
- 'max-w-full ml-2 px-2 text-body-small h-0 overflow-hidden whitespace-nowrap'
375
- }
376
- transition={{ duration: 0.2 }}
377
- >
378
- <span className={'transform inline-flex -translate-y-1/2 opacity-0'}>
379
- {label}
380
- </span>
381
- </motion.legend>
382
-
383
- <div className={'flex-1 relative'}>
384
- {showLabel && (
385
- <motion.label
386
- htmlFor={id}
387
- className={classNames(
388
- 'absolute left-4 transition-all duration-300 pointer-events-none',
389
- {
390
- 'text-body-small top-2': variant == 'filled' && isFloating,
391
- 'text-body-large top-1/2 transform -translate-y-1/2': !(
392
- variant == 'filled' && isFloating
393
- ),
394
- },
395
- )}
396
- transition={{ duration: 0.3 }}
397
- layoutId={variant === 'outlined' ? `${id}-label` : undefined}
398
- >
399
- <span className={styles.label}>{label}</span>
400
- </motion.label>
401
- )}
402
-
403
- {showLegend && (
404
- <motion.label
405
- htmlFor={id}
406
- className={classNames(
407
- 'absolute left-2 -top-3 px-1 text-body-small z-10',
408
- styles.label,
409
- )}
410
- layoutId={`${id}-label`}
411
- transition={{ duration: 0.3 }}
412
- >
413
- {label}
414
- </motion.label>
415
- )}
416
-
417
- <TextComponent
418
- {...(restProps as any)}
419
- ref={inputRef as any}
420
- value={displayValue} // Use displayValue for select
421
- onChange={handleChange}
422
- className={classNames(styles.input, inputSpecialClass)}
423
- id={id}
424
- name={name}
425
- placeholder={isFocused ? (placeholder ?? undefined) : ''}
426
- onFocus={() => {
427
- if (!isSelectInput) setIsFocused(true);
428
- }}
429
- onBlur={() => {
430
- // For select, we manage focus manually with menu state usually
431
- if (!isSelectInput) setIsFocused(false);
432
- }}
433
- disabled={disabled}
434
- autoComplete={autoComplete}
435
- aria-invalid={!!errorText?.length}
436
- aria-describedby={hasSupportingText ? helperTextId : undefined}
437
- {...textComponentProps}
438
- />
439
- </div>
440
-
441
- <div className={styles.activeIndicator}></div>
442
-
443
- {!showErrorIcon && (
444
- <>
445
- {effectiveTrailingIcon && (
446
- <div
447
- ref={isDateInput ? calendarTriggerRef : undefined}
448
- onClick={(event) => {
449
- event.stopPropagation();
450
- if (isDateInput) handleDatePickerToggle();
451
- if (isSelectInput) handleSelectToggle();
452
- }}
453
- className={classNames(
454
- styles.trailingIcon,
455
- (isDateInput || isSelectInput) && 'cursor-pointer',
456
- )}
457
- >
458
- <div className="flex items-center justify-center w-full h-full">
459
- {React.isValidElement(effectiveTrailingIcon) ? (
460
- effectiveTrailingIcon
461
- ) : (
462
- <Icon
463
- className={'h-5'}
464
- icon={effectiveTrailingIcon as any}
465
- />
466
- )}
467
- </div>
468
- </div>
469
- )}
470
- {!effectiveTrailingIcon && suffix && (
471
- <span className={styles.suffix}>{suffix}</span>
472
- )}
473
- </>
474
- )}
475
-
476
- {showErrorIcon && (
477
- <div
478
- className={classNames(styles.trailingIcon, {
479
- ' absolute right-0': !effectiveTrailingIcon,
480
- })}
481
- >
482
- <Icon
483
- className={'h-5 text-error'}
484
- icon={faCircleExclamation}
485
- ></Icon>
486
- </div>
487
- )}
488
- </fieldset>
489
-
490
- {hasSupportingText && (
491
- <p className={styles.supportingText} id={helperTextId}>
492
- {errorText?.length
493
- ? errorText
494
- : supportingText?.length
495
- ? supportingText
496
- : '\u00A0'}
497
- </p>
498
- )}
499
-
500
- {isDateInput && showDatePicker && (
501
- <>
502
- <AnchorPositioner anchorRef={textFieldRef} position="bottom">
503
- <div
504
- ref={datePickerRef}
505
- className="z-50 shadow-xl rounded-[28px] bg-surface-container-high overflow-hidden"
506
- >
507
- <DatePicker
508
- className={''}
509
- value={tempDate}
510
- onChange={setTempDate}
511
- />
512
- <div className="flex justify-end gap-2 p-4 pt-0">
513
- <Button
514
- variant="text"
515
- size="small"
516
- onClick={() => setShowDatePicker(false)}
517
- >
518
- Cancel
519
- </Button>
520
- <Button
521
- variant="filled"
522
- size="small"
523
- onClick={handleDateConfirm}
524
- >
525
- OK
526
- </Button>
527
- </div>
528
- </div>
529
- </AnchorPositioner>
530
- </>
531
- )}
532
-
533
- {isSelectInput && showMenu && (
534
- <AnchorPositioner
535
- anchorRef={textFieldRef}
536
- position="bottom"
537
- style={{ width: textFieldRef.current?.offsetWidth }}
538
- >
539
- <div ref={menuRef}>
540
- <Menu className={'max-w-full'} selected={value}>
541
- {children
542
- ? React.Children.map(children, (child) => {
543
- if (
544
- React.isValidElement(child) &&
545
- child.type === MenuItem
546
- ) {
547
- return React.cloneElement(child, {
548
- onClick: (e: React.MouseEvent) => {
549
- if (child.props.onClick) {
550
- child.props.onClick(e);
551
- }
552
- handleSelectOption(child.props.value ?? '');
553
- },
554
- } as any);
555
- }
556
- return child;
557
- })
558
- : options?.map((opt, i) => {
559
- if (opt.type === 'divider') {
560
- return <Divider key={i} className="my-1" />;
561
- }
562
- if (opt.type === 'headline') {
563
- return <MenuHeadline key={i} label={opt.label} />;
564
- }
565
- return (
566
- <MenuItem
567
- key={opt.value ?? i}
568
- onClick={(e) => {
569
- if (opt.onClick) {
570
- opt.onClick(e);
571
- }
572
- handleSelectOption(opt.value ?? '');
573
- }}
574
- {...opt}
575
- >
576
- {opt.label}
577
- </MenuItem>
578
- );
579
- })}
580
- </Menu>
581
- </div>
582
- </AnchorPositioner>
583
- )}
584
- </div>
585
- );
586
- };