@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.
- package/package.json +6 -1
- package/.storybook/globals.d.ts +0 -1
- package/.storybook/main.ts +0 -29
- package/.storybook/preview.ts +0 -32
- package/components/Welcome.mdx +0 -74
- package/components/lenis/index.tsx +0 -48
- package/components/motion/auto-height.tsx +0 -56
- package/components/motion/cursor.tsx +0 -108
- package/components/motion/highlight.tsx +0 -605
- package/components/motion/number-ticker.tsx +0 -55
- package/components/motion/slot.tsx +0 -106
- package/components/motion/waves.tsx +0 -417
- package/components/primitives/tabs.tsx +0 -174
- package/components/ui/Accordion/index.stories.tsx +0 -39
- package/components/ui/Accordion/index.tsx +0 -170
- package/components/ui/Alert/index.stories.tsx +0 -39
- package/components/ui/Alert/index.tsx +0 -60
- package/components/ui/AlertDialog/index.stories.tsx +0 -47
- package/components/ui/AlertDialog/index.tsx +0 -172
- package/components/ui/AspectRatio/index.stories.tsx +0 -40
- package/components/ui/AspectRatio/index.tsx +0 -9
- package/components/ui/Avatar/index.stories.tsx +0 -39
- package/components/ui/Avatar/index.tsx +0 -44
- package/components/ui/Badge/index.stories.tsx +0 -64
- package/components/ui/Badge/index.tsx +0 -46
- package/components/ui/Breadcrumb/index.stories.tsx +0 -64
- package/components/ui/Breadcrumb/index.tsx +0 -102
- package/components/ui/Button/index.stories.tsx +0 -232
- package/components/ui/Button/index.tsx +0 -114
- package/components/ui/Calendar/index.stories.tsx +0 -20
- package/components/ui/Calendar/index.tsx +0 -149
- package/components/ui/Card/index.stories.tsx +0 -39
- package/components/ui/Card/index.tsx +0 -65
- package/components/ui/Carousel/index.stories.tsx +0 -37
- package/components/ui/Carousel/index.tsx +0 -242
- package/components/ui/Chart/index.stories.tsx +0 -53
- package/components/ui/Chart/index.tsx +0 -322
- package/components/ui/Checkbox/index.stories.tsx +0 -56
- package/components/ui/Checkbox/index.tsx +0 -167
- package/components/ui/CircleProcess/index.stories.tsx +0 -29
- package/components/ui/CircleProcess/index.tsx +0 -50
- package/components/ui/Collapsible/index.stories.tsx +0 -33
- package/components/ui/Collapsible/index.tsx +0 -124
- package/components/ui/Command/index.stories.tsx +0 -65
- package/components/ui/Command/index.tsx +0 -161
- package/components/ui/Container/index.stories.tsx +0 -22
- package/components/ui/Container/index.tsx +0 -30
- package/components/ui/ContextMenu/index.stories.tsx +0 -51
- package/components/ui/ContextMenu/index.tsx +0 -224
- package/components/ui/Dialog/index.stories.tsx +0 -44
- package/components/ui/Dialog/index.tsx +0 -156
- package/components/ui/Drawer/index.stories.tsx +0 -54
- package/components/ui/Drawer/index.tsx +0 -124
- package/components/ui/DropdownMenu/index.stories.tsx +0 -83
- package/components/ui/DropdownMenu/index.tsx +0 -231
- package/components/ui/Dropzone/index.stories.tsx +0 -18
- package/components/ui/Dropzone/index.tsx +0 -47
- package/components/ui/Form/date-field.tsx +0 -77
- package/components/ui/Form/index.stories.tsx +0 -67
- package/components/ui/Form/index.tsx +0 -193
- package/components/ui/Form/select-field.tsx +0 -55
- package/components/ui/Form/text-area-field.tsx +0 -37
- package/components/ui/Form/text-field.tsx +0 -72
- package/components/ui/HStack/index.stories.tsx +0 -48
- package/components/ui/HStack/index.tsx +0 -73
- package/components/ui/HoverCard/index.stories.tsx +0 -38
- package/components/ui/HoverCard/index.tsx +0 -38
- package/components/ui/Icons/index.stories.tsx +0 -27
- package/components/ui/Icons/index.tsx +0 -33
- package/components/ui/ImageWithFallback/index.stories.tsx +0 -32
- package/components/ui/ImageWithFallback/index.tsx +0 -34
- package/components/ui/Input/index.stories.tsx +0 -47
- package/components/ui/Input/index.tsx +0 -21
- package/components/ui/InputOtp/index.stories.tsx +0 -35
- package/components/ui/InputOtp/index.tsx +0 -70
- package/components/ui/Label/index.stories.tsx +0 -18
- package/components/ui/Label/index.tsx +0 -21
- package/components/ui/Marquee/index.stories.tsx +0 -71
- package/components/ui/Marquee/index.tsx +0 -65
- package/components/ui/Menubar/index.stories.tsx +0 -116
- package/components/ui/Menubar/index.tsx +0 -252
- package/components/ui/NavigationMenu/index.stories.tsx +0 -112
- package/components/ui/NavigationMenu/index.tsx +0 -185
- package/components/ui/NoData/index.stories.tsx +0 -24
- package/components/ui/NoData/index.tsx +0 -19
- package/components/ui/Pagination/index.stories.tsx +0 -53
- package/components/ui/Pagination/index.tsx +0 -114
- package/components/ui/Popover/index.stories.tsx +0 -31
- package/components/ui/Popover/index.tsx +0 -42
- package/components/ui/Progress/index.stories.tsx +0 -35
- package/components/ui/Progress/index.tsx +0 -28
- package/components/ui/RadioGroup/index.stories.tsx +0 -28
- package/components/ui/RadioGroup/index.tsx +0 -45
- package/components/ui/Resizable/index.stories.tsx +0 -44
- package/components/ui/Resizable/index.tsx +0 -54
- package/components/ui/ScrollArea/index.stories.tsx +0 -31
- package/components/ui/ScrollArea/index.tsx +0 -56
- package/components/ui/Select/index.stories.tsx +0 -64
- package/components/ui/Select/index.tsx +0 -170
- package/components/ui/Separator/index.stories.tsx +0 -31
- package/components/ui/Separator/index.tsx +0 -28
- package/components/ui/Sheet/index.stories.tsx +0 -45
- package/components/ui/Sheet/index.tsx +0 -130
- package/components/ui/Sidebar/index.stories.tsx +0 -82
- package/components/ui/Sidebar/index.tsx +0 -676
- package/components/ui/Skeleton/index.stories.tsx +0 -36
- package/components/ui/Skeleton/index.tsx +0 -13
- package/components/ui/Slider/index.stories.tsx +0 -48
- package/components/ui/Slider/index.tsx +0 -82
- package/components/ui/Slot/index.stories.tsx +0 -29
- package/components/ui/Slot/index.tsx +0 -106
- package/components/ui/Sonner/index.stories.tsx +0 -36
- package/components/ui/Sonner/index.tsx +0 -31
- package/components/ui/Switch/index.stories.tsx +0 -33
- package/components/ui/Switch/index.tsx +0 -28
- package/components/ui/Table/index.stories.tsx +0 -74
- package/components/ui/Table/index.tsx +0 -95
- package/components/ui/Tabs/index.stories.tsx +0 -38
- package/components/ui/Tabs/index.tsx +0 -78
- package/components/ui/Text/index.stories.tsx +0 -53
- package/components/ui/Text/index.tsx +0 -138
- package/components/ui/Textarea/index.stories.tsx +0 -25
- package/components/ui/Textarea/index.tsx +0 -18
- package/components/ui/Toggle/index.stories.tsx +0 -52
- package/components/ui/Toggle/index.tsx +0 -46
- package/components/ui/ToggleGroup/index.stories.tsx +0 -52
- package/components/ui/ToggleGroup/index.tsx +0 -69
- package/components/ui/Tooltip/index.stories.tsx +0 -29
- package/components/ui/Tooltip/index.tsx +0 -35
- package/components/ui/VStack/index.stories.tsx +0 -45
- package/components/ui/VStack/index.tsx +0 -69
- package/components/ui/colors.stories.tsx +0 -148
- package/eslint.config.js +0 -10
- package/hooks/index.ts +0 -3
- package/hooks/use-auto-height.tsx +0 -99
- package/hooks/use-controlled-state.tsx +0 -32
- package/hooks/use-mobile.ts +0 -19
- package/index.ts +0 -58
- package/lib/get-strict-context.ts +0 -15
- package/lib/utils.ts +0 -10
- package/scripts/generate-exports.ts +0 -32
- package/tsconfig.json +0 -12
- package/tsconfig.tsbuildinfo +0 -1
- package/tsup.config.ts +0 -11
- package/types/svg.d.ts +0 -10
- package/vercel.json +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luanthnh/cntt-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
},
|
|
16
16
|
"./globals.css": "./globals.css"
|
|
17
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"globals.css",
|
|
21
|
+
"assets"
|
|
22
|
+
],
|
|
18
23
|
"publishConfig": {
|
|
19
24
|
"access": "public"
|
|
20
25
|
},
|
package/.storybook/globals.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare module '*.css';
|
package/.storybook/main.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import type { StorybookConfig } from '@storybook/react-vite';
|
|
3
|
-
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
-
|
|
5
|
-
const config: StorybookConfig = {
|
|
6
|
-
stories: ['../components/**/*.mdx', '../components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
|
7
|
-
addons: [
|
|
8
|
-
'@storybook/addon-essentials',
|
|
9
|
-
'@storybook/addon-interactions',
|
|
10
|
-
'@storybook/addon-onboarding',
|
|
11
|
-
],
|
|
12
|
-
framework: {
|
|
13
|
-
name: '@storybook/react-vite',
|
|
14
|
-
options: {},
|
|
15
|
-
},
|
|
16
|
-
viteFinal: async (config) => {
|
|
17
|
-
config.plugins = config.plugins || [];
|
|
18
|
-
config.plugins.push(tailwindcss());
|
|
19
|
-
|
|
20
|
-
if (config.resolve) {
|
|
21
|
-
config.resolve.alias = {
|
|
22
|
-
...config.resolve.alias,
|
|
23
|
-
'@': path.resolve(__dirname, '../'),
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
return config;
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
export default config;
|
package/.storybook/preview.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Preview } from '@storybook/react';
|
|
2
|
-
|
|
3
|
-
import '../globals.css';
|
|
4
|
-
|
|
5
|
-
const preview: Preview = {
|
|
6
|
-
parameters: {
|
|
7
|
-
controls: {
|
|
8
|
-
matchers: {
|
|
9
|
-
color: /(background|color)$/i,
|
|
10
|
-
date: /Date$/i,
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
backgrounds: {
|
|
14
|
-
default: 'light',
|
|
15
|
-
values: [
|
|
16
|
-
{ name: 'light', value: '#ffffff' },
|
|
17
|
-
{ name: 'dark', value: '#0a0e1a' },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
decorators: [
|
|
22
|
-
(Story, context) => {
|
|
23
|
-
const theme = context.globals.backgrounds?.value === '#0a0e1a' ? 'dark' : 'light';
|
|
24
|
-
if (typeof document !== 'undefined') {
|
|
25
|
-
document.documentElement.classList.toggle('dark', theme === 'dark');
|
|
26
|
-
}
|
|
27
|
-
return Story();
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default preview;
|
package/components/Welcome.mdx
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Meta } from '@storybook/blocks';
|
|
2
|
-
|
|
3
|
-
<Meta title="Welcome" />
|
|
4
|
-
|
|
5
|
-
# Welcome 👋🏼
|
|
6
|
-
|
|
7
|
-
Welcome to the **@luanthnh/cntt-ui** component library. A modern, high-performance UI component library built with **React**, **Tailwind CSS (v4)**, **Radix UI**, and **Framer Motion**.
|
|
8
|
-
|
|
9
|
-
🔗 **Live Demo**: [https://cntt-ui.vercel.app/](https://cntt-ui.vercel.app/)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 📦 Install
|
|
14
|
-
|
|
15
|
-
Use your preferred package manager to install the library:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# npm
|
|
19
|
-
npm install @luanthnh/cntt-ui
|
|
20
|
-
|
|
21
|
-
# pnpm
|
|
22
|
-
pnpm add @luanthnh/cntt-ui
|
|
23
|
-
|
|
24
|
-
# yarn
|
|
25
|
-
yarn add @luanthnh/cntt-ui
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 🛠️ Setup
|
|
31
|
-
|
|
32
|
-
To get started, follow these simple steps:
|
|
33
|
-
|
|
34
|
-
### 1. Import Global CSS
|
|
35
|
-
|
|
36
|
-
Include the global styles in your application's entry point (e.g., `layout.tsx` or `index.tsx`):
|
|
37
|
-
|
|
38
|
-
```tsx
|
|
39
|
-
import '@luanthnh/cntt-ui/globals.css';
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 2. Configure Tailwind CSS (v4)
|
|
43
|
-
|
|
44
|
-
Ensure your project is set up with Tailwind CSS v4. If you are using Next.js, make sure to configure `@tailwindcss/postcss` for optimal performance.
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## 🚀 Use the component
|
|
49
|
-
|
|
50
|
-
Start using our premium components in your project:
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import { Button, Card, Text } from '@luanthnh/cntt-ui';
|
|
54
|
-
|
|
55
|
-
export default function App() {
|
|
56
|
-
return (
|
|
57
|
-
<Card className="max-w-sm p-6">
|
|
58
|
-
<Text variant="h3" className="mb-4">
|
|
59
|
-
Hello World!
|
|
60
|
-
</Text>
|
|
61
|
-
<Text className="mb-6 opacity-80">
|
|
62
|
-
You are now using the powerful @luanthnh/cntt-ui components.
|
|
63
|
-
</Text>
|
|
64
|
-
<Button variant="primary" onClick={() => alert('Ready to build!')}>
|
|
65
|
-
Get Started
|
|
66
|
-
</Button>
|
|
67
|
-
</Card>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
Designed with ❤️ for a superior developer experience.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { LenisOptions } from 'lenis';
|
|
4
|
-
|
|
5
|
-
import 'lenis/dist/lenis.css';
|
|
6
|
-
|
|
7
|
-
import { useRef } from 'react';
|
|
8
|
-
import { LenisRef, ReactLenis, LenisProps as ReactLenisProps } from 'lenis/react';
|
|
9
|
-
import { useTempus } from 'tempus/react';
|
|
10
|
-
|
|
11
|
-
interface LenisProps extends Omit<ReactLenisProps, 'ref'> {
|
|
12
|
-
root: boolean;
|
|
13
|
-
options: LenisOptions;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function Lenis({ root, options }: LenisProps) {
|
|
17
|
-
const lenisRef = useRef<LenisRef>(null);
|
|
18
|
-
// const lenis = useLenis();
|
|
19
|
-
|
|
20
|
-
useTempus((time: number) => {
|
|
21
|
-
if (lenisRef.current?.lenis) {
|
|
22
|
-
lenisRef.current.lenis.raf(time);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// useEffect(() => {
|
|
27
|
-
// if (isNavOpened) {
|
|
28
|
-
// lenis?.stop();
|
|
29
|
-
// } else {
|
|
30
|
-
// lenis?.start();
|
|
31
|
-
// }
|
|
32
|
-
// }, [isNavOpened, lenis]);
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<ReactLenis
|
|
36
|
-
ref={lenisRef}
|
|
37
|
-
root={root}
|
|
38
|
-
options={{
|
|
39
|
-
...options,
|
|
40
|
-
lerp: options?.lerp ?? 0.125,
|
|
41
|
-
autoRaf: false,
|
|
42
|
-
anchors: true,
|
|
43
|
-
prevent: (node: Element | null) =>
|
|
44
|
-
node?.nodeName === 'VERCEL-LIVE-FEEDBACK' || node?.id === 'theatrejs-studio-root',
|
|
45
|
-
}}
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import {
|
|
5
|
-
motion,
|
|
6
|
-
type AnimationControls,
|
|
7
|
-
type HTMLMotionProps,
|
|
8
|
-
type TargetAndTransition,
|
|
9
|
-
type Transition,
|
|
10
|
-
} from 'motion/react';
|
|
11
|
-
|
|
12
|
-
import { useAutoHeight } from '@/hooks/use-auto-height';
|
|
13
|
-
|
|
14
|
-
import { Slot, WithAsChild } from './slot';
|
|
15
|
-
|
|
16
|
-
type AutoHeightProps = WithAsChild<
|
|
17
|
-
{
|
|
18
|
-
children: React.ReactNode;
|
|
19
|
-
deps?: React.DependencyList;
|
|
20
|
-
animate?: TargetAndTransition | AnimationControls;
|
|
21
|
-
transition?: Transition;
|
|
22
|
-
} & Omit<HTMLMotionProps<'div'>, 'animate'>
|
|
23
|
-
>;
|
|
24
|
-
|
|
25
|
-
function AutoHeight({
|
|
26
|
-
children,
|
|
27
|
-
deps = [],
|
|
28
|
-
transition = {
|
|
29
|
-
type: 'spring',
|
|
30
|
-
stiffness: 300,
|
|
31
|
-
damping: 30,
|
|
32
|
-
bounce: 0,
|
|
33
|
-
restDelta: 0.01,
|
|
34
|
-
},
|
|
35
|
-
style,
|
|
36
|
-
animate,
|
|
37
|
-
asChild = false,
|
|
38
|
-
...props
|
|
39
|
-
}: AutoHeightProps) {
|
|
40
|
-
const { ref, height } = useAutoHeight<HTMLDivElement>(deps);
|
|
41
|
-
|
|
42
|
-
const Comp = asChild ? Slot : motion.div;
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<Comp
|
|
46
|
-
style={{ overflow: 'hidden', ...style }}
|
|
47
|
-
animate={{ height, ...animate }}
|
|
48
|
-
transition={transition}
|
|
49
|
-
{...props}
|
|
50
|
-
>
|
|
51
|
-
<div ref={ref}>{children}</div>
|
|
52
|
-
</Comp>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export { AutoHeight, type AutoHeightProps };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
export default function Cursor() {
|
|
6
|
-
const mousePosition = useRef({ x: 0, y: 0 });
|
|
7
|
-
|
|
8
|
-
const dotPosition = useRef({ x: 0, y: 0 });
|
|
9
|
-
const borderDotPosition = useRef({ x: 0, y: 0 });
|
|
10
|
-
|
|
11
|
-
const [renderPos, setRenderPos] = useState({ dot: { x: 0, y: 0 }, border: { x: 0, y: 0 } });
|
|
12
|
-
const [isHovering, setIsHovering] = useState(false);
|
|
13
|
-
|
|
14
|
-
const DOT_SMOOTHNESS = 0.2;
|
|
15
|
-
const BORDER_DOT_SMOOTHNESS = 0.1;
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
const handleMouseMove = (e: MouseEvent) => {
|
|
19
|
-
mousePosition.current = { x: e.clientX, y: e.clientY };
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const handleMouseEnter = () => setIsHovering(true);
|
|
23
|
-
const handleMouseLeave = () => setIsHovering(false);
|
|
24
|
-
|
|
25
|
-
// Add event listeners
|
|
26
|
-
window.addEventListener('mousemove', handleMouseMove);
|
|
27
|
-
|
|
28
|
-
const interactiveElements = document.querySelectorAll(
|
|
29
|
-
'a, button, img, input, textarea, select',
|
|
30
|
-
);
|
|
31
|
-
interactiveElements.forEach((element) => {
|
|
32
|
-
element.addEventListener('mouseenter', handleMouseEnter);
|
|
33
|
-
element.addEventListener('mouseleave', handleMouseLeave);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Animation function for smooth movement
|
|
37
|
-
const animate = () => {
|
|
38
|
-
const lerp = (start: number, end: number, factor: number) => {
|
|
39
|
-
return start + (end - start) * factor;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
dotPosition.current.x = lerp(dotPosition.current.x, mousePosition.current.x, DOT_SMOOTHNESS);
|
|
43
|
-
dotPosition.current.y = lerp(dotPosition.current.y, mousePosition.current.y, DOT_SMOOTHNESS);
|
|
44
|
-
|
|
45
|
-
borderDotPosition.current.x = lerp(
|
|
46
|
-
borderDotPosition.current.x,
|
|
47
|
-
mousePosition.current.x,
|
|
48
|
-
BORDER_DOT_SMOOTHNESS,
|
|
49
|
-
);
|
|
50
|
-
borderDotPosition.current.y = lerp(
|
|
51
|
-
borderDotPosition.current.y,
|
|
52
|
-
mousePosition.current.y,
|
|
53
|
-
BORDER_DOT_SMOOTHNESS,
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
setRenderPos({
|
|
57
|
-
dot: { x: dotPosition.current.x, y: dotPosition.current.y },
|
|
58
|
-
border: { x: borderDotPosition.current.x, y: borderDotPosition.current.y },
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
requestAnimationFrame(animate);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Start animation loop
|
|
65
|
-
const animationId = requestAnimationFrame(animate);
|
|
66
|
-
|
|
67
|
-
// Clean up
|
|
68
|
-
return () => {
|
|
69
|
-
window.removeEventListener('mousemove', handleMouseMove);
|
|
70
|
-
|
|
71
|
-
interactiveElements.forEach((element) => {
|
|
72
|
-
element.removeEventListener('mouseenter', handleMouseEnter);
|
|
73
|
-
element.removeEventListener('mouseleave', handleMouseLeave);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
cancelAnimationFrame(animationId);
|
|
77
|
-
};
|
|
78
|
-
}, []);
|
|
79
|
-
|
|
80
|
-
if (typeof window === 'undefined') return null;
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<div className="pointer-events-none fixed inset-0 z-[999]">
|
|
84
|
-
<div
|
|
85
|
-
className="bg-primary absolute rounded-full"
|
|
86
|
-
style={{
|
|
87
|
-
width: '8px',
|
|
88
|
-
height: '8px',
|
|
89
|
-
transform: 'translate(-50%, -50%)',
|
|
90
|
-
left: `${renderPos.dot.x}px`,
|
|
91
|
-
top: `${renderPos.dot.y}px`,
|
|
92
|
-
}}
|
|
93
|
-
/>
|
|
94
|
-
|
|
95
|
-
<div
|
|
96
|
-
className="border-primary absolute rounded-full border"
|
|
97
|
-
style={{
|
|
98
|
-
width: isHovering ? '32px' : '24px',
|
|
99
|
-
height: isHovering ? '32px' : '24px',
|
|
100
|
-
transform: 'translate(-50%, -50%)',
|
|
101
|
-
left: `${renderPos.border.x}px`,
|
|
102
|
-
top: `${renderPos.border.y}px`,
|
|
103
|
-
transition: 'width 0.3s, height 0.3s',
|
|
104
|
-
}}
|
|
105
|
-
/>
|
|
106
|
-
</div>
|
|
107
|
-
);
|
|
108
|
-
}
|