Spaces:
Running
Running
invite friends modal
Browse files- package-lock.json +37 -0
- package.json +1 -0
- src/components/ask-ai/ask-ai-new.tsx +3 -62
- src/components/invite-friends/invite-friends.tsx +78 -0
- src/components/ui/button.tsx +1 -0
- src/components/ui/dialog.tsx +144 -0
package-lock.json
CHANGED
@@ -12,6 +12,7 @@
|
|
12 |
"@huggingface/inference": "^3.6.1",
|
13 |
"@monaco-editor/react": "^4.7.0",
|
14 |
"@radix-ui/react-avatar": "^1.1.10",
|
|
|
15 |
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
16 |
"@radix-ui/react-popover": "^1.1.14",
|
17 |
"@radix-ui/react-select": "^2.2.5",
|
@@ -1278,6 +1279,42 @@
|
|
1278 |
}
|
1279 |
}
|
1280 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1281 |
"node_modules/@radix-ui/react-direction": {
|
1282 |
"version": "1.1.1",
|
1283 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
|
|
12 |
"@huggingface/inference": "^3.6.1",
|
13 |
"@monaco-editor/react": "^4.7.0",
|
14 |
"@radix-ui/react-avatar": "^1.1.10",
|
15 |
+
"@radix-ui/react-dialog": "^1.1.14",
|
16 |
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
17 |
"@radix-ui/react-popover": "^1.1.14",
|
18 |
"@radix-ui/react-select": "^2.2.5",
|
|
|
1279 |
}
|
1280 |
}
|
1281 |
},
|
1282 |
+
"node_modules/@radix-ui/react-dialog": {
|
1283 |
+
"version": "1.1.14",
|
1284 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
|
1285 |
+
"integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
|
1286 |
+
"license": "MIT",
|
1287 |
+
"dependencies": {
|
1288 |
+
"@radix-ui/primitive": "1.1.2",
|
1289 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
1290 |
+
"@radix-ui/react-context": "1.1.2",
|
1291 |
+
"@radix-ui/react-dismissable-layer": "1.1.10",
|
1292 |
+
"@radix-ui/react-focus-guards": "1.1.2",
|
1293 |
+
"@radix-ui/react-focus-scope": "1.1.7",
|
1294 |
+
"@radix-ui/react-id": "1.1.1",
|
1295 |
+
"@radix-ui/react-portal": "1.1.9",
|
1296 |
+
"@radix-ui/react-presence": "1.1.4",
|
1297 |
+
"@radix-ui/react-primitive": "2.1.3",
|
1298 |
+
"@radix-ui/react-slot": "1.2.3",
|
1299 |
+
"@radix-ui/react-use-controllable-state": "1.2.2",
|
1300 |
+
"aria-hidden": "^1.2.4",
|
1301 |
+
"react-remove-scroll": "^2.6.3"
|
1302 |
+
},
|
1303 |
+
"peerDependencies": {
|
1304 |
+
"@types/react": "*",
|
1305 |
+
"@types/react-dom": "*",
|
1306 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
1307 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
1308 |
+
},
|
1309 |
+
"peerDependenciesMeta": {
|
1310 |
+
"@types/react": {
|
1311 |
+
"optional": true
|
1312 |
+
},
|
1313 |
+
"@types/react-dom": {
|
1314 |
+
"optional": true
|
1315 |
+
}
|
1316 |
+
}
|
1317 |
+
},
|
1318 |
"node_modules/@radix-ui/react-direction": {
|
1319 |
"version": "1.1.1",
|
1320 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
package.json
CHANGED
@@ -15,6 +15,7 @@
|
|
15 |
"@huggingface/inference": "^3.6.1",
|
16 |
"@monaco-editor/react": "^4.7.0",
|
17 |
"@radix-ui/react-avatar": "^1.1.10",
|
|
|
18 |
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
19 |
"@radix-ui/react-popover": "^1.1.14",
|
20 |
"@radix-ui/react-select": "^2.2.5",
|
|
|
15 |
"@huggingface/inference": "^3.6.1",
|
16 |
"@monaco-editor/react": "^4.7.0",
|
17 |
"@radix-ui/react-avatar": "^1.1.10",
|
18 |
+
"@radix-ui/react-dialog": "^1.1.14",
|
19 |
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
20 |
"@radix-ui/react-popover": "^1.1.14",
|
21 |
"@radix-ui/react-select": "^2.2.5",
|
src/components/ask-ai/ask-ai-new.tsx
CHANGED
@@ -3,7 +3,7 @@ import { useState, useRef } from "react";
|
|
3 |
import classNames from "classnames";
|
4 |
import { toast } from "sonner";
|
5 |
import { useLocalStorage, useUpdateEffect } from "react-use";
|
6 |
-
import { ArrowUp, ChevronDown
|
7 |
import { FaStopCircle } from "react-icons/fa";
|
8 |
|
9 |
import Login from "../login/login";
|
@@ -16,6 +16,7 @@ import { Button } from "../ui/button";
|
|
16 |
import { MODELS } from "../../../utils/providers";
|
17 |
import Loading from "../loading/loading";
|
18 |
import { HtmlHistory } from "../../../utils/types";
|
|
|
19 |
|
20 |
function AskAI({
|
21 |
html,
|
@@ -37,7 +38,6 @@ function AskAI({
|
|
37 |
onSuccess: (h: string, p: string, n?: number[][]) => void;
|
38 |
}) {
|
39 |
const refThink = useRef<HTMLDivElement | null>(null);
|
40 |
-
// const uploadInputRef = useRef<HTMLInputElement | null>(null);
|
41 |
|
42 |
const [open, setOpen] = useState(false);
|
43 |
const [prompt, setPrompt] = useState("");
|
@@ -51,7 +51,6 @@ function AskAI({
|
|
51 |
const [think, setThink] = useState<string | undefined>(undefined);
|
52 |
const [openThink, setOpenThink] = useState(false);
|
53 |
const [isThinking, setIsThinking] = useState(true);
|
54 |
-
const [files, setFiles] = useState<File[]>([]);
|
55 |
const [controller, setController] = useState<AbortController | null>(null);
|
56 |
|
57 |
const audio = new Audio(SuccessSound);
|
@@ -252,17 +251,6 @@ function AskAI({
|
|
252 |
}
|
253 |
};
|
254 |
|
255 |
-
// const handleUploadFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
256 |
-
// const filesList = event.target.files;
|
257 |
-
// if (filesList && filesList.length > 0) {
|
258 |
-
// // add files to the state to show them in the UI
|
259 |
-
// const newFiles = Array.from(filesList);
|
260 |
-
// setFiles((prevFiles) => [...prevFiles, ...newFiles]);
|
261 |
-
// // clear the input value to allow re-uploading the same file
|
262 |
-
// event.target.value = "";
|
263 |
-
// }
|
264 |
-
// };
|
265 |
-
|
266 |
useUpdateEffect(() => {
|
267 |
if (refThink.current) {
|
268 |
refThink.current.scrollTop = refThink.current.scrollHeight;
|
@@ -277,33 +265,6 @@ function AskAI({
|
|
277 |
|
278 |
return (
|
279 |
<div className="bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 absolute bottom-3 left-3 w-[calc(100%-20px)] group">
|
280 |
-
{files.length > 0 && (
|
281 |
-
<div className="w-full absolute top-0 left-0 -translate-y-full pb-4 flex items-center justify-start gap-4">
|
282 |
-
{files.map((file, index) => (
|
283 |
-
<div
|
284 |
-
key={index}
|
285 |
-
className="flex items-center justify-between bg-neutral-700/50 rounded-lg w-20 aspect-square relative"
|
286 |
-
style={{
|
287 |
-
backgroundImage: `url(${URL.createObjectURL(file)})`,
|
288 |
-
backgroundSize: "cover",
|
289 |
-
backgroundPosition: "center",
|
290 |
-
}}
|
291 |
-
>
|
292 |
-
<Button
|
293 |
-
size="iconXss"
|
294 |
-
className="absolute -top-2 -right-2 ring-[3px] ring-neutral-900"
|
295 |
-
onClick={() => {
|
296 |
-
setFiles((prevFiles) =>
|
297 |
-
prevFiles.filter((f) => f.name !== file.name)
|
298 |
-
);
|
299 |
-
}}
|
300 |
-
>
|
301 |
-
<X className="size-4" />
|
302 |
-
</Button>
|
303 |
-
</div>
|
304 |
-
))}
|
305 |
-
</div>
|
306 |
-
)}
|
307 |
{think && (
|
308 |
<div className="w-full border-b border-neutral-700 relative overflow-hidden">
|
309 |
<header
|
@@ -377,27 +338,7 @@ function AskAI({
|
|
377 |
</div>
|
378 |
<div className="flex items-center justify-between gap-2 px-4 pb-3">
|
379 |
<div className="flex-1">
|
380 |
-
|
381 |
-
size="iconXs"
|
382 |
-
variant="outline"
|
383 |
-
className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300"
|
384 |
-
onClick={() => {
|
385 |
-
if (uploadInputRef.current) {
|
386 |
-
uploadInputRef.current.click();
|
387 |
-
}
|
388 |
-
}}
|
389 |
-
>
|
390 |
-
<ImagePlus className="size-4" />
|
391 |
-
</Button>
|
392 |
-
<input
|
393 |
-
ref={uploadInputRef}
|
394 |
-
type="file"
|
395 |
-
accept="image/*"
|
396 |
-
multiple
|
397 |
-
onChange={handleUploadFile}
|
398 |
-
className="hidden"
|
399 |
-
id="file-upload"
|
400 |
-
/> */}
|
401 |
</div>
|
402 |
<div className="flex items-center justify-end gap-2">
|
403 |
<Settings
|
|
|
3 |
import classNames from "classnames";
|
4 |
import { toast } from "sonner";
|
5 |
import { useLocalStorage, useUpdateEffect } from "react-use";
|
6 |
+
import { ArrowUp, ChevronDown } from "lucide-react";
|
7 |
import { FaStopCircle } from "react-icons/fa";
|
8 |
|
9 |
import Login from "../login/login";
|
|
|
16 |
import { MODELS } from "../../../utils/providers";
|
17 |
import Loading from "../loading/loading";
|
18 |
import { HtmlHistory } from "../../../utils/types";
|
19 |
+
import InviteFriends from "../invite-friends/invite-friends";
|
20 |
|
21 |
function AskAI({
|
22 |
html,
|
|
|
38 |
onSuccess: (h: string, p: string, n?: number[][]) => void;
|
39 |
}) {
|
40 |
const refThink = useRef<HTMLDivElement | null>(null);
|
|
|
41 |
|
42 |
const [open, setOpen] = useState(false);
|
43 |
const [prompt, setPrompt] = useState("");
|
|
|
51 |
const [think, setThink] = useState<string | undefined>(undefined);
|
52 |
const [openThink, setOpenThink] = useState(false);
|
53 |
const [isThinking, setIsThinking] = useState(true);
|
|
|
54 |
const [controller, setController] = useState<AbortController | null>(null);
|
55 |
|
56 |
const audio = new Audio(SuccessSound);
|
|
|
251 |
}
|
252 |
};
|
253 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
useUpdateEffect(() => {
|
255 |
if (refThink.current) {
|
256 |
refThink.current.scrollTop = refThink.current.scrollHeight;
|
|
|
265 |
|
266 |
return (
|
267 |
<div className="bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 absolute bottom-3 left-3 w-[calc(100%-20px)] group">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
{think && (
|
269 |
<div className="w-full border-b border-neutral-700 relative overflow-hidden">
|
270 |
<header
|
|
|
338 |
</div>
|
339 |
<div className="flex items-center justify-between gap-2 px-4 pb-3">
|
340 |
<div className="flex-1">
|
341 |
+
<InviteFriends />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
</div>
|
343 |
<div className="flex items-center justify-end gap-2">
|
344 |
<Settings
|
src/components/invite-friends/invite-friends.tsx
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { TiUserAdd } from "react-icons/ti";
|
2 |
+
import { Link } from "lucide-react";
|
3 |
+
import { FaXTwitter } from "react-icons/fa6";
|
4 |
+
|
5 |
+
import { Button } from "../ui/button";
|
6 |
+
import { Dialog, DialogContent, DialogTrigger } from "../ui/dialog";
|
7 |
+
import { useCopyToClipboard } from "react-use";
|
8 |
+
import { toast } from "sonner";
|
9 |
+
export default function InviteFriends() {
|
10 |
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
11 |
+
const [_, copyToClipboard] = useCopyToClipboard();
|
12 |
+
|
13 |
+
return (
|
14 |
+
<Dialog>
|
15 |
+
<form>
|
16 |
+
<DialogTrigger asChild>
|
17 |
+
<Button
|
18 |
+
size="iconXs"
|
19 |
+
variant="outline"
|
20 |
+
className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300"
|
21 |
+
>
|
22 |
+
<TiUserAdd className="size-4" />
|
23 |
+
</Button>
|
24 |
+
</DialogTrigger>
|
25 |
+
<DialogContent className="sm:max-w-lg lg:!p-8 !rounded-3xl !bg-white !border-neutral-100">
|
26 |
+
<main>
|
27 |
+
<div className="flex items-center justify-start -space-x-4 mb-5">
|
28 |
+
<div className="size-11 rounded-full bg-pink-300 shadow-2xs flex items-center justify-center text-2xl">
|
29 |
+
😎
|
30 |
+
</div>
|
31 |
+
<div className="size-11 rounded-full bg-amber-300 shadow-2xs flex items-center justify-center text-2xl z-2">
|
32 |
+
😇
|
33 |
+
</div>
|
34 |
+
<div className="size-11 rounded-full bg-sky-300 shadow-2xs flex items-center justify-center text-2xl">
|
35 |
+
😜
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
<p className="text-xl font-semibold text-neutral-950 max-w-[200px]">
|
39 |
+
Invite your friends to join us!
|
40 |
+
</p>
|
41 |
+
<p className="text-sm text-neutral-500 mt-2 max-w-sm">
|
42 |
+
Support us and share the love and let them know about our awesome
|
43 |
+
platform.
|
44 |
+
</p>
|
45 |
+
<div className="mt-4 space-x-3.5">
|
46 |
+
<a
|
47 |
+
href="https://x.com/intent/post?url=https://enzostvs-deepsite.hf.space/&text=Checkout%20this%20awesome%20Ai%20Tool!%20Vibe%20coding%20has%20never%20been%20so%20easy✨"
|
48 |
+
target="_blank"
|
49 |
+
rel="noopener noreferrer"
|
50 |
+
>
|
51 |
+
<Button
|
52 |
+
variant="ghostWhite"
|
53 |
+
size="sm"
|
54 |
+
className="!text-neutral-700"
|
55 |
+
>
|
56 |
+
<FaXTwitter className="size-4" />
|
57 |
+
Share on
|
58 |
+
</Button>
|
59 |
+
</a>
|
60 |
+
<Button
|
61 |
+
variant="ghostWhite"
|
62 |
+
size="sm"
|
63 |
+
className="!text-neutral-700"
|
64 |
+
onClick={() => {
|
65 |
+
copyToClipboard("https://enzostvs-deepsite.hf.space/");
|
66 |
+
toast.success("Invite link copied to clipboard!");
|
67 |
+
}}
|
68 |
+
>
|
69 |
+
<Link className="size-4" />
|
70 |
+
Copy Invite Link
|
71 |
+
</Button>
|
72 |
+
</div>
|
73 |
+
</main>
|
74 |
+
</DialogContent>
|
75 |
+
</form>
|
76 |
+
</Dialog>
|
77 |
+
);
|
78 |
+
}
|
src/components/ui/button.tsx
CHANGED
@@ -21,6 +21,7 @@ const buttonVariants = cva(
|
|
21 |
link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
|
22 |
pink: "bg-sky-500 text-white hover:brightness-110",
|
23 |
gray: "bg-neutral-900 text-neutral-300 hover:brightness-110",
|
|
|
24 |
},
|
25 |
size: {
|
26 |
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
|
21 |
link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
|
22 |
pink: "bg-sky-500 text-white hover:brightness-110",
|
23 |
gray: "bg-neutral-900 text-neutral-300 hover:brightness-110",
|
24 |
+
ghostWhite: "bg-neutral-200/60 text-neutral-900 hover:bg-neutral-200",
|
25 |
},
|
26 |
size: {
|
27 |
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
src/components/ui/dialog.tsx
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react";
|
2 |
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
3 |
+
import { XIcon } from "lucide-react";
|
4 |
+
|
5 |
+
import { cn } from "./../../lib/utils";
|
6 |
+
|
7 |
+
function Dialog({
|
8 |
+
...props
|
9 |
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
10 |
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
11 |
+
}
|
12 |
+
|
13 |
+
function DialogTrigger({
|
14 |
+
...props
|
15 |
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
16 |
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
17 |
+
}
|
18 |
+
|
19 |
+
function DialogPortal({
|
20 |
+
...props
|
21 |
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
22 |
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
23 |
+
}
|
24 |
+
|
25 |
+
function DialogClose({
|
26 |
+
...props
|
27 |
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
28 |
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
29 |
+
}
|
30 |
+
|
31 |
+
function DialogOverlay({
|
32 |
+
className,
|
33 |
+
...props
|
34 |
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
35 |
+
return (
|
36 |
+
<DialogPrimitive.Overlay
|
37 |
+
data-slot="dialog-overlay"
|
38 |
+
className={cn(
|
39 |
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
40 |
+
className
|
41 |
+
)}
|
42 |
+
{...props}
|
43 |
+
/>
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
function DialogContent({
|
48 |
+
className,
|
49 |
+
children,
|
50 |
+
showCloseButton = true,
|
51 |
+
...props
|
52 |
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
53 |
+
showCloseButton?: boolean;
|
54 |
+
}) {
|
55 |
+
return (
|
56 |
+
<DialogPortal data-slot="dialog-portal">
|
57 |
+
<DialogOverlay />
|
58 |
+
<DialogPrimitive.Content
|
59 |
+
data-slot="dialog-content"
|
60 |
+
className={cn(
|
61 |
+
"bg-white data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-neutral-200 p-6 shadow-lg duration-200 sm:max-w-lg dark:bg-neutral-950 dark:border-neutral-800",
|
62 |
+
className
|
63 |
+
)}
|
64 |
+
{...props}
|
65 |
+
>
|
66 |
+
{children}
|
67 |
+
{showCloseButton && (
|
68 |
+
<DialogPrimitive.Close
|
69 |
+
data-slot="dialog-close"
|
70 |
+
className="ring-offset-white focus:ring-neutral-950 data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:ring-offset-neutral-950 dark:focus:ring-neutral-300 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-400"
|
71 |
+
>
|
72 |
+
<XIcon />
|
73 |
+
<span className="sr-only">Close</span>
|
74 |
+
</DialogPrimitive.Close>
|
75 |
+
)}
|
76 |
+
</DialogPrimitive.Content>
|
77 |
+
</DialogPortal>
|
78 |
+
);
|
79 |
+
}
|
80 |
+
|
81 |
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
82 |
+
return (
|
83 |
+
<div
|
84 |
+
data-slot="dialog-header"
|
85 |
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
86 |
+
{...props}
|
87 |
+
/>
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
92 |
+
return (
|
93 |
+
<div
|
94 |
+
data-slot="dialog-footer"
|
95 |
+
className={cn(
|
96 |
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
97 |
+
className
|
98 |
+
)}
|
99 |
+
{...props}
|
100 |
+
/>
|
101 |
+
);
|
102 |
+
}
|
103 |
+
|
104 |
+
function DialogTitle({
|
105 |
+
className,
|
106 |
+
...props
|
107 |
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
108 |
+
return (
|
109 |
+
<DialogPrimitive.Title
|
110 |
+
data-slot="dialog-title"
|
111 |
+
className={cn("text-lg leading-none font-semibold", className)}
|
112 |
+
{...props}
|
113 |
+
/>
|
114 |
+
);
|
115 |
+
}
|
116 |
+
|
117 |
+
function DialogDescription({
|
118 |
+
className,
|
119 |
+
...props
|
120 |
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
121 |
+
return (
|
122 |
+
<DialogPrimitive.Description
|
123 |
+
data-slot="dialog-description"
|
124 |
+
className={cn(
|
125 |
+
"text-neutral-500 text-sm dark:text-neutral-400",
|
126 |
+
className
|
127 |
+
)}
|
128 |
+
{...props}
|
129 |
+
/>
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
export {
|
134 |
+
Dialog,
|
135 |
+
DialogClose,
|
136 |
+
DialogContent,
|
137 |
+
DialogDescription,
|
138 |
+
DialogFooter,
|
139 |
+
DialogHeader,
|
140 |
+
DialogOverlay,
|
141 |
+
DialogPortal,
|
142 |
+
DialogTitle,
|
143 |
+
DialogTrigger,
|
144 |
+
};
|