get the whole friends system to work now
This commit is contained in:
parent
342fd30d62
commit
126acf52f3
34 changed files with 1101 additions and 40 deletions
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { type Data } from "$lib";
|
||||
import { type Data, type OverviewUser } from "$lib";
|
||||
import * as Collapsible from "$lib/components/ui/collapsible/index.js";
|
||||
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
||||
import * as Dialog from "$lib/components/ui/dialog/index.js";
|
||||
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import MessagesSquare from "@lucide/svelte/icons/messages-square";
|
||||
import MinusIcon from "@lucide/svelte/icons/minus";
|
||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
||||
|
|
@ -12,8 +14,9 @@
|
|||
import Input from "./ui/input/input.svelte";
|
||||
import Button, { buttonVariants } from "./ui/button/button.svelte";
|
||||
import User from "./extra/User.svelte";
|
||||
import type { SessionValidationResult } from "$lib/server/auth";
|
||||
|
||||
let { currentPage = $bindable<string|null>(), data, ...restProps }: {currentPage: string|null, data: Data }= $props();
|
||||
let { currentPage = $bindable<string|null>(), data, user, ...restProps }: {currentPage: string|null, data: Data, user: SessionValidationResult['user'] }= $props();
|
||||
</script>
|
||||
|
||||
<Sidebar.Root {...restProps}>
|
||||
|
|
@ -34,22 +37,24 @@
|
|||
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>
|
||||
<Button variant="outline" size="icon">
|
||||
<Button variant={user!.friendRequests.length > 0 ? "destructive" : "outline"} size="icon">
|
||||
<UserRoundPlus />
|
||||
|
||||
</Button>
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content class="sm:max-w-[425px]">
|
||||
<form method="POST" action="?/addFriend">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Add a friend</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
Add a friend using their username.
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
<Input name="username" placeholder="username" required />
|
||||
<Dialog.Content class="sm:max-w-[425px]">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Add a friend</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
Add a friend using their username or manage pending requests.
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
<!-- input to add a new friend -->
|
||||
<form method="POST" action="?/addFriend" class="mb-4">
|
||||
<Input name="username" placeholder="username" required class="mb-2" />
|
||||
<Dialog.Footer>
|
||||
<Dialog.Close class={buttonVariants({ variant: "outline" })}>
|
||||
Cancel
|
||||
|
|
@ -57,7 +62,69 @@
|
|||
<Button type="submit">Send request</Button>
|
||||
</Dialog.Footer>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
|
||||
<!-- Tabs for Friend Requests -->
|
||||
<Tabs.Root value="outgoing">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="outgoing">Outgoing</Tabs.Trigger>
|
||||
<Tabs.Trigger value="incoming">Incoming</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
||||
<!-- Outgoing Requests -->
|
||||
<Tabs.Content value="outgoing">
|
||||
{#if user.friendRequests.filter(r => r.fromUser === user.id).length === 0}
|
||||
<p class="text-sm text-muted-foreground">No outgoing requests</p>
|
||||
{:else}
|
||||
{#each user.friendRequests.filter(r => r.fromUser === user.id) as request (request.id)}
|
||||
<Card.Root class="mb-2">
|
||||
<Card.Header>
|
||||
<Card.Title>{request.username}</Card.Title>
|
||||
<Card.Description>Request sent</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Footer>
|
||||
<form method="POST" action="?/cancelFriendRequest">
|
||||
<input type="hidden" name="requestId" value={request.id} />
|
||||
<Button type="submit" variant="outline" size="sm">
|
||||
Cancel
|
||||
</Button>
|
||||
</form>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
{/if}
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Incoming Requests -->
|
||||
<Tabs.Content value="incoming">
|
||||
{#if user.friendRequests.filter(r => r.toUser === user.id).length === 0}
|
||||
<p class="text-sm text-muted-foreground">No incoming requests</p>
|
||||
{:else}
|
||||
{#each user.friendRequests.filter(r => r.toUser === user.id) as request (request.id)}
|
||||
<Card.Root class="mb-2">
|
||||
<Card.Header>
|
||||
<Card.Title>{request.username}</Card.Title>
|
||||
<Card.Description>Sent you a friend request</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Footer class="flex gap-2">
|
||||
<!-- accept friend -->
|
||||
<form method="POST" action="?/addFriend">
|
||||
<input type="hidden" name="userId" value={request.fromUser} />
|
||||
<Button type="submit" size="sm">Accept</Button>
|
||||
</form>
|
||||
<!-- decline friend -->
|
||||
<form method="POST" action="?/cancelFriendRequest">
|
||||
<input type="hidden" name="requestId" value={request.id} />
|
||||
<Button type="submit" variant="outline" size="sm">Decline</Button>
|
||||
</form>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
{/if}
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Dialog.Content>
|
||||
|
||||
|
||||
</Dialog.Root>
|
||||
|
||||
<Dialog.Root>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.ActionProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Action
|
||||
bind:ref
|
||||
data-slot="alert-dialog-action"
|
||||
class={cn(buttonVariants(), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.CancelProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Cancel
|
||||
bind:ref
|
||||
data-slot="alert-dialog-cancel"
|
||||
class={cn(buttonVariants({ variant: "outline" }), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import AlertDialogPortal from "./alert-dialog-portal.svelte";
|
||||
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
||||
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
import type { ComponentProps } from "svelte";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
...restProps
|
||||
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
||||
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPortal {...portalProps}>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="alert-dialog-content"
|
||||
class={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.DescriptionProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Description
|
||||
bind:ref
|
||||
data-slot="alert-dialog-description"
|
||||
class={cn("text-muted-foreground text-sm", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-footer"
|
||||
class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-header"
|
||||
class={cn("flex flex-col gap-2 text-center sm:text-start", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.OverlayProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Overlay
|
||||
bind:ref
|
||||
data-slot="alert-dialog-overlay"
|
||||
class={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
|
||||
let { ...restProps }: AlertDialogPrimitive.PortalProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Portal {...restProps} />
|
||||
17
src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
Normal file
17
src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.TitleProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Title
|
||||
bind:ref
|
||||
data-slot="alert-dialog-title"
|
||||
class={cn("text-lg font-semibold", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
|
||||
7
src/lib/components/ui/alert-dialog/alert-dialog.svelte
Normal file
7
src/lib/components/ui/alert-dialog/alert-dialog.svelte
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
|
||||
let { open = $bindable(false), ...restProps }: AlertDialogPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Root bind:open {...restProps} />
|
||||
37
src/lib/components/ui/alert-dialog/index.ts
Normal file
37
src/lib/components/ui/alert-dialog/index.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import Root from "./alert-dialog.svelte";
|
||||
import Portal from "./alert-dialog-portal.svelte";
|
||||
import Trigger from "./alert-dialog-trigger.svelte";
|
||||
import Title from "./alert-dialog-title.svelte";
|
||||
import Action from "./alert-dialog-action.svelte";
|
||||
import Cancel from "./alert-dialog-cancel.svelte";
|
||||
import Footer from "./alert-dialog-footer.svelte";
|
||||
import Header from "./alert-dialog-header.svelte";
|
||||
import Overlay from "./alert-dialog-overlay.svelte";
|
||||
import Content from "./alert-dialog-content.svelte";
|
||||
import Description from "./alert-dialog-description.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Title,
|
||||
Action,
|
||||
Cancel,
|
||||
Portal,
|
||||
Footer,
|
||||
Header,
|
||||
Trigger,
|
||||
Overlay,
|
||||
Content,
|
||||
Description,
|
||||
//
|
||||
Root as AlertDialog,
|
||||
Title as AlertDialogTitle,
|
||||
Action as AlertDialogAction,
|
||||
Cancel as AlertDialogCancel,
|
||||
Portal as AlertDialogPortal,
|
||||
Footer as AlertDialogFooter,
|
||||
Header as AlertDialogHeader,
|
||||
Trigger as AlertDialogTrigger,
|
||||
Overlay as AlertDialogOverlay,
|
||||
Content as AlertDialogContent,
|
||||
Description as AlertDialogDescription,
|
||||
};
|
||||
20
src/lib/components/ui/card/card-action.svelte
Normal file
20
src/lib/components/ui/card/card-action.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-action"
|
||||
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
15
src/lib/components/ui/card/card-content.svelte
Normal file
15
src/lib/components/ui/card/card-content.svelte
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
20
src/lib/components/ui/card/card-description.svelte
Normal file
20
src/lib/components/ui/card/card-description.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||
</script>
|
||||
|
||||
<p
|
||||
bind:this={ref}
|
||||
data-slot="card-description"
|
||||
class={cn("text-muted-foreground text-sm", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
20
src/lib/components/ui/card/card-footer.svelte
Normal file
20
src/lib/components/ui/card/card-footer.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-footer"
|
||||
class={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
23
src/lib/components/ui/card/card-header.svelte
Normal file
23
src/lib/components/ui/card/card-header.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-header"
|
||||
class={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
20
src/lib/components/ui/card/card-title.svelte
Normal file
20
src/lib/components/ui/card/card-title.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-title"
|
||||
class={cn("leading-none font-semibold", className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
23
src/lib/components/ui/card/card.svelte
Normal file
23
src/lib/components/ui/card/card.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card"
|
||||
class={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
25
src/lib/components/ui/card/index.ts
Normal file
25
src/lib/components/ui/card/index.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import Root from "./card.svelte";
|
||||
import Content from "./card-content.svelte";
|
||||
import Description from "./card-description.svelte";
|
||||
import Footer from "./card-footer.svelte";
|
||||
import Header from "./card-header.svelte";
|
||||
import Title from "./card-title.svelte";
|
||||
import Action from "./card-action.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Description,
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
//
|
||||
Root as Card,
|
||||
Content as CardContent,
|
||||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle,
|
||||
Action as CardAction,
|
||||
};
|
||||
16
src/lib/components/ui/tabs/index.ts
Normal file
16
src/lib/components/ui/tabs/index.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import Root from "./tabs.svelte";
|
||||
import Content from "./tabs-content.svelte";
|
||||
import List from "./tabs-list.svelte";
|
||||
import Trigger from "./tabs-trigger.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
List,
|
||||
Trigger,
|
||||
//
|
||||
Root as Tabs,
|
||||
Content as TabsContent,
|
||||
List as TabsList,
|
||||
Trigger as TabsTrigger,
|
||||
};
|
||||
17
src/lib/components/ui/tabs/tabs-content.svelte
Normal file
17
src/lib/components/ui/tabs/tabs-content.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.ContentProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="tabs-content"
|
||||
class={cn("flex-1 outline-none", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
20
src/lib/components/ui/tabs/tabs-list.svelte
Normal file
20
src/lib/components/ui/tabs/tabs-list.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.ListProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.List
|
||||
bind:ref
|
||||
data-slot="tabs-list"
|
||||
class={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
20
src/lib/components/ui/tabs/tabs-trigger.svelte
Normal file
20
src/lib/components/ui/tabs/tabs-trigger.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Trigger
|
||||
bind:ref
|
||||
data-slot="tabs-trigger"
|
||||
class={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
19
src/lib/components/ui/tabs/tabs.svelte
Normal file
19
src/lib/components/ui/tabs/tabs.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(""),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Root
|
||||
bind:ref
|
||||
bind:value
|
||||
data-slot="tabs"
|
||||
class={cn("flex flex-col gap-2", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
Loading…
Add table
Add a link
Reference in a new issue