theme
This commit is contained in:
BIN
front/public/kapitan_papryk_dark.png
Normal file
BIN
front/public/kapitan_papryk_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
front/public/kapitan_papryk_light.png
Normal file
BIN
front/public/kapitan_papryk_light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
26
front/src/components/ThemeToggle.tsx
Normal file
26
front/src/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Moon, Sun } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useTheme } from "./theme-provider"
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() =>
|
||||
setTheme(theme === "dark" ? "light" : "dark")
|
||||
}
|
||||
>
|
||||
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
|
||||
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
|
||||
<span className="sr-only">
|
||||
Toggle theme
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
125
front/src/components/ui/bubble.tsx
Normal file
125
front/src/components/ui/bubble.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { Slot } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function BubbleGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="bubble-group"
|
||||
className={cn("flex min-w-0 flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const bubbleVariants = cva(
|
||||
"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",
|
||||
secondary:
|
||||
"*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]",
|
||||
muted:
|
||||
"*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]",
|
||||
tinted:
|
||||
"*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]",
|
||||
outline:
|
||||
"*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",
|
||||
ghost:
|
||||
"border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50",
|
||||
destructive:
|
||||
"*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Bubble({
|
||||
variant = "default",
|
||||
align = "start",
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> &
|
||||
VariantProps<typeof bubbleVariants> & {
|
||||
align?: "start" | "end"
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="bubble"
|
||||
data-variant={variant}
|
||||
data-align={align}
|
||||
className={cn(bubbleVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BubbleContent({
|
||||
asChild = false,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot.Root : "div"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="bubble-content"
|
||||
className={cn(
|
||||
"w-fit max-w-full min-w-0 overflow-hidden rounded-lg border border-transparent px-2.5 py-1.5 text-xs/relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-2 [button,a]:focus-visible:ring-ring/30",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const bubbleReactionsVariants = cva(
|
||||
"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-xs ring-2 ring-card has-[button]:p-0",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "top-0 -translate-y-3/4",
|
||||
bottom: "bottom-0 translate-y-3/4",
|
||||
},
|
||||
align: {
|
||||
start: "left-3",
|
||||
end: "right-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "bottom",
|
||||
align: "end",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function BubbleReactions({
|
||||
side = "bottom",
|
||||
align = "end",
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
align?: "start" | "end"
|
||||
side?: "top" | "bottom"
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="bubble-reactions"
|
||||
data-align={align}
|
||||
data-side={side}
|
||||
className={cn(bubbleReactionsVariants({ side, align }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { BubbleGroup, Bubble, BubbleContent, BubbleReactions }
|
||||
@@ -3,7 +3,10 @@ import { Outlet } from "react-router";
|
||||
|
||||
export default function AuthLayout() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-muted/40 p-4">
|
||||
<div
|
||||
className="min-h-screen flex items-center justify-center bg-muted/40 p-4"
|
||||
style={{ backgroundImage: "url(/paprika_bg.webp)" }}
|
||||
>
|
||||
<Card className="w-full max-w-md shadow-xl rounded-2xl">
|
||||
<CardHeader className="text-center space-y-2">
|
||||
<CardTitle className="text-2xl font-semibold">PAPRYK.COM</CardTitle>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useTheme } from "@/components/theme-provider";
|
||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useLogout } from "@/hooks/useLogout";
|
||||
@@ -22,13 +24,19 @@ export function MainLayout() {
|
||||
const { data: user } = useMe();
|
||||
const isAuthenticated = !!user;
|
||||
const logout = useLogout();
|
||||
const { theme } = useTheme();
|
||||
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<div
|
||||
className="min-h-screen flex flex-col"
|
||||
style={{ backgroundImage: theme === "light" ? "url(/kapitan_papryk_light.png)" : "url(/kapitan_papryk_dark.png)" }}
|
||||
>
|
||||
{/* Header */}
|
||||
<header className="border-b bg-background">
|
||||
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
|
||||
<header className="border-b bg-background/40 backdrop-blur">
|
||||
<div
|
||||
className="container mx-auto flex h-16 items-center gap-4 px-4"
|
||||
>
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center gap-2 shrink-0">
|
||||
<img
|
||||
@@ -74,6 +82,7 @@ export function MainLayout() {
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -83,7 +92,7 @@ export function MainLayout() {
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t">
|
||||
<footer className="border-t bg-background/40">
|
||||
<div className="container mx-auto px-4 py-4 text-center text-sm text-muted-foreground">
|
||||
{ /* © {new Date().getFullYear()} papryk */}
|
||||
papryk
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Bubble, BubbleContent, BubbleGroup, BubbleReactions } from "@/components/ui/bubble";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export function Home() {
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 py-16">
|
||||
<YTVideo url="https://youtube.com/embed/ioiu6L3SCq8" title="EEEE" description="EEEEE... Papryka" />
|
||||
<YTVideo url="https://youtube.com/embed/o2tUOQrkjhg" title="RHCP - WAP" description="..." />
|
||||
{ /* <YTVideo url="https://youtube.com/embed/o2tUOQrkjhg" title="RHCP - WAP" description="..." /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -39,6 +40,21 @@ function YTVideo({ url, title, description }: {
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
|
||||
<BubbleGroup>
|
||||
<Bubble variant="muted">
|
||||
<BubbleContent>
|
||||
Wiecie co robią lekarze w kuchni? Leczo XD
|
||||
</BubbleContent>
|
||||
<BubbleReactions role="img" aria-label="Reaction: fire">
|
||||
<span>🔥</span>
|
||||
</BubbleReactions>
|
||||
</Bubble>
|
||||
</BubbleGroup>
|
||||
<Bubble align="end">
|
||||
<BubbleContent>XD</BubbleContent>
|
||||
</Bubble>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user