Updating shadcn files, moving routes around.

This commit is contained in:
Bradley Shellnut 2023-09-08 16:30:32 -07:00
parent ea9a0f60a8
commit 13162f8270
84 changed files with 1553 additions and 770 deletions

View file

@ -26,7 +26,7 @@
"@playwright/test": "^1.37.0",
"@sveltejs/adapter-auto": "^1.0.3",
"@sveltejs/adapter-vercel": "^1.0.6",
"@sveltejs/kit": "^1.22.6",
"@sveltejs/kit": "^1.24.1",
"@types/cookie": "^0.5.1",
"@types/node": "^18.17.5",
"@typescript-eslint/eslint-plugin": "^5.62.0",
@ -37,27 +37,27 @@
"eslint-plugin-svelte": "^2.32.4",
"just-clone": "^6.2.0",
"just-debounce-it": "^3.2.0",
"postcss": "^8.4.27",
"postcss": "^8.4.29",
"postcss-import": "^15.1.0",
"postcss-load-config": "^4.0.1",
"postcss-preset-env": "^8.5.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1",
"prisma": "^5.1.1",
"prisma": "^5.2.0",
"sass": "^1.65.1",
"svelte": "^4.2.0",
"svelte-check": "^3.5.0",
"svelte-preprocess": "^5.0.4",
"svelte-sequential-preprocessor": "^2.0.1",
"sveltekit-flash-message": "^2.2.0",
"sveltekit-superforms": "^1.6.0",
"sveltekit-superforms": "^1.6.1",
"tailwindcss": "^3.3.3",
"ts-node": "^10.9.1",
"tslib": "^2.6.1",
"typescript": "^5.1.6",
"vite": "^4.4.9",
"vitest": "^0.25.3",
"zod": "^3.21.4"
"zod": "^3.22.2"
},
"type": "module",
"engines": {
@ -67,12 +67,12 @@
"dependencies": {
"@axiomhq/axiom-node": "^0.12.0",
"@fontsource/fira-mono": "^4.5.10",
"@iconify-icons/line-md": "^1.2.23",
"@iconify-icons/line-md": "^1.2.26",
"@iconify-icons/mdi": "^1.2.47",
"@lucia-auth/adapter-mysql": "^2.0.0",
"@lucia-auth/adapter-prisma": "^3.0.1",
"@lukeed/uuid": "^2.0.1",
"@melt-ui/svelte": "^0.37.5",
"@melt-ui/svelte": "^0.37.6",
"@prisma/client": "5.1.1",
"@types/feather-icons": "^4.29.1",
"bits-ui": "^0.0.27",
@ -80,12 +80,13 @@
"clsx": "^1.2.1",
"cookie": "^0.5.0",
"feather-icons": "^4.29.1",
"formsnap": "^0.0.9",
"iconify-icon": "^1.0.8",
"just-kebab-case": "^4.2.0",
"loader": "^2.1.1",
"lucia": "^2.4.0",
"lucia": "^2.4.2",
"lucide-svelte": "^0.256.1",
"open-props": "^1.5.11",
"open-props": "^1.5.13",
"radix-svelte": "^0.9.0",
"svelte-french-toast": "^1.2.0",
"svelte-lazy": "^1.2.1",

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
const tailwindcss = require("tailwindcss");
const tailwindNesting = require('tailwindcss/nesting');
const autoprefixer = require('autoprefixer');
const postcssPresetEnv = require('postcss-preset-env');
const atImport = require('postcss-import');
@ -6,7 +7,7 @@ const atImport = require('postcss-import');
const config = {
plugins: [
atImport(),
// 'tailwindcss/nesting'(),
tailwindNesting(),
tailwindcss(),
postcssPresetEnv({
stage: 2,
@ -16,8 +17,6 @@ const config = {
'media-query-ranges': true
}
}),
] //Some plugins, like tailwindcss/nesting, need to run before Tailwind, tailwindcss(), //But others, like autoprefixer, need to run after, autoprefixer]
};

View file

@ -5,46 +5,46 @@
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 224 71.4% 4.1%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 224 71.4% 4.1%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%;
--primary: 262.1 83.3% 57.8%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--muted: 220 14.3% 95.9%;
--muted-foreground: 220 8.9% 46.1%;
--accent: 220 14.3% 95.9%;
--accent-foreground: 220.9 39.3% 11%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 20% 98%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--ring: 262.1 83.3% 57.8%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71.4% 4.1%;
--foreground: 210 20% 98%;
--card: 224 71.4% 4.1%;
--card-foreground: 210 20% 98%;
--popover: 224 71.4% 4.1%;
--popover-foreground: 210 20% 98%;
--primary: 263.4 70% 50.4%;
--primary-foreground: 210 20% 98%;
--secondary: 215 27.9% 16.9%;
--secondary-foreground: 210 20% 98%;
--muted: 215 27.9% 16.9%;
--muted-foreground: 217.9 10.6% 64.9%;
--accent: 215 27.9% 16.9%;
--accent-foreground: 210 20% 98%;
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%;
--border: 215 27.9% 16.9%;
--input: 215 27.9% 16.9%;
--ring: 263.4 70% 50.4%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 35.5 91.7% 32.9%;
}
}

View file

@ -42,8 +42,8 @@ export const authentication: Handle = async function ({ event, resolve }) {
console.log('user', session?.user);
event.locals.user = session?.user;
// if (event.route.id?.startsWith('/(protected)')) {
// if (!user) throw redirect(302, '/auth/sign-in');
// if (!user.verified) throw redirect(302, '/auth/verify/email');
// if (!user) throw redirect(302, '/sign-in');
// if (!user.verified) throw redirect(302, '/verify/email');
// }
} catch (error) {
console.error('Error validating user', error);

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -3,7 +3,7 @@
import { fly } from "svelte/transition";
import { createSelect, melt } from "@melt-ui/svelte";
import { Check, ChevronDown, MinusCircle, PlusCircle } from "lucide-svelte";
import Button from "./ui/button/Button.svelte";
import { Button } from '$components/ui/button';
import type { Collection, Wishlist } from "@prisma/client";
export let game_id: string;

View file

@ -1,5 +1,5 @@
<script lang="ts">
import Button from "./ui/button/Button.svelte";
import { Button } from '$components/ui/button';
</script>
<Button type="submit">Add to wishlist</Button>

View file

@ -1,27 +1,21 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { LogOut } from 'lucide-svelte';
import { Button } from '$components/ui/button';
import { Separator } from "$components/ui/separator";
import logo from './bored-game.png';
import { Avatar, AvatarFallback } from '$components/ui/avatar';
import {
Sheet,
SheetClose,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger
} from "$components/ui/sheet";
import { Button } from '$lib/components/ui/button';
import { Toggle } from "$lib/components/ui/toggle";
import * as Avatar from "$lib/components/ui/avatar";
import * as Sheet from "$lib/components/ui/sheet";
import Logo from '$components/logo.svelte';
export let user: any;
let avatar = user?.username.slice(0, 1).toUpperCase() || '?';
</script>
<header>
<div class="corner">
<a href="/" title="Home">
<img src={logo} alt="Bored Game Home" />
<Logo />
</a>
</div>
<!-- <TextSearch /> -->
@ -29,39 +23,42 @@
{#if user}
<a href="/collection" title="Go to your collection" data-sveltekit-preload-data>Collection</a>
<a href="/wishlist" title="Go to your wishlist" data-sveltekit-preload-data>Wishlist</a>
<Sheet>
<SheetTrigger>
<Avatar class="h-16 w-16 bg-neutral-100">
<AvatarFallback class="text-3xl font-medium text-magnum-700">
{user?.username.slice(0, 1).toUpperCase() || '?'}
</AvatarFallback>
</Avatar>
</SheetTrigger>
<SheetContent position="right" size="lg">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<Sheet.Root>
<Sheet.Trigger>
<Avatar.Root asChild>
<Avatar.Fallback class="text-3xl font-medium text-magnum-700 h-16 w-16 bg-neutral-100">
{avatar}
</Avatar.Fallback>
</Avatar.Root>
</Sheet.Trigger>
<Sheet.Content side="right">
<Sheet.Header>
<Sheet.Title>Menu</Sheet.Title>
<Toggle aria-label="toggle bold">
</Toggle>
</Sheet.Header>
<div class="menu">
<div class="item">
<SheetClose>
<Button variant="link" href="/profile">View Profile</Button>
</SheetClose>
</div>
<div class="item">
<SheetClose>
<Button variant="link" href="/collection">Your Collection</Button>
</SheetClose>
</div>
<div class="item">
<SheetClose>
<Button variant="link" href="/wishlist">Your Wishlist</Button>
</SheetClose>
</div>
<Sheet.Close asChild let:builder>
<div class="item">
<Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/profile">View Profile</Button>
</div>
</Sheet.Close>
<Sheet.Close asChild let:builder>
<div class="item">
<Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/collection">Your Collection</Button>
</div>
</Sheet.Close>
<Sheet.Close asChild let:builder>
<div class="item">
<Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/wishlist">Your Wishlist</Button>
</div>
</Sheet.Close>
<div class="separator" />
<div class="item">
<form
use:enhance
action="/auth/signout"
action="/logout"
method="POST"
>
<Button type="submit">
@ -71,33 +68,22 @@
</form>
</div>
</div>
<SheetFooter>
<SheetClose>
<Button type="button">Close</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
<!-- <form
use:enhance
action="/auth/signout"
method="POST"
>
<Button type="submit">
<LogOut class="mr-2 h-4 w-4"/>
Sign out
</Button>
</form> -->
<Sheet.Footer>
<Sheet.Close asChild let:builder>
<Button builders={[builder]} type="button">Close</Button>
</Sheet.Close>
</Sheet.Footer>
</Sheet.Content>
</Sheet.Root>
{/if}
{#if !user}
<a href="/auth/signin">
<span class="flex-auto">Sign In</span></a
<a href="/login">
<span class="flex-auto">Login</span></a
>
<a href="/auth/signup">
<a href="/sign-up">
<span class="flex-auto">Sign Up</span></a
>
{/if}
<!-- <Profile /> -->
</nav>
</header>
@ -119,12 +105,13 @@
}
.item {
margin-bottom: 0.2rem;
/* margin: 0.2rem 0; */
}
.corner {
width: 3em;
height: 3em;
margin-left: 1rem;
}
.corner a {

View file

@ -0,0 +1,5 @@
<script lang="ts">
import logo from '$lib/assets/bored-game.png';
</script>
<img src={logo} alt="Bored Game Home" />

View file

@ -6,7 +6,7 @@
import { ToastType } from '$lib/types';
import { superForm } from 'sveltekit-superforms/client';
import { toast } from '../../toast/toast';
import Button from '$components/ui/button/Button.svelte';
import { Button } from '$components/ui/button';
export let data: SuperValidated<SearchSchema>;
const { enhance } = superForm(data, {

View file

@ -19,7 +19,7 @@
];
</script>
<form method="POST" action="/auth/sign-in" use:enhance>
<form method="POST" action="/sign-in" use:enhance>
<!--<SuperDebug data={$form} />-->
{#if $errors._errors}
<aside class="alert variant-filled-error mt-6">
@ -77,6 +77,6 @@
>
</div>
<div class="flex flex-row justify-center items-center mt-10">
<a href="/auth/password/reset" class="font-semibold">{i("forgotPassword")}</a>
<a href="/password/reset" class="font-semibold">{i("forgotPassword")}</a>
</div>
</form>

View file

@ -19,7 +19,7 @@
// $: termsValue = $form.terms as Writable<boolean>;
</script>
<form method="POST" action="/auth/signup" use:enhance>
<form method="POST" action="/sign-up" use:enhance>
<h1>Signup user</h1>
<label class="label">
<span class="sr-only">First Name</span>
@ -105,7 +105,7 @@
<small>{$errors.password}</small>
{/if}
</label>
<button type="submit">Signup</button>
<a class="back" href="/"> or Cancel </a>

View file

@ -1,30 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { cn } from "$lib/utils";
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive"
}
},
defaultVariants: {
variant: "default"
}
}
);
let className: string | undefined | null = undefined;
export { className as class };
export let variant: VariantProps<typeof alertVariants>["variant"] =
"default";
</script>
<div class={cn(alertVariants({ variant }), className)} {...$$restProps}>
<slot />
</div>

View file

@ -1,15 +0,0 @@
<script lang="ts">
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
export { className as class };
export let level: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" = "h5";
</script>
<svelte:element
this={level}
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View file

@ -1,7 +1,10 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined;
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
import type { HeadingLevel } from ".";
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
level?: HeadingLevel;
};
let className: $$Props["class"] = undefined;
export let level: $$Props["level"] = "h5";
export { className as class };
</script>
<svelte:element
this={level}
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
import { alertVariants, type Variant } from ".";
type $$Props = HTMLAttributes<HTMLDivElement> & {
variant?: Variant;
};
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export { className as class };
</script>
<div
class={cn(alertVariants({ variant }), className)}
{...$$restProps}
role="alert"
>
<slot />
</div>

View file

@ -1,3 +1,33 @@
export { default as Alert } from "./Alert.svelte";
export { default as AlertDescription } from "./AlertDescription.svelte";
export { default as AlertTitle } from "./AlertTitle.svelte";
import { tv, type VariantProps } from "tailwind-variants";
import Root from "./alert.svelte";
import Description from "./alert-description.svelte";
import Title from "./alert-title.svelte";
export const alertVariants = tv({
base: "relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11",
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive"
}
},
defaultVariants: {
variant: "default"
}
});
export type Variant = VariantProps<typeof alertVariants>["variant"];
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
export {
Root,
Description,
Title,
//
Root as Alert,
Description as AlertDescription,
Title as AlertTitle
};

View file

@ -1,51 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import type {
HTMLAnchorAttributes,
HTMLButtonAttributes
} from "svelte/elements";
import { cn } from "$lib/utils";
import { buttonVariants } from ".";
let className: string | undefined | null = undefined;
export { className as class };
export let href: HTMLAnchorAttributes["href"] = undefined;
export let type: HTMLButtonAttributes["type"] = undefined;
export let variant: VariantProps<typeof buttonVariants>["variant"] =
"default";
export let size: VariantProps<typeof buttonVariants>["size"] = "default";
type Props = {
class?: string | null;
variant?: VariantProps<typeof buttonVariants>["variant"];
size?: VariantProps<typeof buttonVariants>["size"];
};
interface AnchorElement extends Props, HTMLAnchorAttributes {
href?: HTMLAnchorAttributes["href"];
type?: never;
}
interface ButtonElement extends Props, HTMLButtonAttributes {
type?: HTMLButtonAttributes["type"];
href?: never;
}
type $$Props = AnchorElement | ButtonElement;
</script>
<svelte:element
this={href ? "a" : "button"}
type={href ? undefined : type}
{href}
class={cn(buttonVariants({ variant, size, className }))}
{...$$restProps}
on:click
on:change
on:keydown
on:keyup
on:mouseenter
on:mouseleave
>
<slot />
</svelte:element>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { Button as ButtonPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { buttonVariants, type Size, type Variant } from ".";
type $$Props = ButtonPrimitive.Props & {
variant?: Variant;
size?: Size;
};
type $$Events = ButtonPrimitive.Events;
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export let size: $$Props["size"] = "default";
export let builders: $$Props["builders"] = [];
export { className as class };
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
{...$$restProps}
on:click
on:keydown
>
<slot />
</ButtonPrimitive.Root>

View file

@ -1,32 +1,35 @@
import { cva } from "class-variance-authority";
import Root from './button.svelte';
import { tv, type VariantProps } from 'tailwind-variants';
export { default as Button } from "./Button.svelte";
export const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "underline-offset-4 hover:underline text-primary"
},
size: {
default: "h-10 py-2 px-4",
sm: "h-9 px-3 rounded-md",
lg: "h-11 px-8 rounded-md"
}
export const buttonVariants = tv({
base: 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline'
},
defaultVariants: {
variant: "default",
size: "default"
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
);
});
export type Variant = VariantProps<typeof buttonVariants>['variant'];
export type Size = VariantProps<typeof buttonVariants>['size'];
export {
Root,
//
Root as Button
};

View file

@ -0,0 +1,34 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from "bits-ui";
import { Check, Minus } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = CheckboxPrimitive.Props;
type $$Events = CheckboxPrimitive.Events;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = false;
export { className as class };
</script>
<CheckboxPrimitive.Root
bind:checked
class={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...$$restProps}
on:click
>
<CheckboxPrimitive.Indicator
class={cn("flex items-center justify-center text-current h-4 w-4")}
let:isChecked
let:isIndeterminate
>
{#if isChecked}
<Check class="h-4 w-4" />
{:else if isIndeterminate}
<Minus class="h-4 w-4" />
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

View file

@ -0,0 +1,6 @@
import Root from "./checkbox.svelte";
export {
Root,
//
Root as Checkbox
};

View file

@ -0,0 +1,15 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { slide } from "svelte/transition";
type $$Props = CollapsiblePrimitive.ContentProps;
export let transition: $$Props["transition"] = slide;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150
};
</script>
<CollapsiblePrimitive.Content {transition} {transitionConfig} {...$$restProps}>
<slot />
</CollapsiblePrimitive.Content>

View file

@ -1,7 +1,15 @@
import { Collapsible as CollapsiblePrimitive } from "radix-svelte";
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import Content from "./collapsible-content.svelte";
export const Collapsible = CollapsiblePrimitive.Root;
const Root = CollapsiblePrimitive.Root;
const Trigger = CollapsiblePrimitive.Trigger;
export const CollapsibleTrigger = CollapsiblePrimitive.Trigger;
export const CollapsibleContent = CollapsiblePrimitive.Content;
export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};

View file

@ -0,0 +1,9 @@
<script lang="ts">
import * as Button from "$lib/components/ui/button";
type $$Props = Button.Props;
type $$Events = Button.Events;
</script>
<Button.Root type="submit" {...$$restProps} on:click on:keydown>
<slot />
</Button.Root>

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { Checkbox as CheckboxPrimitive } from "bits-ui";
import { Checkbox } from "$lib/components/ui/checkbox";
type $$Props = CheckboxPrimitive.Props;
type $$Events = CheckboxPrimitive.Events;
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
const { name, setValue, attrStore, value } = getFormField();
const { name: nameAttr, value: valueAttr, ...rest } = $attrStore;
</script>
<Checkbox
{...rest}
checked={typeof $value === "boolean" ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v);
setValue(v);
}}
{...$$restProps}
on:click
on:keydown
/>
<input hidden {name} value={$value} />

View file

@ -0,0 +1,16 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "@/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLSpanElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Description
class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
>
<slot />
</FormPrimitive.Description>

View file

@ -0,0 +1,28 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { HTMLInputAttributes } from "svelte/elements";
import { Input, type InputEvents } from "$lib/components/ui/input";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
const { attrStore, value } = getFormField();
</script>
<Input
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
/>

View file

@ -0,0 +1,12 @@
<script lang="ts">
import { cn } from "@/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<div class={cn("space-y-2", className)} {...$$restProps}>
<slot />
</div>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import type { Label as LabelPrimitive } from "bits-ui";
import { getFormField } from "formsnap";
import { cn } from "@/utils";
import { Label } from "$lib/components/ui/label";
type $$Props = LabelPrimitive.Props;
let className: $$Props["class"] = undefined;
export { className as class };
const { errors, ids } = getFormField();
</script>
<Label
for={ids.input}
class={cn($errors && "text-destructive", className)}
{...$$restProps}
>
<slot />
</Label>

View file

@ -0,0 +1,24 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { buttonVariants } from "$lib/components/ui/button";
import { cn } from "@/utils";
import { ChevronDown } from "lucide-svelte";
import type { HTMLSelectAttributes } from "svelte/elements";
type $$Props = HTMLSelectAttributes;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Select
class={cn(
buttonVariants({ variant: "outline" }),
"appearance-none bg-transparent font-normal",
className
)}
{...$$restProps}
>
<slot />
</FormPrimitive.Select>
<ChevronDown class="absolute right-3 top-2.5 h-4 w-4 opacity-50" />

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { RadioGroup as RadioGroupPrimitive } from "bits-ui";
import * as RadioGroup from "$lib/components/ui/radio-group";
type $$Props = RadioGroupPrimitive.Props;
const { attrStore, setValue, name, value } = getFormField();
export let onValueChange: $$Props["onValueChange"] = undefined;
</script>
<RadioGroup.Root
{...$attrStore}
onValueChange={(v) => {
onValueChange?.(v);
setValue(v);
}}
{...$$restProps}
>
<slot />
<input hidden {name} value={$value} />
</RadioGroup.Root>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import * as Select from "$lib/components/ui/select";
import type { Select as SelectPrimitive } from "bits-ui";
import { getFormField } from "formsnap";
type $$Props = SelectPrimitive.TriggerProps & {
placeholder?: string;
};
type $$Events = SelectPrimitive.TriggerEvents;
const { attrStore } = getFormField();
export let placeholder = "";
</script>
<Select.Trigger {...$$restProps} {...$attrStore} on:click on:keydown>
<Select.Value {placeholder} />
<slot />
</Select.Trigger>

View file

@ -0,0 +1,20 @@
<script lang="ts">
import * as Select from "$lib/components/ui/select";
import { getFormField } from "formsnap";
import type { Select as SelectPrimitive } from "bits-ui";
type $$Props = SelectPrimitive.Props;
const { setValue, name, value } = getFormField();
export let onSelectedChange: $$Props["onSelectedChange"];
</script>
<Select.Root
onSelectedChange={(v) => {
onSelectedChange?.(v);
setValue(v ? v.value : undefined);
}}
{...$$restProps}
>
<slot />
<input hidden {name} value={$value} />
</Select.Root>

View file

@ -0,0 +1,24 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { Switch as SwitchPrimitive } from "bits-ui";
import { Switch } from "$lib/components/ui/switch";
type $$Props = SwitchPrimitive.Props;
type $$Events = SwitchPrimitive.Events;
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
const { name, setValue, attrStore, value } = getFormField();
</script>
<Switch
{...$attrStore}
checked={typeof $value === "boolean" ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v);
setValue(v);
}}
{...$$restProps}
on:click
on:keydown
/>
<input hidden {name} value={$value} />

View file

@ -0,0 +1,32 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { HTMLTextareaAttributes } from "svelte/elements";
import type { TextareaGetFormField } from ".";
import {
Textarea,
type TextareaEvents
} from "$lib/components/ui/textarea";
type $$Props = HTMLTextareaAttributes;
type $$Events = TextareaEvents;
const { attrStore, value } = getFormField() as TextareaGetFormField;
</script>
<Textarea
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
/>

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "@/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLParagraphElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Validation
class={cn("text-sm font-medium text-destructive", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,82 @@
import { Form as FormPrimitive, getFormField } from "formsnap";
import * as RadioGroupComp from "$lib/components/ui/radio-group";
import * as SelectComp from "$lib/components/ui/select";
import type { Writable } from "svelte/store";
import Item from "./form-item.svelte";
import Input from "./form-input.svelte";
import Textarea from "./form-textarea.svelte";
import Description from "./form-description.svelte";
import Label from "./form-label.svelte";
import Validation from "./form-validation.svelte";
import Checkbox from "./form-checkbox.svelte";
import Switch from "./form-switch.svelte";
import NativeSelect from "./form-native-select.svelte";
import RadioGroup from "./form-radio-group.svelte";
import Select from "./form-select.svelte";
import SelectTrigger from "./form-select-trigger.svelte";
import Button from "./form-button.svelte";
const Root = FormPrimitive.Root;
const Field = FormPrimitive.Field;
const RadioItem = RadioGroupComp.Item;
const NativeRadio = FormPrimitive.Radio;
const SelectContent = SelectComp.Content;
const SelectLabel = SelectComp.Label;
const SelectGroup = SelectComp.Group;
const SelectItem = SelectComp.Item;
const SelectSeparator = SelectComp.Separator;
export type TextareaGetFormField = Omit<
ReturnType<typeof getFormField>,
"value"
> & {
value: Writable<string>;
};
export {
Root,
Field,
Item,
Input,
Label,
Button,
Switch,
Select,
Checkbox,
Textarea,
Validation,
RadioGroup,
RadioItem,
Description,
SelectContent,
SelectLabel,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
NativeSelect,
NativeRadio,
//
Root as Form,
Field as FormField,
Item as FormItem,
Input as FormInput,
Textarea as FormTextarea,
Description as FormDescription,
Label as FormLabel,
Validation as FormValidation,
NativeSelect as FormNativeSelect,
NativeRadio as FormNativeRadio,
Checkbox as FormCheckbox,
Switch as FormSwitch,
RadioGroup as FormRadioGroup,
RadioItem as FormRadioItem,
Select as FormSelect,
SelectContent as FormSelectContent,
SelectLabel as FormSelectLabel,
SelectGroup as FormSelectGroup,
SelectItem as FormSelectItem,
SelectSeparator as FormSelectSeparator,
SelectTrigger as FormSelectTrigger,
Button as FormButton
};

View file

@ -1 +1,25 @@
export { default as Input } from "./Input.svelte";
import Root from "./input.svelte";
type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement;
};
export type InputEvents = {
blur: FormInputEvent<FocusEvent>;
change: FormInputEvent<Event>;
click: FormInputEvent<MouseEvent>;
focus: FormInputEvent<FocusEvent>;
keydown: FormInputEvent<KeyboardEvent>;
keypress: FormInputEvent<KeyboardEvent>;
keyup: FormInputEvent<KeyboardEvent>;
mouseover: FormInputEvent<MouseEvent>;
mouseenter: FormInputEvent<MouseEvent>;
mouseleave: FormInputEvent<MouseEvent>;
paste: FormInputEvent<ClipboardEvent>;
input: FormInputEvent<InputEvent>;
};
export {
Root,
//
Root as Input
};

View file

@ -0,0 +1,33 @@
<script lang="ts">
import type { HTMLInputAttributes } from "svelte/elements";
import { cn } from "$lib/utils";
import type { InputEvents } from ".";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<input
class={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
/>

View file

@ -0,0 +1,33 @@
import { Select as SelectPrimitive } from "bits-ui";
import Root from "./select.svelte";
import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export {
Root,
Group,
Input,
Label,
Item,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Item as SelectItem,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator
};

View file

@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
import { scale } from "svelte/transition";
type $$Props = SelectPrimitive.ContentProps;
type $$Events = SelectPrimitive.ContentEvents;
export let inTransition: $$Props["inTransition"] = flyAndScale;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = scale;
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
start: 0.95,
opacity: 0,
duration: 50
};
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md outline-none",
className
)}
{...$$restProps}
on:keydown
>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>

View file

@ -0,0 +1,38 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { Check } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.ItemProps;
type $$Events = SelectPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export let label: $$Props["label"] = undefined;
export let disabled: $$Props["disabled"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot />
</SelectPrimitive.Item>

View file

@ -0,0 +1,16 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.LabelProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Label
class={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...$$restProps}
>
<slot />
</SelectPrimitive.Label>

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { ChevronDown } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.TriggerProps;
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Trigger
class={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...$$restProps}
let:builder
on:click
on:keydown
>
<slot {builder} />
<div>
<ChevronDown class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

View file

@ -0,0 +1,12 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
type $$Props = SelectPrimitive.Props;
export let selected: $$Props["selected"] = undefined;
export let open: $$Props["open"] = undefined;
</script>
<SelectPrimitive.Root bind:selected bind:open {...$$restProps}>
<slot />
</SelectPrimitive.Root>

View file

@ -1,15 +0,0 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
export { className as class };
</script>
<SheetPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
className
)}
{...$$restProps}
/>

View file

@ -1,29 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils";
const portalVariants = cva("fixed inset-0 z-50 flex", {
variants: {
position: {
top: "items-start",
bottom: "items-end",
left: "justify-start",
right: "justify-end"
}
},
defaultVariants: { position: "right" }
});
let className: string | undefined | null = undefined;
export { className as class };
export let position: VariantProps<typeof portalVariants>["position"] =
"right";
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<div class={portalVariants({ position })}>
<slot />
</div>
</SheetPrimitive.Portal>

View file

@ -1,102 +1,55 @@
import { cva } from "class-variance-authority";
import { Dialog as SheetPrimitive } from "radix-svelte";
import { Dialog as SheetPrimitive } from "bits-ui";
import { tv, type VariantProps } from "tailwind-variants";
export { default as SheetContent } from "./SheetContent.svelte";
export { default as SheetDescription } from "./SheetDescription.svelte";
export { default as SheetFooter } from "./SheetFooter.svelte";
export { default as SheetHeader } from "./SheetHeader.svelte";
export { default as SheetOverlay } from "./SheetOverlay.svelte";
export { default as SheetPortal } from "./SheetPortal.svelte";
export { default as SheetTitle } from "./SheetTitle.svelte";
import Portal from "./sheet-portal.svelte";
import Overlay from "./sheet-overlay.svelte";
import Content from "./sheet-content.svelte";
import Header from "./sheet-header.svelte";
import Footer from "./sheet-footer.svelte";
import Title from "./sheet-title.svelte";
import Description from "./sheet-description.svelte";
export const Sheet = SheetPrimitive.Root;
export const SheetTrigger = SheetPrimitive.Trigger;
export const SheetClose = SheetPrimitive.Close;
const Root = SheetPrimitive.Root;
const Close = SheetPrimitive.Close;
const Trigger = SheetPrimitive.Trigger;
export const sheetVariants = cva(
"fixed z-50 scale-100 gap-4 bg-background p-6 opacity-100 shadow-lg border",
{
variants: {
position: {
top: "animate-in slide-in-from-top w-full duration-300",
bottom: "animate-in slide-in-from-bottom w-full duration-300",
left: "animate-in slide-in-from-left h-full duration-300",
right: "animate-in slide-in-from-right h-full duration-300"
},
size: {
content: "",
default: "",
sm: "",
lg: "",
xl: "",
full: ""
}
},
compoundVariants: [
{
position: ["top", "bottom"],
size: "content",
class: "max-h-screen"
},
{
position: ["top", "bottom"],
size: "default",
class: "h-1/3"
},
{
position: ["top", "bottom"],
size: "sm",
class: "h-1/4"
},
{
position: ["top", "bottom"],
size: "lg",
class: "h-1/2"
},
{
position: ["top", "bottom"],
size: "xl",
class: "h-5/6"
},
{
position: ["top", "bottom"],
size: "full",
class: "h-screen"
},
{
position: ["right", "left"],
size: "content",
class: "max-w-screen"
},
{
position: ["right", "left"],
size: "default",
class: "w-1/3"
},
{
position: ["right", "left"],
size: "sm",
class: "w-1/4"
},
{
position: ["right", "left"],
size: "lg",
class: "w-1/2"
},
{
position: ["right", "left"],
size: "xl",
class: "w-5/6"
},
{
position: ["right", "left"],
size: "full",
class: "w-screen"
}
],
defaultVariants: {
position: "right",
size: "default"
export {
Root,
Close,
Trigger,
Portal,
Overlay,
Content,
Header,
Footer,
Title,
Description,
//
Root as Sheet,
Close as SheetClose,
Trigger as SheetTrigger,
Portal as SheetPortal,
Overlay as SheetOverlay,
Content as SheetContent,
Header as SheetHeader,
Footer as SheetFooter,
Title as SheetTitle,
Description as SheetDescription
};
export const sheetVariants = tv({
base: "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"
}
);
});
export type Side = VariantProps<typeof sheetVariants>["side"];

View file

@ -1,23 +1,22 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import { Dialog as SheetPrimitive } from "bits-ui";
import { SheetOverlay, SheetPortal, sheetVariants, type Side } from ".";
import { X } from "lucide-svelte";
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils";
import { sheetVariants } from ".";
import SheetOverlay from "./SheetOverlay.svelte";
import SheetPortal from "./SheetPortal.svelte";
let className: string | undefined | null = undefined;
type $$Props = SheetPrimitive.ContentProps & {
side?: Side;
};
let className: $$Props["class"] = undefined;
export let side: $$Props["side"] = "right";
export { className as class };
export let position: VariantProps<typeof sheetVariants>["position"] =
"right";
export let size: VariantProps<typeof sheetVariants>["size"] = "default";
</script>
<SheetPortal {position}>
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
class={cn(sheetVariants({ position, size }), className)}
class={cn(sheetVariants({ side }), className)}
{...$$restProps}
>
<slot />

View file

@ -1,8 +1,10 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte";
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
type $$Props = SheetPrimitive.DescriptionProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

View file

@ -1,7 +1,10 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined;
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

View file

@ -1,7 +1,10 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined;
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.OverlayProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...$$restProps}
/>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.PortalProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<slot />
</SheetPrimitive.Portal>

View file

@ -1,8 +1,10 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte";
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
type $$Props = SheetPrimitive.TitleProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>

View file

@ -0,0 +1,7 @@
import Root from "./switch.svelte";
export {
Root,
//
Root as Switch
};

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { Switch as SwitchPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SwitchPrimitive.Props;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = undefined;
export { className as class };
</script>
<SwitchPrimitive.Root
bind:checked
class={cn(
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...$$restProps}
>
<SwitchPrimitive.Thumb
class={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>

View file

@ -0,0 +1,28 @@
import Root from "./textarea.svelte";
type FormTextareaEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLTextAreaElement;
};
type TextareaEvents = {
blur: FormTextareaEvent<FocusEvent>;
change: FormTextareaEvent<Event>;
click: FormTextareaEvent<MouseEvent>;
focus: FormTextareaEvent<FocusEvent>;
keydown: FormTextareaEvent<KeyboardEvent>;
keypress: FormTextareaEvent<KeyboardEvent>;
keyup: FormTextareaEvent<KeyboardEvent>;
mouseover: FormTextareaEvent<MouseEvent>;
mouseenter: FormTextareaEvent<MouseEvent>;
mouseleave: FormTextareaEvent<MouseEvent>;
paste: FormTextareaEvent<ClipboardEvent>;
input: FormTextareaEvent<InputEvent>;
};
export {
Root,
//
Root as Textarea,
type TextareaEvents,
type FormTextareaEvent
};

View file

@ -0,0 +1,31 @@
<script lang="ts">
import type { HTMLTextareaAttributes } from "svelte/elements";
import { cn } from "$lib/utils";
type $$Props = HTMLTextareaAttributes;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<textarea
class={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
/>

View file

@ -0,0 +1,31 @@
import Root from "./toggle.svelte";
import { tv, type VariantProps } from "tailwind-variants";
export const toggleVariants = tv({
base: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background hover:bg-muted hover:text-muted-foreground",
variants: {
variant: {
default: "bg-transparent",
outline:
"bg-transparent border border-input hover:bg-accent hover:text-accent-foreground"
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
});
export type Variant = VariantProps<typeof toggleVariants>["variant"];
export type Size = VariantProps<typeof toggleVariants>["size"];
export {
Root,
//
Root as Toggle
};

View file

@ -0,0 +1,26 @@
<script lang="ts">
import { Toggle as TogglePrimitive } from "bits-ui";
import { toggleVariants, type Variant, type Size } from ".";
import { cn } from "$lib/utils";
type $$Props = TogglePrimitive.Props & {
variant?: Variant;
size?: Size;
};
type $$Events = TogglePrimitive.Events;
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export let size: $$Props["size"] = "default";
export let pressed: $$Props["pressed"] = undefined;
export { className as class };
</script>
<TogglePrimitive.Root
bind:pressed
class={cn(toggleVariants({ variant, size, className }))}
{...$$restProps}
on:m-click
>
<slot />
</TogglePrimitive.Root>

View file

@ -69,6 +69,11 @@ export const signUpSchema = userSchema
}
});
export const signInSchema = userSchema.pick({
username: true,
password: true
});
export const updateUserPasswordSchema = userSchema
.pick({ password: true, confirm_password: true })
.superRefine(({ confirm_password, password }, ctx) => {

View file

@ -11,7 +11,7 @@ const signInSchema = userSchema.pick({
});
export const load = async (event) => {
console.log('sign in load event', event);
console.log('login load event', event);
const session = await event.locals.auth.validate();
if (session) {
const message = { type: 'info', message: 'You are already signed in' };

View file

@ -4,11 +4,11 @@
import * as flashModule from 'sveltekit-flash-message/client';
import toast from 'svelte-french-toast';
import { AlertCircle } from "lucide-svelte";
import { userSchema } from '$lib/config/zod-schemas.js';
import Label from '$components/ui/label/Label.svelte';
import Input from '$components/ui/input/Input.svelte';
import Button from '$components/ui/button/Button.svelte';
import { Alert, AlertDescription, AlertTitle } from "$components/ui/alert";
import { signInSchema } from '$lib/config/zod-schemas.js';
import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input';
import { Button } from '$components/ui/button';
import * as Alert from "$components/ui/alert";
export let data;
const { form, errors, enhance, delayed } = superForm(data.form, {
@ -24,6 +24,7 @@
},
syncFlashMessage: false,
taintedMessage: null,
validators: signInSchema,
validationMethod: 'oninput',
delayMs: 0,
});
@ -39,34 +40,38 @@
}
</script>
<div class="signin">
<svelte:head>
<title>Bored Game | Login</title>
</svelte:head>
<div class="login">
<form method="POST" use:enhance>
<div class="grid w-full max-w-sm items-center gap-2">
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
Sign into your account
Log into your account
</h2>
<Label for="username">Username</Label>
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} required />
<Label for="password">Password</Label>
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="password" data-invalid={$errors.password} bind:value={$form.password} />
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="password" data-invalid={$errors.password} bind:value={$form.password} required />
<Button type="submit">Sign In</Button>
</div>
</form>
{#if $errors._errors}
<Alert variant="destructive">
<Alert.Root variant="destructive">
<AlertCircle class="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
<Alert.Title>Error</Alert.Title>
<Alert.Description>
{$errors._errors}
</AlertDescription>
</Alert>
</Alert.Description>
</Alert.Root>
{/if}
</div>
<style lang="postcss">
.signin {
.login {
display: grid;
gap: 2rem;
}

View file

@ -6,10 +6,10 @@ export const actions = {
console.log('Signing out user');
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
await auth.invalidateSession(session.sessionId); // invalidate session
locals.auth.setSession(null); // remove cookie
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
};

View file

@ -40,9 +40,8 @@ export const load = async (event) => {
if (session) {
throw redirect(302, '/');
}
const form = await superValidate<typeof signUpSchema, Message>(event, signUpSchema);
return {
form
form: await superValidate<typeof signUpSchema, Message>(event, signUpSchema)
};
};

View file

@ -4,21 +4,16 @@
import * as flashModule from 'sveltekit-flash-message/client';
import toast from 'svelte-french-toast';
import { ChevronsUpDown } from "lucide-svelte";
import Button from '$components/ui/button/Button.svelte';
import Input from '$components/ui/input/Input.svelte';
import { Button } from '$components/ui/button';
import { Input } from '$components/ui/input';
import Label from '$components/ui/label/Label.svelte';
import { signUpSchema } from '$lib/config/zod-schemas.js';
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger
} from "$components/ui/collapsible";
import Alert from '$components/ui/alert/Alert.svelte';
import AlertTitle from '$components/ui/alert/AlertTitle.svelte';
import AlertDescription from '$components/ui/alert/AlertDescription.svelte';
import * as Collapsible from '$lib/components/ui/collapsible';
import * as Alert from '$lib/components/ui/alert';
import { slide } from 'svelte/transition';
import { quintIn } from 'svelte/easing';
export let data;
let isOpen = false;
const { form, errors, constraints, enhance, delayed } = superForm(data.form, {
flashMessage: {
@ -44,72 +39,82 @@
}
</script>
<svelte:head>
<title>Bored Game | Sign Up</title>
</svelte:head>
<div class="page">
<form method="POST" action="/auth/signup" use:enhance>
<form method="POST" action="/sign-up" use:enhance>
<div class="grid w-full max-w-sm items-center gap-2.5">
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
Signup for an account
</h2>
<Label for="username">Username <small class="text-destructive">(Required)</small></Label>
<Label for="username">Username</Label>
<Input type="text" id="username" class={$errors.username && "outline outline-destructive"} name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
{#if $errors.username}
<p class="text-sm text-destructive">{$errors.username}</p>
{/if}
<Label for="password">Password <small class="text-destructive">(Required)</small></Label>
<Label for="password">Password</Label>
<Input type="password" id="password" class={$errors.password && "outline outline-destructive"} name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} />
{#if $errors.password}
<p class="text-sm text-destructive">{$errors.password}</p>
{/if}
<Label for="confirm_password">Confirm Password <small class="text-destructive">(Required)</small></Label>
<Label for="confirm_password">Confirm Password</Label>
<Input type="password" id="confirm_password" class={$errors.confirm_password && "outline outline-destructive"} name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" data-invalid={$errors.confirm_password} bind:value={$form.confirm_password} />
{#if $errors.confirm_password}
<p class="text-sm text-destructive">{$errors.confirm_password}</p>
{/if}
<Collapsible open={isOpen} class="grid w-full max-w-sm items-center gap-2.5">
<Collapsible.Root class="grid w-full max-w-sm items-center gap-2.5">
<div>
Optional Fields:
<CollapsibleTrigger>
<Button variant="ghost" size="sm" type="button" class="w-9 p-0">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" type="button" class="w-9 p-0">
<ChevronsUpDown class="h-4 w-4" />
<span class="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</Collapsible.Trigger>
</div>
<CollapsibleContent>
<Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="email">Email</Label>
<Input type="email" id="email" class={$errors.email && "outline outline-destructive"} name="email" placeholder="Email" autocomplete="email" data-invalid={$errors.email} bind:value={$form.email} />
{#if $errors.email}
<p class="text-sm text-destructive">{$errors.email}</p>
{/if}
</CollapsibleContent>
<CollapsibleContent>
</div>
</Collapsible.Content>
<Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">First Name</Label>
<Input type="text" id="firstName" class={$errors.firstName && "outline outline-destructive"} name="firstName" placeholder="First Name" autocomplete="given-name" data-invalid={$errors.firstName} bind:value={$form.firstName} />
{#if $errors.firstName}
<p class="text-sm text-destructive">{$errors.firstName}</p>
{/if}
</CollapsibleContent>
<CollapsibleContent>
</div>
</Collapsible.Content>
<Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">Last Name</Label>
<Input type="text" id="lastName" class={$errors.firstName && "outline outline-destructive"} name="lastName" placeholder="Last Name" autocomplete="family-name" data-invalid={$errors.lastName} bind:value={$form.lastName} />
{#if $errors.lastName}
<p class="text-sm text-destructive">{$errors.lastName}</p>
{/if}
</CollapsibleContent>
</Collapsible>
</div>
</Collapsible.Content>
</Collapsible.Root>
<div class="grid grid-cols-2">
<Button type="submit">Signup</Button>
<Button variant="link" href="/">or Cancel</Button>
<Button variant="link" class="text-secondary-foreground" href="/">or Cancel</Button>
</div>
{#if !$form.email}
<Alert>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
<Alert.Root>
<Alert.Title level="h3">Heads up!</Alert.Title>
<Alert.Description>
Without an email address, you won't be able to reset your password. Submit only if you are sure. You can always add this later.
</AlertDescription>
</Alert>
</Alert.Description>
</Alert.Root>
{/if}
</div>
</form>

View file

@ -9,7 +9,7 @@ import { search_schema } from '$lib/zodValidation.js';
export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
// console.log('locals load', locals);
@ -107,7 +107,7 @@ export const actions = {
try {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
let game = await prisma.game.findUnique({
@ -159,7 +159,7 @@ export const actions = {
create: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
return error(405, 'Method not allowed');
},
@ -167,7 +167,7 @@ export const actions = {
delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
return error(405, 'Method not allowed');
},
@ -179,7 +179,7 @@ export const actions = {
try {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
let game = await prisma.game.findUnique({

View file

@ -4,7 +4,7 @@ import { redirect } from '@sveltejs/kit';
export async function load({ params, locals }) {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
try {

View file

@ -6,7 +6,7 @@ import { list_game_request_schema } from '$lib/zodValidation';
export async function load({ params, locals }) {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
try {
@ -49,7 +49,7 @@ export const actions = {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
let game = await prisma.game.findUnique({
@ -106,21 +106,21 @@ export const actions = {
create: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
},
// Delete a wishlist
delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
},
// Remove game from a wishlist
remove: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
}
};

View file

@ -17,7 +17,7 @@ export const load = async (event) => {
const session = await event.locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
const { user } = session;
@ -48,7 +48,7 @@ export const actions = {
const session = await event.locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
const user = session.user;

View file

@ -5,7 +5,7 @@
import { AlertTriangle } from 'lucide-svelte';
import Label from '$components/ui/label/Label.svelte';
import Input from '$components/ui/input/Input.svelte';
import Button from '$components/ui/button/Button.svelte';
import { Button } from '$components/ui/button';
export let data;
const profileSchema = userSchema.pick({
@ -63,8 +63,8 @@
{/if}
</div>
<div class="mt-6">
<Button variant="link" href="/auth/password/reset">Change Password</Button>
<!-- <a href="/auth/password/reset">Change Password</a> -->
<Button variant="link" href="/password/reset">Change Password</Button>
<!-- <a href="/password/reset">Change Password</a> -->
</div>
<div class="mt-6">

View file

@ -7,7 +7,7 @@ import { modifyListGameSchema } from '$lib/config/zod-schemas.js';
export async function load({ params, locals }) {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
console.log('Wishlist load User id', session.user);
@ -56,7 +56,7 @@ export const actions = {
try {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
let game = await prisma.game.findUnique({
@ -107,7 +107,7 @@ export const actions = {
create: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
return error(405, 'Method not allowed');
},
@ -115,7 +115,7 @@ export const actions = {
delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
return error(405, 'Method not allowed');
},
@ -127,7 +127,7 @@ export const actions = {
try {
const session = await locals.auth.validate();
if (!session) {
throw redirect(302, '/auth/signin');
throw redirect(302, '/login');
}
let game = await prisma.game.findUnique({

View file

@ -66,7 +66,7 @@
<AddToList {in_collection} {in_wishlist} game_id={game.id} {wishlist} {collection} />
{:else}
<span>
<Button href="/auth/signup">Sign Up</Button> or <Button href="/auth/signin">Sign In</Button> to add to a list.
<Button href="/sign-up">Sign Up</Button> or <Button href="/login">Sign In</Button> to add to a list.
</span>
{/if}
</div>

View file

@ -5,11 +5,6 @@
export let data;
console.log("search page data", data);
// $: if (data?.searchData?.games) {
// gameStore.removeAll();
// gameStore.addAll(data?.searchData?.games);
// }
</script>
<div class="game-search">