kmod-cli 1.0.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/README.md +53 -0
- package/bin/gen-components.js +68 -0
- package/bin/index.js +153 -0
- package/component-templates/components/access-denied.tsx +130 -0
- package/component-templates/components/breadcumb.tsx +42 -0
- package/component-templates/components/count-down.tsx +94 -0
- package/component-templates/components/count-input.tsx +221 -0
- package/component-templates/components/date-range-calendar/button.tsx +61 -0
- package/component-templates/components/date-range-calendar/calendar.tsx +132 -0
- package/component-templates/components/date-range-calendar/date-input.tsx +259 -0
- package/component-templates/components/date-range-calendar/date-range-picker.tsx +594 -0
- package/component-templates/components/date-range-calendar/label.tsx +31 -0
- package/component-templates/components/date-range-calendar/popover.tsx +32 -0
- package/component-templates/components/date-range-calendar/select.tsx +125 -0
- package/component-templates/components/date-range-calendar/switch.tsx +30 -0
- package/component-templates/components/datetime-picker/button.tsx +61 -0
- package/component-templates/components/datetime-picker/calendar.tsx +156 -0
- package/component-templates/components/datetime-picker/datetime-picker.tsx +75 -0
- package/component-templates/components/datetime-picker/input.tsx +20 -0
- package/component-templates/components/datetime-picker/label.tsx +18 -0
- package/component-templates/components/datetime-picker/period-input.tsx +62 -0
- package/component-templates/components/datetime-picker/popover.tsx +32 -0
- package/component-templates/components/datetime-picker/select.tsx +125 -0
- package/component-templates/components/datetime-picker/time-picker-input.tsx +131 -0
- package/component-templates/components/datetime-picker/time-picker-utils.tsx +204 -0
- package/component-templates/components/datetime-picker/time-picker.tsx +59 -0
- package/component-templates/components/gradient-outline.tsx +233 -0
- package/component-templates/components/gradient-svg.tsx +157 -0
- package/component-templates/components/grid-layout.tsx +69 -0
- package/component-templates/components/hydrate-guard.tsx +40 -0
- package/component-templates/components/image.tsx +92 -0
- package/component-templates/components/loader-slash-gradient.tsx +85 -0
- package/component-templates/components/masonry-gallery.tsx +221 -0
- package/component-templates/components/modal.tsx +110 -0
- package/component-templates/components/multi-select.tsx +447 -0
- package/component-templates/components/non-hydration.tsx +27 -0
- package/component-templates/components/portal.tsx +34 -0
- package/component-templates/components/segments-circle.tsx +235 -0
- package/component-templates/components/single-select.tsx +248 -0
- package/component-templates/components/stroke-circle.tsx +57 -0
- package/component-templates/components/table/column-table.tsx +15 -0
- package/component-templates/components/table/data-table.tsx +339 -0
- package/component-templates/components/table/readme.tsx +95 -0
- package/component-templates/components/table/table.tsx +60 -0
- package/component-templates/components/text-hover-effect.tsx +120 -0
- package/component-templates/components/timout-loader.tsx +52 -0
- package/component-templates/components/toast.tsx +994 -0
- package/component-templates/configs/config.ts +33 -0
- package/component-templates/configs/feature-config.tsx +432 -0
- package/component-templates/configs/keys.ts +7 -0
- package/component-templates/core/api-service.ts +202 -0
- package/component-templates/core/calculate.ts +18 -0
- package/component-templates/core/idb.ts +166 -0
- package/component-templates/core/storage.ts +213 -0
- package/component-templates/hooks/count-down.ts +38 -0
- package/component-templates/hooks/fade-on-scroll.ts +52 -0
- package/component-templates/hooks/safe-action.ts +59 -0
- package/component-templates/hooks/spam-guard.ts +31 -0
- package/component-templates/lib/utils.ts +6 -0
- package/component-templates/providers/feature-guard.tsx +432 -0
- package/component-templates/queries/query.tsx +775 -0
- package/component-templates/utils/colors/color-by-text.ts +307 -0
- package/component-templates/utils/colors/stripe-effect.ts +100 -0
- package/component-templates/utils/hash/hash-aes.ts +35 -0
- package/components.json +348 -0
- package/package.json +60 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// "use client";
|
|
2
|
+
// import React, {FC, HTMLAttributes, useState, useEffect, useRef} from "react";
|
|
3
|
+
|
|
4
|
+
// type GradientOutlineProps = {
|
|
5
|
+
// children: React.ReactNode;
|
|
6
|
+
// strokeWidth?: number;
|
|
7
|
+
// borderRadius?: number;
|
|
8
|
+
// gradientColors?: string[];
|
|
9
|
+
// hoverGradientColors?: string[];
|
|
10
|
+
// gradientAngle?: number;
|
|
11
|
+
// allowHover?: boolean;
|
|
12
|
+
// className?: string;
|
|
13
|
+
// id: string;
|
|
14
|
+
// onClick?: () => void;
|
|
15
|
+
// } & HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
|
|
17
|
+
// const GradientOutline: FC<GradientOutlineProps> = ({
|
|
18
|
+
// children,
|
|
19
|
+
// id,
|
|
20
|
+
// allowHover = true,
|
|
21
|
+
// strokeWidth = 1,
|
|
22
|
+
// borderRadius = 16,
|
|
23
|
+
// hoverGradientColors = ["#E6CAA4", "#E9B84E"],
|
|
24
|
+
// gradientColors = [
|
|
25
|
+
// "rgba(255, 255, 255, 0.08)",
|
|
26
|
+
// "rgba(255, 255, 255, 0.4)",
|
|
27
|
+
// "rgba(255, 255, 255, 0)",
|
|
28
|
+
// "rgba(255, 255, 255, 0)",
|
|
29
|
+
// "rgba(255, 255, 255, 0.1)",
|
|
30
|
+
// ],
|
|
31
|
+
// gradientAngle = 45,
|
|
32
|
+
// className = "",
|
|
33
|
+
// onClick,
|
|
34
|
+
// ...props
|
|
35
|
+
// }) => {
|
|
36
|
+
// const [isHovered, setIsHovered] = useState(false);
|
|
37
|
+
// const gradientId = `gradient-${Math.random().toString(36).substr(2, 9)}`;
|
|
38
|
+
// const containerRef = useRef<HTMLDivElement>(null);
|
|
39
|
+
// const [dimensions, setDimensions] = useState({width: 100, height: 100});
|
|
40
|
+
|
|
41
|
+
// useEffect(() => {
|
|
42
|
+
// if (containerRef.current) {
|
|
43
|
+
// const {width, height} = containerRef.current.getBoundingClientRect();
|
|
44
|
+
// setDimensions({width, height});
|
|
45
|
+
// }
|
|
46
|
+
// }, []);
|
|
47
|
+
|
|
48
|
+
// const colors = isHovered ? hoverGradientColors : gradientColors;
|
|
49
|
+
|
|
50
|
+
// return (
|
|
51
|
+
// <div
|
|
52
|
+
// ref={containerRef}
|
|
53
|
+
// id={id}
|
|
54
|
+
// onClick={onClick}
|
|
55
|
+
// onMouseEnter={() => allowHover && setIsHovered(true)}
|
|
56
|
+
// onMouseLeave={() => allowHover && setIsHovered(false)}
|
|
57
|
+
// className={`relative inline-block overflow-hidden p-2 ${className}`}
|
|
58
|
+
// style={{borderRadius}}
|
|
59
|
+
// {...props}
|
|
60
|
+
// >
|
|
61
|
+
// {children}
|
|
62
|
+
// <svg
|
|
63
|
+
// width="100%"
|
|
64
|
+
// height="100%"
|
|
65
|
+
// className="absolute top-0 left-0"
|
|
66
|
+
// viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
|
|
67
|
+
// preserveAspectRatio="none"
|
|
68
|
+
// >
|
|
69
|
+
// <defs>
|
|
70
|
+
// <linearGradient
|
|
71
|
+
// id={gradientId}
|
|
72
|
+
// x1={Math.cos((gradientAngle * Math.PI) / 180)}
|
|
73
|
+
// y1={Math.sin((gradientAngle * Math.PI) / 180)}
|
|
74
|
+
// x2={1 - Math.cos((gradientAngle * Math.PI) / 180)}
|
|
75
|
+
// y2={1 - Math.sin((gradientAngle * Math.PI) / 180)}
|
|
76
|
+
// >
|
|
77
|
+
// {colors.map((color, index) => (
|
|
78
|
+
// <stop
|
|
79
|
+
// key={index}
|
|
80
|
+
// offset={`${(index / (colors.length - 1)) * 100}%`}
|
|
81
|
+
// stopColor={color}
|
|
82
|
+
// />
|
|
83
|
+
// ))}
|
|
84
|
+
// </linearGradient>
|
|
85
|
+
// </defs>
|
|
86
|
+
// <rect
|
|
87
|
+
// x={strokeWidth / 2}
|
|
88
|
+
// y={strokeWidth / 2}
|
|
89
|
+
// width={dimensions.width - strokeWidth}
|
|
90
|
+
// height={dimensions.height - strokeWidth}
|
|
91
|
+
// rx={borderRadius}
|
|
92
|
+
// ry={borderRadius}
|
|
93
|
+
// stroke={`url(#${gradientId})`}
|
|
94
|
+
// strokeWidth={strokeWidth}
|
|
95
|
+
// fill="none"
|
|
96
|
+
// />
|
|
97
|
+
// </svg>
|
|
98
|
+
// </div>
|
|
99
|
+
// );
|
|
100
|
+
// };
|
|
101
|
+
|
|
102
|
+
// export default GradientOutline;
|
|
103
|
+
|
|
104
|
+
// // GradientOutline id="gc-1" className="w-full h-fit">
|
|
105
|
+
// // <div className="w-full text-white bg-black">
|
|
106
|
+
// // <span>abc cbac</span>
|
|
107
|
+
// // <span>abc cbac</span>
|
|
108
|
+
// // <span>abc cbac</span>
|
|
109
|
+
// // <span>abc cbac</span>
|
|
110
|
+
// // </div>
|
|
111
|
+
// // </GradientOutline>
|
|
112
|
+
|
|
113
|
+
"use client";
|
|
114
|
+
import React, {
|
|
115
|
+
FC,
|
|
116
|
+
HTMLAttributes,
|
|
117
|
+
useEffect,
|
|
118
|
+
useId,
|
|
119
|
+
useRef,
|
|
120
|
+
useState,
|
|
121
|
+
} from 'react';
|
|
122
|
+
|
|
123
|
+
export type GradientOutlineProps = {
|
|
124
|
+
children: React.ReactNode;
|
|
125
|
+
strokeWidth?: number;
|
|
126
|
+
borderRadius?: number;
|
|
127
|
+
gradientColors?: string[];
|
|
128
|
+
hoverGradientColors?: string[];
|
|
129
|
+
gradientAngle?: number;
|
|
130
|
+
allowHover?: boolean;
|
|
131
|
+
className?: string;
|
|
132
|
+
onClick?: () => void;
|
|
133
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
134
|
+
|
|
135
|
+
export const GradientOutline: FC<GradientOutlineProps> = ({
|
|
136
|
+
children,
|
|
137
|
+
allowHover = true,
|
|
138
|
+
strokeWidth = 1,
|
|
139
|
+
borderRadius = 16,
|
|
140
|
+
gradientColors = [
|
|
141
|
+
"rgba(255, 255, 255, 0.08)",
|
|
142
|
+
"rgba(255, 255, 255, 0.4)",
|
|
143
|
+
"rgba(255, 255, 255, 0)",
|
|
144
|
+
"rgba(255, 255, 255, 0)",
|
|
145
|
+
"rgba(255, 255, 255, 0.1)",
|
|
146
|
+
],
|
|
147
|
+
hoverGradientColors = ["#E6CAA4", "#E9B84E"],
|
|
148
|
+
gradientAngle = 45,
|
|
149
|
+
className = "",
|
|
150
|
+
onClick,
|
|
151
|
+
...props
|
|
152
|
+
}) => {
|
|
153
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
154
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
155
|
+
const [dimensions, setDimensions] = useState({ width: 100, height: 100 });
|
|
156
|
+
const gradientId = `gradient-${useId()}`; // SSR-safe ID
|
|
157
|
+
|
|
158
|
+
// Resize observer để responsive
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const updateDimensions = () => {
|
|
161
|
+
if (containerRef.current) {
|
|
162
|
+
const { width, height } = containerRef.current.getBoundingClientRect();
|
|
163
|
+
setDimensions({ width, height });
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
updateDimensions();
|
|
167
|
+
|
|
168
|
+
const resizeObserver = new ResizeObserver(updateDimensions);
|
|
169
|
+
if (containerRef.current) resizeObserver.observe(containerRef.current);
|
|
170
|
+
window.addEventListener("resize", updateDimensions);
|
|
171
|
+
|
|
172
|
+
return () => {
|
|
173
|
+
resizeObserver.disconnect();
|
|
174
|
+
window.removeEventListener("resize", updateDimensions);
|
|
175
|
+
};
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
const colors = isHovered ? hoverGradientColors : gradientColors;
|
|
179
|
+
|
|
180
|
+
// Gradient angle helper
|
|
181
|
+
const rad = (gradientAngle % 360) * (Math.PI / 180);
|
|
182
|
+
const x1 = 0.5 - 0.5 * Math.cos(rad);
|
|
183
|
+
const y1 = 0.5 - 0.5 * Math.sin(rad);
|
|
184
|
+
const x2 = 0.5 + 0.5 * Math.cos(rad);
|
|
185
|
+
const y2 = 0.5 + 0.5 * Math.sin(rad);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div
|
|
189
|
+
ref={containerRef}
|
|
190
|
+
onClick={onClick}
|
|
191
|
+
role={onClick ? "button" : undefined}
|
|
192
|
+
tabIndex={onClick ? 0 : undefined}
|
|
193
|
+
onKeyDown={(e) => e.key === "Enter" && onClick?.()}
|
|
194
|
+
onMouseEnter={() => allowHover && setIsHovered(true)}
|
|
195
|
+
onMouseLeave={() => allowHover && setIsHovered(false)}
|
|
196
|
+
className={`relative inline-block overflow-hidden ${className}`}
|
|
197
|
+
style={{ borderRadius }}
|
|
198
|
+
{...props}
|
|
199
|
+
>
|
|
200
|
+
{children}
|
|
201
|
+
<svg
|
|
202
|
+
width="100%"
|
|
203
|
+
height="100%"
|
|
204
|
+
className="absolute top-0 left-0 pointer-events-none"
|
|
205
|
+
viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
|
|
206
|
+
preserveAspectRatio="none"
|
|
207
|
+
>
|
|
208
|
+
<defs>
|
|
209
|
+
<linearGradient id={gradientId} x1={x1} y1={y1} x2={x2} y2={y2}>
|
|
210
|
+
{colors.map((color, index) => (
|
|
211
|
+
<stop
|
|
212
|
+
key={index}
|
|
213
|
+
offset={`${(index / (colors.length - 1)) * 100}%`}
|
|
214
|
+
stopColor={color}
|
|
215
|
+
/>
|
|
216
|
+
))}
|
|
217
|
+
</linearGradient>
|
|
218
|
+
</defs>
|
|
219
|
+
<rect
|
|
220
|
+
x={strokeWidth / 2}
|
|
221
|
+
y={strokeWidth / 2}
|
|
222
|
+
width={dimensions.width - strokeWidth}
|
|
223
|
+
height={dimensions.height - strokeWidth}
|
|
224
|
+
rx={borderRadius}
|
|
225
|
+
ry={borderRadius}
|
|
226
|
+
stroke={`url(#${gradientId})`}
|
|
227
|
+
strokeWidth={strokeWidth}
|
|
228
|
+
fill="none"
|
|
229
|
+
/>
|
|
230
|
+
</svg>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// import React from 'react';
|
|
2
|
+
|
|
3
|
+
// import { ReactSVG } from 'react-svg';
|
|
4
|
+
|
|
5
|
+
// export type GradientSVGProps = {
|
|
6
|
+
// image: string;
|
|
7
|
+
// isActive?: boolean;
|
|
8
|
+
// };
|
|
9
|
+
// export const GradientSVG = ({ image, isActive }: GradientSVGProps) => {
|
|
10
|
+
// return (
|
|
11
|
+
// <ReactSVG
|
|
12
|
+
// src={image}
|
|
13
|
+
// beforeInjection={(svg) => {
|
|
14
|
+
// const defs = document.createElementNS(
|
|
15
|
+
// "http://www.w3.org/2000/svg",
|
|
16
|
+
// "defs"
|
|
17
|
+
// );
|
|
18
|
+
// if (isActive) {
|
|
19
|
+
// defs.innerHTML = `
|
|
20
|
+
// <linearGradient id="customGradient" x1="0" y1="0" x2="0" y2="1">
|
|
21
|
+
// <stop offset="0%" stop-color="#A079FC" />
|
|
22
|
+
// <stop offset="100%" stop-color="#0D5BB5" />
|
|
23
|
+
// </linearGradient>
|
|
24
|
+
// `;
|
|
25
|
+
// }
|
|
26
|
+
// svg.prepend(defs);
|
|
27
|
+
|
|
28
|
+
// svg.querySelectorAll("[stroke]").forEach((el) => {
|
|
29
|
+
// el.setAttribute(
|
|
30
|
+
// "stroke",
|
|
31
|
+
// isActive ? "url(#customGradient)" : "#515158"
|
|
32
|
+
// );
|
|
33
|
+
// });
|
|
34
|
+
// svg.querySelectorAll("[fill]").forEach((el) => {
|
|
35
|
+
// el.setAttribute(
|
|
36
|
+
// "fill",
|
|
37
|
+
// isActive ? "url(#customGradient)" : "#515158"
|
|
38
|
+
// );
|
|
39
|
+
// });
|
|
40
|
+
// }}
|
|
41
|
+
// className="h-9 w-9"
|
|
42
|
+
// />
|
|
43
|
+
// );
|
|
44
|
+
// };
|
|
45
|
+
|
|
46
|
+
import React from 'react';
|
|
47
|
+
|
|
48
|
+
import { ReactSVG } from 'react-svg';
|
|
49
|
+
|
|
50
|
+
export type GradientStop = {
|
|
51
|
+
offset: string; // "0%", "50%", ...
|
|
52
|
+
color: string; // "#hex" or "rgb(...)"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type GradientSVGProps = {
|
|
56
|
+
image: string;
|
|
57
|
+
isActive?: boolean;
|
|
58
|
+
gradientId?: string;
|
|
59
|
+
gradientStops?: GradientStop[];
|
|
60
|
+
stroke?: string | { gradient: true };
|
|
61
|
+
fill?: string | { gradient: true };
|
|
62
|
+
gradientType?: "linear" | "radial";
|
|
63
|
+
angle?: number; // For linear gradients, if needed
|
|
64
|
+
className?: string;
|
|
65
|
+
style: React.CSSProperties;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const GradientSVG = ({
|
|
69
|
+
image,
|
|
70
|
+
isActive = false,
|
|
71
|
+
gradientId = "customGradient",
|
|
72
|
+
gradientStops = [
|
|
73
|
+
{ offset: "0%", color: "#A079FC" },
|
|
74
|
+
{ offset: "100%", color: "#0D5BB5" },
|
|
75
|
+
],
|
|
76
|
+
stroke = { gradient: true },
|
|
77
|
+
fill = { gradient: true },
|
|
78
|
+
gradientType = "linear",
|
|
79
|
+
angle = 180, // it can be used for linear gradients
|
|
80
|
+
className = "h-9 w-9",
|
|
81
|
+
style = {},
|
|
82
|
+
...props
|
|
83
|
+
}: GradientSVGProps) => {
|
|
84
|
+
return (
|
|
85
|
+
<ReactSVG
|
|
86
|
+
src={image}
|
|
87
|
+
beforeInjection={(svg) => {
|
|
88
|
+
if (isActive && ((stroke as any).gradient || (fill as any).gradient)) {
|
|
89
|
+
if (!svg.querySelector(`#${gradientId}`)) {
|
|
90
|
+
const defs = document.createElementNS(
|
|
91
|
+
"http://www.w3.org/2000/svg",
|
|
92
|
+
"defs"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const gradient = document.createElementNS(
|
|
96
|
+
"http://www.w3.org/2000/svg",
|
|
97
|
+
gradientType === "linear" ? "linearGradient" : "radialGradient"
|
|
98
|
+
);
|
|
99
|
+
gradient.setAttribute("id", gradientId);
|
|
100
|
+
|
|
101
|
+
if (gradientType === "linear") {
|
|
102
|
+
const rad = (angle % 360) * (Math.PI / 180);
|
|
103
|
+
const x1 = (Math.cos(rad) + 1) / 2;
|
|
104
|
+
const y1 = (Math.sin(rad) + 1) / 2;
|
|
105
|
+
const x2 = 1 - x1;
|
|
106
|
+
const y2 = 1 - y1;
|
|
107
|
+
|
|
108
|
+
gradient.setAttribute("x1", x1.toString());
|
|
109
|
+
gradient.setAttribute("y1", y1.toString());
|
|
110
|
+
gradient.setAttribute("x2", x2.toString());
|
|
111
|
+
gradient.setAttribute("y2", y2.toString());
|
|
112
|
+
} else {
|
|
113
|
+
gradient.setAttribute("cx", "50%");
|
|
114
|
+
gradient.setAttribute("cy", "50%");
|
|
115
|
+
gradient.setAttribute("r", "50%");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
gradientStops.forEach((stop) => {
|
|
119
|
+
const stopEl = document.createElementNS(
|
|
120
|
+
"http://www.w3.org/2000/svg",
|
|
121
|
+
"stop"
|
|
122
|
+
);
|
|
123
|
+
stopEl.setAttribute("offset", stop.offset);
|
|
124
|
+
stopEl.setAttribute("stop-color", stop.color);
|
|
125
|
+
gradient.appendChild(stopEl);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
defs.appendChild(gradient);
|
|
129
|
+
svg.prepend(defs);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
svg.querySelectorAll("[stroke]").forEach((el) => {
|
|
134
|
+
let strokeVal = "#515158";
|
|
135
|
+
if (isActive) {
|
|
136
|
+
if (typeof stroke === "string") strokeVal = stroke;
|
|
137
|
+
else if ((stroke as any).gradient) strokeVal = `url(#${gradientId})`;
|
|
138
|
+
}
|
|
139
|
+
el.setAttribute("stroke", strokeVal);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
svg.querySelectorAll("[fill]").forEach((el) => {
|
|
143
|
+
let fillVal = "#515158";
|
|
144
|
+
if (isActive) {
|
|
145
|
+
if (typeof fill === "string") fillVal = fill;
|
|
146
|
+
else if ((fill as any).gradient) fillVal = `url(#${gradientId})`;
|
|
147
|
+
}
|
|
148
|
+
el.setAttribute("fill", fillVal);
|
|
149
|
+
});
|
|
150
|
+
}}
|
|
151
|
+
className={className}
|
|
152
|
+
style={style}
|
|
153
|
+
{...props}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, {
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
} from 'react';
|
|
7
|
+
|
|
8
|
+
import { twMerge } from 'tailwind-merge';
|
|
9
|
+
|
|
10
|
+
interface GridLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
columns?: number;
|
|
12
|
+
gap?: number;
|
|
13
|
+
responsive?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
breakpoints?: {
|
|
17
|
+
minWidth: number;
|
|
18
|
+
columns: number;
|
|
19
|
+
}[];
|
|
20
|
+
mode?: "window" | "container";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const GridLayout: React.FC<GridLayoutProps> = ({
|
|
24
|
+
columns = 3,
|
|
25
|
+
gap = 4,
|
|
26
|
+
responsive = true,
|
|
27
|
+
breakpoints,
|
|
28
|
+
className,
|
|
29
|
+
children,
|
|
30
|
+
mode = "window",
|
|
31
|
+
...props
|
|
32
|
+
}) => {
|
|
33
|
+
const [currentColumns, setCurrentColumns] = useState(columns);
|
|
34
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const updateColumns = () => {
|
|
38
|
+
if (responsive && breakpoints) {
|
|
39
|
+
const width =
|
|
40
|
+
mode === "container" && contentRef.current
|
|
41
|
+
? contentRef.current.offsetWidth
|
|
42
|
+
: window.innerWidth;
|
|
43
|
+
|
|
44
|
+
const sorted = [...breakpoints].sort((a, b) => b.minWidth - a.minWidth);
|
|
45
|
+
const match = sorted.find((bp) => width >= bp.minWidth);
|
|
46
|
+
setCurrentColumns(match ? match.columns : columns);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
updateColumns(); // initial
|
|
51
|
+
window.addEventListener("resize", updateColumns);
|
|
52
|
+
return () => window.removeEventListener("resize", updateColumns);
|
|
53
|
+
}, [responsive, breakpoints, columns, mode]);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
ref={contentRef}
|
|
58
|
+
style={{
|
|
59
|
+
gridTemplateColumns: `repeat(${currentColumns}, minmax(0, 1fr))`,
|
|
60
|
+
display: "grid",
|
|
61
|
+
gap: `${gap}px`,
|
|
62
|
+
}}
|
|
63
|
+
className={twMerge(`w-full`, className)}
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, {
|
|
4
|
+
FC,
|
|
5
|
+
ReactNode,
|
|
6
|
+
useEffect,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
9
|
+
|
|
10
|
+
export type HydrationGuardProps = {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* HydrationGuard is a component that prevents its children from being
|
|
16
|
+
* rendered on the server, while still allowing them to be rendered on
|
|
17
|
+
* the client. This is useful for components that need to use browser
|
|
18
|
+
* APIs that are not available on the server, such as the `window`
|
|
19
|
+
* object or Web APIs that require user interaction.
|
|
20
|
+
*
|
|
21
|
+
* The component works by using the `useState` hook to keep track of
|
|
22
|
+
* whether the component has been mounted or not. On the server, the
|
|
23
|
+
* component is not mounted, so the `mounted` state is `false`, and
|
|
24
|
+
* the component does not render its children. On the client, the
|
|
25
|
+
* component is mounted, so the `mounted` state is `true`, and the
|
|
26
|
+
* component renders its children.
|
|
27
|
+
* @param {ReactNode} children - The children to render only on the client.
|
|
28
|
+
* @returns {ReactElement} The rendered children, or null if on the server.
|
|
29
|
+
*/
|
|
30
|
+
export const HydrationGuard:FC<HydrationGuardProps> = ({ children }) => {
|
|
31
|
+
const [mounted, setMounted] = useState(false);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setMounted(true);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
if (!mounted) return null;
|
|
38
|
+
|
|
39
|
+
return <>{children}</>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface ImageProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
src: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
imageClass?: string;
|
|
10
|
+
fallbackSrc?: string;
|
|
11
|
+
fit?: "w" | "h" | "both";
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
containerStyle?: React.CSSProperties;
|
|
14
|
+
id?: string;
|
|
15
|
+
onClick?: (event: React.MouseEvent<HTMLImageElement>) => void;
|
|
16
|
+
resourceLoadPriority?: "auto" | "high" | "low";
|
|
17
|
+
loadmode?: "lazy" | "eager";
|
|
18
|
+
sendAccessUrlToImageServer?:
|
|
19
|
+
| "no-referrer"
|
|
20
|
+
| "no-referrer-when-downgrade"
|
|
21
|
+
| "origin"
|
|
22
|
+
| "strict-origin";
|
|
23
|
+
// crossOrigin?: "anonymous" | "use-credentials";
|
|
24
|
+
fetchPriority?: "auto" | "high" | "low";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Image: React.FC<ImageProps> = ({
|
|
28
|
+
src,
|
|
29
|
+
text,
|
|
30
|
+
alt = "image",
|
|
31
|
+
className = "",
|
|
32
|
+
imageClass = "",
|
|
33
|
+
fallbackSrc = "https://picsum.photos/200/300/?blur=8",
|
|
34
|
+
// fallbackSrc = "https://images.pexels.com/photos/349758/hummingbird-bird-birds-349758.jpeg",
|
|
35
|
+
fit = "both",
|
|
36
|
+
style = {},
|
|
37
|
+
containerStyle = {},
|
|
38
|
+
resourceLoadPriority = "auto",
|
|
39
|
+
loadmode = "lazy",
|
|
40
|
+
sendAccessUrlToImageServer = "no-referrer",
|
|
41
|
+
// crossOrigin = "anonymous",
|
|
42
|
+
id,
|
|
43
|
+
onClick,
|
|
44
|
+
...props
|
|
45
|
+
}) => {
|
|
46
|
+
const [imgSrc, setImgSrc] = useState(src);
|
|
47
|
+
const [loading, setLoading] = useState(true);
|
|
48
|
+
// console.log("Image component rendered with src:", imgSrc);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
style={containerStyle}
|
|
53
|
+
className={`relative overflow-hidden ${className}`}
|
|
54
|
+
onClick={onClick}
|
|
55
|
+
{...props}
|
|
56
|
+
>
|
|
57
|
+
{loading && (
|
|
58
|
+
<div className="absolute inset-0 bg-gray-200 animate-pulse" />
|
|
59
|
+
)}
|
|
60
|
+
<img
|
|
61
|
+
id={id}
|
|
62
|
+
style={style}
|
|
63
|
+
src={imgSrc}
|
|
64
|
+
// crossOrigin={crossOrigin}
|
|
65
|
+
loading={loadmode}
|
|
66
|
+
referrerPolicy={sendAccessUrlToImageServer}
|
|
67
|
+
fetchPriority={resourceLoadPriority}
|
|
68
|
+
alt={alt}
|
|
69
|
+
className={`${
|
|
70
|
+
fit === "w"
|
|
71
|
+
? "w-full h-auto"
|
|
72
|
+
: fit === "h"
|
|
73
|
+
? "h-full w-auto"
|
|
74
|
+
: "w-full h-full"
|
|
75
|
+
} object-cover transition-all duration-500 ${
|
|
76
|
+
loading ? "opacity-0" : "opacity-100"
|
|
77
|
+
} ${imageClass}`}
|
|
78
|
+
onLoad={() => setLoading(false)}
|
|
79
|
+
onError={(e) => {
|
|
80
|
+
// console.error("Error loading image:", e);
|
|
81
|
+
if (text) {
|
|
82
|
+
setImgSrc(text);
|
|
83
|
+
setLoading(false);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setImgSrc(fallbackSrc);
|
|
87
|
+
setLoading(false);
|
|
88
|
+
}}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
HTMLAttributes,
|
|
3
|
+
JSX,
|
|
4
|
+
ReactNode,
|
|
5
|
+
} from 'react';
|
|
6
|
+
|
|
7
|
+
import { cn } from '../lib/utils';
|
|
8
|
+
|
|
9
|
+
export type LoadSlashBarProps = {
|
|
10
|
+
slashSize?: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
};
|
|
14
|
+
slashColors?: {
|
|
15
|
+
primary: string;
|
|
16
|
+
secondary: string;
|
|
17
|
+
};
|
|
18
|
+
rotate?: number;
|
|
19
|
+
className?: string;
|
|
20
|
+
containerClass?: string;
|
|
21
|
+
progressClass?: string;
|
|
22
|
+
elementClass?: string;
|
|
23
|
+
value?: number;
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
slashWidth?: number;
|
|
26
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
27
|
+
/**
|
|
28
|
+
* LoadSlashBar component renders a progress bar with a slash effect.
|
|
29
|
+
* It accepts various props to customize the appearance and behavior of the bar.
|
|
30
|
+
* @param {LoadSlashBarProps} props - Properties for customizing the LoadSlashBar component.
|
|
31
|
+
* @returns {JSX.Element} Rendered LoadSlashBar component.
|
|
32
|
+
*/
|
|
33
|
+
export const LoaderSlashGradient: React.FC<LoadSlashBarProps> = ({
|
|
34
|
+
slashColors = {primary: "#ffffff", secondary: "transparent"},
|
|
35
|
+
slashSize = {x: 20, y: 10},
|
|
36
|
+
className,
|
|
37
|
+
containerClass = "bg-black",
|
|
38
|
+
progressClass,
|
|
39
|
+
elementClass,
|
|
40
|
+
value = 0,
|
|
41
|
+
rotate = 45,
|
|
42
|
+
slashWidth = 100,
|
|
43
|
+
children,
|
|
44
|
+
...props
|
|
45
|
+
}):JSX.Element => {
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
{...props}
|
|
49
|
+
className={cn("w-full h-2 relative rounded-full", containerClass)}
|
|
50
|
+
>
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
width: `${value}%`,
|
|
54
|
+
}}
|
|
55
|
+
className={cn(
|
|
56
|
+
"absolute z-[1] top-0 left-0 w-1/2 rounded-full h-full",
|
|
57
|
+
progressClass
|
|
58
|
+
)}
|
|
59
|
+
></div>
|
|
60
|
+
<div
|
|
61
|
+
style={{
|
|
62
|
+
width: `${value}%`,
|
|
63
|
+
}}
|
|
64
|
+
className={cn(
|
|
65
|
+
"absolute z-[3] top-0 left-0 w-1/2 flex items-center justify-end bg-transparent rounded-full h-full",
|
|
66
|
+
elementClass
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</div>
|
|
71
|
+
<div
|
|
72
|
+
style={{
|
|
73
|
+
width: `${slashWidth}%`,
|
|
74
|
+
backgroundSize: `${slashSize?.x}px ${slashSize?.y}px`,
|
|
75
|
+
backgroundImage: `linear-gradient(${rotate}deg, ${slashColors?.primary} 35%, ${slashColors?.secondary} 10%, ${slashColors?.secondary} 75%, ${slashColors?.primary} 75%, ${slashColors?.primary})`,
|
|
76
|
+
}}
|
|
77
|
+
className={cn(
|
|
78
|
+
"absolute z-[2] top-0 left-0 h-full",
|
|
79
|
+
!slashWidth && "w-full",
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
></div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|