diff --git a/app/@modal/(...)preview/[id]/modal.tsx b/app/@modal/(...)preview/[id]/modal.tsx index 3a7e67e6..11c86e69 100644 --- a/app/@modal/(...)preview/[id]/modal.tsx +++ b/app/@modal/(...)preview/[id]/modal.tsx @@ -18,7 +18,7 @@ export function Modal({ children }: { children: React.ReactNode }) { event.preventDefault()} - className="h-full w-full rounded-none max-w-full sm:rounded-md sm:shadow-xl" + className="inset-0 top-0 left-0 h-dvh w-full max-h-none max-w-none translate-x-0 translate-y-0 gap-0 overflow-hidden rounded-none p-0 ring-0 shadow-none sm:max-w-none sm:rounded-none" > {children} diff --git a/app/admin/page.tsx b/app/admin/page.tsx index bf8e2512..95d7d521 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -248,12 +248,17 @@ export default async function Admin() { - - {t('Dashboard.openImageManagement')} - - - + + + - + + {t('Button.removeTwoFactor')} + + } + /> {t('Tips.confirmRemoveTwoFactor')} @@ -219,4 +221,4 @@ export default function Authenticator() { } ) -} \ No newline at end of file +} diff --git a/app/admin/settings/passkey/page.tsx b/app/admin/settings/passkey/page.tsx index d7edaf8d..31ec3f32 100644 --- a/app/admin/settings/passkey/page.tsx +++ b/app/admin/settings/passkey/page.tsx @@ -172,15 +172,17 @@ export default function PasskeySettings() { - - - + + + + } + /> {t('deletePasskey')} @@ -229,4 +231,4 @@ export default function PasskeySettings() { )} ) -} \ No newline at end of file +} diff --git a/app/layout.tsx b/app/layout.tsx index a7d3b0ea..0a25d1dc 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from 'next/types' -import { Source_Serif_4, Source_Sans_3 } from 'next/font/google' +import { Source_Serif_4, Source_Sans_3, Inter } from 'next/font/google' import { ThemeProvider } from '~/app/providers/next-ui-providers' import { ToasterProviders } from '~/app/providers/toaster-providers' @@ -13,6 +13,7 @@ import { NextIntlClientProvider } from 'next-intl' import { getLocale, getMessages } from 'next-intl/server' import { ConfigStoreProvider } from '~/app/providers/config-store-providers' import Script from 'next/script' +import { cn } from "~/lib/utils"; const sourceSerif4 = Source_Serif_4({ subsets: ['latin'], @@ -21,12 +22,7 @@ const sourceSerif4 = Source_Serif_4({ weight: ['400', '600', '700'], }) -const sourceSans3 = Source_Sans_3({ - subsets: ['latin'], - variable: '--font-sans', - display: 'swap', - weight: ['400', '500', '600'], -}) +const inter = Inter({subsets:['latin'],variable:'--font-sans'}) type ConfigItem = { id: string; @@ -101,7 +97,7 @@ export default async function RootLayout({ const umamiAnalytics = data?.find((item: ConfigItem) => item.config_key === 'umami_analytics')?.config_value return ( - + diff --git a/components.json b/components.json index 45948fd2..f517b817 100644 --- a/components.json +++ b/components.json @@ -1,15 +1,17 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", + "style": "base-mira", "rsc": true, "tsx": true, "tailwind": { "config": "", "css": "style/globals.css", - "baseColor": "zinc", + "baseColor": "neutral", "cssVariables": true, "prefix": "" }, + "iconLibrary": "hugeicons", + "rtl": false, "aliases": { "components": "~/components", "utils": "~/lib/utils", @@ -17,5 +19,7 @@ "lib": "~/lib", "hooks": "~/hooks" }, - "iconLibrary": "lucide" -} \ No newline at end of file + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/components/admin/album/album-add-sheet.tsx b/components/admin/album/album-add-sheet.tsx index b30def4a..ab233669 100644 --- a/components/admin/album/album-add-sheet.tsx +++ b/components/admin/album/album-add-sheet.tsx @@ -66,8 +66,7 @@ export default function AlbumAddSheet(props : Readonly) { event.preventDefault()}> {t('Album.addAlbum')} - -
+ }>
diff --git a/components/admin/album/album-list.tsx b/components/admin/album/album-list.tsx index b2e933b8..4c02859a 100644 --- a/components/admin/album/album-list.tsx +++ b/components/admin/album/album-list.tsx @@ -133,20 +133,22 @@ export default function AlbumList(props : Readonly) { }}> {({ iconRef, triggerProps }) => ( - - - + { + setAlbum(album) + }, + }, triggerProps)} + > + + + } + /> )} diff --git a/components/admin/list/image-batch-delete-sheet.tsx b/components/admin/list/image-batch-delete-sheet.tsx index 12f767c6..e81d40c6 100644 --- a/components/admin/list/image-batch-delete-sheet.tsx +++ b/components/admin/list/image-batch-delete-sheet.tsx @@ -122,15 +122,17 @@ export default function ImageBatchDeleteSheet(props : Readonly - - - + + {t('Button.delete')} + + } + /> {t('List.confirmDelete')} diff --git a/components/admin/list/list-props.tsx b/components/admin/list/list-props.tsx index 89814ba4..d85ca4ce 100644 --- a/components/admin/list/list-props.tsx +++ b/components/admin/list/list-props.tsx @@ -300,16 +300,18 @@ export default function ListProps(props : Readonly) { {({ iconRef, triggerProps }) => ( - - - + + + + } + /> )} @@ -424,20 +426,22 @@ export default function ListProps(props : Readonly) {
- - - + { + setImage(image) + setImageAlbum(image.album_value) + }} + aria-label={t('List.bindAlbum')} + > + + + } + /> {t('List.bindAlbum')} diff --git a/components/album/floating-filter-ball.tsx b/components/album/floating-filter-ball.tsx index 8512bc11..b308da12 100644 --- a/components/album/floating-filter-ball.tsx +++ b/components/album/floating-filter-ball.tsx @@ -326,9 +326,11 @@ export default function FloatingFilterBall({ return ( - - {BallButton} - + ) { {({ iconRef, triggerProps }) => ( - - - + + + + } + /> )} diff --git a/components/layout/admin/nav-main.tsx b/components/layout/admin/nav-main.tsx index 56543b61..904c8158 100644 --- a/components/layout/admin/nav-main.tsx +++ b/components/layout/admin/nav-main.tsx @@ -40,25 +40,23 @@ export function NavMain({ {({ iconRef, triggerProps }) => ( } > - - { - setOpenMobile(false) - router.push(item.url) - }, - }, triggerProps)} - > - {item.icon && } - {item.title} - - + { + setOpenMobile(false) + router.push(item.url) + }, + }, triggerProps)} + > + {item.icon && } + {item.title} + )} diff --git a/components/layout/admin/nav-title.tsx b/components/layout/admin/nav-title.tsx index acebf590..2ce99c30 100644 --- a/components/layout/admin/nav-title.tsx +++ b/components/layout/admin/nav-title.tsx @@ -21,18 +21,20 @@ export function NavTitle() { - - router.push('/')}> -
- Logo -
-
- - {'PicImpact'} - -
-
-
+ router.push('/')}> +
+ Logo +
+
+ + {'PicImpact'} + +
+ + } + />
diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx index be3ce800..f12c4527 100644 --- a/components/ui/accordion.tsx +++ b/components/ui/accordion.tsx @@ -1,25 +1,29 @@ -'use client' +"use client" -import * as React from 'react' -import * as AccordionPrimitive from '@radix-ui/react-accordion' -import { ChevronDownIcon } from 'lucide-react' +import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion" -import { cn } from '~/lib/utils' +import { cn } from "~/lib/utils" +import { HugeiconsIcon } from "@hugeicons/react" +import { ArrowDown01Icon, ArrowUp01Icon } from "@hugeicons/core-free-icons" -function Accordion({ - ...props -}: React.ComponentProps) { - return +function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { + return ( + + ) } -function AccordionItem({ - className, - ...props -}: React.ComponentProps) { +function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { return ( ) @@ -29,19 +33,20 @@ function AccordionTrigger({ className, children, ...props -}: React.ComponentProps) { +}: AccordionPrimitive.Trigger.Props) { return ( svg]:rotate-180', + "group/accordion-trigger relative flex flex-1 items-start justify-between gap-6 border border-transparent p-2 text-left text-xs/relaxed font-medium transition-all outline-none hover:underline aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground", className )} {...props} > {children} - + + ) @@ -51,15 +56,22 @@ function AccordionContent({ className, children, ...props -}: React.ComponentProps) { +}: AccordionPrimitive.Panel.Props) { return ( - -
{children}
-
+
+ {children} +
+ ) } diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx index a71e645e..cedc5800 100644 --- a/components/ui/alert-dialog.tsx +++ b/components/ui/alert-dialog.tsx @@ -1,28 +1,22 @@ -'use client' +"use client" -import * as React from 'react' -import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" -import { cn } from '~/lib/utils' -import { buttonVariants } from '~/components/ui/button' +import { cn } from "~/lib/utils" +import { Button } from "~/components/ui/button" -function AlertDialog({ - ...props -}: React.ComponentProps) { +function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { return } -function AlertDialogTrigger({ - ...props -}: React.ComponentProps) { +function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { return ( ) } -function AlertDialogPortal({ - ...props -}: React.ComponentProps) { +function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { return ( ) @@ -31,12 +25,12 @@ function AlertDialogPortal({ function AlertDialogOverlay({ className, ...props -}: React.ComponentProps) { +}: AlertDialogPrimitive.Backdrop.Props) { return ( - ) { +}: AlertDialogPrimitive.Popup.Props & { + size?: "default" | "sm" +}) { return ( - ) { +}: React.ComponentProps<"div">) { return (
) @@ -79,12 +80,28 @@ function AlertDialogHeader({ function AlertDialogFooter({ className, ...props -}: React.ComponentProps<'div'>) { +}: React.ComponentProps<"div">) { return (
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
) @@ -112,7 +132,10 @@ function AlertDialogDescription({ return ( ) @@ -121,10 +144,11 @@ function AlertDialogDescription({ function AlertDialogAction({ className, ...props -}: React.ComponentProps) { +}: React.ComponentProps) { return ( - ) @@ -132,11 +156,16 @@ function AlertDialogAction({ function AlertDialogCancel({ className, + variant = "outline", + size = "default", ...props -}: React.ComponentProps) { +}: AlertDialogPrimitive.Close.Props & + Pick, "variant" | "size">) { return ( - } {...props} /> ) @@ -144,14 +173,15 @@ function AlertDialogCancel({ export { AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, - AlertDialogTrigger, + AlertDialogAction, + AlertDialogCancel, AlertDialogContent, - AlertDialogHeader, + AlertDialogDescription, AlertDialogFooter, + AlertDialogHeader, + AlertDialogMedia, + AlertDialogOverlay, + AlertDialogPortal, AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, + AlertDialogTrigger, } diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx index c47c39bc..512711e4 100644 --- a/components/ui/alert.tsx +++ b/components/ui/alert.tsx @@ -1,20 +1,20 @@ -import * as React from 'react' -import { cva, type VariantProps } from 'class-variance-authority' +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" -import { cn } from '~/lib/utils' +import { cn } from "~/lib/utils" const alertVariants = cva( - 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + "group/alert relative grid w-full gap-0.5 rounded-lg border px-2 py-1.5 text-left text-xs/relaxed has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-1.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-3.5", { variants: { variant: { - default: 'bg-card text-card-foreground', + default: "bg-card text-card-foreground", destructive: - 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', + "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current", }, }, defaultVariants: { - variant: 'default', + variant: "default", }, } ) @@ -23,7 +23,7 @@ function Alert({ className, variant, ...props -}: React.ComponentProps<'div'> & VariantProps) { +}: React.ComponentProps<"div"> & VariantProps) { return (
) { +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { return (
svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground", className )} {...props} @@ -50,12 +50,12 @@ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { function AlertDescription({ className, ...props -}: React.ComponentProps<'div'>) { +}: React.ComponentProps<"div">) { return (
) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription, AlertAction } diff --git a/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx index 40bb1208..dcbbe4aa 100644 --- a/components/ui/aspect-ratio.tsx +++ b/components/ui/aspect-ratio.tsx @@ -1,11 +1,22 @@ -'use client' - -import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio' +import { cn } from "~/lib/utils" function AspectRatio({ + ratio, + className, ...props -}: React.ComponentProps) { - return +}: React.ComponentProps<"div"> & { ratio: number }) { + return ( +
+ ) } export { AspectRatio } diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx index d54e9932..ebb94a6b 100644 --- a/components/ui/avatar.tsx +++ b/components/ui/avatar.tsx @@ -1,19 +1,23 @@ -'use client' +"use client" -import * as React from 'react' -import * as AvatarPrimitive from '@radix-ui/react-avatar' +import * as React from "react" +import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar" -import { cn } from '~/lib/utils' +import { cn } from "~/lib/utils" function Avatar({ className, + size = "default", ...props -}: React.ComponentProps) { +}: AvatarPrimitive.Root.Props & { + size?: "default" | "sm" | "lg" +}) { return ( ) { +function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) { return ( ) @@ -37,12 +41,28 @@ function AvatarImage({ function AvatarFallback({ className, ...props -}: React.ComponentProps) { +}: AvatarPrimitive.Fallback.Props) { return ( + ) +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", className )} {...props} @@ -50,4 +70,40 @@ function AvatarFallback({ ) } -export { Avatar, AvatarImage, AvatarFallback } +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AvatarGroupCount({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className + )} + {...props} + /> + ) +} + +export { + Avatar, + AvatarImage, + AvatarFallback, + AvatarGroup, + AvatarGroupCount, + AvatarBadge, +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index 49b2080a..9928013e 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,46 +1,52 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { cva, type VariantProps } from 'class-variance-authority' +import { mergeProps } from "@base-ui/react/merge-props" +import { useRender } from "@base-ui/react/use-render" +import { cva, type VariantProps } from "class-variance-authority" -import { cn } from '~/lib/utils' +import { cn } from "~/lib/utils" const badgeVariants = cva( - 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-[0.625rem] font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-2.5!", { variants: { variant: { - default: - 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", secondary: - 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", destructive: - 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", outline: - 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + "border-border bg-input/20 text-foreground dark:bg-input/30 [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", }, }, defaultVariants: { - variant: 'default', + variant: "default", }, } ) function Badge({ className, - variant, - asChild = false, + variant = "default", + render, ...props -}: React.ComponentProps<'span'> & - VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : 'span' - - return ( - - ) +}: useRender.ComponentProps<"span"> & VariantProps) { + return useRender({ + defaultTagName: "span", + props: mergeProps<"span">( + { + className: cn(badgeVariants({ variant }), className), + }, + props + ), + render, + state: { + slot: "badge", + variant, + }, + }) } export { Badge, badgeVariants } diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx index bde56a2d..59f21830 100644 --- a/components/ui/breadcrumb.tsx +++ b/components/ui/breadcrumb.tsx @@ -1,19 +1,28 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { ChevronRight, MoreHorizontal } from 'lucide-react' +import * as React from "react" +import { mergeProps } from "@base-ui/react/merge-props" +import { useRender } from "@base-ui/react/use-render" -import { cn } from '~/lib/utils' +import { cn } from "~/lib/utils" +import { HugeiconsIcon } from "@hugeicons/react" +import { ArrowRight01Icon, MoreHorizontalCircle01Icon } from "@hugeicons/core-free-icons" -function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { - return