add treasures (artworks)
This commit is contained in:
@@ -9,7 +9,7 @@ import { cn } from "@/utility/utils";
|
||||
const AppBar = () => {
|
||||
return (
|
||||
<header className="w-full bg-[#111a21] shadow">
|
||||
<div className="container py-8 md:py-4 flex flex-col md:flex-row items-center gap-4">
|
||||
<div className="mx-auto max-w-5xl md:px-4 pt-6 md:py-4 flex flex-col md:flex-row items-center gap-4">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex flex-col md:flex-row items-center gap-2 md:gap-4"
|
||||
@@ -24,6 +24,7 @@ const AppBar = () => {
|
||||
|
||||
<Navbar>
|
||||
<NavbarItem path="/" title="Pet the Furina" />
|
||||
<NavbarItem path="/treasures" title="Treasures‧₊˚" />
|
||||
<NavbarItem path="/toodle" title="Toodle-oo~" />
|
||||
</Navbar>
|
||||
</div>
|
||||
@@ -33,7 +34,7 @@ const AppBar = () => {
|
||||
|
||||
const Navbar = ({ children }: ComponentProps<"div">) => {
|
||||
return (
|
||||
<nav className="flex-1 flex items-center justify-end gap-3 md:gap-5 overflow-x-auto md:overflow-x-hidden">
|
||||
<nav className="md:flex-1 self-stretch md:self-center flex items-center px-2 md:px-0 md:justify-end md:gap-5 overflow-x-auto md:overflow-x-hidden">
|
||||
{children}
|
||||
</nav>
|
||||
);
|
||||
@@ -50,7 +51,10 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
const isActive = isExact ? pathname === path : pathname.startsWith(path);
|
||||
|
||||
return (
|
||||
<Link to={path} className="group flex items-center md:py-4">
|
||||
<Link
|
||||
to={path}
|
||||
className="group flex flex-shrink-0 items-center px-2 md:px-0 md:py-4 first:ml-auto last:mr-auto"
|
||||
>
|
||||
<img
|
||||
src={ahogeImg}
|
||||
alt="ahoge"
|
||||
@@ -62,8 +66,8 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
|
||||
<p
|
||||
className={cn(
|
||||
"text-white ml-2 md:ml-4 md:text-xl border-b-2 border-dotted group-hover:border-white/80 border-transparent transition-all",
|
||||
isActive ? "border-white" : ""
|
||||
"text-white ml-2 md:ml-4 md:text-xl py-2 md:py-0 border-b-2 md:border-dotted group-hover:border-primary-500 border-transparent transition-all",
|
||||
isActive ? "border-primary-500 md:border-white" : ""
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
|
||||
@@ -17,11 +17,8 @@ const MainLayout = ({ children }: ComponentProps<"div">) => {
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
<footer className="bg-primary-700 px-4 py-8 text-center">
|
||||
<footer className="bg-primary-700 shadow relative z-10 px-4 py-8 text-center">
|
||||
<p className="text-sm text-white">
|
||||
<a href="http://rul.sh/">
|
||||
Made with <span className="text-red-300">♡</span> by Khairul.
|
||||
</a>{" "}
|
||||
Artworks displayed belong to respective owners.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/utility/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded border border-primary-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-primary-950 focus:ring-offset-2 dark:border-primary-800 dark:focus:ring-primary-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary-600 text-primary-50 hover:bg-primary-600/80 dark:bg-primary-50 dark:text-primary-900 dark:hover:bg-primary-50/80",
|
||||
secondary:
|
||||
"border-transparent bg-primary-100 text-primary-900 hover:bg-primary-100/80 dark:bg-primary-800 dark:text-primary-50 dark:hover:bg-primary-800/80",
|
||||
destructive:
|
||||
"border-transparent bg-red-500 text-primary-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-primary-50 dark:hover:bg-red-900/80",
|
||||
outline: "text-primary-950 dark:text-primary-50",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export default Badge;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { cn } from "@/utility/utils";
|
||||
import React, { useState } from "react";
|
||||
|
||||
type Props = React.ComponentProps<"img"> & {
|
||||
lazySrc: string;
|
||||
containerClassName?: string;
|
||||
};
|
||||
|
||||
const LazyImage = ({
|
||||
lazySrc,
|
||||
src,
|
||||
containerClassName,
|
||||
className,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [isLoaded, setLoaded] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-no-repeat bg-cover",
|
||||
!isLoaded ? "blur-md" : "",
|
||||
containerClassName
|
||||
)}
|
||||
style={{ backgroundImage: `url('${lazySrc}')` }}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
loading="lazy"
|
||||
onLoad={() => setLoaded(true)}
|
||||
className={cn(
|
||||
"transition-all",
|
||||
isLoaded ? "opacity-100" : "opacity-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LazyImage;
|
||||
@@ -0,0 +1,156 @@
|
||||
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 "@/utility/utils";
|
||||
|
||||
const SheetRoot = SheetPrimitive.Root;
|
||||
|
||||
const SheetPortal = SheetPrimitive.Portal;
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-white 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 dark:bg-slate-950",
|
||||
{
|
||||
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<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
));
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||
|
||||
const SheetHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col p-4 space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetHeader.displayName = "SheetHeader";
|
||||
|
||||
const SheetFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetFooter.displayName = "SheetFooter";
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold text-slate-950 dark:text-slate-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
type SheetProps = React.ComponentProps<typeof SheetRoot> & {
|
||||
isOpen?: boolean;
|
||||
title?: string;
|
||||
description?: string;
|
||||
position?: SheetContentProps["side"];
|
||||
};
|
||||
|
||||
const Sheet = ({
|
||||
isOpen,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
position,
|
||||
...props
|
||||
}: SheetProps) => {
|
||||
return (
|
||||
<SheetRoot open={isOpen} {...props}>
|
||||
<SheetContent side={position} className="rounded-t-2xl">
|
||||
<SheetHeader>
|
||||
{title ? <SheetTitle>{title}</SheetTitle> : null}
|
||||
{description ? (
|
||||
<SheetDescription>{description}</SheetDescription>
|
||||
) : null}
|
||||
</SheetHeader>
|
||||
<div className="max-h-[90vh] overflow-y-auto">{children}</div>
|
||||
</SheetContent>
|
||||
</SheetRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sheet;
|
||||
Reference in New Issue
Block a user