@luanthnh/cntt-ui 0.1.8 → 0.1.10

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 (146) hide show
  1. package/package.json +6 -1
  2. package/.storybook/globals.d.ts +0 -1
  3. package/.storybook/main.ts +0 -29
  4. package/.storybook/preview.ts +0 -32
  5. package/components/Welcome.mdx +0 -74
  6. package/components/lenis/index.tsx +0 -48
  7. package/components/motion/auto-height.tsx +0 -56
  8. package/components/motion/cursor.tsx +0 -108
  9. package/components/motion/highlight.tsx +0 -605
  10. package/components/motion/number-ticker.tsx +0 -55
  11. package/components/motion/slot.tsx +0 -106
  12. package/components/motion/waves.tsx +0 -417
  13. package/components/primitives/tabs.tsx +0 -174
  14. package/components/ui/Accordion/index.stories.tsx +0 -39
  15. package/components/ui/Accordion/index.tsx +0 -170
  16. package/components/ui/Alert/index.stories.tsx +0 -39
  17. package/components/ui/Alert/index.tsx +0 -60
  18. package/components/ui/AlertDialog/index.stories.tsx +0 -47
  19. package/components/ui/AlertDialog/index.tsx +0 -172
  20. package/components/ui/AspectRatio/index.stories.tsx +0 -40
  21. package/components/ui/AspectRatio/index.tsx +0 -9
  22. package/components/ui/Avatar/index.stories.tsx +0 -39
  23. package/components/ui/Avatar/index.tsx +0 -44
  24. package/components/ui/Badge/index.stories.tsx +0 -64
  25. package/components/ui/Badge/index.tsx +0 -46
  26. package/components/ui/Breadcrumb/index.stories.tsx +0 -64
  27. package/components/ui/Breadcrumb/index.tsx +0 -102
  28. package/components/ui/Button/index.stories.tsx +0 -232
  29. package/components/ui/Button/index.tsx +0 -114
  30. package/components/ui/Calendar/index.stories.tsx +0 -20
  31. package/components/ui/Calendar/index.tsx +0 -149
  32. package/components/ui/Card/index.stories.tsx +0 -39
  33. package/components/ui/Card/index.tsx +0 -65
  34. package/components/ui/Carousel/index.stories.tsx +0 -37
  35. package/components/ui/Carousel/index.tsx +0 -242
  36. package/components/ui/Chart/index.stories.tsx +0 -53
  37. package/components/ui/Chart/index.tsx +0 -322
  38. package/components/ui/Checkbox/index.stories.tsx +0 -56
  39. package/components/ui/Checkbox/index.tsx +0 -167
  40. package/components/ui/CircleProcess/index.stories.tsx +0 -29
  41. package/components/ui/CircleProcess/index.tsx +0 -50
  42. package/components/ui/Collapsible/index.stories.tsx +0 -33
  43. package/components/ui/Collapsible/index.tsx +0 -124
  44. package/components/ui/Command/index.stories.tsx +0 -65
  45. package/components/ui/Command/index.tsx +0 -161
  46. package/components/ui/Container/index.stories.tsx +0 -22
  47. package/components/ui/Container/index.tsx +0 -30
  48. package/components/ui/ContextMenu/index.stories.tsx +0 -51
  49. package/components/ui/ContextMenu/index.tsx +0 -224
  50. package/components/ui/Dialog/index.stories.tsx +0 -44
  51. package/components/ui/Dialog/index.tsx +0 -156
  52. package/components/ui/Drawer/index.stories.tsx +0 -54
  53. package/components/ui/Drawer/index.tsx +0 -124
  54. package/components/ui/DropdownMenu/index.stories.tsx +0 -83
  55. package/components/ui/DropdownMenu/index.tsx +0 -231
  56. package/components/ui/Dropzone/index.stories.tsx +0 -18
  57. package/components/ui/Dropzone/index.tsx +0 -47
  58. package/components/ui/Form/date-field.tsx +0 -77
  59. package/components/ui/Form/index.stories.tsx +0 -67
  60. package/components/ui/Form/index.tsx +0 -193
  61. package/components/ui/Form/select-field.tsx +0 -55
  62. package/components/ui/Form/text-area-field.tsx +0 -37
  63. package/components/ui/Form/text-field.tsx +0 -72
  64. package/components/ui/HStack/index.stories.tsx +0 -48
  65. package/components/ui/HStack/index.tsx +0 -73
  66. package/components/ui/HoverCard/index.stories.tsx +0 -38
  67. package/components/ui/HoverCard/index.tsx +0 -38
  68. package/components/ui/Icons/index.stories.tsx +0 -27
  69. package/components/ui/Icons/index.tsx +0 -33
  70. package/components/ui/ImageWithFallback/index.stories.tsx +0 -32
  71. package/components/ui/ImageWithFallback/index.tsx +0 -34
  72. package/components/ui/Input/index.stories.tsx +0 -47
  73. package/components/ui/Input/index.tsx +0 -21
  74. package/components/ui/InputOtp/index.stories.tsx +0 -35
  75. package/components/ui/InputOtp/index.tsx +0 -70
  76. package/components/ui/Label/index.stories.tsx +0 -18
  77. package/components/ui/Label/index.tsx +0 -21
  78. package/components/ui/Marquee/index.stories.tsx +0 -71
  79. package/components/ui/Marquee/index.tsx +0 -65
  80. package/components/ui/Menubar/index.stories.tsx +0 -116
  81. package/components/ui/Menubar/index.tsx +0 -252
  82. package/components/ui/NavigationMenu/index.stories.tsx +0 -112
  83. package/components/ui/NavigationMenu/index.tsx +0 -185
  84. package/components/ui/NoData/index.stories.tsx +0 -24
  85. package/components/ui/NoData/index.tsx +0 -19
  86. package/components/ui/Pagination/index.stories.tsx +0 -53
  87. package/components/ui/Pagination/index.tsx +0 -114
  88. package/components/ui/Popover/index.stories.tsx +0 -31
  89. package/components/ui/Popover/index.tsx +0 -42
  90. package/components/ui/Progress/index.stories.tsx +0 -35
  91. package/components/ui/Progress/index.tsx +0 -28
  92. package/components/ui/RadioGroup/index.stories.tsx +0 -28
  93. package/components/ui/RadioGroup/index.tsx +0 -45
  94. package/components/ui/Resizable/index.stories.tsx +0 -44
  95. package/components/ui/Resizable/index.tsx +0 -54
  96. package/components/ui/ScrollArea/index.stories.tsx +0 -31
  97. package/components/ui/ScrollArea/index.tsx +0 -56
  98. package/components/ui/Select/index.stories.tsx +0 -64
  99. package/components/ui/Select/index.tsx +0 -170
  100. package/components/ui/Separator/index.stories.tsx +0 -31
  101. package/components/ui/Separator/index.tsx +0 -28
  102. package/components/ui/Sheet/index.stories.tsx +0 -45
  103. package/components/ui/Sheet/index.tsx +0 -130
  104. package/components/ui/Sidebar/index.stories.tsx +0 -82
  105. package/components/ui/Sidebar/index.tsx +0 -676
  106. package/components/ui/Skeleton/index.stories.tsx +0 -36
  107. package/components/ui/Skeleton/index.tsx +0 -13
  108. package/components/ui/Slider/index.stories.tsx +0 -48
  109. package/components/ui/Slider/index.tsx +0 -82
  110. package/components/ui/Slot/index.stories.tsx +0 -29
  111. package/components/ui/Slot/index.tsx +0 -106
  112. package/components/ui/Sonner/index.stories.tsx +0 -36
  113. package/components/ui/Sonner/index.tsx +0 -31
  114. package/components/ui/Switch/index.stories.tsx +0 -33
  115. package/components/ui/Switch/index.tsx +0 -28
  116. package/components/ui/Table/index.stories.tsx +0 -74
  117. package/components/ui/Table/index.tsx +0 -95
  118. package/components/ui/Tabs/index.stories.tsx +0 -38
  119. package/components/ui/Tabs/index.tsx +0 -78
  120. package/components/ui/Text/index.stories.tsx +0 -53
  121. package/components/ui/Text/index.tsx +0 -138
  122. package/components/ui/Textarea/index.stories.tsx +0 -25
  123. package/components/ui/Textarea/index.tsx +0 -18
  124. package/components/ui/Toggle/index.stories.tsx +0 -52
  125. package/components/ui/Toggle/index.tsx +0 -46
  126. package/components/ui/ToggleGroup/index.stories.tsx +0 -52
  127. package/components/ui/ToggleGroup/index.tsx +0 -69
  128. package/components/ui/Tooltip/index.stories.tsx +0 -29
  129. package/components/ui/Tooltip/index.tsx +0 -35
  130. package/components/ui/VStack/index.stories.tsx +0 -45
  131. package/components/ui/VStack/index.tsx +0 -69
  132. package/components/ui/colors.stories.tsx +0 -148
  133. package/eslint.config.js +0 -10
  134. package/hooks/index.ts +0 -3
  135. package/hooks/use-auto-height.tsx +0 -99
  136. package/hooks/use-controlled-state.tsx +0 -32
  137. package/hooks/use-mobile.ts +0 -19
  138. package/index.ts +0 -58
  139. package/lib/get-strict-context.ts +0 -15
  140. package/lib/utils.ts +0 -10
  141. package/scripts/generate-exports.ts +0 -32
  142. package/tsconfig.json +0 -12
  143. package/tsconfig.tsbuildinfo +0 -1
  144. package/tsup.config.ts +0 -11
  145. package/types/svg.d.ts +0 -10
  146. package/vercel.json +0 -5
@@ -1,605 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import { AnimatePresence, motion, Transition } from 'motion/react';
5
-
6
- import { cn } from '@/lib/utils';
7
-
8
- type HighlightMode = 'children' | 'parent';
9
-
10
- type Bounds = {
11
- top: number;
12
- left: number;
13
- width: number;
14
- height: number;
15
- };
16
-
17
- type HighlightContextType<T extends string> = {
18
- as?: keyof HTMLElementTagNameMap;
19
- mode: HighlightMode;
20
- activeValue: T | null;
21
- setActiveValue: (value: T | null) => void;
22
- setBounds: (bounds: DOMRect) => void;
23
- clearBounds: () => void;
24
- id: string;
25
- hover: boolean;
26
- click: boolean;
27
- className?: string;
28
- style?: React.CSSProperties;
29
- activeClassName?: string;
30
- setActiveClassName: (className: string) => void;
31
- transition?: Transition;
32
- disabled?: boolean;
33
- enabled?: boolean;
34
- exitDelay?: number;
35
- forceUpdateBounds?: boolean;
36
- };
37
-
38
- const HighlightContext = React.createContext<HighlightContextType<string> | undefined>(undefined);
39
-
40
- function useHighlight<T extends string>(): HighlightContextType<T> {
41
- const context = React.useContext(HighlightContext);
42
- if (!context) {
43
- throw new Error('useHighlight must be used within a HighlightProvider');
44
- }
45
- return context as unknown as HighlightContextType<T>;
46
- }
47
-
48
- type BaseHighlightProps<T extends React.ElementType = 'div'> = {
49
- as?: T;
50
- ref?: React.Ref<HTMLDivElement>;
51
- mode?: HighlightMode;
52
- value?: string | null;
53
- defaultValue?: string | null;
54
- onValueChange?: (value: string | null) => void;
55
- className?: string;
56
- style?: React.CSSProperties;
57
- transition?: Transition;
58
- hover?: boolean;
59
- click?: boolean;
60
- disabled?: boolean;
61
- enabled?: boolean;
62
- exitDelay?: number;
63
- };
64
-
65
- type ParentModeHighlightProps = {
66
- boundsOffset?: Partial<Bounds>;
67
- containerClassName?: string;
68
- forceUpdateBounds?: boolean;
69
- };
70
-
71
- type ControlledParentModeHighlightProps<T extends React.ElementType = 'div'> =
72
- BaseHighlightProps<T> &
73
- ParentModeHighlightProps & {
74
- mode: 'parent';
75
- controlledItems: true;
76
- children: React.ReactNode;
77
- };
78
-
79
- type ControlledChildrenModeHighlightProps<T extends React.ElementType = 'div'> =
80
- BaseHighlightProps<T> & {
81
- mode?: 'children' | undefined;
82
- controlledItems: true;
83
- children: React.ReactNode;
84
- };
85
-
86
- type UncontrolledParentModeHighlightProps<T extends React.ElementType = 'div'> =
87
- BaseHighlightProps<T> &
88
- ParentModeHighlightProps & {
89
- mode: 'parent';
90
- controlledItems?: false;
91
- itemsClassName?: string;
92
- children: React.ReactElement | React.ReactElement[];
93
- };
94
-
95
- type UncontrolledChildrenModeHighlightProps<T extends React.ElementType = 'div'> =
96
- BaseHighlightProps<T> & {
97
- mode?: 'children';
98
- controlledItems?: false;
99
- itemsClassName?: string;
100
- children: React.ReactElement | React.ReactElement[];
101
- };
102
-
103
- type HighlightProps<T extends React.ElementType = 'div'> =
104
- | ControlledParentModeHighlightProps<T>
105
- | ControlledChildrenModeHighlightProps<T>
106
- | UncontrolledParentModeHighlightProps<T>
107
- | UncontrolledChildrenModeHighlightProps<T>;
108
-
109
- function Highlight<T extends React.ElementType = 'div'>({ ref, ...props }: HighlightProps<T>) {
110
- const {
111
- as: Component = 'div',
112
- children,
113
- value,
114
- defaultValue,
115
- onValueChange,
116
- className,
117
- style,
118
- transition = { type: 'spring', stiffness: 350, damping: 35 },
119
- hover = false,
120
- click = true,
121
- enabled = true,
122
- controlledItems,
123
- disabled = false,
124
- exitDelay = 200,
125
- mode = 'children',
126
- } = props;
127
-
128
- const localRef = React.useRef<HTMLDivElement>(null);
129
- React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
130
-
131
- const [activeValue, setActiveValue] = React.useState<string | null>(
132
- value ?? defaultValue ?? null,
133
- );
134
- const [boundsState, setBoundsState] = React.useState<Bounds | null>(null);
135
- const [activeClassNameState, setActiveClassNameState] = React.useState<string>('');
136
-
137
- const onValueChangeRef = React.useRef(onValueChange);
138
- React.useEffect(() => {
139
- onValueChangeRef.current = onValueChange;
140
- }, [onValueChange]);
141
-
142
- const activeValueRef = React.useRef(activeValue);
143
- React.useEffect(() => {
144
- activeValueRef.current = activeValue;
145
- }, [activeValue]);
146
-
147
- const safeSetActiveValue = (id: string | null) => {
148
- setActiveValue((prev) => {
149
- if (prev === id) return prev;
150
- if (id !== activeValueRef.current) {
151
- onValueChangeRef.current?.(id);
152
- }
153
- return id;
154
- });
155
- };
156
-
157
- const boundsOffsetRef = React.useRef(
158
- (props as ParentModeHighlightProps)?.boundsOffset ?? {
159
- top: 0,
160
- left: 0,
161
- width: 0,
162
- height: 0,
163
- },
164
- );
165
- const boundsOffset = (props as ParentModeHighlightProps)?.boundsOffset;
166
- React.useEffect(() => {
167
- boundsOffsetRef.current = boundsOffset ?? {
168
- top: 0,
169
- left: 0,
170
- width: 0,
171
- height: 0,
172
- };
173
- }, [boundsOffset]);
174
-
175
- const safeSetBoundsRef = React.useRef<(bounds: DOMRect) => void>(() => {});
176
- const safeSetBounds = (bounds: DOMRect) => {
177
- if (!localRef.current) return;
178
-
179
- const boundsOffset = boundsOffsetRef.current;
180
- const containerRect = localRef.current.getBoundingClientRect();
181
- const newBounds: Bounds = {
182
- top: bounds.top - containerRect.top + (boundsOffset.top ?? 0),
183
- left: bounds.left - containerRect.left + (boundsOffset.left ?? 0),
184
- width: bounds.width + (boundsOffset.width ?? 0),
185
- height: bounds.height + (boundsOffset.height ?? 0),
186
- };
187
-
188
- setBoundsState((prev) => {
189
- if (
190
- prev &&
191
- prev.top === newBounds.top &&
192
- prev.left === newBounds.left &&
193
- prev.width === newBounds.width &&
194
- prev.height === newBounds.height
195
- ) {
196
- return prev;
197
- }
198
- return newBounds;
199
- });
200
- };
201
- React.useEffect(() => {
202
- safeSetBoundsRef.current = safeSetBounds;
203
- });
204
-
205
- const clearBounds = () => {
206
- setBoundsState((prev) => (prev === null ? prev : null));
207
- };
208
-
209
- React.useEffect(() => {
210
- if (value !== undefined) setActiveValue(value);
211
- else if (defaultValue !== undefined) setActiveValue(defaultValue);
212
- }, [value, defaultValue]);
213
-
214
- const id = React.useId();
215
-
216
- React.useEffect(() => {
217
- if (mode !== 'parent') return undefined;
218
- const container = localRef.current;
219
- if (!container) return;
220
-
221
- const onScroll = () => {
222
- if (!activeValue) return;
223
- const activeEl = container.querySelector<HTMLElement>(
224
- `[data-value="${activeValue}"][data-highlight="true"]`,
225
- );
226
- if (activeEl) safeSetBoundsRef.current?.(activeEl.getBoundingClientRect());
227
- };
228
-
229
- container.addEventListener('scroll', onScroll, { passive: true });
230
- return () => container.removeEventListener('scroll', onScroll);
231
- }, [mode, activeValue]);
232
-
233
- const containerClassName = (props as ParentModeHighlightProps)?.containerClassName;
234
-
235
- const render = (children: React.ReactNode) => {
236
- if (mode === 'parent') {
237
- return (
238
- <Component
239
- ref={localRef}
240
- data-slot="motion-highlight-container"
241
- style={{ position: 'relative', zIndex: 1 }}
242
- className={containerClassName}
243
- >
244
- <AnimatePresence initial={false} mode="wait">
245
- {boundsState && (
246
- <motion.div
247
- data-slot="motion-highlight"
248
- animate={{
249
- top: boundsState.top,
250
- left: boundsState.left,
251
- width: boundsState.width,
252
- height: boundsState.height,
253
- opacity: 1,
254
- }}
255
- initial={{
256
- top: boundsState.top,
257
- left: boundsState.left,
258
- width: boundsState.width,
259
- height: boundsState.height,
260
- opacity: 0,
261
- }}
262
- exit={{
263
- opacity: 0,
264
- transition: {
265
- ...transition,
266
- delay: (transition?.delay ?? 0) + (exitDelay ?? 0) / 1000,
267
- },
268
- }}
269
- transition={transition}
270
- style={{ position: 'absolute', zIndex: 0, ...style }}
271
- className={cn(className, activeClassNameState)}
272
- />
273
- )}
274
- </AnimatePresence>
275
- {children}
276
- </Component>
277
- );
278
- }
279
-
280
- return children;
281
- };
282
-
283
- return (
284
- <HighlightContext.Provider
285
- value={{
286
- mode,
287
- activeValue,
288
- setActiveValue: safeSetActiveValue,
289
- id,
290
- hover,
291
- click,
292
- className,
293
- style,
294
- transition,
295
- disabled,
296
- enabled,
297
- exitDelay,
298
- setBounds: safeSetBounds,
299
- clearBounds,
300
- activeClassName: activeClassNameState,
301
- setActiveClassName: setActiveClassNameState,
302
- forceUpdateBounds: (props as ParentModeHighlightProps)?.forceUpdateBounds,
303
- }}
304
- >
305
- {enabled
306
- ? controlledItems
307
- ? render(children)
308
- : render(
309
- React.Children.map(children, (child, index) => (
310
- <HighlightItem key={index} className={props?.itemsClassName}>
311
- {child}
312
- </HighlightItem>
313
- )),
314
- )
315
- : children}
316
- </HighlightContext.Provider>
317
- );
318
- }
319
-
320
- function getNonOverridingDataAttributes(
321
- element: React.ReactElement,
322
- dataAttributes: Record<string, unknown>,
323
- ): Record<string, unknown> {
324
- return Object.keys(dataAttributes).reduce<Record<string, unknown>>((acc, key) => {
325
- if ((element.props as Record<string, unknown>)[key] === undefined) {
326
- acc[key] = dataAttributes[key];
327
- }
328
- return acc;
329
- }, {});
330
- }
331
-
332
- type ExtendedChildProps = React.ComponentProps<'div'> & {
333
- id?: string;
334
- ref?: React.Ref<HTMLElement>;
335
- 'data-active'?: string;
336
- 'data-value'?: string;
337
- 'data-disabled'?: boolean;
338
- 'data-highlight'?: boolean;
339
- 'data-slot'?: string;
340
- };
341
-
342
- type HighlightItemProps<T extends React.ElementType = 'div'> = React.ComponentProps<T> & {
343
- as?: T;
344
- children: React.ReactElement;
345
- id?: string;
346
- value?: string;
347
- className?: string;
348
- style?: React.CSSProperties;
349
- transition?: Transition;
350
- activeClassName?: string;
351
- disabled?: boolean;
352
- exitDelay?: number;
353
- asChild?: boolean;
354
- forceUpdateBounds?: boolean;
355
- };
356
-
357
- function HighlightItem<T extends React.ElementType>({
358
- ref,
359
- as,
360
- children,
361
- id,
362
- value,
363
- className,
364
- style,
365
- transition,
366
- disabled = false,
367
- activeClassName,
368
- exitDelay,
369
- asChild = false,
370
- forceUpdateBounds,
371
- ...props
372
- }: HighlightItemProps<T>) {
373
- const itemId = React.useId();
374
- const {
375
- activeValue,
376
- setActiveValue,
377
- mode,
378
- setBounds,
379
- clearBounds,
380
- hover,
381
- click,
382
- enabled,
383
- className: contextClassName,
384
- style: contextStyle,
385
- transition: contextTransition,
386
- id: contextId,
387
- disabled: contextDisabled,
388
- exitDelay: contextExitDelay,
389
- forceUpdateBounds: contextForceUpdateBounds,
390
- setActiveClassName,
391
- } = useHighlight();
392
-
393
- const Component = as ?? 'div';
394
- const element = children as React.ReactElement<ExtendedChildProps>;
395
- const childValue = id ?? value ?? element.props?.['data-value'] ?? element.props?.id ?? itemId;
396
- const isActive = activeValue === childValue;
397
- const isDisabled = disabled === undefined ? contextDisabled : disabled;
398
- const itemTransition = transition ?? contextTransition;
399
-
400
- const localRef = React.useRef<HTMLElement | null>(null);
401
- React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
402
-
403
- const setRef = React.useCallback((node: HTMLElement | null) => {
404
- localRef.current = node;
405
- }, []);
406
-
407
- React.useEffect(() => {
408
- if (mode !== 'parent') return;
409
- let rafId: number;
410
- let previousBounds: Bounds | null = null;
411
- const shouldUpdateBounds =
412
- forceUpdateBounds === true || (contextForceUpdateBounds && forceUpdateBounds !== false);
413
-
414
- const updateBounds = () => {
415
- if (!localRef.current) return;
416
-
417
- const bounds = localRef.current.getBoundingClientRect();
418
-
419
- if (shouldUpdateBounds) {
420
- if (
421
- previousBounds &&
422
- previousBounds.top === bounds.top &&
423
- previousBounds.left === bounds.left &&
424
- previousBounds.width === bounds.width &&
425
- previousBounds.height === bounds.height
426
- ) {
427
- rafId = requestAnimationFrame(updateBounds);
428
- return;
429
- }
430
- previousBounds = bounds;
431
- rafId = requestAnimationFrame(updateBounds);
432
- }
433
-
434
- setBounds(bounds);
435
- };
436
-
437
- if (isActive) {
438
- updateBounds();
439
- setActiveClassName(activeClassName ?? '');
440
- } else if (!activeValue) clearBounds();
441
-
442
- if (shouldUpdateBounds) return () => cancelAnimationFrame(rafId);
443
- return undefined;
444
- }, [
445
- mode,
446
- isActive,
447
- activeValue,
448
- setBounds,
449
- clearBounds,
450
- activeClassName,
451
- setActiveClassName,
452
- forceUpdateBounds,
453
- contextForceUpdateBounds,
454
- ]);
455
-
456
- if (!React.isValidElement(children)) return children;
457
-
458
- const dataAttributes = {
459
- 'data-active': isActive ? 'true' : 'false',
460
- 'aria-selected': isActive,
461
- 'data-disabled': isDisabled,
462
- 'data-value': childValue,
463
- 'data-highlight': true,
464
- };
465
-
466
- const commonHandlers = hover
467
- ? {
468
- onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => {
469
- setActiveValue(childValue);
470
- element.props.onMouseEnter?.(e);
471
- },
472
- onMouseLeave: (e: React.MouseEvent<HTMLDivElement>) => {
473
- setActiveValue(null);
474
- element.props.onMouseLeave?.(e);
475
- },
476
- }
477
- : click
478
- ? {
479
- onClick: (e: React.MouseEvent<HTMLDivElement>) => {
480
- setActiveValue(childValue);
481
- element.props.onClick?.(e);
482
- },
483
- }
484
- : {};
485
-
486
- if (asChild) {
487
- if (mode === 'children') {
488
- return React.cloneElement(
489
- element,
490
- {
491
- key: childValue,
492
- ref: setRef,
493
- className: cn('relative', element.props.className),
494
- ...getNonOverridingDataAttributes(element, {
495
- ...dataAttributes,
496
- 'data-slot': 'motion-highlight-item-container',
497
- }),
498
- ...commonHandlers,
499
- ...props,
500
- },
501
- <>
502
- <AnimatePresence initial={false} mode="wait">
503
- {isActive && !isDisabled && (
504
- <motion.div
505
- layoutId={`transition-background-${contextId}`}
506
- data-slot="motion-highlight"
507
- style={{
508
- position: 'absolute',
509
- zIndex: 0,
510
- ...contextStyle,
511
- ...style,
512
- }}
513
- className={cn(contextClassName, activeClassName)}
514
- transition={itemTransition}
515
- initial={{ opacity: 0 }}
516
- animate={{ opacity: 1 }}
517
- exit={{
518
- opacity: 0,
519
- transition: {
520
- ...itemTransition,
521
- delay:
522
- (itemTransition?.delay ?? 0) + (exitDelay ?? contextExitDelay ?? 0) / 1000,
523
- },
524
- }}
525
- {...dataAttributes}
526
- />
527
- )}
528
- </AnimatePresence>
529
-
530
- <Component
531
- data-slot="motion-highlight-item"
532
- style={{ position: 'relative', zIndex: 1 }}
533
- className={className}
534
- {...dataAttributes}
535
- >
536
- {children}
537
- </Component>
538
- </>,
539
- );
540
- }
541
-
542
- return React.cloneElement(element, {
543
- ref: setRef,
544
- ...getNonOverridingDataAttributes(element, {
545
- ...dataAttributes,
546
- 'data-slot': 'motion-highlight-item',
547
- }),
548
- ...commonHandlers,
549
- });
550
- }
551
-
552
- return enabled ? (
553
- <Component
554
- key={childValue}
555
- ref={localRef}
556
- data-slot="motion-highlight-item-container"
557
- className={cn(mode === 'children' && 'relative', className)}
558
- {...dataAttributes}
559
- {...props}
560
- {...commonHandlers}
561
- >
562
- {mode === 'children' && (
563
- <AnimatePresence initial={false} mode="wait">
564
- {isActive && !isDisabled && (
565
- <motion.div
566
- layoutId={`transition-background-${contextId}`}
567
- data-slot="motion-highlight"
568
- style={{
569
- position: 'absolute',
570
- zIndex: 0,
571
- ...contextStyle,
572
- ...style,
573
- }}
574
- className={cn(contextClassName, activeClassName)}
575
- transition={itemTransition}
576
- initial={{ opacity: 0 }}
577
- animate={{ opacity: 1 }}
578
- exit={{
579
- opacity: 0,
580
- transition: {
581
- ...itemTransition,
582
- delay: (itemTransition?.delay ?? 0) + (exitDelay ?? contextExitDelay ?? 0) / 1000,
583
- },
584
- }}
585
- {...dataAttributes}
586
- />
587
- )}
588
- </AnimatePresence>
589
- )}
590
-
591
- {React.cloneElement(element, {
592
- style: { position: 'relative', zIndex: 1 },
593
- className: element.props.className,
594
- ...getNonOverridingDataAttributes(element, {
595
- ...dataAttributes,
596
- 'data-slot': 'motion-highlight-item',
597
- }),
598
- })}
599
- </Component>
600
- ) : (
601
- children
602
- );
603
- }
604
-
605
- export { Highlight, HighlightItem, useHighlight, type HighlightProps, type HighlightItemProps };
@@ -1,55 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useRef } from 'react';
4
- import { useInView, useMotionValue, useSpring } from 'motion/react';
5
-
6
- import { cn } from '@/lib/utils';
7
-
8
- export default function NumberTicker({
9
- value,
10
- direction = 'up',
11
- delay = 0,
12
- subValue = '',
13
- preValue = '',
14
- className,
15
- }: {
16
- value: number;
17
- direction?: 'up' | 'down';
18
- className?: string;
19
- delay?: number; // delay in s
20
- subValue?: string;
21
- preValue?: string;
22
- }) {
23
- const ref = useRef<HTMLSpanElement>(null);
24
- const motionValue = useMotionValue(direction === 'down' ? value : 0);
25
- const springValue = useSpring(motionValue, {
26
- damping: 60,
27
- stiffness: 100,
28
- });
29
- const isInView = useInView(ref, { once: true, margin: '0px' });
30
-
31
- useEffect(() => {
32
- if (isInView) {
33
- setTimeout(() => {
34
- motionValue.set(direction === 'down' ? 0 : value);
35
- }, delay * 1000);
36
- }
37
- }, [motionValue, isInView, delay, value, direction]);
38
-
39
- useEffect(() => {
40
- const updateText = (val: number) => {
41
- if (ref.current) {
42
- ref.current.textContent = `${preValue}${Intl.NumberFormat('en-US').format(Math.round(val))}${subValue}`;
43
- }
44
- };
45
-
46
- const initialValue = direction === 'down' ? value : 0;
47
- updateText(initialValue);
48
-
49
- return springValue.on('change', (latest: number) => {
50
- updateText(latest);
51
- });
52
- }, [preValue, springValue, subValue, value, direction]);
53
-
54
- return <span className={cn('text-primary-50 inline-block tabular-nums', className)} ref={ref} />;
55
- }