@hunterchen/canvas 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/components/canvas/canvas.d.ts.map +1 -1
- package/dist/components/canvas/canvas.js +0 -15
- package/dist/components/canvas/canvas.js.map +1 -1
- package/dist/components/canvas/navbar/single-button.d.ts.map +1 -1
- package/dist/components/canvas/navbar/single-button.js +0 -26
- package/dist/components/canvas/navbar/single-button.js.map +1 -1
- package/dist/hooks/usePerformanceMode.d.ts +11 -3
- package/dist/hooks/usePerformanceMode.d.ts.map +1 -1
- package/dist/hooks/usePerformanceMode.js +37 -3
- package/dist/hooks/usePerformanceMode.js.map +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +1 -1
- package/package.json +50 -48
- package/src/components/canvas/canvas.tsx +0 -16
- package/src/components/canvas/navbar/single-button.tsx +0 -30
- package/src/hooks/usePerformanceMode.ts +53 -3
- package/src/index.ts +0 -10
- package/dist/components/canvas/cursor.d.ts +0 -8
- package/dist/components/canvas/cursor.d.ts.map +0 -1
- package/dist/components/canvas/cursor.js +0 -32
- package/dist/components/canvas/cursor.js.map +0 -1
- package/dist/components/ui/FolderIcon.d.ts +0 -9
- package/dist/components/ui/FolderIcon.d.ts.map +0 -1
- package/dist/components/ui/FolderIcon.js +0 -25
- package/dist/components/ui/FolderIcon.js.map +0 -1
- package/dist/components/ui/button.d.ts +0 -14
- package/dist/components/ui/button.d.ts.map +0 -1
- package/dist/components/ui/button.js +0 -54
- package/dist/components/ui/button.js.map +0 -1
- package/dist/components/ui/label.d.ts +0 -6
- package/dist/components/ui/label.d.ts.map +0 -1
- package/dist/components/ui/label.js +0 -10
- package/dist/components/ui/label.js.map +0 -1
- package/dist/components/ui/toast.d.ts +0 -16
- package/dist/components/ui/toast.d.ts.map +0 -1
- package/dist/components/ui/toast.js +0 -41
- package/dist/components/ui/toast.js.map +0 -1
- package/dist/components/ui/toaster.d.ts +0 -2
- package/dist/components/ui/toaster.d.ts.map +0 -1
- package/dist/components/ui/toaster.js +0 -10
- package/dist/components/ui/toaster.js.map +0 -1
- package/dist/hooks/use-mobile.d.ts +0 -2
- package/dist/hooks/use-mobile.d.ts.map +0 -1
- package/dist/hooks/use-mobile.js +0 -16
- package/dist/hooks/use-mobile.js.map +0 -1
- package/dist/hooks/use-toast.d.ts +0 -45
- package/dist/hooks/use-toast.d.ts.map +0 -1
- package/dist/hooks/use-toast.js +0 -126
- package/dist/hooks/use-toast.js.map +0 -1
- package/src/components/canvas/cursor.tsx +0 -161
- package/src/components/ui/FolderIcon.tsx +0 -116
- package/src/components/ui/button.tsx +0 -162
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/toast.tsx +0 -136
- package/src/components/ui/toaster.tsx +0 -33
- package/src/hooks/use-mobile.ts +0 -21
- package/src/hooks/use-toast.ts +0 -186
- package/src/lib/copy.ts +0 -18
- package/src/lib/utils.ts +0 -18
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import Image from "next/image";
|
|
3
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
-
import { cn } from "../../lib/utils";
|
|
6
|
-
|
|
7
|
-
const buttonBase =
|
|
8
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 text-white font-figtree cursor-pointer";
|
|
9
|
-
|
|
10
|
-
const lift =
|
|
11
|
-
"-translate-y-[3px] group-hover:-translate-y-[4px] group-active:-translate-y-[1px] transition-all duration-100";
|
|
12
|
-
|
|
13
|
-
export const buttonVariants = cva(buttonBase, {
|
|
14
|
-
variants: {
|
|
15
|
-
variant: {
|
|
16
|
-
default: "",
|
|
17
|
-
primary: cn(
|
|
18
|
-
"bg-button-primary shadow-button-primary hover:bg-button-primary-hover active:bg-button-primary-active",
|
|
19
|
-
lift,
|
|
20
|
-
),
|
|
21
|
-
secondary: cn(
|
|
22
|
-
"text-[#625679] bg-button-secondary hover:bg-button-secondary-hover active:bg-button-secondary-active active:shadow-button-secondary",
|
|
23
|
-
lift,
|
|
24
|
-
),
|
|
25
|
-
tertiary: "bg-transparent text-[#625679] px-4 active:text-[#8F57AD]",
|
|
26
|
-
"tertiary-arrow":
|
|
27
|
-
"bg-transparent text-[#625679] px-4 active:text-[#8F57AD] flex items-center gap-2",
|
|
28
|
-
destructive:
|
|
29
|
-
"bg-destructive text-destructive-foreground hover:bg-destructive-dark",
|
|
30
|
-
outline: "bg-violet-100 hover:bg-muted border border-[1px] border-muted",
|
|
31
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
32
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
33
|
-
"apply-ghost":
|
|
34
|
-
"bg-[#ebdff7] bg-opacity-50 text-heavy font-semibold hover:bg-[#e6cdff] w-full justify-start",
|
|
35
|
-
apply: "text-medium hover:bg-[#ebdff7] hover:text-heavy",
|
|
36
|
-
},
|
|
37
|
-
size: {
|
|
38
|
-
default: "h-10 px-4 py-2",
|
|
39
|
-
sm: "h-8 rounded-md px-3",
|
|
40
|
-
lg: "h-11 rounded-md px-8",
|
|
41
|
-
icon: "h-10 w-10",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
defaultVariants: { variant: "default", size: "default" },
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
export interface ButtonProps
|
|
48
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
49
|
-
VariantProps<typeof buttonVariants> {
|
|
50
|
-
asChild?: boolean;
|
|
51
|
-
isPending?: boolean;
|
|
52
|
-
full?: boolean;
|
|
53
|
-
secondClass?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const overlays = {
|
|
57
|
-
primary: { bg: "bg-button-primary-back", border: "border border-white/30" },
|
|
58
|
-
secondary: {
|
|
59
|
-
bg: "bg-button-secondary-back",
|
|
60
|
-
border: "border border-white/50",
|
|
61
|
-
},
|
|
62
|
-
} as const;
|
|
63
|
-
|
|
64
|
-
const pressedByVariant: Record<"primary" | "secondary", string> = {
|
|
65
|
-
primary:
|
|
66
|
-
"!-translate-y-[1px] shadow-none bg-button-primary-active hover:!bg-button-primary-active",
|
|
67
|
-
secondary:
|
|
68
|
-
"!-translate-y-[1px] shadow-button-secondary bg-button-secondary-active hover:!bg-button-secondary-active",
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const noLift =
|
|
72
|
-
"group-hover:!translate-y-[1px] group-active:!translate-y-[1px] transition-none";
|
|
73
|
-
|
|
74
|
-
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
75
|
-
(
|
|
76
|
-
{
|
|
77
|
-
className,
|
|
78
|
-
variant = "default",
|
|
79
|
-
size,
|
|
80
|
-
isPending = false,
|
|
81
|
-
full = false,
|
|
82
|
-
asChild = false,
|
|
83
|
-
disabled,
|
|
84
|
-
secondClass = "",
|
|
85
|
-
children,
|
|
86
|
-
...props
|
|
87
|
-
},
|
|
88
|
-
ref,
|
|
89
|
-
) => {
|
|
90
|
-
const Comp = asChild ? Slot : "button";
|
|
91
|
-
const lockPressed =
|
|
92
|
-
isPending && (variant === "primary" || variant === "secondary");
|
|
93
|
-
|
|
94
|
-
const btnClasses = cn(
|
|
95
|
-
buttonVariants({ variant, size, className }),
|
|
96
|
-
lockPressed && [pressedByVariant[variant], noLift],
|
|
97
|
-
full && "w-full",
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const overlay = overlays[variant as keyof typeof overlays];
|
|
101
|
-
|
|
102
|
-
const wrapperClasses = cn(
|
|
103
|
-
"group relative inline-block w-max",
|
|
104
|
-
full && "block w-full",
|
|
105
|
-
lockPressed && ["pointer-events-none", noLift],
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div className={wrapperClasses}>
|
|
110
|
-
{overlay && (
|
|
111
|
-
<>
|
|
112
|
-
<span
|
|
113
|
-
className={cn(
|
|
114
|
-
"pointer-events-none absolute inset-0 h-full w-full rounded-lg",
|
|
115
|
-
overlay.bg,
|
|
116
|
-
)}
|
|
117
|
-
/>
|
|
118
|
-
<span
|
|
119
|
-
className={cn(
|
|
120
|
-
"pointer-events-none absolute inset-0 z-50 h-full w-full rounded-lg",
|
|
121
|
-
overlay.border,
|
|
122
|
-
lockPressed ? "-translate-y-[1px] " + noLift : lift,
|
|
123
|
-
)}
|
|
124
|
-
/>
|
|
125
|
-
</>
|
|
126
|
-
)}
|
|
127
|
-
|
|
128
|
-
<Comp
|
|
129
|
-
ref={ref}
|
|
130
|
-
{...props}
|
|
131
|
-
className={cn("flex flex-row gap-2", btnClasses)}
|
|
132
|
-
disabled={disabled ?? isPending}
|
|
133
|
-
>
|
|
134
|
-
{variant === "tertiary-arrow" ? (
|
|
135
|
-
<div className="w-full">
|
|
136
|
-
<Image
|
|
137
|
-
src="/arrow-left.svg"
|
|
138
|
-
alt="Left Arrow"
|
|
139
|
-
width={10}
|
|
140
|
-
height={10}
|
|
141
|
-
/>
|
|
142
|
-
{children}
|
|
143
|
-
</div>
|
|
144
|
-
) : (
|
|
145
|
-
children
|
|
146
|
-
)}
|
|
147
|
-
</Comp>
|
|
148
|
-
|
|
149
|
-
{(variant === "tertiary" || variant === "tertiary-arrow") && (
|
|
150
|
-
<span
|
|
151
|
-
className={cn(
|
|
152
|
-
"block h-0 max-w-0 border-b-2 border-dashed border-[#625679] transition-all duration-200 group-hover:max-w-full group-active:border-[#8F57AD]",
|
|
153
|
-
secondClass,
|
|
154
|
-
)}
|
|
155
|
-
/>
|
|
156
|
-
)}
|
|
157
|
-
</div>
|
|
158
|
-
);
|
|
159
|
-
},
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
Button.displayName = "Button";
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
-
|
|
5
|
-
import { cn } from "../../lib/utils";
|
|
6
|
-
|
|
7
|
-
const labelVariants = cva(
|
|
8
|
-
"text-[#5f476c] text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const Label = React.forwardRef<
|
|
12
|
-
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
-
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
14
|
-
VariantProps<typeof labelVariants>
|
|
15
|
-
>(({ className, ...props }, ref) => (
|
|
16
|
-
<LabelPrimitive.Root
|
|
17
|
-
ref={ref}
|
|
18
|
-
className={cn(labelVariants(), className)}
|
|
19
|
-
{...props}
|
|
20
|
-
/>
|
|
21
|
-
));
|
|
22
|
-
Label.displayName = LabelPrimitive.Root.displayName;
|
|
23
|
-
|
|
24
|
-
export { Label };
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import * as ToastPrimitives from "@radix-ui/react-toast";
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
|
|
8
|
-
const ToastProvider = ToastPrimitives.Provider;
|
|
9
|
-
|
|
10
|
-
const ToastViewport = React.forwardRef<
|
|
11
|
-
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
12
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
13
|
-
>(({ className, ...props }, ref) => (
|
|
14
|
-
<ToastPrimitives.Viewport
|
|
15
|
-
ref={ref}
|
|
16
|
-
className={cn(
|
|
17
|
-
"fixed left-1/2 top-4 z-[100] flex max-h-screen w-auto -translate-x-1/2 flex-col p-4 sm:max-w-fit",
|
|
18
|
-
className,
|
|
19
|
-
)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
));
|
|
23
|
-
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
24
|
-
|
|
25
|
-
const toastVariants = cva(
|
|
26
|
-
"group pointer-events-auto relative flex w-auto min-w-fit items-center justify-between space-x-4 overflow-hidden rounded-md border px-4 py-3 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-top-full data-[state=open]:slide-in-from-top-full",
|
|
27
|
-
{
|
|
28
|
-
variants: {
|
|
29
|
-
variant: {
|
|
30
|
-
default: "border bg-background text-foreground",
|
|
31
|
-
destructive:
|
|
32
|
-
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
|
33
|
-
success: "border-green-200 bg-green-50 text-green-900 shadow-lg",
|
|
34
|
-
cute: cn(
|
|
35
|
-
"border-purple-200 bg-gradient-to-r from-purple-50 to-pink-50 text-purple-900 shadow-lg rounded-lg", // Base styling
|
|
36
|
-
"w-[calc(100vw-4rem)]", // Mobile: take (100vw - 4rem) width for near full-bleed with margin
|
|
37
|
-
"mx-auto", // Center it (viewport already centers, but keep safety)
|
|
38
|
-
"min-w-0", // Prevent it from shrinking too small if content wraps
|
|
39
|
-
"sm:w-auto", // On larger screens revert to intrinsic sizing
|
|
40
|
-
"justify-center text-center space-x-0", // Center content & text horizontally
|
|
41
|
-
),
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
defaultVariants: {
|
|
45
|
-
variant: "default",
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const Toast = React.forwardRef<
|
|
51
|
-
React.ElementRef<typeof ToastPrimitives.Root>,
|
|
52
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
|
53
|
-
VariantProps<typeof toastVariants>
|
|
54
|
-
>(({ className, variant, ...props }, ref) => {
|
|
55
|
-
return (
|
|
56
|
-
<ToastPrimitives.Root
|
|
57
|
-
ref={ref}
|
|
58
|
-
className={cn(toastVariants({ variant }), className)}
|
|
59
|
-
{...props}
|
|
60
|
-
/>
|
|
61
|
-
);
|
|
62
|
-
});
|
|
63
|
-
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
64
|
-
|
|
65
|
-
const ToastAction = React.forwardRef<
|
|
66
|
-
React.ElementRef<typeof ToastPrimitives.Action>,
|
|
67
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
|
68
|
-
>(({ className, ...props }, ref) => (
|
|
69
|
-
<ToastPrimitives.Action
|
|
70
|
-
ref={ref}
|
|
71
|
-
className={cn(
|
|
72
|
-
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
|
73
|
-
className,
|
|
74
|
-
)}
|
|
75
|
-
{...props}
|
|
76
|
-
/>
|
|
77
|
-
));
|
|
78
|
-
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
79
|
-
|
|
80
|
-
const ToastClose = React.forwardRef<
|
|
81
|
-
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
82
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
83
|
-
>(({ className, ...props }, ref) => (
|
|
84
|
-
<ToastPrimitives.Close
|
|
85
|
-
ref={ref}
|
|
86
|
-
className={cn(
|
|
87
|
-
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
|
88
|
-
className,
|
|
89
|
-
)}
|
|
90
|
-
toast-close=""
|
|
91
|
-
{...props}
|
|
92
|
-
>
|
|
93
|
-
<X className="h-3 w-3" />
|
|
94
|
-
</ToastPrimitives.Close>
|
|
95
|
-
));
|
|
96
|
-
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
97
|
-
|
|
98
|
-
const ToastTitle = React.forwardRef<
|
|
99
|
-
React.ElementRef<typeof ToastPrimitives.Title>,
|
|
100
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
|
101
|
-
>(({ className, ...props }, ref) => (
|
|
102
|
-
<ToastPrimitives.Title
|
|
103
|
-
ref={ref}
|
|
104
|
-
className={cn("text-sm font-semibold", className)}
|
|
105
|
-
{...props}
|
|
106
|
-
/>
|
|
107
|
-
));
|
|
108
|
-
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
109
|
-
|
|
110
|
-
const ToastDescription = React.forwardRef<
|
|
111
|
-
React.ElementRef<typeof ToastPrimitives.Description>,
|
|
112
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
|
113
|
-
>(({ className, ...props }, ref) => (
|
|
114
|
-
<ToastPrimitives.Description
|
|
115
|
-
ref={ref}
|
|
116
|
-
className={cn("text-sm opacity-90", className)}
|
|
117
|
-
{...props}
|
|
118
|
-
/>
|
|
119
|
-
));
|
|
120
|
-
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
121
|
-
|
|
122
|
-
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|
123
|
-
|
|
124
|
-
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
125
|
-
|
|
126
|
-
export {
|
|
127
|
-
type ToastProps,
|
|
128
|
-
type ToastActionElement,
|
|
129
|
-
ToastProvider,
|
|
130
|
-
ToastViewport,
|
|
131
|
-
Toast,
|
|
132
|
-
ToastTitle,
|
|
133
|
-
ToastDescription,
|
|
134
|
-
ToastClose,
|
|
135
|
-
ToastAction,
|
|
136
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useToast } from "../../hooks/use-toast";
|
|
2
|
-
import {
|
|
3
|
-
Toast,
|
|
4
|
-
ToastClose,
|
|
5
|
-
ToastDescription,
|
|
6
|
-
ToastProvider,
|
|
7
|
-
ToastTitle,
|
|
8
|
-
ToastViewport,
|
|
9
|
-
} from "./toast";
|
|
10
|
-
|
|
11
|
-
export function Toaster() {
|
|
12
|
-
const { toasts } = useToast();
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<ToastProvider>
|
|
16
|
-
{toasts.map(function ({ id, title, description, action, ...props }) {
|
|
17
|
-
return (
|
|
18
|
-
<Toast key={id} {...props}>
|
|
19
|
-
<div className="grid gap-1">
|
|
20
|
-
{title && <ToastTitle>{title}</ToastTitle>}
|
|
21
|
-
{description && (
|
|
22
|
-
<ToastDescription>{description}</ToastDescription>
|
|
23
|
-
)}
|
|
24
|
-
</div>
|
|
25
|
-
{action}
|
|
26
|
-
<ToastClose />
|
|
27
|
-
</Toast>
|
|
28
|
-
);
|
|
29
|
-
})}
|
|
30
|
-
<ToastViewport />
|
|
31
|
-
</ToastProvider>
|
|
32
|
-
);
|
|
33
|
-
}
|
package/src/hooks/use-mobile.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
const MOBILE_BREAKPOINT = 768;
|
|
4
|
-
|
|
5
|
-
export function useIsMobile() {
|
|
6
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
-
undefined,
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
React.useEffect(() => {
|
|
11
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
-
const onChange = () => {
|
|
13
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
-
};
|
|
15
|
-
mql.addEventListener("change", onChange);
|
|
16
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
-
return () => mql.removeEventListener("change", onChange);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return !!isMobile;
|
|
21
|
-
}
|
package/src/hooks/use-toast.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import type { ToastActionElement, ToastProps } from "../components/ui/toast";
|
|
4
|
-
|
|
5
|
-
const TOAST_LIMIT = 1;
|
|
6
|
-
const TOAST_REMOVE_DELAY = 5000;
|
|
7
|
-
|
|
8
|
-
type ToasterToast = ToastProps & {
|
|
9
|
-
id: string;
|
|
10
|
-
title?: React.ReactNode;
|
|
11
|
-
description?: React.ReactNode;
|
|
12
|
-
action?: ToastActionElement;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const actionTypes = {
|
|
16
|
-
ADD_TOAST: "ADD_TOAST",
|
|
17
|
-
UPDATE_TOAST: "UPDATE_TOAST",
|
|
18
|
-
DISMISS_TOAST: "DISMISS_TOAST",
|
|
19
|
-
REMOVE_TOAST: "REMOVE_TOAST",
|
|
20
|
-
} as const;
|
|
21
|
-
|
|
22
|
-
let count = 0;
|
|
23
|
-
|
|
24
|
-
function genId() {
|
|
25
|
-
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
|
26
|
-
return count.toString();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
type ActionType = typeof actionTypes;
|
|
30
|
-
|
|
31
|
-
type Action =
|
|
32
|
-
| {
|
|
33
|
-
type: ActionType["ADD_TOAST"];
|
|
34
|
-
toast: ToasterToast;
|
|
35
|
-
}
|
|
36
|
-
| {
|
|
37
|
-
type: ActionType["UPDATE_TOAST"];
|
|
38
|
-
toast: Partial<ToasterToast>;
|
|
39
|
-
}
|
|
40
|
-
| {
|
|
41
|
-
type: ActionType["DISMISS_TOAST"];
|
|
42
|
-
toastId?: ToasterToast["id"];
|
|
43
|
-
}
|
|
44
|
-
| {
|
|
45
|
-
type: ActionType["REMOVE_TOAST"];
|
|
46
|
-
toastId?: ToasterToast["id"];
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
interface State {
|
|
50
|
-
toasts: ToasterToast[];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
54
|
-
|
|
55
|
-
const addToRemoveQueue = (toastId: string) => {
|
|
56
|
-
if (toastTimeouts.has(toastId)) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const timeout = setTimeout(() => {
|
|
61
|
-
toastTimeouts.delete(toastId);
|
|
62
|
-
dispatch({
|
|
63
|
-
type: "REMOVE_TOAST",
|
|
64
|
-
toastId: toastId,
|
|
65
|
-
});
|
|
66
|
-
}, TOAST_REMOVE_DELAY);
|
|
67
|
-
|
|
68
|
-
toastTimeouts.set(toastId, timeout);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const reducer = (state: State, action: Action): State => {
|
|
72
|
-
switch (action.type) {
|
|
73
|
-
case "ADD_TOAST":
|
|
74
|
-
return {
|
|
75
|
-
...state,
|
|
76
|
-
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
case "UPDATE_TOAST":
|
|
80
|
-
return {
|
|
81
|
-
...state,
|
|
82
|
-
toasts: state.toasts.map((t) =>
|
|
83
|
-
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
|
84
|
-
),
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
case "DISMISS_TOAST": {
|
|
88
|
-
const { toastId } = action;
|
|
89
|
-
|
|
90
|
-
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
91
|
-
// but I'll keep it here for simplicity
|
|
92
|
-
if (toastId) {
|
|
93
|
-
addToRemoveQueue(toastId);
|
|
94
|
-
} else {
|
|
95
|
-
state.toasts.forEach((toast) => {
|
|
96
|
-
addToRemoveQueue(toast.id);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
...state,
|
|
102
|
-
toasts: state.toasts.map((t) =>
|
|
103
|
-
t.id === toastId || toastId === undefined
|
|
104
|
-
? {
|
|
105
|
-
...t,
|
|
106
|
-
open: false,
|
|
107
|
-
}
|
|
108
|
-
: t,
|
|
109
|
-
),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
case "REMOVE_TOAST":
|
|
113
|
-
if (action.toastId === undefined) {
|
|
114
|
-
return {
|
|
115
|
-
...state,
|
|
116
|
-
toasts: [],
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
...state,
|
|
121
|
-
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const listeners: Array<(state: State) => void> = [];
|
|
127
|
-
|
|
128
|
-
let memoryState: State = { toasts: [] };
|
|
129
|
-
|
|
130
|
-
function dispatch(action: Action) {
|
|
131
|
-
memoryState = reducer(memoryState, action);
|
|
132
|
-
listeners.forEach((listener) => {
|
|
133
|
-
listener(memoryState);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
type Toast = Omit<ToasterToast, "id">;
|
|
138
|
-
|
|
139
|
-
export function toast({ ...props }: Toast) {
|
|
140
|
-
const id = genId();
|
|
141
|
-
|
|
142
|
-
const update = (props: ToasterToast) =>
|
|
143
|
-
dispatch({
|
|
144
|
-
type: "UPDATE_TOAST",
|
|
145
|
-
toast: { ...props, id },
|
|
146
|
-
});
|
|
147
|
-
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
148
|
-
|
|
149
|
-
dispatch({
|
|
150
|
-
type: "ADD_TOAST",
|
|
151
|
-
toast: {
|
|
152
|
-
...props,
|
|
153
|
-
id,
|
|
154
|
-
open: true,
|
|
155
|
-
onOpenChange: (open) => {
|
|
156
|
-
if (!open) dismiss();
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
id: id,
|
|
163
|
-
dismiss,
|
|
164
|
-
update,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function useToast() {
|
|
169
|
-
const [state, setState] = React.useState<State>(memoryState);
|
|
170
|
-
|
|
171
|
-
React.useEffect(() => {
|
|
172
|
-
listeners.push(setState);
|
|
173
|
-
return () => {
|
|
174
|
-
const index = listeners.indexOf(setState);
|
|
175
|
-
if (index > -1) {
|
|
176
|
-
listeners.splice(index, 1);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
}, [state]);
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
...state,
|
|
183
|
-
toast,
|
|
184
|
-
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
|
185
|
-
};
|
|
186
|
-
}
|
package/src/lib/copy.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const copyText = async (text: string): Promise<boolean> => {
|
|
2
|
-
try {
|
|
3
|
-
if (navigator.clipboard?.writeText && window.isSecureContext) {
|
|
4
|
-
await navigator.clipboard.writeText(text);
|
|
5
|
-
return true;
|
|
6
|
-
}
|
|
7
|
-
// fallback: execCommand
|
|
8
|
-
const ta = document.createElement("textarea");
|
|
9
|
-
ta.value = text;
|
|
10
|
-
document.body.appendChild(ta);
|
|
11
|
-
ta.select();
|
|
12
|
-
const ok = document.execCommand("copy");
|
|
13
|
-
document.body.removeChild(ta);
|
|
14
|
-
return ok;
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
};
|
package/src/lib/utils.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type ClassValue, clsx } from "clsx";
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs));
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function debounce<T extends (...args: unknown[]) => void>(
|
|
9
|
-
cb: T,
|
|
10
|
-
wait = 20,
|
|
11
|
-
) {
|
|
12
|
-
let h: number | NodeJS.Timeout = 0;
|
|
13
|
-
const callable = (...args: unknown[]) => {
|
|
14
|
-
clearTimeout(h);
|
|
15
|
-
h = setTimeout(() => cb(...args), wait);
|
|
16
|
-
};
|
|
17
|
-
return callable as T;
|
|
18
|
-
}
|