From 8842400f2a5f313c0b3f0fdb9fabffa7f0d092e6 Mon Sep 17 00:00:00 2001 From: mroxso <24775431+mroxso@users.noreply.github.com> Date: Fri, 18 Apr 2025 21:21:17 +0200 Subject: [PATCH] feat: implement custom relay management and add relay addition functionality (#73) Co-authored-by: highperfocused --- app/layout.tsx | 22 +- app/relays/page.tsx | 20 +- bun.lockb | Bin 358375 -> 364614 bytes components/AddRelaySheet.tsx | 118 ++++++++++ components/ManageCustomRelays.tsx | 85 ++++++++ components/ui/sheet.tsx | 140 ++++++++++++ package-lock.json | 351 +++++++++++++++++++++++++++--- package.json | 2 +- 8 files changed, 703 insertions(+), 35 deletions(-) create mode 100644 components/AddRelaySheet.tsx create mode 100644 components/ManageCustomRelays.tsx create mode 100644 components/ui/sheet.tsx diff --git a/app/layout.tsx b/app/layout.tsx index f5595eb..f310c58 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -10,6 +10,7 @@ import { Inter } from "next/font/google"; import { Toaster } from "@/components/ui/toaster" import Script from "next/script"; import Umami from "@/components/Umami"; +import { useEffect, useState } from "react"; const inter = Inter({ subsets: ["latin"] }); @@ -18,11 +19,26 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode; }>) { - - const relayUrls = [ + const [relayUrls, setRelayUrls] = useState([ "wss://relay.nostr.band", "wss://relay.damus.io", - ]; + ]); + + useEffect(() => { + // Load custom relays from localStorage + try { + const customRelays = JSON.parse(localStorage.getItem("customRelays") || "[]"); + if (customRelays.length > 0) { + setRelayUrls(prevRelays => { + // Combine default relays with custom relays, removing duplicates + const allRelays = [...prevRelays, ...customRelays]; + return Array.from(new Set(allRelays)); // Remove duplicates + }); + } + } catch (error) { + console.error("Error loading custom relays:", error); + } + }, []); return ( diff --git a/app/relays/page.tsx b/app/relays/page.tsx index d99ba17..f7c7e08 100644 --- a/app/relays/page.tsx +++ b/app/relays/page.tsx @@ -9,11 +9,14 @@ import { Skeleton } from "@/components/ui/skeleton"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; +import { AddRelaySheet } from "@/components/AddRelaySheet"; +import { ManageCustomRelays } from "@/components/ManageCustomRelays"; export default function RelaysPage() { const { connectedRelays } = useNostr(); const [relayStatus, setRelayStatus] = useState<{ [url: string]: 'connected' | 'connecting' | 'disconnected' | 'error' }>({}); const [loading, setLoading] = useState(true); + const [refreshKey, setRefreshKey] = useState(0); useEffect(() => { document.title = `Relays | LUMINA`; @@ -35,7 +38,7 @@ export default function RelaysPage() { setRelayStatus(status); setLoading(false); } - }, [connectedRelays]); + }, [connectedRelays, refreshKey]); // Function to get the appropriate status icon const getStatusIcon = (status: string) => { @@ -69,9 +72,17 @@ export default function RelaysPage() { } }; + const handleRelayAdded = () => { + // Trigger a refresh of the component when a relay is added + setRefreshKey(prev => prev + 1); + }; + return (
-

Nostr Relays

+
+

Nostr Relays

+ +
@@ -116,9 +127,6 @@ export default function RelaysPage() { - {/*
- Active subscriptions: {activeSubscriptions?.length || 0} -
*/} @@ -167,6 +175,8 @@ export default function RelaysPage() {
+ +
diff --git a/bun.lockb b/bun.lockb index c98738859ad534c4accedcc05e5f60350a847eab..3bb9e82e091f76e6b9748827521e3b78a59789a1 100755 GIT binary patch delta 14196 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 diff --git a/components/AddRelaySheet.tsx b/components/AddRelaySheet.tsx new file mode 100644 index 0000000..2be0f6c --- /dev/null +++ b/components/AddRelaySheet.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { useState } from "react"; +import { useNostr } from "nostr-react"; +import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { PlusCircle } from "lucide-react"; +import { useToast } from "@/components/ui/use-toast"; + +interface AddRelaySheetProps { + onRelayAdded: () => void; +} + +export function AddRelaySheet({ onRelayAdded }: AddRelaySheetProps) { + const [relayUrl, setRelayUrl] = useState(""); + const [isOpen, setIsOpen] = useState(false); + const { toast } = useToast(); + const { connectedRelays } = useNostr(); + + const handleAddRelay = () => { + // Basic validation + if (!relayUrl) { + toast({ + title: "Invalid relay URL", + description: "Please enter a relay URL", + variant: "destructive", + }); + return; + } + + // Format URL + let formattedUrl = relayUrl; + if (!formattedUrl.startsWith("wss://")) { + formattedUrl = `wss://${formattedUrl}`; + } + + // Check if relay already exists in connected relays + const existingRelays = connectedRelays?.map(relay => relay.url) || []; + if (existingRelays.includes(formattedUrl)) { + toast({ + title: "Relay already exists", + description: "This relay is already in your list", + variant: "destructive", + }); + return; + } + + // Get existing custom relays from localStorage + const customRelays = JSON.parse(localStorage.getItem("customRelays") || "[]"); + + // Add new relay to the list if not already in custom relays + if (!customRelays.includes(formattedUrl)) { + customRelays.push(formattedUrl); + localStorage.setItem("customRelays", JSON.stringify(customRelays)); + + toast({ + title: "Relay added", + description: "The relay has been added to your list. Refresh to connect.", + variant: "default", + }); + + // Reset form and close sheet + setRelayUrl(""); + setIsOpen(false); + + // Call the callback to notify parent component + onRelayAdded(); + } else { + toast({ + title: "Relay already exists", + description: "This relay is already in your custom list", + variant: "destructive", + }); + } + }; + + return ( + + + + + + + Add New Relay + + Enter the URL of a Nostr relay to add to your connection list. + + +
+
+ setRelayUrl(e.target.value)} + className="w-full" + /> +

+ Relay URLs typically start with wss:// but you can omit it if needed. +

+
+
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/ManageCustomRelays.tsx b/components/ManageCustomRelays.tsx new file mode 100644 index 0000000..ee19186 --- /dev/null +++ b/components/ManageCustomRelays.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useNostr } from "nostr-react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Trash2 } from "lucide-react"; +import { useToast } from "@/components/ui/use-toast"; +import { Badge } from "@/components/ui/badge"; + +export function ManageCustomRelays() { + const [customRelays, setCustomRelays] = useState([]); + const { toast } = useToast(); + const { connectedRelays } = useNostr(); + + useEffect(() => { + // Load custom relays from localStorage + const storedRelays = JSON.parse(localStorage.getItem("customRelays") || "[]"); + setCustomRelays(storedRelays); + }, []); + + const handleRemoveRelay = (relayUrl: string) => { + // Filter out the relay to remove + const updatedRelays = customRelays.filter(url => url !== relayUrl); + + // Update state and localStorage + setCustomRelays(updatedRelays); + localStorage.setItem("customRelays", JSON.stringify(updatedRelays)); + + toast({ + title: "Relay removed", + description: "The relay has been removed from your list. Refresh to apply changes.", + variant: "default", + }); + }; + + // Check if a relay is currently connected + const isConnected = (relayUrl: string) => { + return connectedRelays?.some(relay => relay.url === relayUrl && relay.status === 1) || false; + }; + + if (customRelays.length === 0) { + return null; + } + + return ( + + + Custom Relays + + +
+ {customRelays.map((relayUrl) => ( +
+
+
+

{relayUrl}

+
+ {isConnected(relayUrl) ? ( + Connected + ) : ( + Not Connected + )} +
+
+
+ +
+ ))} +

+ Note: Refresh the page after adding or removing relays to apply changes to connections. +

+
+
+
+ ); +} \ No newline at end of file diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx new file mode 100644 index 0000000..a37f17b --- /dev/null +++ b/components/ui/sheet.tsx @@ -0,0 +1,140 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/package-lock.json b/package-lock.json index 6590e7f..ec937fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@hookform/resolvers": "^3.4.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.4", - "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dialog": "^1.1.10", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", @@ -2750,30 +2750,31 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.10.tgz", + "integrity": "sha512-m6pZb0gEM5uHPSb+i2nKKGQi/HMSVjARMsLMWQfKDP+eJ6B+uqryHnXhpnohTWElw+vEcMk/o4wJODtdRKHwqg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -2784,18 +2785,283 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", + "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", + "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", + "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", + "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4080,6 +4346,39 @@ } } }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.0.3", "license": "MIT", diff --git a/package.json b/package.json index de6ea2f..e7b984a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@hookform/resolvers": "^3.4.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.4", - "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dialog": "^1.1.10", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2",