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": {
|
||||
"bits-ui": "^0.21.1",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk-sv": "^0.0.16",
|
||||
"dotenv": "^16.4.5",
|
||||
"drizzle-orm": "^0.30.4",
|
||||
"lucide-svelte": "^0.363.0",
|
||||
"pg": "^8.11.3",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwind-variants": "^0.2.1"
|
||||
@ -1714,6 +1716,80 @@
|
||||
"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": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||
@ -3123,6 +3199,14 @@
|
||||
"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": {
|
||||
"version": "0.30.8",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
|
||||
|
@ -29,8 +29,10 @@
|
||||
"dependencies": {
|
||||
"bits-ui": "^0.21.1",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk-sv": "^0.0.16",
|
||||
"dotenv": "^16.4.5",
|
||||
"drizzle-orm": "^0.30.4",
|
||||
"lucide-svelte": "^0.363.0",
|
||||
"pg": "^8.11.3",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"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