aaa
This commit is contained in:
parent
f31d6e9e65
commit
cc0cb11879
84
package-lock.json
generated
84
package-lock.json
generated
@ -10,8 +10,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bits-ui": "^0.21.1",
|
"bits-ui": "^0.21.1",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
|
"cmdk-sv": "^0.0.16",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"drizzle-orm": "^0.30.4",
|
"drizzle-orm": "^0.30.4",
|
||||||
|
"lucide-svelte": "^0.363.0",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwind-variants": "^0.2.1"
|
"tailwind-variants": "^0.2.1"
|
||||||
@ -1714,6 +1716,80 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cmdk-sv": {
|
||||||
|
"version": "0.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/cmdk-sv/-/cmdk-sv-0.0.16.tgz",
|
||||||
|
"integrity": "sha512-erlrIrRPaPgiiCFYZgWxTbqgfaLKjiwAC4tKNJH4eUpMBilGqEe2Ruiip9oYakWcZM14JQhcuiYqOl9ee4KI1Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"bits-ui": "^0.9.0",
|
||||||
|
"nanoid": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk-sv/node_modules/@melt-ui/svelte": {
|
||||||
|
"version": "0.61.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@melt-ui/svelte/-/svelte-0.61.2.tgz",
|
||||||
|
"integrity": "sha512-BHkD9G31zQBToA4euDRBgTQRvWxT9scufOVCXgDO6HKTvyxFspbWT2bgiSFqAK4BbAGDn9Ao36Q8F9O71KN4OQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.3.1",
|
||||||
|
"@floating-ui/dom": "^1.4.5",
|
||||||
|
"@internationalized/date": "^3.5.0",
|
||||||
|
"dequal": "^2.0.3",
|
||||||
|
"focus-trap": "^7.5.2",
|
||||||
|
"nanoid": "^4.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": ">=3 <5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk-sv/node_modules/@melt-ui/svelte/node_modules/nanoid": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk-sv/node_modules/bits-ui": {
|
||||||
|
"version": "0.9.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.9.9.tgz",
|
||||||
|
"integrity": "sha512-LkdkyTtpXdkjBzPZJVJgpcre4fut6DONoprMfadHFo82HNUhph+02CxDjYEcZcThb5z4YjSxMlCYvQPZm+YtfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@melt-ui/svelte": "0.61.2",
|
||||||
|
"nanoid": "^5.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk-sv/node_modules/nanoid": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/code-red": {
|
"node_modules/code-red": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||||
@ -3123,6 +3199,14 @@
|
|||||||
"es5-ext": "~0.10.2"
|
"es5-ext": "~0.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-svelte": {
|
||||||
|
"version": "0.363.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.363.0.tgz",
|
||||||
|
"integrity": "sha512-zpUBFtMEEOOjILgiDX48ijibUww3JUCLrMo5YDGX/De/m6I+vn+oWIGvdyZtuc8nz/P8xHW9vWLKzIWeMrRYbA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^3 || ^4 || ^5.0.0-next.42"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.8",
|
"version": "0.30.8",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
|
||||||
|
@ -29,8 +29,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bits-ui": "^0.21.1",
|
"bits-ui": "^0.21.1",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
|
"cmdk-sv": "^0.0.16",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"drizzle-orm": "^0.30.4",
|
"drizzle-orm": "^0.30.4",
|
||||||
|
"lucide-svelte": "^0.363.0",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwind-variants": "^0.2.1"
|
"tailwind-variants": "^0.2.1"
|
||||||
|
130
src/lib/admin/nav.svelte
Normal file
130
src/lib/admin/nav.svelte
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import LineChart from "lucide-svelte/icons/line-chart";
|
||||||
|
import Menu from "lucide-svelte/icons/menu";
|
||||||
|
import Package2 from "lucide-svelte/icons/package-2";
|
||||||
|
import Users from "lucide-svelte/icons/users";
|
||||||
|
|
||||||
|
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import * as Sheet from "$lib/components/ui/sheet/index.js";
|
||||||
|
import { Book } from "lucide-svelte";
|
||||||
|
|
||||||
|
export let title = "Example title";
|
||||||
|
|
||||||
|
function isSelected(buttonText: string) {
|
||||||
|
return title.toLowerCase() === buttonText.toLowerCase() ? "bg-muted" : "text-muted-foreground text-primary";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="grid h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]"
|
||||||
|
>
|
||||||
|
<div class="hidden border-r bg-muted/40 md:block">
|
||||||
|
<div class="flex h-full max-h-screen flex-col gap-2">
|
||||||
|
<div
|
||||||
|
class="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6"
|
||||||
|
>
|
||||||
|
<a href="/" class="flex items-center gap-2 font-semibold">
|
||||||
|
<Package2 class="h-6 w-6" />
|
||||||
|
<span class="">Acme Inc</span>
|
||||||
|
</a>
|
||||||
|
<!-- <Button variant="outline" size="icon" class="ml-auto h-8 w-8">
|
||||||
|
<Bell class="h-4 w-4" />
|
||||||
|
<span class="sr-only">Toggle notifications</span>
|
||||||
|
</Button> -->
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<nav class="grid items-start px-2 text-sm font-medium lg:px-4">
|
||||||
|
<a
|
||||||
|
href="##"
|
||||||
|
class="flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary {isSelected("activity")}"
|
||||||
|
>
|
||||||
|
<LineChart class="h-4 w-4" />
|
||||||
|
Activity
|
||||||
|
<Badge
|
||||||
|
class="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full"
|
||||||
|
>
|
||||||
|
6
|
||||||
|
</Badge>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/admin/users"
|
||||||
|
class="flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary {isSelected("users")}"
|
||||||
|
>
|
||||||
|
<Users class="h-4 w-4" />
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="##"
|
||||||
|
class="flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary {isSelected("manga")}"
|
||||||
|
>
|
||||||
|
<Book class="h-4 w-4" />
|
||||||
|
Manga
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<header
|
||||||
|
class="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6"
|
||||||
|
>
|
||||||
|
<Sheet.Root>
|
||||||
|
<Sheet.Trigger asChild let:builder>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
class="shrink-0 md:hidden"
|
||||||
|
builders={[builder]}
|
||||||
|
>
|
||||||
|
<Menu class="h-5 w-5" />
|
||||||
|
<span class="sr-only">Toggle navigation menu</span>
|
||||||
|
</Button>
|
||||||
|
</Sheet.Trigger>
|
||||||
|
<Sheet.Content side="left" class="flex flex-col">
|
||||||
|
<nav class="grid gap-2 text-lg font-medium">
|
||||||
|
<a
|
||||||
|
href="##"
|
||||||
|
class="flex items-center gap-2 text-lg font-semibold"
|
||||||
|
>
|
||||||
|
<Package2 class="h-6 w-6" />
|
||||||
|
<span class="sr-only">Acme Inc</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="##"
|
||||||
|
class="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<LineChart class="h-5 w-5" />
|
||||||
|
Activity
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/admin/users"
|
||||||
|
class="mx-[-0.65rem] flex items-center gap-4 rounded-xl bg-muted px-3 py-2 text-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<Users class="h-5 w-5" />
|
||||||
|
Users
|
||||||
|
<Badge
|
||||||
|
class="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full"
|
||||||
|
>
|
||||||
|
6
|
||||||
|
</Badge>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="##"
|
||||||
|
class="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<Book class="h-5 w-5" />
|
||||||
|
Manga
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</Sheet.Content>
|
||||||
|
</Sheet.Root>
|
||||||
|
</header>
|
||||||
|
<main class="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<h1 class="text-lg font-semibold md:text-2xl">{title}</h1>
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
68
src/lib/admin/user-role-dropdown.svelte
Normal file
68
src/lib/admin/user-role-dropdown.svelte
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<script>
|
||||||
|
import { Button } from "$lib/components/ui/button";
|
||||||
|
import * as Popover from "$lib/components/ui/popover";
|
||||||
|
import * as Select from "$lib/components/ui/select";
|
||||||
|
import * as Command from "$lib/components/ui/command";
|
||||||
|
import { ChevronDown } from "lucide-svelte";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const roles = ["Member", "Admin"];
|
||||||
|
|
||||||
|
/** @type {"Member" | "Admin"} */
|
||||||
|
export let selected = "Member";
|
||||||
|
|
||||||
|
/** @param {"Member" | "Admin"} value */
|
||||||
|
function select(value) {
|
||||||
|
console.log(value);
|
||||||
|
selected = value;
|
||||||
|
dispatch("select", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: selected;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- <Popover.Root>
|
||||||
|
<Popover.Trigger asChild let:builder>
|
||||||
|
<Button builders={[builder]} variant="outline" class="ml-auto">
|
||||||
|
{selected}
|
||||||
|
<ChevronDown class="ml-2 h-4 w-4 text-muted-foreground" />
|
||||||
|
</Button>
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Content class="p-0" align="end">
|
||||||
|
<Command.Root>
|
||||||
|
<Command.List>
|
||||||
|
<Command.Group>
|
||||||
|
<Command.Item class="flex flex-col items-start space-y-1 px-4 py-2 cursor-pointer" on:click={() => select("Member")}>
|
||||||
|
<p>Member</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Can read manga and submit updates.
|
||||||
|
</p>
|
||||||
|
</Command.Item>
|
||||||
|
<Command.Item class="flex flex-col items-start space-y-1 px-4 py-2 cursor-pointer" on:click={() => select("Admin")}>
|
||||||
|
<p>Admin</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Can read, edit, delete and add users, manga and activity.
|
||||||
|
</p>
|
||||||
|
</Command.Item>
|
||||||
|
</Command.Group>
|
||||||
|
</Command.List>
|
||||||
|
</Command.Root>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root> -->
|
||||||
|
|
||||||
|
<Select.Root>
|
||||||
|
<Select.Trigger class="w-[180px]">
|
||||||
|
<Select.Value placeholder="Select a role" />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Group>
|
||||||
|
<Select.Label>Roles</Select.Label>
|
||||||
|
{#each roles as role}
|
||||||
|
<Select.Item value={role} label={role}>{role}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Group>
|
||||||
|
</Select.Content>
|
||||||
|
<Select.Input name="userRole" />
|
||||||
|
</Select.Root>
|
16
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
16
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.FallbackProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Fallback
|
||||||
|
class={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AvatarPrimitive.Fallback>
|
18
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
18
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.ImageProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let src: $$Props["src"] = undefined;
|
||||||
|
export let alt: $$Props["alt"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Image
|
||||||
|
{src}
|
||||||
|
{alt}
|
||||||
|
class={cn("aspect-square h-full w-full", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
18
src/lib/components/ui/avatar/avatar.svelte
Normal file
18
src/lib/components/ui/avatar/avatar.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.Props;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let delayMs: $$Props["delayMs"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Root
|
||||||
|
{delayMs}
|
||||||
|
class={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AvatarPrimitive.Root>
|
13
src/lib/components/ui/avatar/index.ts
Normal file
13
src/lib/components/ui/avatar/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Root from "./avatar.svelte";
|
||||||
|
import Image from "./avatar-image.svelte";
|
||||||
|
import Fallback from "./avatar-fallback.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Image,
|
||||||
|
Fallback,
|
||||||
|
//
|
||||||
|
Root as Avatar,
|
||||||
|
Image as AvatarImage,
|
||||||
|
Fallback as AvatarFallback,
|
||||||
|
};
|
18
src/lib/components/ui/badge/badge.svelte
Normal file
18
src/lib/components/ui/badge/badge.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { badgeVariants, type Variant } from "./index.js";
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export let href: string | undefined = undefined;
|
||||||
|
export let variant: Variant = "default";
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={href ? "a" : "span"}
|
||||||
|
{href}
|
||||||
|
class={cn(badgeVariants({ variant, className }))}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</svelte:element>
|
21
src/lib/components/ui/badge/index.ts
Normal file
21
src/lib/components/ui/badge/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { tv, type VariantProps } from "tailwind-variants";
|
||||||
|
export { default as Badge } from "./badge.svelte";
|
||||||
|
|
||||||
|
export const badgeVariants = tv({
|
||||||
|
base: "inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none select-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
|
||||||
|
outline: "text-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Variant = VariantProps<typeof badgeVariants>["variant"];
|
23
src/lib/components/ui/command/command-dialog.svelte
Normal file
23
src/lib/components/ui/command/command-dialog.svelte
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Command from "./command.svelte";
|
||||||
|
import * as Dialog from "$lib/components/ui/dialog/index.js";
|
||||||
|
import type { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import type { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.Props & CommandPrimitive.CommandProps;
|
||||||
|
|
||||||
|
export let open: $$Props["open"] = false;
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Root bind:open {...$$restProps}>
|
||||||
|
<Dialog.Content class="overflow-hidden p-0 shadow-lg">
|
||||||
|
<Command
|
||||||
|
class="[&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:font-medium [&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group]:not([hidden])_~[data-cmdk-group]]:pt-0 [&_[data-cmdk-group]]:px-2 [&_[data-cmdk-input-wrapper]_svg]:h-5 [&_[data-cmdk-input-wrapper]_svg]:w-5 [&_[data-cmdk-input]]:h-12 [&_[data-cmdk-item]]:px-2 [&_[data-cmdk-item]]:py-3 [&_[data-cmdk-item]_svg]:h-5 [&_[data-cmdk-item]_svg]:w-5"
|
||||||
|
{...$$restProps}
|
||||||
|
bind:value
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Command>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
12
src/lib/components/ui/command/command-empty.svelte
Normal file
12
src/lib/components/ui/command/command-empty.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.EmptyProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Empty class={cn("py-6 text-center text-sm", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Empty>
|
18
src/lib/components/ui/command/command-group.svelte
Normal file
18
src/lib/components/ui/command/command-group.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
type $$Props = CommandPrimitive.GroupProps;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Group
|
||||||
|
class={cn(
|
||||||
|
"overflow-hidden p-1 text-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-medium [&_[data-cmdk-group-heading]]:text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Group>
|
23
src/lib/components/ui/command/command-input.svelte
Normal file
23
src/lib/components/ui/command/command-input.svelte
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import Search from "lucide-svelte/icons/search";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.InputProps;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let value: string = "";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex items-center border-b px-2" data-cmdk-input-wrapper="">
|
||||||
|
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
<CommandPrimitive.Input
|
||||||
|
class={cn(
|
||||||
|
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
bind:value
|
||||||
|
/>
|
||||||
|
</div>
|
24
src/lib/components/ui/command/command-item.svelte
Normal file
24
src/lib/components/ui/command/command-item.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.ItemProps;
|
||||||
|
|
||||||
|
export let asChild = false;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Item
|
||||||
|
{asChild}
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
let:action
|
||||||
|
let:attrs
|
||||||
|
>
|
||||||
|
<slot {action} {attrs} />
|
||||||
|
</CommandPrimitive.Item>
|
15
src/lib/components/ui/command/command-list.svelte
Normal file
15
src/lib/components/ui/command/command-list.svelte
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.ListProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.List
|
||||||
|
class={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.List>
|
10
src/lib/components/ui/command/command-separator.svelte
Normal file
10
src/lib/components/ui/command/command-separator.svelte
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.SeparatorProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Separator class={cn("-mx-1 h-px bg-border", className)} {...$$restProps} />
|
16
src/lib/components/ui/command/command-shortcut.svelte
Normal file
16
src/lib/components/ui/command/command-shortcut.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</span>
|
22
src/lib/components/ui/command/command.svelte
Normal file
22
src/lib/components/ui/command/command.svelte
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.CommandProps;
|
||||||
|
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Root
|
||||||
|
class={cn(
|
||||||
|
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
bind:value
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Root>
|
37
src/lib/components/ui/command/index.ts
Normal file
37
src/lib/components/ui/command/index.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { Command as CommandPrimitive } from "cmdk-sv";
|
||||||
|
|
||||||
|
import Root from "./command.svelte";
|
||||||
|
import Dialog from "./command-dialog.svelte";
|
||||||
|
import Empty from "./command-empty.svelte";
|
||||||
|
import Group from "./command-group.svelte";
|
||||||
|
import Item from "./command-item.svelte";
|
||||||
|
import Input from "./command-input.svelte";
|
||||||
|
import List from "./command-list.svelte";
|
||||||
|
import Separator from "./command-separator.svelte";
|
||||||
|
import Shortcut from "./command-shortcut.svelte";
|
||||||
|
|
||||||
|
const Loading = CommandPrimitive.Loading;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Dialog,
|
||||||
|
Empty,
|
||||||
|
Group,
|
||||||
|
Item,
|
||||||
|
Input,
|
||||||
|
List,
|
||||||
|
Separator,
|
||||||
|
Shortcut,
|
||||||
|
Loading,
|
||||||
|
//
|
||||||
|
Root as Command,
|
||||||
|
Dialog as CommandDialog,
|
||||||
|
Empty as CommandEmpty,
|
||||||
|
Group as CommandGroup,
|
||||||
|
Item as CommandItem,
|
||||||
|
Input as CommandInput,
|
||||||
|
List as CommandList,
|
||||||
|
Separator as CommandSeparator,
|
||||||
|
Shortcut as CommandShortcut,
|
||||||
|
Loading as CommandLoading,
|
||||||
|
};
|
36
src/lib/components/ui/dialog/dialog-content.svelte
Normal file
36
src/lib/components/ui/dialog/dialog-content.svelte
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import X from "lucide-svelte/icons/x";
|
||||||
|
import * as Dialog from "./index.js";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.ContentProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
duration: 200,
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg md:w-full",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<DialogPrimitive.Close
|
||||||
|
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||||
|
>
|
||||||
|
<X class="h-4 w-4" />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</Dialog.Portal>
|
16
src/lib/components/ui/dialog/dialog-description.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-description.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.DescriptionProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
class={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Description>
|
16
src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
src/lib/components/ui/dialog/dialog-header.svelte
Normal file
13
src/lib/components/ui/dialog/dialog-header.svelte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
21
src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.OverlayProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = fade;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
duration: 150,
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn("fixed inset-0 z-50 bg-background/80 backdrop-blur-sm", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
8
src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
8
src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
type $$Props = DialogPrimitive.PortalProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Portal {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Portal>
|
16
src/lib/components/ui/dialog/dialog-title.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-title.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.TitleProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Title>
|
34
src/lib/components/ui/dialog/index.ts
Normal file
34
src/lib/components/ui/dialog/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
const Root = DialogPrimitive.Root;
|
||||||
|
const Trigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
|
import Title from "./dialog-title.svelte";
|
||||||
|
import Portal from "./dialog-portal.svelte";
|
||||||
|
import Footer from "./dialog-footer.svelte";
|
||||||
|
import Header from "./dialog-header.svelte";
|
||||||
|
import Overlay from "./dialog-overlay.svelte";
|
||||||
|
import Content from "./dialog-content.svelte";
|
||||||
|
import Description from "./dialog-description.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
Portal,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Trigger,
|
||||||
|
Overlay,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
//
|
||||||
|
Root as Dialog,
|
||||||
|
Title as DialogTitle,
|
||||||
|
Portal as DialogPortal,
|
||||||
|
Footer as DialogFooter,
|
||||||
|
Header as DialogHeader,
|
||||||
|
Trigger as DialogTrigger,
|
||||||
|
Overlay as DialogOverlay,
|
||||||
|
Content as DialogContent,
|
||||||
|
Description as DialogDescription,
|
||||||
|
};
|
@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import Check from "lucide-svelte/icons/check";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let checked: $$Props["checked"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
|
bind:checked
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.CheckboxIndicator>
|
||||||
|
<Check class="h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.CheckboxIndicator>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.CheckboxItem>
|
@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.ContentProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.ContentEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let sideOffset: $$Props["sideOffset"] = 4;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
{sideOffset}
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Content>
|
@ -0,0 +1,31 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.ItemProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
type $$Events = DropdownMenuPrimitive.ItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Item
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Item>
|
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.LabelProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Label
|
||||||
|
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Label>
|
@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.RadioGroupProps;
|
||||||
|
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.RadioGroup>
|
@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import Circle from "lucide-svelte/icons/circle";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.RadioItemProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.RadioItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: $$Props["value"];
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.RadioItem
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{value}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.RadioIndicator>
|
||||||
|
<Circle class="h-2 w-2 fill-current" />
|
||||||
|
</DropdownMenuPrimitive.RadioIndicator>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.RadioItem>
|
@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SeparatorProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Separator
|
||||||
|
class={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</span>
|
@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SubContentProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.SubContentEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
x: -10,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.SubContent
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:keydown
|
||||||
|
on:focusout
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.SubContent>
|
@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
type $$Events = DropdownMenuPrimitive.SubTriggerEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
|
class={cn(
|
||||||
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<ChevronRight class="ml-auto h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.SubTrigger>
|
48
src/lib/components/ui/dropdown-menu/index.ts
Normal file
48
src/lib/components/ui/dropdown-menu/index.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import Item from "./dropdown-menu-item.svelte";
|
||||||
|
import Label from "./dropdown-menu-label.svelte";
|
||||||
|
import Content from "./dropdown-menu-content.svelte";
|
||||||
|
import Shortcut from "./dropdown-menu-shortcut.svelte";
|
||||||
|
import RadioItem from "./dropdown-menu-radio-item.svelte";
|
||||||
|
import Separator from "./dropdown-menu-separator.svelte";
|
||||||
|
import RadioGroup from "./dropdown-menu-radio-group.svelte";
|
||||||
|
import SubContent from "./dropdown-menu-sub-content.svelte";
|
||||||
|
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
|
||||||
|
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
|
||||||
|
|
||||||
|
const Sub = DropdownMenuPrimitive.Sub;
|
||||||
|
const Root = DropdownMenuPrimitive.Root;
|
||||||
|
const Trigger = DropdownMenuPrimitive.Trigger;
|
||||||
|
const Group = DropdownMenuPrimitive.Group;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Sub,
|
||||||
|
Root,
|
||||||
|
Item,
|
||||||
|
Label,
|
||||||
|
Group,
|
||||||
|
Trigger,
|
||||||
|
Content,
|
||||||
|
Shortcut,
|
||||||
|
Separator,
|
||||||
|
RadioItem,
|
||||||
|
SubContent,
|
||||||
|
SubTrigger,
|
||||||
|
RadioGroup,
|
||||||
|
CheckboxItem,
|
||||||
|
//
|
||||||
|
Root as DropdownMenu,
|
||||||
|
Sub as DropdownMenuSub,
|
||||||
|
Item as DropdownMenuItem,
|
||||||
|
Label as DropdownMenuLabel,
|
||||||
|
Group as DropdownMenuGroup,
|
||||||
|
Content as DropdownMenuContent,
|
||||||
|
Trigger as DropdownMenuTrigger,
|
||||||
|
Shortcut as DropdownMenuShortcut,
|
||||||
|
RadioItem as DropdownMenuRadioItem,
|
||||||
|
Separator as DropdownMenuSeparator,
|
||||||
|
RadioGroup as DropdownMenuRadioGroup,
|
||||||
|
SubContent as DropdownMenuSubContent,
|
||||||
|
SubTrigger as DropdownMenuSubTrigger,
|
||||||
|
CheckboxItem as DropdownMenuCheckboxItem,
|
||||||
|
};
|
27
src/lib/components/ui/input/index.ts
Normal file
27
src/lib/components/ui/input/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Root from "./input.svelte";
|
||||||
|
|
||||||
|
export 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>;
|
||||||
|
focusin: FormInputEvent<FocusEvent>;
|
||||||
|
focusout: 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,
|
||||||
|
};
|
35
src/lib/components/ui/input/input.svelte
Normal file
35
src/lib/components/ui/input/input.svelte
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLInputAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { InputEvents } from "./index.js";
|
||||||
|
|
||||||
|
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-background 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:focusin
|
||||||
|
on:focusout
|
||||||
|
on:keydown
|
||||||
|
on:keypress
|
||||||
|
on:keyup
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:paste
|
||||||
|
on:input
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
7
src/lib/components/ui/label/index.ts
Normal file
7
src/lib/components/ui/label/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Root from "./label.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
//
|
||||||
|
Root as Label,
|
||||||
|
};
|
21
src/lib/components/ui/label/label.svelte
Normal file
21
src/lib/components/ui/label/label.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Label as LabelPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = LabelPrimitive.Props;
|
||||||
|
type $$Events = LabelPrimitive.Events;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
class={cn(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:mousedown
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</LabelPrimitive.Root>
|
17
src/lib/components/ui/popover/index.ts
Normal file
17
src/lib/components/ui/popover/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Popover as PopoverPrimitive } from "bits-ui";
|
||||||
|
import Content from "./popover-content.svelte";
|
||||||
|
const Root = PopoverPrimitive.Root;
|
||||||
|
const Trigger = PopoverPrimitive.Trigger;
|
||||||
|
const Close = PopoverPrimitive.Close;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Trigger,
|
||||||
|
Close,
|
||||||
|
//
|
||||||
|
Root as Popover,
|
||||||
|
Content as PopoverContent,
|
||||||
|
Trigger as PopoverTrigger,
|
||||||
|
Close as PopoverClose,
|
||||||
|
};
|
22
src/lib/components/ui/popover/popover-content.svelte
Normal file
22
src/lib/components/ui/popover/popover-content.svelte
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Popover as PopoverPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = PopoverPrimitive.ContentProps;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</PopoverPrimitive.Content>
|
34
src/lib/components/ui/select/index.ts
Normal file
34
src/lib/components/ui/select/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
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 Root = SelectPrimitive.Root;
|
||||||
|
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,
|
||||||
|
};
|
39
src/lib/components/ui/select/select-content.svelte
Normal file
39
src/lib/components/ui/select/select-content.svelte
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
import { scale } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.ContentProps;
|
||||||
|
type $$Events = SelectPrimitive.ContentEvents;
|
||||||
|
|
||||||
|
export let sideOffset: $$Props["sideOffset"] = 4;
|
||||||
|
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}
|
||||||
|
{sideOffset}
|
||||||
|
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>
|
40
src/lib/components/ui/select/select-item.svelte
Normal file
40
src/lib/components/ui/select/select-item.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import Check from "lucide-svelte/icons/check";
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
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 data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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>
|
||||||
|
{label ? label : value}
|
||||||
|
</slot>
|
||||||
|
</SelectPrimitive.Item>
|
16
src/lib/components/ui/select/select-label.svelte
Normal file
16
src/lib/components/ui/select/select-label.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
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>
|
11
src/lib/components/ui/select/select-separator.svelte
Normal file
11
src/lib/components/ui/select/select-separator.svelte
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
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} />
|
27
src/lib/components/ui/select/select-trigger.svelte
Normal file
27
src/lib/components/ui/select/select-trigger.svelte
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import ChevronDown from "lucide-svelte/icons/chevron-down";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
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-background 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 [&>span]:line-clamp-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
let:builder
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<slot {builder} />
|
||||||
|
<div>
|
||||||
|
<ChevronDown class="h-4 w-4 opacity-50" />
|
||||||
|
</div>
|
||||||
|
</SelectPrimitive.Trigger>
|
106
src/lib/components/ui/sheet/index.ts
Normal file
106
src/lib/components/ui/sheet/index.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import { tv, type VariantProps } from "tailwind-variants";
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
const Root = SheetPrimitive.Root;
|
||||||
|
const Close = SheetPrimitive.Close;
|
||||||
|
const Trigger = SheetPrimitive.Trigger;
|
||||||
|
|
||||||
|
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",
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: "inset-x-0 top-0 border-b",
|
||||||
|
bottom: "inset-x-0 bottom-0 border-t",
|
||||||
|
left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||||
|
right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: "right",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sheetTransitions = {
|
||||||
|
top: {
|
||||||
|
in: {
|
||||||
|
y: "-100%",
|
||||||
|
duration: 500,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
y: "-100%",
|
||||||
|
duration: 300,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
in: {
|
||||||
|
y: "100%",
|
||||||
|
duration: 500,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
y: "100%",
|
||||||
|
duration: 300,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
in: {
|
||||||
|
x: "-100%",
|
||||||
|
duration: 500,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
x: "-100%",
|
||||||
|
duration: 300,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
in: {
|
||||||
|
x: "100%",
|
||||||
|
duration: 500,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
x: "100%",
|
||||||
|
duration: 300,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Side = VariantProps<typeof sheetVariants>["side"];
|
47
src/lib/components/ui/sheet/sheet-content.svelte
Normal file
47
src/lib/components/ui/sheet/sheet-content.svelte
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import X from "lucide-svelte/icons/x";
|
||||||
|
import {
|
||||||
|
SheetOverlay,
|
||||||
|
SheetPortal,
|
||||||
|
sheetTransitions,
|
||||||
|
sheetVariants,
|
||||||
|
type Side,
|
||||||
|
} from "./index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = SheetPrimitive.ContentProps & {
|
||||||
|
side?: Side;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let side: $$Props["side"] = "right";
|
||||||
|
export { className as class };
|
||||||
|
export let inTransition: $$Props["inTransition"] = fly;
|
||||||
|
export let inTransitionConfig: $$Props["inTransitionConfig"] =
|
||||||
|
sheetTransitions[side ? side : "right"]["in"];
|
||||||
|
export let outTransition: $$Props["outTransition"] = fly;
|
||||||
|
export let outTransitionConfig: $$Props["outTransitionConfig"] =
|
||||||
|
sheetTransitions[side ? side : "right"]["out"];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
{inTransition}
|
||||||
|
{inTransitionConfig}
|
||||||
|
{outTransition}
|
||||||
|
{outTransitionConfig}
|
||||||
|
class={cn(sheetVariants({ side }), className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<SheetPrimitive.Close
|
||||||
|
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
||||||
|
>
|
||||||
|
<X class="h-4 w-4" />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
13
src/lib/components/ui/sheet/sheet-description.svelte
Normal file
13
src/lib/components/ui/sheet/sheet-description.svelte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = SheetPrimitive.DescriptionProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SheetPrimitive.Description class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</SheetPrimitive.Description>
|
16
src/lib/components/ui/sheet/sheet-footer.svelte
Normal file
16
src/lib/components/ui/sheet/sheet-footer.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
src/lib/components/ui/sheet/sheet-header.svelte
Normal file
13
src/lib/components/ui/sheet/sheet-header.svelte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
src/lib/components/ui/sheet/sheet-overlay.svelte
Normal file
21
src/lib/components/ui/sheet/sheet-overlay.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = SheetPrimitive.OverlayProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = fade;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
duration: 150,
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn("fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
13
src/lib/components/ui/sheet/sheet-portal.svelte
Normal file
13
src/lib/components/ui/sheet/sheet-portal.svelte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = SheetPrimitive.PortalProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</SheetPrimitive.Portal>
|
16
src/lib/components/ui/sheet/sheet-title.svelte
Normal file
16
src/lib/components/ui/sheet/sheet-title.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as SheetPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = SheetPrimitive.TitleProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
class={cn("text-lg font-semibold text-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</SheetPrimitive.Title>
|
45
src/lib/components/user-nav.svelte
Normal file
45
src/lib/components/user-nav.svelte
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||||
|
import * as Avatar from "$lib/components/ui/avatar";
|
||||||
|
import { Button } from "$lib/components/ui/button";
|
||||||
|
import { userStore } from "$lib/state";
|
||||||
|
|
||||||
|
type UserData = {
|
||||||
|
id: string,
|
||||||
|
username: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export let user: UserData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
|
<Button variant="ghost" builders={[builder]} class="relative h-8 w-8 rounded-full">
|
||||||
|
<Avatar.Root class="h-8 w-8">
|
||||||
|
<!-- <Avatar.Image src="/avatars/01.png" alt="@{user.username}" /> -->
|
||||||
|
<Avatar.Fallback>{user.username?.substring(0,2)}</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Content class="w-56" align="end">
|
||||||
|
<DropdownMenu.Label class="font-normal">
|
||||||
|
<div class="flex flex-col space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">@{user.username}</p>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Item href="/profile">
|
||||||
|
Profile
|
||||||
|
<DropdownMenu.Shortcut>⇧⌘P</DropdownMenu.Shortcut>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Item on:click={() => {
|
||||||
|
userStore.set(null);
|
||||||
|
}}>
|
||||||
|
Log out
|
||||||
|
<DropdownMenu.Shortcut>⇧⌘Q</DropdownMenu.Shortcut>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
19
src/lib/state.js
Normal file
19
src/lib/state.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { browser } from '$app/environment';
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
|
let stored = null;
|
||||||
|
|
||||||
|
if(browser && localStorage.auth) {
|
||||||
|
stored = JSON.parse(localStorage.auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('svelte/store').Writable<{id: string, username: string} | null>}
|
||||||
|
*/
|
||||||
|
export const userStore = writable(stored || null)
|
||||||
|
|
||||||
|
userStore.subscribe((value) => {
|
||||||
|
if(browser) {
|
||||||
|
localStorage.auth = JSON.stringify(value)
|
||||||
|
}
|
||||||
|
})
|
57
src/routes/(front)/+layout.svelte
Normal file
57
src/routes/(front)/+layout.svelte
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<script>
|
||||||
|
import UserNav from "$lib/components/user-nav.svelte";
|
||||||
|
import { Button } from "$lib/components/ui/button";
|
||||||
|
import * as Popover from "$lib/components/ui/popover";
|
||||||
|
import { Label } from "$lib/components/ui/label";
|
||||||
|
import { Input } from "$lib/components/ui/input";
|
||||||
|
import { userStore } from "$lib/state";
|
||||||
|
// const user = {
|
||||||
|
// id: "helloworld",
|
||||||
|
// username: "Jar Allerton",
|
||||||
|
// };
|
||||||
|
let tokenInput = "";
|
||||||
|
|
||||||
|
function logIn() {
|
||||||
|
userStore.set({
|
||||||
|
id: tokenInput,
|
||||||
|
username: "joe"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="max-w-6xl mx-auto">
|
||||||
|
<div class="h-16 flex items-center px-4">
|
||||||
|
<Button>Start new manga</Button>
|
||||||
|
<div class="ml-auto">
|
||||||
|
{#if $userStore !== null}
|
||||||
|
<UserNav user={$userStore} />
|
||||||
|
{:else}
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger asChild let:builder>
|
||||||
|
<Button builders={[builder]} variant="outline"
|
||||||
|
>Log in</Button
|
||||||
|
>
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Content class="w-80">
|
||||||
|
<div class="grid gap-4">
|
||||||
|
<div class="grid gap-2">
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-3 items-center gap-4"
|
||||||
|
>
|
||||||
|
<Label for="width">Auth token</Label>
|
||||||
|
<Input
|
||||||
|
id="width"
|
||||||
|
class="col-span-2 h-8"
|
||||||
|
bind:value={tokenInput}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button on:click={logIn}>Log in</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
</div>
|
36
src/routes/(front)/+page.svelte
Normal file
36
src/routes/(front)/+page.svelte
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<script>
|
||||||
|
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||||
|
import * as Card from "$lib/components/ui/card";
|
||||||
|
import { Button } from "$lib/components/ui/button";
|
||||||
|
|
||||||
|
const mangas = [
|
||||||
|
{
|
||||||
|
name: "Solo Leveling",
|
||||||
|
type: "Manhua",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl font-bold margin-y-4">Currently reading</h2>
|
||||||
|
<ScrollArea
|
||||||
|
class="w-full whitespace-nowrap border-y"
|
||||||
|
orientation="horizontal"
|
||||||
|
>
|
||||||
|
<div class="flex w-max space-x-4 p-4">
|
||||||
|
{#each mangas as manga}
|
||||||
|
<Card.Root>
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title>{manga.name}</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Content>
|
||||||
|
<p>Card Content</p>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Footer>
|
||||||
|
<Button>Complete chapter 10-20</Button>
|
||||||
|
</Card.Footer>
|
||||||
|
</Card.Root>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
@ -1 +1,5 @@
|
|||||||
<script>import "../app.pcss";</script><slot></slot>
|
<script>
|
||||||
|
import "../app.pcss"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
@ -1,33 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
|
||||||
import * as Card from "$lib/components/ui/card";
|
|
||||||
import { Button } from "$lib/components/ui/button";
|
|
||||||
|
|
||||||
const mangas = [
|
|
||||||
{
|
|
||||||
name: "Solo Leveling",
|
|
||||||
type: "Manhua"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ScrollArea
|
|
||||||
class="w-96 whitespace-nowrap rounded-md border"
|
|
||||||
orientation="horizontal"
|
|
||||||
>
|
|
||||||
<div class="flex w-max space-x-4 p-4">
|
|
||||||
{#each mangas as manga}
|
|
||||||
<Card.Root>
|
|
||||||
<Card.Header>
|
|
||||||
<Card.Title>{manga.name}</Card.Title>
|
|
||||||
</Card.Header>
|
|
||||||
<Card.Content>
|
|
||||||
<p>Card Content</p>
|
|
||||||
</Card.Content>
|
|
||||||
<Card.Footer>
|
|
||||||
<Button>Read</Button>
|
|
||||||
</Card.Footer>
|
|
||||||
</Card.Root>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
20
src/routes/admin/+page.svelte
Normal file
20
src/routes/admin/+page.svelte
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import Nav from "$lib/admin/nav.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Nav title="Inventory">
|
||||||
|
<div
|
||||||
|
class="flex flex-1 items-center justify-center rounded-lg border border-dashed shadow-sm"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center gap-1 text-center">
|
||||||
|
<h3 class="text-2xl font-bold tracking-tight">
|
||||||
|
You have no products
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
You can start selling as soon as you add a product.
|
||||||
|
</p>
|
||||||
|
<Button class="mt-4">Add Product</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Nav>
|
7
src/routes/admin/users/+page.server.ts
Normal file
7
src/routes/admin/users/+page.server.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { Actions } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
default: async (event) => {
|
||||||
|
console.log(event.request.body);
|
||||||
|
},
|
||||||
|
};
|
69
src/routes/admin/users/+page.svelte
Normal file
69
src/routes/admin/users/+page.svelte
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import Nav from "$lib/admin/nav.svelte";
|
||||||
|
import * as Avatar from "$lib/components/ui/avatar";
|
||||||
|
import UserRoleDropdown from "$lib/admin/user-role-dropdown.svelte";
|
||||||
|
import * as Dialog from "$lib/components/ui/dialog";
|
||||||
|
import { Label } from "$lib/components/ui/label";
|
||||||
|
import {Input} from "$lib/components/ui/input";
|
||||||
|
|
||||||
|
const users: { username: string; role: "Member" | "Admin"; }[] = [
|
||||||
|
{
|
||||||
|
username: "Joe Biden",
|
||||||
|
role: "Member",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "BoeJiden",
|
||||||
|
role: "Admin",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Nav title="Users">
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger class="w-12">
|
||||||
|
<Button>
|
||||||
|
Add User
|
||||||
|
</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
<Dialog.Content class="sm:max-w-[425px]">
|
||||||
|
<Dialog.Header>
|
||||||
|
<Dialog.Title>Create user</Dialog.Title>
|
||||||
|
</Dialog.Header>
|
||||||
|
<div class="grid gap-4 py-4">
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label for="name" class="text-right">Username</Label>
|
||||||
|
<Input id="name" value="Pedro Duarte" class="col-span-3" />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label for="username" class="text-right">Role</Label>
|
||||||
|
<UserRoleDropdown />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Dialog.Footer>
|
||||||
|
<Button type="submit">Save changes</Button>
|
||||||
|
</Dialog.Footer>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
<div class="grid gap-6 max-w-2xl">
|
||||||
|
{#each users as user}
|
||||||
|
<div class="flex items-center justify-between space-x-4">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<Avatar.Root>
|
||||||
|
<!-- <Avatar.Image src="/avatars/01.png" alt="Sofia Davis" /> -->
|
||||||
|
<Avatar.Fallback
|
||||||
|
>{user.username.substring(0, 2)}</Avatar.Fallback
|
||||||
|
>
|
||||||
|
</Avatar.Root>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
{user.username}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<UserRoleDropdown selected={user.role} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Nav>
|
Loading…
Reference in New Issue
Block a user