@udixio/ui-react 2.9.13 → 2.9.15

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 (100) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/index.cjs +3 -3
  3. package/dist/index.js +4348 -3781
  4. package/dist/lib/components/AnchorPositioner.d.ts +11 -0
  5. package/dist/lib/components/AnchorPositioner.d.ts.map +1 -0
  6. package/dist/lib/components/Button.d.ts.map +1 -1
  7. package/dist/lib/components/Card.d.ts +2 -2
  8. package/dist/lib/components/Card.d.ts.map +1 -1
  9. package/dist/lib/components/Checkbox.d.ts +15 -0
  10. package/dist/lib/components/Checkbox.d.ts.map +1 -0
  11. package/dist/lib/components/DatePicker.d.ts +9 -0
  12. package/dist/lib/components/DatePicker.d.ts.map +1 -0
  13. package/dist/lib/components/FabMenu.d.ts.map +1 -1
  14. package/dist/lib/components/IconButton.d.ts.map +1 -1
  15. package/dist/lib/components/TabGroup.d.ts +1 -0
  16. package/dist/lib/components/TabGroup.d.ts.map +1 -1
  17. package/dist/lib/components/TabGroupContext.d.ts +1 -0
  18. package/dist/lib/components/TabGroupContext.d.ts.map +1 -1
  19. package/dist/lib/components/TabPanel.d.ts +1 -0
  20. package/dist/lib/components/TabPanel.d.ts.map +1 -1
  21. package/dist/lib/components/TabPanels.d.ts +1 -0
  22. package/dist/lib/components/TabPanels.d.ts.map +1 -1
  23. package/dist/lib/components/TextField.d.ts +4 -5
  24. package/dist/lib/components/TextField.d.ts.map +1 -1
  25. package/dist/lib/components/Tooltip.d.ts +1 -1
  26. package/dist/lib/components/Tooltip.d.ts.map +1 -1
  27. package/dist/lib/components/index.d.ts +3 -0
  28. package/dist/lib/components/index.d.ts.map +1 -1
  29. package/dist/lib/effects/State.d.ts +3 -1
  30. package/dist/lib/effects/State.d.ts.map +1 -1
  31. package/dist/lib/effects/smooth-scroll.effect.d.ts +14 -0
  32. package/dist/lib/effects/smooth-scroll.effect.d.ts.map +1 -1
  33. package/dist/lib/hooks/index.d.ts +0 -1
  34. package/dist/lib/hooks/index.d.ts.map +1 -1
  35. package/dist/lib/interfaces/card.interface.d.ts +1 -1
  36. package/dist/lib/interfaces/card.interface.d.ts.map +1 -1
  37. package/dist/lib/interfaces/checkbox.interface.d.ts +38 -0
  38. package/dist/lib/interfaces/checkbox.interface.d.ts.map +1 -0
  39. package/dist/lib/interfaces/date-picker.interface.d.ts +67 -0
  40. package/dist/lib/interfaces/date-picker.interface.d.ts.map +1 -0
  41. package/dist/lib/interfaces/icon-button.interface.d.ts +2 -1
  42. package/dist/lib/interfaces/icon-button.interface.d.ts.map +1 -1
  43. package/dist/lib/interfaces/index.d.ts +1 -0
  44. package/dist/lib/interfaces/index.d.ts.map +1 -1
  45. package/dist/lib/interfaces/text-field.interface.d.ts +8 -5
  46. package/dist/lib/interfaces/text-field.interface.d.ts.map +1 -1
  47. package/dist/lib/interfaces/tooltip.interface.d.ts +2 -0
  48. package/dist/lib/interfaces/tooltip.interface.d.ts.map +1 -1
  49. package/dist/lib/styles/card.style.d.ts +5 -5
  50. package/dist/lib/styles/checkbox.style.d.ts +45 -0
  51. package/dist/lib/styles/checkbox.style.d.ts.map +1 -0
  52. package/dist/lib/styles/date-picker.style.d.ts +45 -0
  53. package/dist/lib/styles/date-picker.style.d.ts.map +1 -0
  54. package/dist/lib/styles/fab.style.d.ts +2 -2
  55. package/dist/lib/styles/icon-button.style.d.ts +10 -4
  56. package/dist/lib/styles/icon-button.style.d.ts.map +1 -1
  57. package/dist/lib/styles/index.d.ts +1 -0
  58. package/dist/lib/styles/index.d.ts.map +1 -1
  59. package/dist/lib/styles/navigation-rail-item.style.d.ts +2 -2
  60. package/dist/lib/styles/side-sheet.style.d.ts +2 -2
  61. package/dist/lib/styles/slider.style.d.ts +2 -2
  62. package/dist/lib/styles/tab.style.d.ts +2 -2
  63. package/dist/lib/styles/text-field.style.d.ts +22 -13
  64. package/dist/lib/styles/text-field.style.d.ts.map +1 -1
  65. package/dist/lib/styles/tooltip.style.d.ts +8 -4
  66. package/dist/lib/styles/tooltip.style.d.ts.map +1 -1
  67. package/package.json +3 -3
  68. package/src/lib/components/AnchorPositioner.tsx +132 -0
  69. package/src/lib/components/Button.tsx +1 -0
  70. package/src/lib/components/Card.tsx +9 -4
  71. package/src/lib/components/Checkbox.tsx +120 -0
  72. package/src/lib/components/DatePicker.tsx +432 -0
  73. package/src/lib/components/FabMenu.tsx +4 -5
  74. package/src/lib/components/IconButton.tsx +9 -7
  75. package/src/lib/components/TabGroup.tsx +8 -6
  76. package/src/lib/components/TabGroupContext.tsx +1 -1
  77. package/src/lib/components/TabPanel.tsx +1 -0
  78. package/src/lib/components/TabPanels.tsx +1 -0
  79. package/src/lib/components/TextField.tsx +222 -123
  80. package/src/lib/components/Tooltip.tsx +13 -13
  81. package/src/lib/components/index.ts +3 -0
  82. package/src/lib/effects/State.tsx +4 -1
  83. package/src/lib/effects/smooth-scroll.effect.tsx +15 -1
  84. package/src/lib/hooks/index.ts +0 -1
  85. package/src/lib/interfaces/card.interface.ts +1 -1
  86. package/src/lib/interfaces/checkbox.interface.ts +39 -0
  87. package/src/lib/interfaces/date-picker.interface.ts +79 -0
  88. package/src/lib/interfaces/icon-button.interface.ts +2 -1
  89. package/src/lib/interfaces/index.ts +1 -0
  90. package/src/lib/interfaces/text-field.interface.ts +8 -5
  91. package/src/lib/interfaces/tooltip.interface.ts +2 -0
  92. package/src/lib/styles/checkbox.style.ts +64 -0
  93. package/src/lib/styles/date-picker.style.ts +43 -0
  94. package/src/lib/styles/index.ts +1 -0
  95. package/src/lib/styles/side-sheet.style.ts +2 -2
  96. package/src/lib/styles/text-field.style.ts +2 -2
  97. package/src/stories/containment/card.stories.tsx +1 -1
  98. package/dist/lib/hooks/useTooltipPosition.d.ts +0 -22
  99. package/dist/lib/hooks/useTooltipPosition.d.ts.map +0 -1
  100. package/src/lib/hooks/useTooltipPosition.ts +0 -95
@@ -1,24 +1,28 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
2
2
  import { Icon } from '../icon';
3
- import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons';
3
+ import {
4
+ faCalendarDays,
5
+ faCircleExclamation,
6
+ } from '@fortawesome/free-solid-svg-icons';
4
7
  import { motion } from 'motion/react';
5
- import { v4 as uuidv4 } from 'uuid';
8
+ import { DatePicker } from './DatePicker';
9
+ import { Button } from './Button';
6
10
 
7
11
  import TextareaAutosize from 'react-textarea-autosize';
8
12
  import { useTextFieldStyle } from '../styles/text-field.style';
9
13
  import { classNames } from '../utils';
10
14
  import { ReactProps } from '../utils/component';
11
- import { TextFieldInterface } from '../interfaces/text-field.interface';
15
+ import { AnchorPositioner } from './AnchorPositioner';
12
16
 
13
17
  /**
14
18
  * Text fields let users enter text into a UI
15
19
  * @status beta
16
20
  * @category Input
17
21
  * @devx
18
- * - `onChange` receives the string value (not the DOM event).
19
- * - `value` syncs internal state; not a fully controlled input.
22
+ * - Supports controlled (`value`) and uncontrolled (`defaultValue`) usage.
23
+ * - `multiline` switches to textarea mode.
20
24
  * @a11y
21
- * - Uses `label` for `aria-label`; no `aria-describedby` for supporting text.
25
+ * - `aria-describedby` links supporting text/error to input.
22
26
  */
23
27
  export const TextField = ({
24
28
  variant = 'filled',
@@ -33,51 +37,40 @@ export const TextField = ({
33
37
  trailingIcon,
34
38
  leadingIcon,
35
39
  type = 'text',
36
- textLine = 'singleLine',
40
+ multiline = false,
37
41
  autoComplete = 'on',
38
42
  onChange,
39
- value: defaultValue,
40
- showSupportingText: defaultShowSupportingText = false,
43
+ value: valueProp,
44
+ defaultValue,
45
+ showSupportingText,
46
+ id: idProp,
47
+ style,
48
+ ref,
41
49
  ...restProps
42
50
  }: ReactProps<TextFieldInterface>) => {
43
- const [value, setValue] = useState(defaultValue ?? '');
51
+ const generatedId = useId();
52
+ const id = idProp || generatedId;
53
+ const helperTextId = `${id}-helper`;
54
+
55
+ const isControlled = valueProp !== undefined;
56
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '');
57
+ const value = isControlled ? valueProp : internalValue;
58
+
44
59
  const [isFocused, setIsFocused] = useState(false);
45
- const [showErrorIcon, setShowErrorIcon] = useState(false);
46
- const [showSupportingText, setShowSupportingText] = useState(
47
- defaultShowSupportingText,
48
- );
60
+ const [showErrorIcon, setShowErrorIcon] = useState(!!errorText?.length);
49
61
 
50
- useEffect(() => {
51
- setValue(defaultValue ?? '');
52
- }, [defaultValue]);
62
+ const internalRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
63
+ const inputRef = (ref as any) || internalRef;
53
64
 
54
- useEffect(() => {
55
- if (errorText?.length) {
56
- setShowErrorIcon(true);
57
- } else {
58
- setShowErrorIcon(false);
59
- }
60
- }, [errorText]);
65
+ const textFieldRef = useRef<HTMLDivElement>(null);
66
+ const calendarTriggerRef = useRef<HTMLDivElement>(null);
61
67
 
62
- useEffect(() => {
63
- if (defaultShowSupportingText) {
64
- setShowSupportingText(defaultShowSupportingText);
65
- } else {
66
- if (supportingText?.length) {
67
- setShowSupportingText(true);
68
- } else {
69
- setShowSupportingText(false);
70
- }
71
- }
72
- }, [showSupportingText, supportingText]);
68
+ const hasSupportingText =
69
+ showSupportingText ?? (!!errorText?.length || !!supportingText?.length);
73
70
 
74
71
  useEffect(() => {
75
- if (isFocused) {
76
- setShowErrorIcon(false);
77
- }
78
- }, [isFocused]);
79
-
80
- const inputRef = React.useRef<HTMLInputElement & HTMLTextAreaElement>(null);
72
+ setShowErrorIcon(!!errorText?.length);
73
+ }, [errorText]);
81
74
 
82
75
  const focusInput = () => {
83
76
  if (inputRef.current && !isFocused) {
@@ -87,28 +80,82 @@ export const TextField = ({
87
80
 
88
81
  const handleOnFocus = () => {
89
82
  setIsFocused(true);
83
+ setShowErrorIcon(false);
90
84
  };
91
85
 
92
86
  const handleChange = (
93
- event: React.ChangeEvent<HTMLInputElement & HTMLTextAreaElement>,
87
+ event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
94
88
  ) => {
95
89
  const newValue = event.target.value;
96
- setValue(newValue); // Update local state
90
+
91
+ if (!isControlled) {
92
+ setInternalValue(newValue);
93
+ }
97
94
 
98
95
  setShowErrorIcon(false);
99
96
 
100
- // If external onChange prop is provided, call it with the new value
101
- if (typeof onChange === 'function') {
102
- onChange(newValue);
97
+ if (onChange) {
98
+ onChange(event);
103
99
  }
104
100
  };
105
101
 
106
102
  const handleBlur = () => {
107
103
  setIsFocused(false);
104
+ if (errorText?.length) {
105
+ setShowErrorIcon(true);
106
+ }
107
+ };
108
+
109
+ // Date Picker Logic
110
+ const isDateInput = type === 'date';
111
+ const [showDatePicker, setShowDatePicker] = useState(false);
112
+ const [tempDate, setTempDate] = useState<Date | null>(null);
113
+
114
+ const initialDateValue = useMemo(() => {
115
+ const val = String(value);
116
+ if (!val) return null;
117
+ const [y, m, d] = val.split('-').map(Number);
118
+ if (y && m && d) return new Date(y, m - 1, d);
119
+ return null;
120
+ }, [value]);
121
+
122
+ const handleDatePickerOpen = () => {
123
+ if (disabled) return;
124
+ setTempDate(initialDateValue);
125
+ setShowDatePicker(true);
108
126
  };
109
127
 
128
+ const handleDateConfirm = () => {
129
+ const newValue = tempDate ? tempDate.toLocaleDateString('en-CA') : '';
130
+
131
+ if (!isControlled) {
132
+ setInternalValue(newValue);
133
+ }
134
+
135
+ if (onChange) {
136
+ // Create a synthetic event
137
+ const event = {
138
+ target: {
139
+ value: newValue,
140
+ name,
141
+ type,
142
+ },
143
+ } as React.ChangeEvent<HTMLInputElement>;
144
+ onChange(event);
145
+ }
146
+ setShowDatePicker(false);
147
+ };
148
+
149
+ const effectiveTrailingIcon =
150
+ isDateInput && !trailingIcon ? faCalendarDays : trailingIcon;
151
+
152
+ // Enhance styles for date input
153
+ const inputDateClass = isDateInput
154
+ ? '[&::-webkit-calendar-picker-indicator]:hidden cursor-pointer'
155
+ : '';
156
+
110
157
  const styles = useTextFieldStyle({
111
- showSupportingText,
158
+ showSupportingText: hasSupportingText,
112
159
  isFocused,
113
160
  showErrorIcon,
114
161
  disabled,
@@ -121,37 +168,31 @@ export const TextField = ({
121
168
  supportingText,
122
169
  type,
123
170
  leadingIcon,
124
- trailingIcon,
171
+ trailingIcon: effectiveTrailingIcon,
125
172
  variant,
126
173
  errorText,
127
- value,
174
+ value: String(value),
128
175
  suffix,
129
- textLine,
176
+ multiline,
130
177
  });
131
178
 
132
- const [uuid] = useState(uuidv4());
133
-
134
- let textComponentProps: object;
135
- let TextComponent;
136
- switch (textLine) {
137
- case 'multiLine':
138
- TextComponent = TextareaAutosize;
139
- textComponentProps = {};
140
- break;
141
- case 'textAreas':
142
- TextComponent = 'textarea';
143
- textComponentProps = {};
144
- break;
145
- case 'singleLine':
146
- default:
147
- TextComponent = 'input';
148
- textComponentProps = { type: type };
149
- break;
150
- }
179
+ const TextComponent = multiline ? TextareaAutosize : 'input';
180
+ const textComponentProps = multiline ? {} : { type };
181
+
182
+ const isFloating =
183
+ isFocused ||
184
+ (typeof value === 'string' && value.length > 0) ||
185
+ type == 'date'; // Float label when picker open
186
+ const showLegend = isFloating && variant === 'outlined';
187
+ const showLabel = !showLegend;
151
188
 
152
189
  return (
153
- <div className={styles.textField} {...restProps}>
154
- <fieldset onClick={focusInput} className={styles.content}>
190
+ <div ref={textFieldRef} className={styles.textField} style={style}>
191
+ <fieldset
192
+ onClick={focusInput}
193
+ className={styles.content}
194
+ role="presentation"
195
+ >
155
196
  <div className={styles.stateLayer}></div>
156
197
  {leadingIcon && (
157
198
  <div className={styles.leadingIcon}>
@@ -163,68 +204,81 @@ export const TextField = ({
163
204
  </div>
164
205
  )}
165
206
 
166
- {!((!isFocused && !value.length) || variant == 'filled') && (
167
- <motion.legend
168
- variants={{
169
- hidden: { width: 0, padding: 0 },
170
- visible: { width: 'auto', padding: '0 8px' },
171
- }}
172
- initial={'hidden'}
173
- animate={!(!isFocused && !value.length) ? 'visible' : 'hidden'}
174
- className={'max-w-full ml-2 px-2 text-body-small h-0'}
175
- transition={{ duration: 0.2 }}
176
- >
177
- <span className={'transform inline-flex -translate-y-1/2'}>
178
- <motion.span
179
- className={styles.label}
180
- transition={{ duration: 0.3 }}
181
- layoutId={uuid}
182
- >
183
- {label}
184
- </motion.span>
185
- </span>
186
- </motion.legend>
187
- )}
207
+ <motion.legend
208
+ aria-hidden="true"
209
+ variants={{
210
+ hidden: { width: 0, padding: 0 },
211
+ visible: { width: 'auto', padding: '0 8px' },
212
+ }}
213
+ initial={showLegend ? 'visible' : 'hidden'}
214
+ animate={showLegend ? 'visible' : 'hidden'}
215
+ className={
216
+ 'max-w-full ml-2 px-2 text-body-small h-0 overflow-hidden whitespace-nowrap'
217
+ }
218
+ transition={{ duration: 0.2 }}
219
+ >
220
+ <span className={'transform inline-flex -translate-y-1/2 opacity-0'}>
221
+ {label}
222
+ </span>
223
+ </motion.legend>
224
+
188
225
  <div className={'flex-1 relative'}>
189
- {((!isFocused && !value.length) || variant == 'filled') && (
226
+ {showLabel && (
190
227
  <motion.label
191
- htmlFor={name}
228
+ htmlFor={id}
192
229
  className={classNames(
193
- 'absolute left-4 transition-all duration-300',
230
+ 'absolute left-4 transition-all duration-300 pointer-events-none',
194
231
  {
195
- 'text-body-small top-2':
196
- variant == 'filled' && !(!isFocused && !value.length),
232
+ 'text-body-small top-2': variant == 'filled' && isFloating,
197
233
  'text-body-large top-1/2 transform -translate-y-1/2': !(
198
- variant == 'filled' && !(!isFocused && !value.length)
234
+ variant == 'filled' && isFloating
199
235
  ),
200
236
  },
201
237
  )}
202
238
  transition={{ duration: 0.3 }}
239
+ layoutId={variant === 'outlined' ? `${id}-label` : undefined}
203
240
  >
204
- <motion.span
205
- className={styles.label}
206
- transition={{ duration: 0.3 }}
207
- layoutId={variant == 'outlined' ? uuid : undefined}
208
- >
209
- {label}
210
- </motion.span>
241
+ <span className={styles.label}>{label}</span>
242
+ </motion.label>
243
+ )}
244
+
245
+ {showLegend && (
246
+ <motion.label
247
+ htmlFor={id}
248
+ className={classNames(
249
+ 'absolute left-2 -top-3 px-1 text-body-small z-10',
250
+ styles.label,
251
+ )}
252
+ layoutId={`${id}-label`}
253
+ transition={{ duration: 0.3 }}
254
+ >
255
+ {label}
211
256
  </motion.label>
212
257
  )}
258
+
213
259
  <TextComponent
214
- ref={inputRef}
260
+ ref={inputRef as any}
215
261
  value={value}
216
262
  onChange={handleChange}
217
- className={styles.input}
218
- id={name}
263
+ className={classNames(styles.input, inputDateClass)}
264
+ id={id}
219
265
  name={name}
220
266
  placeholder={isFocused ? (placeholder ?? undefined) : ''}
221
- onFocus={handleOnFocus}
267
+ onFocus={(e) => {
268
+ handleOnFocus();
269
+ if (isDateInput) {
270
+ // Maybe open picker on focus? User preference.
271
+ // Often better on click/icon click.
272
+ // But let's stick to icon click for now or explicit open.
273
+ }
274
+ }}
222
275
  onBlur={handleBlur}
223
276
  disabled={disabled}
224
277
  autoComplete={autoComplete}
225
278
  aria-invalid={!!errorText?.length}
226
- aria-label={label}
279
+ aria-describedby={hasSupportingText ? helperTextId : undefined}
227
280
  {...textComponentProps}
281
+ {...(restProps as any)}
228
282
  />
229
283
  </div>
230
284
 
@@ -232,21 +286,31 @@ export const TextField = ({
232
286
 
233
287
  {!showErrorIcon && (
234
288
  <>
235
- {trailingIcon && (
289
+ {effectiveTrailingIcon && (
236
290
  <div
291
+ ref={isDateInput ? calendarTriggerRef : undefined}
237
292
  onClick={(event) => {
238
293
  event.stopPropagation();
294
+ if (isDateInput) handleDatePickerOpen();
239
295
  }}
240
- className={styles.trailingIcon}
241
- >
242
- {React.isValidElement(trailingIcon) ? (
243
- trailingIcon
244
- ) : (
245
- <Icon className={'h-5'} icon={trailingIcon}></Icon>
296
+ className={classNames(
297
+ styles.trailingIcon,
298
+ isDateInput && 'cursor-pointer',
246
299
  )}
300
+ >
301
+ <div className="flex items-center justify-center w-full h-full">
302
+ {React.isValidElement(effectiveTrailingIcon) ? (
303
+ effectiveTrailingIcon
304
+ ) : (
305
+ <Icon
306
+ className={'h-5'}
307
+ icon={effectiveTrailingIcon as any}
308
+ />
309
+ )}
310
+ </div>
247
311
  </div>
248
312
  )}
249
- {!trailingIcon && suffix && (
313
+ {!effectiveTrailingIcon && suffix && (
250
314
  <span className={styles.suffix}>{suffix}</span>
251
315
  )}
252
316
  </>
@@ -255,7 +319,7 @@ export const TextField = ({
255
319
  {showErrorIcon && (
256
320
  <div
257
321
  className={classNames(styles.trailingIcon, {
258
- ' absolute right-0': !trailingIcon,
322
+ ' absolute right-0': !effectiveTrailingIcon,
259
323
  })}
260
324
  >
261
325
  <Icon
@@ -265,8 +329,9 @@ export const TextField = ({
265
329
  </div>
266
330
  )}
267
331
  </fieldset>
268
- {showSupportingText && (
269
- <p className={styles.supportingText}>
332
+
333
+ {hasSupportingText && (
334
+ <p className={styles.supportingText} id={helperTextId}>
270
335
  {errorText?.length
271
336
  ? errorText
272
337
  : supportingText?.length
@@ -274,6 +339,40 @@ export const TextField = ({
274
339
  : '\u00A0'}
275
340
  </p>
276
341
  )}
342
+
343
+ {isDateInput && showDatePicker && (
344
+ <>
345
+ <div
346
+ className="fixed inset-0 z-40 bg-transparent"
347
+ onClick={() => setShowDatePicker(false)}
348
+ />
349
+ <AnchorPositioner anchorRef={textFieldRef} position="bottom">
350
+ <div className="z-50 shadow-xl rounded-[28px] bg-surface-container-high overflow-hidden">
351
+ <DatePicker
352
+ className={''}
353
+ value={tempDate}
354
+ onChange={setTempDate}
355
+ />
356
+ <div className="flex justify-end gap-2 p-4 pt-0">
357
+ <Button
358
+ variant="text"
359
+ size="small"
360
+ onClick={() => setShowDatePicker(false)}
361
+ >
362
+ Cancel
363
+ </Button>
364
+ <Button
365
+ variant="filled"
366
+ size="small"
367
+ onClick={handleDateConfirm}
368
+ >
369
+ OK
370
+ </Button>
371
+ </div>
372
+ </div>
373
+ </AnchorPositioner>
374
+ </>
375
+ )}
277
376
  </div>
278
377
  );
279
378
  };
@@ -1,11 +1,11 @@
1
1
  import { cloneElement, isValidElement, useEffect, useRef } from 'react';
2
2
  import { MotionProps } from '../utils';
3
3
  import { Button } from './Button';
4
+ import { AnchorPositioner } from './AnchorPositioner';
4
5
  import { ToolTipInterface } from '../interfaces';
5
6
  import { useToolTipStyle } from '../styles';
6
7
  import { AnimatePresence, motion } from 'motion/react';
7
- import { SyncedFixedWrapper } from '../effects';
8
- import { useTooltipTrigger, useTooltipPosition } from '../hooks';
8
+ import { useTooltipTrigger } from '../hooks';
9
9
 
10
10
  /**
11
11
  * Tooltips display brief labels or messages
@@ -36,8 +36,12 @@ export const Tooltip = ({
36
36
  defaultOpen = false,
37
37
  onOpenChange,
38
38
  id,
39
+ anchorRef,
39
40
  ...props
40
41
  }: MotionProps<ToolTipInterface>) => {
42
+ const defaultPosition = variant === 'rich' ? 'bottom-right' : 'bottom';
43
+ const effectivePosition = positionProp || defaultPosition;
44
+
41
45
  transition = { duration: 0.3, ...transition };
42
46
 
43
47
  if (!children && !targetRef) {
@@ -50,6 +54,7 @@ export const Tooltip = ({
50
54
 
51
55
  const internalRef = useRef<HTMLElement | null>(null);
52
56
  const resolvedRef = targetRef || internalRef;
57
+ const positioningRef = anchorRef || resolvedRef;
53
58
 
54
59
  // Use the trigger hook for state management and accessibility
55
60
  const { triggerProps, tooltipProps, isOpen } = useTooltipTrigger({
@@ -62,14 +67,6 @@ export const Tooltip = ({
62
67
  id,
63
68
  });
64
69
 
65
- // Use the position hook for auto-positioning
66
- const { resolvedPosition } = useTooltipPosition({
67
- targetRef: resolvedRef,
68
- position: positionProp,
69
- variant,
70
- isOpen,
71
- });
72
-
73
70
  // Apply trigger props to the target element
74
71
  const enhancedChildren =
75
72
  !targetRef && isValidElement(children)
@@ -150,7 +147,7 @@ export const Tooltip = ({
150
147
  className,
151
148
  title,
152
149
  text,
153
- position: resolvedPosition,
150
+ position: effectivePosition,
154
151
  trigger,
155
152
  targetRef: targetRef as any,
156
153
  children: children as any,
@@ -172,7 +169,10 @@ export const Tooltip = ({
172
169
  {enhancedChildren}
173
170
  <AnimatePresence>
174
171
  {isOpen && (
175
- <SyncedFixedWrapper targetRef={resolvedRef}>
172
+ <AnchorPositioner
173
+ anchorRef={positioningRef}
174
+ position={effectivePosition}
175
+ >
176
176
  <motion.div
177
177
  initial={'close'}
178
178
  variants={variants}
@@ -209,7 +209,7 @@ export const Tooltip = ({
209
209
  )}
210
210
  </div>
211
211
  </motion.div>
212
- </SyncedFixedWrapper>
212
+ </AnchorPositioner>
213
213
  )}
214
214
  </AnimatePresence>
215
215
  </>
@@ -1,8 +1,10 @@
1
+ export * from './AnchorPositioner';
1
2
  export * from './Button';
2
3
  export * from './Card';
3
4
  export * from './Carousel';
4
5
  export * from './CarouselItem';
5
6
  export * from './CarouselItem';
7
+ export * from './Checkbox';
6
8
  export * from './Chip';
7
9
  export * from './Chips';
8
10
  export * from './Divider';
@@ -24,3 +26,4 @@ export * from './TextField';
24
26
  export * from './NavigationRailItem';
25
27
  export * from './NavigationRail';
26
28
  export * from './Tooltip';
29
+ export * from './DatePicker';
@@ -18,6 +18,7 @@ export interface StateInterface {
18
18
  | 'state-layer';
19
19
  className?: string;
20
20
  style?: React.CSSProperties;
21
+ children?: React.ReactNode;
21
22
  };
22
23
  states: { isClient: boolean };
23
24
  elements: ['stateLayer'];
@@ -27,6 +28,7 @@ export const State = ({
27
28
  style,
28
29
  colorName,
29
30
  stateClassName = 'state-ripple-group',
31
+ children,
30
32
  className,
31
33
  }: ReactProps<StateInterface>) => {
32
34
  const ref = useRef<HTMLDivElement>(null);
@@ -67,6 +69,7 @@ export const State = ({
67
69
  }}
68
70
  >
69
71
  {isClient && <RippleEffect triggerRef={groupStateRef} />}
72
+ {children}
70
73
  </div>
71
74
  );
72
75
  };
@@ -76,8 +79,8 @@ const cardConfig: ClassNameComponent<StateInterface> = ({
76
79
  stateClassName,
77
80
  }) => ({
78
81
  stateLayer: classNames([
79
- stateClassName,
80
82
  'w-full top-0 left-0 h-full absolute pointer-events-none overflow-hidden',
83
+ stateClassName,
81
84
  ]),
82
85
  });
83
86
 
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, ReactNode } from 'react';
1
+ import { ReactNode, useEffect, useRef } from 'react';
2
2
  import Lenis from 'lenis';
3
3
 
4
4
  export type SmoothScrollProps = {
@@ -36,6 +36,20 @@ export type SmoothScrollProps = {
36
36
  /**
37
37
  * SmoothScroll component using Lenis for smooth scrolling.
38
38
  * This component enables smooth scrolling at the document level.
39
+ *
40
+ * @warning **Use with caution.** Overriding native scroll behavior can cause:
41
+ * - **Accessibility issues**: Screen readers, keyboard navigation, and assistive technologies
42
+ * may not work correctly with custom scroll implementations.
43
+ * - **Anchor links broken**: `scrollIntoView()`, hash navigation (`#section`), and
44
+ * `window.scrollTo()` may behave unexpectedly or be ignored.
45
+ * - **Third-party library conflicts**: Libraries relying on native scroll events
46
+ * (infinite scroll, lazy loading, scroll-triggered animations) may malfunction.
47
+ * - **Browser features disabled**: Find-in-page (Ctrl+F), autoscroll, and native
48
+ * momentum scrolling on trackpads may not work as expected.
49
+ * - **Performance overhead**: The RAF loop runs continuously, which may impact
50
+ * battery life and performance on low-end devices.
51
+ * - **Mobile issues**: Touch gestures, pull-to-refresh, and overscroll behaviors
52
+ * can conflict with smooth scroll implementations.
39
53
  */
40
54
  export const SmoothScroll = ({
41
55
  transition = 1.2,
@@ -4,7 +4,6 @@ export type {
4
4
  UseTooltipTriggerReturn,
5
5
  } from './useTooltipTrigger';
6
6
 
7
- export { useTooltipPosition } from './useTooltipPosition';
8
7
  export type {
9
8
  UseTooltipPositionOptions,
10
9
  UseTooltipPositionReturn,
@@ -4,7 +4,7 @@ export interface CardInterface {
4
4
  type: 'div';
5
5
  props: {
6
6
  variant?: 'outlined' | 'elevated' | 'filled';
7
- isInteractive?: boolean;
7
+ interactive?: boolean;
8
8
  children: ReactNode;
9
9
  };
10
10
  elements: ['card', 'stateLayer'];