@vuer-ai/vuer-uikit 0.0.12 → 0.0.13

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 (45) hide show
  1. package/package.json +3 -2
  2. package/src/highlight-cursor/cursor-context.tsx +23 -0
  3. package/src/highlight-cursor/cursor-provider.tsx +264 -0
  4. package/src/highlight-cursor/enhanced-components.tsx +16 -0
  5. package/src/highlight-cursor/index.ts +21 -0
  6. package/src/highlight-cursor/tabs-cursor-context.tsx +121 -0
  7. package/src/highlight-cursor/types.ts +40 -0
  8. package/src/highlight-cursor/with-cursor.tsx +144 -0
  9. package/src/index.css +5 -0
  10. package/src/index.ts +5 -0
  11. package/src/styles/theme.css +80 -0
  12. package/src/styles/toast.css +64 -0
  13. package/src/styles/variables.css +194 -0
  14. package/src/ui/avatar.tsx +92 -0
  15. package/src/ui/badge.tsx +68 -0
  16. package/src/ui/button.tsx +100 -0
  17. package/src/ui/card.tsx +88 -0
  18. package/src/ui/checkbox.tsx +76 -0
  19. package/src/ui/collapsible.tsx +36 -0
  20. package/src/ui/dropdown.tsx +375 -0
  21. package/src/ui/index.ts +31 -0
  22. package/src/ui/input-numbers.tsx +201 -0
  23. package/src/ui/input.tsx +141 -0
  24. package/src/ui/layout.tsx +41 -0
  25. package/src/ui/modal.tsx +198 -0
  26. package/src/ui/popover.tsx +59 -0
  27. package/src/ui/radio-group.tsx +56 -0
  28. package/src/ui/select.tsx +292 -0
  29. package/src/ui/sheet.tsx +131 -0
  30. package/src/ui/slider.tsx +189 -0
  31. package/src/ui/switch.tsx +43 -0
  32. package/src/ui/tabs.tsx +128 -0
  33. package/src/ui/textarea.tsx +52 -0
  34. package/src/ui/theme-context.tsx +80 -0
  35. package/src/ui/timeline.tsx +717 -0
  36. package/src/ui/toast.tsx +27 -0
  37. package/src/ui/toggle-group.tsx +82 -0
  38. package/src/ui/toggle.tsx +88 -0
  39. package/src/ui/tooltip.tsx +132 -0
  40. package/src/ui/tree-view-v2.tsx +300 -0
  41. package/src/ui/tree-view.tsx +550 -0
  42. package/src/ui/version-badge.tsx +65 -0
  43. package/src/utils/cn.ts +21 -0
  44. package/src/utils/index.ts +2 -0
  45. package/src/utils/use-local-storage.ts +59 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuer-ai/vuer-uikit",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "React UI Kit components",
5
5
  "keywords": [
6
6
  "react",
@@ -27,7 +27,8 @@
27
27
  },
28
28
  "sideEffects": false,
29
29
  "files": [
30
- "dist"
30
+ "dist",
31
+ "src"
31
32
  ],
32
33
  "lint-staged": {
33
34
  "*.{js,ts,jsx,tsx}": "eslint --fix"
@@ -0,0 +1,23 @@
1
+ import { createContext, useContext } from "react";
2
+
3
+ import type { CursorContextType } from "./types";
4
+
5
+ const defaultDimensions = {
6
+ width: 0,
7
+ height: 0,
8
+ x: 0,
9
+ y: 0,
10
+ };
11
+
12
+ const defaultContext: CursorContextType = {
13
+ mousePosition: { x: 0, y: 0 },
14
+ hoveredElementId: null,
15
+ elementDimensions: defaultDimensions,
16
+ registerHoveredElement: () => {},
17
+ unregisterHoveredElement: () => {},
18
+ updateElementDimensions: () => {},
19
+ };
20
+
21
+ export const CursorContext = createContext<CursorContextType>(defaultContext);
22
+
23
+ export const useCursor = () => useContext(CursorContext);
@@ -0,0 +1,264 @@
1
+ import { useCallback, useEffect, useMemo, useState, useRef } from "react";
2
+ import { createPortal } from "react-dom";
3
+
4
+ import { CursorContext } from "./cursor-context";
5
+ import type { CursorProviderProps } from "./types";
6
+ import { cn } from "../utils/cn";
7
+
8
+ export const CursorProvider = ({
9
+ children,
10
+ maxOffsetX = 5,
11
+ maxOffsetY = 20,
12
+ cursorSize = 20,
13
+ transitionDuration = 100,
14
+ cursorClassName,
15
+ className,
16
+ as: Component = "div",
17
+ ...props
18
+ }: CursorProviderProps) => {
19
+ // Check if we're in a browser environment
20
+ const [isClient, setIsClient] = useState(false);
21
+ const [isMouseInside, setIsMouseInside] = useState(false);
22
+ const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
23
+ const [hoveredElementId, setHoveredElementId] = useState<string | null>(null);
24
+ const [elementDimensions, setElementDimensions] = useState({
25
+ width: 0,
26
+ height: 0,
27
+ x: 0,
28
+ y: 0,
29
+ right: 0,
30
+ bottom: 0,
31
+ });
32
+
33
+ const hoveredElementRef = useRef<HTMLElement | null>(null);
34
+
35
+ // Set isClient to true after component mounts (client-side only)
36
+ useEffect(() => {
37
+ setIsClient(true);
38
+ }, []);
39
+
40
+ useEffect(() => {
41
+ if (!isClient) return;
42
+
43
+ const handleMouseMove = (e: MouseEvent) => {
44
+ setMousePosition({ x: e.clientX, y: e.clientY });
45
+ };
46
+
47
+ document.addEventListener("mousemove", handleMouseMove);
48
+ return () => document.removeEventListener("mousemove", handleMouseMove);
49
+ }, [isClient]);
50
+
51
+ const handleMouseEnter = useCallback(() => {
52
+ setIsMouseInside(true);
53
+ }, []);
54
+
55
+ const handleMouseLeave = useCallback(() => {
56
+ setIsMouseInside(false);
57
+ setHoveredElementId(null);
58
+ hoveredElementRef.current = null;
59
+ }, []);
60
+
61
+ const checkMouseInElement = useCallback(() => {
62
+ if (!hoveredElementRef.current || !hoveredElementId) {
63
+ return;
64
+ }
65
+
66
+ const rect = hoveredElementRef.current.getBoundingClientRect();
67
+
68
+ setElementDimensions({
69
+ width: rect.width,
70
+ height: rect.height,
71
+ x: rect.left,
72
+ y: rect.top,
73
+ right: rect.right,
74
+ bottom: rect.bottom,
75
+ });
76
+
77
+ const isInside =
78
+ mousePosition.x >= rect.left &&
79
+ mousePosition.x <= rect.right &&
80
+ mousePosition.y >= rect.top &&
81
+ mousePosition.y <= rect.bottom;
82
+
83
+ if (!isInside) {
84
+ setHoveredElementId(null);
85
+ hoveredElementRef.current = null;
86
+ }
87
+ }, [mousePosition, hoveredElementId]);
88
+
89
+ useEffect(() => {
90
+ if (!isClient) return;
91
+
92
+ const handleScroll = () => {
93
+ checkMouseInElement();
94
+ };
95
+
96
+ document.addEventListener("scroll", handleScroll, { passive: true, capture: true });
97
+
98
+ return () => {
99
+ document.removeEventListener("scroll", handleScroll, { capture: true });
100
+ };
101
+ }, [checkMouseInElement, isClient]);
102
+
103
+ const registerHoveredElement = useCallback(
104
+ (id: string, dimensions: DOMRect, element?: HTMLElement) => {
105
+ setHoveredElementId(id);
106
+ setElementDimensions({
107
+ width: dimensions.width,
108
+ height: dimensions.height,
109
+ x: dimensions.left,
110
+ y: dimensions.top,
111
+ right: dimensions.right,
112
+ bottom: dimensions.bottom,
113
+ });
114
+
115
+ if (element) {
116
+ hoveredElementRef.current = element;
117
+ }
118
+ },
119
+ [],
120
+ );
121
+
122
+ const unregisterHoveredElement = useCallback(() => {
123
+ setHoveredElementId(null);
124
+ hoveredElementRef.current = null;
125
+ }, []);
126
+
127
+ const updateElementDimensions = useCallback(
128
+ (dimensions: DOMRect) => {
129
+ if (hoveredElementId) {
130
+ setElementDimensions({
131
+ width: dimensions.width,
132
+ height: dimensions.height,
133
+ x: dimensions.left,
134
+ y: dimensions.top,
135
+ right: dimensions.right,
136
+ bottom: dimensions.bottom,
137
+ });
138
+ }
139
+ },
140
+ [hoveredElementId],
141
+ );
142
+
143
+ const getCursorPosition = () => {
144
+ if (!hoveredElementId) {
145
+ return {
146
+ x: mousePosition.x - cursorSize / 2,
147
+ y: mousePosition.y - cursorSize / 2,
148
+ };
149
+ }
150
+
151
+ // Calculate the center of the element
152
+ const elementCenterX = elementDimensions.x + elementDimensions.width / 2;
153
+ const elementCenterY = elementDimensions.y + elementDimensions.height / 2;
154
+
155
+ // Calculate offset from center (with some dampening for smoothness)
156
+ const offsetX = (mousePosition.x - elementCenterX) * 0.1;
157
+ const offsetY = (mousePosition.y - elementCenterY) * 0.1;
158
+
159
+ // Apply bounds to keep cursor within reasonable limits
160
+ const boundedOffsetX = Math.max(-maxOffsetX, Math.min(maxOffsetX, offsetX));
161
+ const boundedOffsetY = Math.max(-maxOffsetY, Math.min(maxOffsetY, offsetY));
162
+
163
+ return {
164
+ x: elementDimensions.x + boundedOffsetX,
165
+ y: elementDimensions.y + boundedOffsetY,
166
+ };
167
+ };
168
+
169
+ const contextValue = useMemo(
170
+ () => ({
171
+ // State values that change during component lifecycle
172
+ mousePosition,
173
+ hoveredElementId,
174
+ elementDimensions,
175
+
176
+ // Callbacks that should be stable references
177
+ registerHoveredElement,
178
+ unregisterHoveredElement,
179
+ updateElementDimensions,
180
+ }),
181
+ [
182
+ mousePosition,
183
+ hoveredElementId,
184
+ elementDimensions,
185
+ registerHoveredElement,
186
+ unregisterHoveredElement,
187
+ updateElementDimensions,
188
+ ],
189
+ );
190
+
191
+ const cursorPosition = getCursorPosition();
192
+
193
+ const cursorStyleObject = useMemo(
194
+ () => ({
195
+ left: cursorPosition.x,
196
+ top: cursorPosition.y,
197
+ width: hoveredElementId
198
+ ? elementDimensions.width > 0
199
+ ? elementDimensions.width
200
+ : cursorSize
201
+ : cursorSize,
202
+ height: hoveredElementId
203
+ ? elementDimensions.height > 0
204
+ ? elementDimensions.height
205
+ : cursorSize
206
+ : cursorSize,
207
+ willChange: "transform, width, height",
208
+ }),
209
+ [
210
+ cursorPosition.x,
211
+ cursorPosition.y,
212
+ hoveredElementId,
213
+ elementDimensions.width,
214
+ elementDimensions.height,
215
+ cursorSize,
216
+ ],
217
+ );
218
+
219
+ let styleClass;
220
+ if (hoveredElementId) {
221
+ styleClass = `opacity-[0.05] rounded-md transition-[width,height,left,top] duration-${transitionDuration} ease-out`;
222
+ } else {
223
+ styleClass = `opacity-90 rounded-xl z-10 transition-[width,height,left,top] duration-${transitionDuration} ease-out transition-opacity duration-${transitionDuration}`;
224
+ }
225
+
226
+ // Create cursor element to be portaled
227
+ const cursorElement = isClient && isMouseInside && (
228
+ <div
229
+ className={`pointer-events-none fixed ${styleClass} ${hoveredElementId ? "" : cursorClassName || ""}`}
230
+ style={{
231
+ ...cursorStyleObject,
232
+ backgroundColor: "var(--shadow-tertiary)",
233
+ }}
234
+ />
235
+ );
236
+
237
+ // Use React.Fragment at the top level to avoid any DOM structure that might affect layout
238
+ return (
239
+ <>
240
+ {/* Portal the cursor overlay to document.body to completely avoid layout impact */}
241
+ {isClient &&
242
+ typeof document !== "undefined" &&
243
+ document.body &&
244
+ cursorElement &&
245
+ createPortal(cursorElement, document.body)}
246
+ {/* Context provider with no DOM element */}
247
+ <CursorContext.Provider value={contextValue}>
248
+ {/* Use the Component directly with no extra wrappers */}
249
+ <Component
250
+ {...props}
251
+ className={cn(className)}
252
+ style={{
253
+ cursor: isMouseInside && !hoveredElementId ? "none" : "auto",
254
+ ...props.style,
255
+ }}
256
+ onMouseEnter={handleMouseEnter}
257
+ onMouseLeave={handleMouseLeave}
258
+ >
259
+ {children}
260
+ </Component>
261
+ </CursorContext.Provider>
262
+ </>
263
+ );
264
+ };
@@ -0,0 +1,16 @@
1
+ import { withCursor } from "./with-cursor";
2
+ import { Button } from "../ui/button";
3
+ import { InputRoot } from "../ui/input";
4
+ import { SelectTrigger } from "../ui/select";
5
+ import { Tabs } from "../ui/tabs";
6
+ import { Textarea } from "../ui/textarea";
7
+
8
+ export const CursorButton = withCursor(Button);
9
+
10
+ export const CursorTabs = withCursor(Tabs);
11
+
12
+ export const CursorInputRoot = withCursor(InputRoot);
13
+
14
+ export const CursorSelectTrigger = withCursor(SelectTrigger) as typeof SelectTrigger;
15
+
16
+ export const CursorTextarea = withCursor(Textarea) as typeof Textarea;
@@ -0,0 +1,21 @@
1
+ import { CursorProvider } from "./cursor-provider";
2
+ import {
3
+ CursorButton,
4
+ CursorTabs,
5
+ CursorInputRoot,
6
+ CursorSelectTrigger,
7
+ CursorTextarea,
8
+ } from "./enhanced-components";
9
+ import { TabsCursorProvider } from "./tabs-cursor-context";
10
+ import { withCursor } from "./with-cursor";
11
+
12
+ export {
13
+ CursorProvider,
14
+ TabsCursorProvider,
15
+ withCursor,
16
+ CursorButton,
17
+ CursorTabs,
18
+ CursorInputRoot,
19
+ CursorSelectTrigger,
20
+ CursorTextarea,
21
+ };
@@ -0,0 +1,121 @@
1
+ import { motion } from "framer-motion";
2
+ import {
3
+ createContext,
4
+ useState,
5
+ useEffect,
6
+ useContext,
7
+ useCallback,
8
+ type ReactNode,
9
+ type CSSProperties,
10
+ } from "react";
11
+ import { createPortal } from "react-dom";
12
+
13
+ interface TabsCursorContextType {
14
+ setCursorTarget: (el: HTMLElement | null) => void;
15
+ }
16
+
17
+ const TabsCursorContext = createContext<TabsCursorContextType | null>(null);
18
+
19
+ export const TabsCursorProvider = ({
20
+ children,
21
+ className,
22
+ ...props
23
+ }: {
24
+ children: ReactNode;
25
+ className?: string;
26
+ style?: CSSProperties;
27
+ }) => {
28
+ const [isClient, setIsClient] = useState(false);
29
+ const [cursorTarget, setCursorTarget] = useState<HTMLElement | null>(null);
30
+ const [targetRect, setTargetRect] = useState<DOMRect | null>(null);
31
+ const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
32
+ const [isMouseInside, setIsMouseInside] = useState(false);
33
+
34
+ useEffect(() => {
35
+ setIsClient(true);
36
+ }, []);
37
+
38
+ useEffect(() => {
39
+ if (!isClient) return;
40
+
41
+ const handleMouseMove = (e: MouseEvent) => {
42
+ setMousePosition({ x: e.clientX, y: e.clientY });
43
+ };
44
+
45
+ document.addEventListener("mousemove", handleMouseMove);
46
+ return () => document.removeEventListener("mousemove", handleMouseMove);
47
+ }, [isClient]);
48
+
49
+ useEffect(() => {
50
+ if (cursorTarget) {
51
+ setTargetRect(cursorTarget.getBoundingClientRect());
52
+ } else {
53
+ setTargetRect(null);
54
+ }
55
+ }, [cursorTarget]);
56
+
57
+ const handleMouseEnter = useCallback(() => {
58
+ setIsMouseInside(true);
59
+ }, []);
60
+
61
+ const handleMouseLeave = useCallback(() => {
62
+ setIsMouseInside(false);
63
+ setCursorTarget(null);
64
+ }, []);
65
+
66
+ const contextValue = { setCursorTarget };
67
+
68
+ return (
69
+ <div
70
+ {...props}
71
+ className={className}
72
+ style={{
73
+ cursor: isMouseInside && !cursorTarget ? "none" : "auto",
74
+ ...props.style,
75
+ }}
76
+ onMouseEnter={handleMouseEnter}
77
+ onMouseLeave={handleMouseLeave}
78
+ >
79
+ <TabsCursorContext.Provider value={contextValue}>
80
+ {children}
81
+ {isClient &&
82
+ isMouseInside &&
83
+ typeof document !== "undefined" &&
84
+ document.body &&
85
+ createPortal(
86
+ <motion.div
87
+ className="pointer-events-none"
88
+ initial={false}
89
+ animate={{
90
+ x: targetRect ? targetRect.left : mousePosition.x - 8,
91
+ y: targetRect ? targetRect.top : mousePosition.y - 8,
92
+ width: targetRect ? targetRect.width : 16,
93
+ height: targetRect ? targetRect.height : 16,
94
+ borderRadius:
95
+ targetRect && cursorTarget && typeof window !== "undefined"
96
+ ? getComputedStyle(cursorTarget).borderRadius
97
+ : "50%",
98
+ }}
99
+ transition={{ type: "spring", stiffness: 1000, damping: 60, mass: 1 }}
100
+ style={{
101
+ position: "fixed",
102
+ top: 0,
103
+ left: 0,
104
+ backgroundColor: "var(--shadow-tertiary)",
105
+ zIndex: 9999,
106
+ }}
107
+ />,
108
+ document.body,
109
+ )}
110
+ </TabsCursorContext.Provider>
111
+ </div>
112
+ );
113
+ };
114
+
115
+ export const useTabsCursor = () => {
116
+ const context = useContext(TabsCursorContext);
117
+ if (!context) {
118
+ return { setCursorTarget: () => {} };
119
+ }
120
+ return context;
121
+ };
@@ -0,0 +1,40 @@
1
+ import type { ReactNode, ComponentType, HTMLAttributes, ElementType, MouseEvent } from "react";
2
+
3
+ export interface CursorContextType {
4
+ mousePosition: { x: number; y: number };
5
+ hoveredElementId: string | null;
6
+ elementDimensions: {
7
+ width: number;
8
+ height: number;
9
+ x: number;
10
+ y: number;
11
+ right?: number;
12
+ bottom?: number;
13
+ };
14
+ registerHoveredElement: (id: string, dimensions: DOMRect, element?: HTMLElement) => void;
15
+ unregisterHoveredElement: () => void;
16
+ updateElementDimensions: (dimensions: DOMRect) => void;
17
+ }
18
+
19
+ export interface CursorProviderProps extends HTMLAttributes<HTMLDivElement> {
20
+ children: ReactNode;
21
+ maxOffsetX?: number;
22
+ maxOffsetY?: number;
23
+ cursorSize?: number;
24
+ transitionDuration?: number;
25
+ cursorClassName?: string;
26
+ as?: ElementType;
27
+ }
28
+
29
+ export interface WithCursorProps {
30
+ id?: string;
31
+ onMouseEnter?: (event: MouseEvent<HTMLElement>) => void;
32
+ onMouseMove?: (event: MouseEvent<HTMLElement>) => void;
33
+ onMouseLeave?: (event: MouseEvent<HTMLElement>) => void;
34
+ }
35
+
36
+ export type EnhancedComponentProps<P> = P & WithCursorProps;
37
+
38
+ export type EnhancedComponent<P = Record<string, unknown>> = ComponentType<
39
+ EnhancedComponentProps<P>
40
+ >;
@@ -0,0 +1,144 @@
1
+ import { forwardRef, useRef, useId, type ComponentType } from "react";
2
+
3
+ import { useCursor } from "./cursor-context";
4
+ import type { EnhancedComponentProps } from "./types";
5
+
6
+ /**
7
+ * Helper function to determine the visible portion of an element within scrollable containers
8
+ * @param element The DOM element we want to check visibility for
9
+ * @param elementRect The original bounding client rect of the element
10
+ * @returns A DOMRect representing only the visible portion of the element
11
+ */
12
+ const getVisibleRect = (element: HTMLElement, elementRect: DOMRect): DOMRect => {
13
+ // Find all scrollable parent containers
14
+ let parent = element.parentElement;
15
+ let visibleRect = {
16
+ ...elementRect,
17
+ left: elementRect.left,
18
+ right: elementRect.right,
19
+ top: elementRect.top,
20
+ bottom: elementRect.bottom,
21
+ width: elementRect.width,
22
+ height: elementRect.height,
23
+ x: elementRect.x,
24
+ y: elementRect.y,
25
+ };
26
+
27
+ // Traverse up through parent elements to find scrollable containers
28
+ while (parent) {
29
+ if (typeof window === "undefined") break;
30
+
31
+ const style = window.getComputedStyle(parent);
32
+ const hasOverflow = [style.overflowY, style.overflowX].some((overflow) =>
33
+ ["auto", "scroll", "hidden"].includes(overflow),
34
+ );
35
+
36
+ if (hasOverflow) {
37
+ const parentRect = parent.getBoundingClientRect();
38
+
39
+ // Calculate the intersection between the element and its scrollable parent
40
+ const newLeft = Math.max(visibleRect.left, parentRect.left);
41
+ const newTop = Math.max(visibleRect.top, parentRect.top);
42
+ const newRight = Math.min(visibleRect.right, parentRect.right);
43
+ const newBottom = Math.min(visibleRect.bottom, parentRect.bottom);
44
+
45
+ // Update the visible rect dimensions
46
+ visibleRect = {
47
+ ...visibleRect,
48
+ left: newLeft,
49
+ top: newTop,
50
+ right: newRight,
51
+ bottom: newBottom,
52
+ width: Math.max(0, newRight - newLeft),
53
+ height: Math.max(0, newBottom - newTop),
54
+ x: newLeft,
55
+ y: newTop,
56
+ };
57
+
58
+ // If element is completely outside the viewport of the parent, return empty rect
59
+ if (visibleRect.width <= 0 || visibleRect.height <= 0) {
60
+ return new DOMRect(0, 0, 0, 0);
61
+ }
62
+ }
63
+
64
+ parent = parent.parentElement;
65
+ }
66
+
67
+ return DOMRect.fromRect(visibleRect);
68
+ };
69
+
70
+ export function withCursor<P extends object>(Component: ComponentType<P>) {
71
+ // Create a display name for the enhanced component
72
+ const displayName = Component.displayName || Component.name || "Component";
73
+
74
+ const EnhancedComponent = forwardRef<HTMLElement, EnhancedComponentProps<P>>(
75
+ ({ id: propId, onMouseEnter, onMouseMove, onMouseLeave, ...props }, ref) => {
76
+ const { registerHoveredElement, unregisterHoveredElement, updateElementDimensions } =
77
+ useCursor();
78
+
79
+ const generatedId = useId();
80
+ const id = propId || `cursor-element-${generatedId}`;
81
+ const elementRef = useRef<HTMLElement>(null);
82
+
83
+ const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
84
+ if (elementRef.current) {
85
+ const elementRect = elementRef.current.getBoundingClientRect();
86
+ const visibleRect = getVisibleRect(elementRef.current, elementRect);
87
+ registerHoveredElement(id, visibleRect, elementRef.current);
88
+ }
89
+ onMouseEnter?.(event);
90
+ };
91
+
92
+ const handleMouseMove = (event: React.MouseEvent<HTMLElement>) => {
93
+ if (elementRef.current) {
94
+ const elementRect = elementRef.current.getBoundingClientRect();
95
+ const visibleRect = getVisibleRect(elementRef.current, elementRect);
96
+ updateElementDimensions(visibleRect);
97
+ }
98
+ onMouseMove?.(event);
99
+ };
100
+
101
+ const handleMouseOut = (event: React.MouseEvent<HTMLElement>) => {
102
+ const rect = elementRef.current?.getBoundingClientRect();
103
+ if (rect) {
104
+ const { clientX, clientY } = event;
105
+ const isOutside =
106
+ clientX < rect.left ||
107
+ clientX > rect.right ||
108
+ clientY < rect.top ||
109
+ clientY > rect.bottom;
110
+
111
+ if (isOutside) {
112
+ unregisterHoveredElement();
113
+ }
114
+ } else {
115
+ unregisterHoveredElement();
116
+ }
117
+ onMouseLeave?.(event);
118
+ };
119
+
120
+ return (
121
+ <Component
122
+ {...(props as P)}
123
+ id={id}
124
+ ref={(node: HTMLElement) => {
125
+ // Handle both function and object refs
126
+ if (typeof ref === "function") {
127
+ ref(node);
128
+ } else if (ref) {
129
+ ref.current = node;
130
+ }
131
+ elementRef.current = node;
132
+ }}
133
+ onMouseEnter={handleMouseEnter}
134
+ onMouseMove={handleMouseMove}
135
+ onMouseOut={handleMouseOut}
136
+ />
137
+ );
138
+ },
139
+ );
140
+
141
+ EnhancedComponent.displayName = `withCursor(${displayName})`;
142
+
143
+ return EnhancedComponent;
144
+ }
package/src/index.css ADDED
@@ -0,0 +1,5 @@
1
+ @import "tw-animate-css";
2
+
3
+ @import "./styles/variables.css";
4
+ @import "./styles/theme.css";
5
+ @import "./styles/toast.css";
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./ui";
2
+
3
+ export * from "./utils";
4
+
5
+ export * from "./highlight-cursor";