@arolariu/components 0.1.0 → 0.1.1
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/EXAMPLES.md +1035 -1035
- package/LICENSE.md +21 -21
- package/changelog.md +9 -0
- package/dist/components/ui/bubble-background.js.map +1 -1
- package/dist/components/ui/chart.d.ts +25 -11
- package/dist/components/ui/chart.d.ts.map +1 -1
- package/dist/components/ui/chart.js +14 -11
- package/dist/components/ui/chart.js.map +1 -1
- package/dist/components/ui/dropdrawer.js.map +1 -1
- package/dist/components/ui/typewriter.d.ts +18 -0
- package/dist/components/ui/typewriter.d.ts.map +1 -0
- package/dist/components/ui/typewriter.js +128 -0
- package/dist/components/ui/typewriter.js.map +1 -0
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/index.css +67 -25
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/utils.js.map +1 -1
- package/package.json +42 -37
- package/src/components/ui/bubble-background.tsx +189 -189
- package/src/components/ui/chart.tsx +67 -35
- package/src/components/ui/dropdrawer.tsx +973 -973
- package/src/components/ui/typewriter.tsx +188 -0
- package/src/hooks/use-mobile.tsx +44 -44
- package/src/index.ts +407 -400
- package/src/lib/utils.ts +10 -10
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { motion, stagger, useAnimate, useInView } from "motion/react";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
|
|
8
|
+
export const TypewriterText = ({
|
|
9
|
+
words,
|
|
10
|
+
className,
|
|
11
|
+
cursorClassName,
|
|
12
|
+
}: {
|
|
13
|
+
words: {
|
|
14
|
+
text: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}[];
|
|
17
|
+
className?: string;
|
|
18
|
+
cursorClassName?: string;
|
|
19
|
+
}): React.JSX.Element => {
|
|
20
|
+
// split text inside of words into array of characters
|
|
21
|
+
const wordsArray = words.map((word) => {
|
|
22
|
+
return {
|
|
23
|
+
...word,
|
|
24
|
+
text: word.text.split(""),
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const [scope, animate] = useAnimate();
|
|
29
|
+
const isInView = useInView(scope);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (isInView) {
|
|
32
|
+
animate(
|
|
33
|
+
"span",
|
|
34
|
+
{
|
|
35
|
+
display: "inline-block",
|
|
36
|
+
opacity: 1,
|
|
37
|
+
width: "fit-content",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
duration: 0.3,
|
|
41
|
+
delay: stagger(0.1),
|
|
42
|
+
ease: "easeInOut",
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}, [isInView]);
|
|
47
|
+
|
|
48
|
+
const renderWords = () => {
|
|
49
|
+
return (
|
|
50
|
+
<motion.div ref={scope} className="inline">
|
|
51
|
+
{wordsArray.map((word, idx) => {
|
|
52
|
+
return (
|
|
53
|
+
<div key={`word-${idx}`} className="inline-block">
|
|
54
|
+
{word.text.map((char, index) => (
|
|
55
|
+
<motion.span
|
|
56
|
+
initial={{}}
|
|
57
|
+
key={`char-${index}`}
|
|
58
|
+
className={cn(
|
|
59
|
+
`dark:text-white text-black opacity-0 hidden`,
|
|
60
|
+
word.className,
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
{char}
|
|
64
|
+
</motion.span>
|
|
65
|
+
))}
|
|
66
|
+
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</motion.div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
className={cn(
|
|
76
|
+
"text-base sm:text-xl md:text-3xl lg:text-5xl font-bold text-center",
|
|
77
|
+
className,
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{renderWords()}
|
|
81
|
+
<motion.span
|
|
82
|
+
initial={{
|
|
83
|
+
opacity: 0,
|
|
84
|
+
}}
|
|
85
|
+
animate={{
|
|
86
|
+
opacity: 1,
|
|
87
|
+
}}
|
|
88
|
+
transition={{
|
|
89
|
+
duration: 0.8,
|
|
90
|
+
repeat: Infinity,
|
|
91
|
+
repeatType: "reverse",
|
|
92
|
+
}}
|
|
93
|
+
className={cn(
|
|
94
|
+
"inline-block rounded-sm w-[4px] h-4 md:h-6 lg:h-10 bg-blue-500",
|
|
95
|
+
cursorClassName,
|
|
96
|
+
)}
|
|
97
|
+
></motion.span>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const TypewriterTextSmooth = ({
|
|
103
|
+
words,
|
|
104
|
+
className,
|
|
105
|
+
cursorClassName,
|
|
106
|
+
}: {
|
|
107
|
+
words: {
|
|
108
|
+
text: string;
|
|
109
|
+
className?: string;
|
|
110
|
+
}[];
|
|
111
|
+
className?: string;
|
|
112
|
+
cursorClassName?: string;
|
|
113
|
+
}): React.JSX.Element => {
|
|
114
|
+
// split text inside of words into array of characters
|
|
115
|
+
const wordsArray = words.map((word) => {
|
|
116
|
+
return {
|
|
117
|
+
...word,
|
|
118
|
+
text: word.text.split(""),
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
const renderWords = () => {
|
|
122
|
+
return (
|
|
123
|
+
<div>
|
|
124
|
+
{wordsArray.map((word, idx) => {
|
|
125
|
+
return (
|
|
126
|
+
<div key={`word-${idx}`} className="inline-block">
|
|
127
|
+
{word.text.map((char, index) => (
|
|
128
|
+
<span
|
|
129
|
+
key={`char-${index}`}
|
|
130
|
+
className={cn(`dark:text-white text-black `, word.className)}
|
|
131
|
+
>
|
|
132
|
+
{char}
|
|
133
|
+
</span>
|
|
134
|
+
))}
|
|
135
|
+
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
})}
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div className={cn("flex space-x-1 my-6", className)}>
|
|
145
|
+
<motion.div
|
|
146
|
+
className="overflow-hidden pb-2"
|
|
147
|
+
initial={{
|
|
148
|
+
width: "0%",
|
|
149
|
+
}}
|
|
150
|
+
whileInView={{
|
|
151
|
+
width: "fit-content",
|
|
152
|
+
}}
|
|
153
|
+
transition={{
|
|
154
|
+
duration: 2,
|
|
155
|
+
ease: "linear",
|
|
156
|
+
delay: 1,
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
<div
|
|
160
|
+
className="text-xs sm:text-base md:text-xl lg:text:3xl xl:text-5xl font-bold"
|
|
161
|
+
style={{
|
|
162
|
+
whiteSpace: "nowrap",
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
165
|
+
{renderWords()}{" "}
|
|
166
|
+
</div>{" "}
|
|
167
|
+
</motion.div>
|
|
168
|
+
<motion.span
|
|
169
|
+
initial={{
|
|
170
|
+
opacity: 0,
|
|
171
|
+
}}
|
|
172
|
+
animate={{
|
|
173
|
+
opacity: 1,
|
|
174
|
+
}}
|
|
175
|
+
transition={{
|
|
176
|
+
duration: 0.8,
|
|
177
|
+
|
|
178
|
+
repeat: Infinity,
|
|
179
|
+
repeatType: "reverse",
|
|
180
|
+
}}
|
|
181
|
+
className={cn(
|
|
182
|
+
"block rounded-sm w-[4px] h-4 sm:h-6 xl:h-12 bg-blue-500",
|
|
183
|
+
cursorClassName,
|
|
184
|
+
)}
|
|
185
|
+
></motion.span>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
};
|
package/src/hooks/use-mobile.tsx
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
const MOBILE_BREAKPOINT = 768;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* A custom React hook that detects whether the current device is a mobile device
|
|
7
|
-
* based on the screen width.
|
|
8
|
-
*
|
|
9
|
-
* This hook uses a media query to check if the viewport width is less than the defined
|
|
10
|
-
* mobile breakpoint (768px). It updates the state when the window size changes.
|
|
11
|
-
*
|
|
12
|
-
* @returns {boolean} Returns true if the viewport width is less than the mobile breakpoint,
|
|
13
|
-
* false otherwise.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* function MyComponent() {
|
|
18
|
-
* const isMobile = useIsMobile();
|
|
19
|
-
*
|
|
20
|
-
* return (
|
|
21
|
-
* <div>
|
|
22
|
-
* {isMobile ? 'Mobile View' : 'Desktop View'}
|
|
23
|
-
* </div>
|
|
24
|
-
* );
|
|
25
|
-
* }
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function useIsMobile(): boolean {
|
|
29
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
30
|
-
undefined,
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
React.useEffect(() => {
|
|
34
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
35
|
-
const onChange = () => {
|
|
36
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
37
|
-
};
|
|
38
|
-
mql.addEventListener("change", onChange);
|
|
39
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
40
|
-
return () => mql.removeEventListener("change", onChange);
|
|
41
|
-
}, []);
|
|
42
|
-
|
|
43
|
-
return !!isMobile;
|
|
44
|
-
}
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A custom React hook that detects whether the current device is a mobile device
|
|
7
|
+
* based on the screen width.
|
|
8
|
+
*
|
|
9
|
+
* This hook uses a media query to check if the viewport width is less than the defined
|
|
10
|
+
* mobile breakpoint (768px). It updates the state when the window size changes.
|
|
11
|
+
*
|
|
12
|
+
* @returns {boolean} Returns true if the viewport width is less than the mobile breakpoint,
|
|
13
|
+
* false otherwise.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* function MyComponent() {
|
|
18
|
+
* const isMobile = useIsMobile();
|
|
19
|
+
*
|
|
20
|
+
* return (
|
|
21
|
+
* <div>
|
|
22
|
+
* {isMobile ? 'Mobile View' : 'Desktop View'}
|
|
23
|
+
* </div>
|
|
24
|
+
* );
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useIsMobile(): boolean {
|
|
29
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
30
|
+
undefined,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
35
|
+
const onChange = () => {
|
|
36
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
37
|
+
};
|
|
38
|
+
mql.addEventListener("change", onChange);
|
|
39
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
40
|
+
return () => mql.removeEventListener("change", onChange);
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
return !!isMobile;
|
|
44
|
+
}
|