feat: Refactor EventEdit and EventFightList components for improved UI and functionality
All checks were successful
SteamWarCI Build successful

- Enhanced EventEdit component with AlertDialog for delete confirmation.
- Added Menubar component to EventFightList for batch editing options.
- Updated alert-dialog components to streamline props and improve reactivity.
- Refactored menubar components for better structure and usability.
- Improved accessibility and code readability across various components.
This commit is contained in:
2025-04-16 12:55:10 +02:00
parent 7757978668
commit 4da8fe50c0
23 changed files with 331 additions and 301 deletions

View File

@ -18,24 +18,35 @@
--> -->
<script lang="ts"> <script lang="ts">
import {Input} from "@components/ui/input"; import { Input } from "@components/ui/input";
import {Label} from "@components/ui/label"; import { Label } from "@components/ui/label";
import {Popover, PopoverContent, PopoverTrigger} from "@components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@components/ui/popover";
import type {SWEvent} from "@type/event.ts" import type { SWEvent } from "@type/event.ts";
import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte"; import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte";
import {fromAbsolute} from "@internationalized/date"; import { fromAbsolute } from "@internationalized/date";
import {Button} from "@components/ui/button"; import { Button, buttonVariants } from "@components/ui/button";
import {ChevronsUpDown} from "lucide-svelte"; import { ChevronsUpDown } from "lucide-svelte";
import {Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList} from "@components/ui/command"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@components/ui/command";
import {schemTypes} from "@stores/stores.ts"; import { schemTypes } from "@stores/stores.ts";
import Check from "lucide-svelte/icons/check"; import Check from "lucide-svelte/icons/check";
import {cn} from "@components/utils.ts"; import { cn } from "@components/utils.ts";
import {Switch} from "@components/ui/switch"; import { Switch } from "@components/ui/switch";
import {eventRepo} from "@repo/event.ts"; import { eventRepo } from "@repo/event.ts";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@components/ui/alert-dialog";
const { event }: { event: SWEvent } = $props(); const { event }: { event: SWEvent } = $props();
let rootEvent: SWEvent = $state(event) let rootEvent: SWEvent = $state(event);
let eventName = $state(rootEvent.name); let eventName = $state(rootEvent.name);
let eventDeadline = $state(fromAbsolute(rootEvent.deadline, "Europe/Berlin")); let eventDeadline = $state(fromAbsolute(rootEvent.deadline, "Europe/Berlin"));
@ -45,13 +56,15 @@
let eventSchematicType = $state(rootEvent.schemType); let eventSchematicType = $state(rootEvent.schemType);
let eventPublicsOnly = $state(rootEvent.publicSchemsOnly); let eventPublicsOnly = $state(rootEvent.publicSchemsOnly);
let dirty = $derived(eventName !== rootEvent.name || let dirty = $derived(
eventDeadline.toDate().getTime() !== rootEvent.deadline || eventName !== rootEvent.name ||
eventStart.toDate().getTime() !== rootEvent.start || eventDeadline.toDate().getTime() !== rootEvent.deadline ||
eventEnd.toDate().getTime() !== rootEvent.end || eventStart.toDate().getTime() !== rootEvent.start ||
eventTeamSize !== rootEvent.maxTeamMembers || eventEnd.toDate().getTime() !== rootEvent.end ||
eventSchematicType !== rootEvent.schemType || eventTeamSize !== rootEvent.maxTeamMembers ||
eventPublicsOnly !== rootEvent.publicSchemsOnly); eventSchematicType !== rootEvent.schemType ||
eventPublicsOnly !== rootEvent.publicSchemsOnly
);
async function updateEvent() { async function updateEvent() {
rootEvent = await $eventRepo.updateEvent(event.id.toString(), { rootEvent = await $eventRepo.updateEvent(event.id.toString(), {
@ -62,7 +75,7 @@
maxTeamMembers: eventTeamSize, maxTeamMembers: eventTeamSize,
schemType: eventSchematicType, schemType: eventSchematicType,
publicSchemsOnly: eventPublicsOnly, publicSchemsOnly: eventPublicsOnly,
}) });
} }
</script> </script>
@ -81,13 +94,8 @@
<Popover> <Popover>
<PopoverTrigger> <PopoverTrigger>
{#snippet child({ props })} {#snippet child({ props })}
<Button <Button variant="outline" class="justify-between" {...props} role="combobox">
variant="outline" {$schemTypes.find((value) => value.db === eventSchematicType)?.name || eventSchematicType || "Select a schematic type..."}
class="justify-between"
{...props}
role="combobox"
>
{$schemTypes.find(value => value.db === eventSchematicType)?.name || eventSchematicType || "Select a schematic type..."}
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" /> <ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
</Button> </Button>
{/snippet} {/snippet}
@ -100,17 +108,12 @@
<CommandGroup> <CommandGroup>
{#each $schemTypes as type} {#each $schemTypes as type}
<CommandItem <CommandItem
value={type.db} value={type.db}
onSelect={() => { onSelect={() => {
eventSchematicType = type.db; eventSchematicType = type.db;
}} }}
> >
<Check <Check class={cn("mr-2 size-4", eventSchematicType !== type.db && "text-transparent")} />
class={cn(
"mr-2 size-4",
eventSchematicType !== type.db && "text-transparent"
)}
/>
{type.name} {type.name}
</CommandItem> </CommandItem>
{/each} {/each}
@ -122,7 +125,19 @@
<Label for="event-publics">Publics Schematics Only</Label> <Label for="event-publics">Publics Schematics Only</Label>
<Switch id="event-publics" bind:checked={eventPublicsOnly} /> <Switch id="event-publics" bind:checked={eventPublicsOnly} />
<div class="flex flex-row justify-end border-t pt-2 gap-4"> <div class="flex flex-row justify-end border-t pt-2 gap-4">
<Button variant="destructive">Delete</Button> <AlertDialog>
<AlertDialogTrigger class={buttonVariants({ variant: "destructive" })}>Delete</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction disabled>Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Button disabled={!dirty} onclick={updateEvent}>Update</Button> <Button disabled={!dirty} onclick={updateEvent}>Update</Button>
</div> </div>
</div> </div>

View File

@ -20,20 +20,11 @@
<script lang="ts"> <script lang="ts">
import type { ExtendedEvent } from "@type/event"; import type { ExtendedEvent } from "@type/event";
import { createSvelteTable, FlexRender } from "@components/ui/data-table"; import { createSvelteTable, FlexRender } from "@components/ui/data-table";
import { import { type ColumnFiltersState, getCoreRowModel, getFilteredRowModel, getGroupedRowModel, getSortedRowModel, type RowSelectionState, type SortingState } from "@tanstack/table-core";
type ColumnFiltersState,
getCoreRowModel,
getFilteredRowModel,
getGroupedRowModel,
getPaginationRowModel,
getSortedRowModel,
type GroupingState,
type RowSelectionState,
type SortingState,
} from "@tanstack/table-core";
import { columns } from "./columns"; import { columns } from "./columns";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@components/ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@components/ui/table";
import { Checkbox } from "@components/ui/checkbox"; import { Checkbox } from "@components/ui/checkbox";
import { Menubar, MenubarContent, MenubarItem, MenubarGroup, MenubarGroupHeading, MenubarMenu, MenubarSeparator, MenubarTrigger } from "@components/ui/menubar";
let { data }: { data: ExtendedEvent } = $props(); let { data }: { data: ExtendedEvent } = $props();
@ -93,6 +84,30 @@
}); });
</script> </script>
<div class="w-fit">
<Menubar>
<MenubarMenu>
<MenubarTrigger>Mehrfach Bearbeiten</MenubarTrigger>
<MenubarContent>
<MenubarItem disabled>Gruppe Ändern</MenubarItem>
<MenubarItem disabled>Startzeit Verschieben</MenubarItem>
<MenubarItem disabled>Spectate Port Ändern</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Erstellen</MenubarTrigger>
<MenubarContent>
<MenubarItem disabled>Fight Erstellen</MenubarItem>
<MenubarGroup>
<MenubarGroupHeading>Generatoren</MenubarGroupHeading>
<MenubarItem disabled>Gruppenphase</MenubarItem>
<MenubarItem disabled>K.O. Phase</MenubarItem>
</MenubarGroup>
</MenubarContent>
</MenubarMenu>
</Menubar>
</div>
<Table> <Table>
<TableHeader> <TableHeader>
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)} {#each table.getHeaderGroups() as headerGroup (headerGroup.id)}

View File

@ -3,19 +3,11 @@
import { buttonVariants } from "$lib/components/ui/button/index.js"; import { buttonVariants } from "$lib/components/ui/button/index.js";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.ActionProps; let {
type $$Events = AlertDialogPrimitive.ActionEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; ...restProps
export { className as class }; }: AlertDialogPrimitive.ActionProps = $props();
</script> </script>
<AlertDialogPrimitive.Action <AlertDialogPrimitive.Action bind:ref class={cn(buttonVariants(), className)} {...restProps} />
class={cn(buttonVariants(), className)}
{...$$restProps}
on:click
on:keydown
let:builder
>
<slot {builder} />
</AlertDialogPrimitive.Action>

View File

@ -3,19 +3,15 @@
import { buttonVariants } from "$lib/components/ui/button/index.js"; import { buttonVariants } from "$lib/components/ui/button/index.js";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.CancelProps; let {
type $$Events = AlertDialogPrimitive.CancelEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; ...restProps
export { className as class }; }: AlertDialogPrimitive.CancelProps = $props();
</script> </script>
<AlertDialogPrimitive.Cancel <AlertDialogPrimitive.Cancel
bind:ref
class={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)} class={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
{...$$restProps} {...restProps}
on:click />
on:keydown
let:builder
>
<slot {builder} />
</AlertDialogPrimitive.Cancel>

View File

@ -1,28 +1,26 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive, type WithoutChild } from "bits-ui";
import * as AlertDialog from "./index.js"; import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
import { cn, flyAndScale } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.ContentProps; let {
ref = $bindable(null),
export let transition: $$Props["transition"] = flyAndScale; class: className,
export let transitionConfig: $$Props["transitionConfig"] = undefined; portalProps,
...restProps
let className: $$Props["class"] = undefined; }: WithoutChild<AlertDialogPrimitive.ContentProps> & {
export { className as class }; portalProps?: AlertDialogPrimitive.PortalProps;
} = $props();
</script> </script>
<AlertDialog.Portal> <AlertDialogPrimitive.Portal {...portalProps}>
<AlertDialog.Overlay /> <AlertDialogOverlay />
<AlertDialogPrimitive.Content <AlertDialogPrimitive.Content
{transition} bind:ref
{transitionConfig}
class={cn( class={cn(
"bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full", "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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
className className
)} )}
{...$$restProps} {...restProps}
> />
<slot /> </AlertDialogPrimitive.Portal>
</AlertDialogPrimitive.Content>
</AlertDialog.Portal>

View File

@ -2,15 +2,15 @@
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.DescriptionProps; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; ...restProps
}: AlertDialogPrimitive.DescriptionProps = $props();
</script> </script>
<AlertDialogPrimitive.Description <AlertDialogPrimitive.Description
bind:ref
class={cn("text-muted-foreground text-sm", className)} class={cn("text-muted-foreground text-sm", className)}
{...$$restProps} {...restProps}
> />
<slot />
</AlertDialogPrimitive.Description>

View File

@ -1,16 +1,20 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from "bits-ui";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div <div
bind:this={ref}
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...$$restProps} {...restProps}
> >
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,13 +1,20 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from "bits-ui";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div class={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...$$restProps}> <div
<slot /> bind:this={ref}
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
{...restProps}
>
{@render children?.()}
</div> </div>

View File

@ -1,21 +1,19 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import { fade } from "svelte/transition";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.OverlayProps; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export let transition: $$Props["transition"] = fade; ...restProps
export let transitionConfig: $$Props["transitionConfig"] = { }: AlertDialogPrimitive.OverlayProps = $props();
duration: 150,
};
export { className as class };
</script> </script>
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
{transition} bind:ref
{transitionConfig} class={cn(
class={cn("bg-background/80 fixed inset-0 z-50 backdrop-blur-sm ", className)} "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/80",
{...$$restProps} className
)}
{...restProps}
/> />

View File

@ -2,13 +2,17 @@
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = AlertDialogPrimitive.TitleProps; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export let level: $$Props["level"] = "h3"; level = 3,
export { className as class }; ...restProps
}: AlertDialogPrimitive.TitleProps = $props();
</script> </script>
<AlertDialogPrimitive.Title class={cn("text-lg font-semibold", className)} {level} {...$$restProps}> <AlertDialogPrimitive.Title
<slot /> bind:ref
</AlertDialogPrimitive.Title> class={cn("text-lg font-semibold", className)}
{level}
{...restProps}
/>

View File

@ -1,9 +1,7 @@
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import Title from "./alert-dialog-title.svelte"; import Title from "./alert-dialog-title.svelte";
import Action from "./alert-dialog-action.svelte"; import Action from "./alert-dialog-action.svelte";
import Cancel from "./alert-dialog-cancel.svelte"; import Cancel from "./alert-dialog-cancel.svelte";
import Portal from "./alert-dialog-portal.svelte";
import Footer from "./alert-dialog-footer.svelte"; import Footer from "./alert-dialog-footer.svelte";
import Header from "./alert-dialog-header.svelte"; import Header from "./alert-dialog-header.svelte";
import Overlay from "./alert-dialog-overlay.svelte"; import Overlay from "./alert-dialog-overlay.svelte";
@ -12,6 +10,7 @@ import Description from "./alert-dialog-description.svelte";
const Root = AlertDialogPrimitive.Root; const Root = AlertDialogPrimitive.Root;
const Trigger = AlertDialogPrimitive.Trigger; const Trigger = AlertDialogPrimitive.Trigger;
const Portal = AlertDialogPrimitive.Portal;
export { export {
Root, Root,

View File

@ -1,10 +1,9 @@
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import Root from "./menubar.svelte"; import Root from "./menubar.svelte";
import CheckboxItem from "./menubar-checkbox-item.svelte"; import CheckboxItem from "./menubar-checkbox-item.svelte";
import Content from "./menubar-content.svelte"; import Content from "./menubar-content.svelte";
import Item from "./menubar-item.svelte"; import Item from "./menubar-item.svelte";
import Label from "./menubar-label.svelte"; import GroupHeading from "./menubar-group-heading.svelte";
import RadioItem from "./menubar-radio-item.svelte"; import RadioItem from "./menubar-radio-item.svelte";
import Separator from "./menubar-separator.svelte"; import Separator from "./menubar-separator.svelte";
import Shortcut from "./menubar-shortcut.svelte"; import Shortcut from "./menubar-shortcut.svelte";
@ -22,7 +21,7 @@ export {
CheckboxItem, CheckboxItem,
Content, Content,
Item, Item,
Label, GroupHeading,
RadioItem, RadioItem,
Separator, Separator,
Shortcut, Shortcut,
@ -38,7 +37,7 @@ export {
CheckboxItem as MenubarCheckboxItem, CheckboxItem as MenubarCheckboxItem,
Content as MenubarContent, Content as MenubarContent,
Item as MenubarItem, Item as MenubarItem,
Label as MenubarLabel, GroupHeading as MenubarGroupHeading,
RadioItem as MenubarRadioItem, RadioItem as MenubarRadioItem,
Separator as MenubarSeparator, Separator as MenubarSeparator,
Shortcut as MenubarShortcut, Shortcut as MenubarShortcut,

View File

@ -1,35 +1,40 @@
<script lang="ts"> <script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive, type WithoutChildrenOrChild } from "bits-ui";
import Check from "lucide-svelte/icons/check"; import Check from "@lucide/svelte/icons/check";
import Minus from "@lucide/svelte/icons/minus";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
import type { Snippet } from "svelte";
type $$Props = MenubarPrimitive.CheckboxItemProps; let {
type $$Events = MenubarPrimitive.CheckboxItemEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; checked = $bindable(false),
export let checked: $$Props["checked"] = false; indeterminate = $bindable(false),
export { className as class }; children: childrenProp,
...restProps
}: WithoutChildrenOrChild<MenubarPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script> </script>
<MenubarPrimitive.CheckboxItem <MenubarPrimitive.CheckboxItem
bind:ref
bind:checked bind:checked
bind:indeterminate
class={cn( class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50", "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50",
className className
)} )}
on:click {...restProps}
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
{...$$restProps}
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked, indeterminate })}
<MenubarPrimitive.CheckboxIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<Check class="h-4 w-4" /> {#if indeterminate}
</MenubarPrimitive.CheckboxIndicator> <Minus class="size-4" />
</span> {:else}
<slot /> <Check class={cn("size-4", !checked && "text-transparent")} />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</MenubarPrimitive.CheckboxItem> </MenubarPrimitive.CheckboxItem>

View File

@ -1,33 +1,32 @@
<script lang="ts"> <script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.ContentProps; let {
type $$Events = MenubarPrimitive.ContentEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; sideOffset = 8,
export let sideOffset: $$Props["sideOffset"] = 8; alignOffset = -4,
export let alignOffset: $$Props["alignOffset"] = -4; align = "start",
export let align: $$Props["align"] = "start"; side = "bottom",
export let side: $$Props["side"] = "bottom"; portalProps,
export let transition: $$Props["transition"] = flyAndScale; ...restProps
export let transitionConfig: $$Props["transitionConfig"] = undefined; }: MenubarPrimitive.ContentProps & {
export { className as class }; portalProps?: MenubarPrimitive.PortalProps;
} = $props();
</script> </script>
<MenubarPrimitive.Content <MenubarPrimitive.Portal {...portalProps}>
{transition} <MenubarPrimitive.Content
{transitionConfig} bind:ref
{sideOffset} {sideOffset}
{align} {align}
{alignOffset} {alignOffset}
{side} {side}
class={cn( class={cn(
"bg-popover text-popover-foreground z-50 min-w-[12rem] rounded-md border p-1 shadow-md focus:outline-none", "bg-popover text-popover-foreground z-50 min-w-[12rem] rounded-md border p-1 shadow-md focus:outline-none",
className className
)} )}
{...$$restProps} {...restProps}
on:keydown />
> </MenubarPrimitive.Portal>
<slot />
</MenubarPrimitive.Content>

View File

@ -0,0 +1,19 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js";
let {
ref = $bindable(null),
class: className,
inset = undefined,
...restProps
}: MenubarPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<MenubarPrimitive.GroupHeading
bind:ref
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...restProps}
/>

View File

@ -2,30 +2,22 @@
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.ItemProps & { let {
ref = $bindable(null),
class: className,
inset = undefined,
...restProps
}: MenubarPrimitive.ItemProps & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = MenubarPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script> </script>
<MenubarPrimitive.Item <MenubarPrimitive.Item
bind:ref
class={cn( class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50", "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className
)} )}
{...$$restProps} {...restProps}
on:click />
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
>
<slot />
</MenubarPrimitive.Item>

View File

@ -1,35 +1,30 @@
<script lang="ts"> <script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive, type WithoutChild } from "bits-ui";
import Circle from "lucide-svelte/icons/circle"; import Circle from "@lucide/svelte/icons/circle";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.RadioItemProps; let {
type $$Events = MenubarPrimitive.RadioItemEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; children: childrenProp,
export let value: $$Props["value"]; ...restProps
export { className as class }; }: WithoutChild<MenubarPrimitive.RadioItemProps> = $props();
</script> </script>
<MenubarPrimitive.RadioItem <MenubarPrimitive.RadioItem
{value} bind:ref
class={cn( class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50", "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground 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-[disabled]:opacity-50",
className className
)} )}
{...$$restProps} {...restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked })}
<MenubarPrimitive.RadioIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<Circle class="h-2 w-2 fill-current" /> {#if checked}
</MenubarPrimitive.RadioIndicator> <Circle class="size-2 fill-current" />
</span> {/if}
<slot /> </span>
{@render childrenProp?.({ checked })}
{/snippet}
</MenubarPrimitive.RadioItem> </MenubarPrimitive.RadioItem>

View File

@ -2,10 +2,15 @@
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.SeparatorProps; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; ...restProps
}: MenubarPrimitive.SeparatorProps = $props();
</script> </script>
<MenubarPrimitive.Separator class={cn("bg-muted -mx-1 my-1 h-px", className)} {...$$restProps} /> <MenubarPrimitive.Separator
bind:ref
class={cn("bg-muted -mx-1 my-1 h-px", className)}
{...restProps}
/>

View File

@ -1,16 +1,20 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
import type { WithElementRef } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = HTMLAttributes<HTMLSpanElement>; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script> </script>
<span <span
bind:this={ref}
class={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)} class={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
{...$$restProps} {...restProps}
> >
<slot /> {@render children?.()}
</span> </span>

View File

@ -1,27 +1,19 @@
<script lang="ts"> <script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.SubContentProps; let {
type $$Events = MenubarPrimitive.SubContentEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; ...restProps
export let transition: $$Props["transition"] = flyAndScale; }: MenubarPrimitive.SubContentProps = $props();
export let transitionConfig: $$Props["transitionConfig"] = { x: -10, y: 0 };
export { className as class };
</script> </script>
<MenubarPrimitive.SubContent <MenubarPrimitive.SubContent
{transition} bind:ref
{transitionConfig}
class={cn( class={cn(
"bg-popover text-popover-foreground z-50 min-w-max rounded-md border p-1 focus:outline-none", "bg-popover text-popover-foreground z-50 min-w-max rounded-md border p-1 focus:outline-none",
className className
)} )}
{...$$restProps} {...restProps}
on:focusout />
on:pointermove
on:keydown
>
<slot />
</MenubarPrimitive.SubContent>

View File

@ -1,32 +1,28 @@
<script lang="ts"> <script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive, type WithoutChild } from "bits-ui";
import ChevronRight from "lucide-svelte/icons/chevron-right"; import ChevronRight from "@lucide/svelte/icons/chevron-right";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.SubTriggerProps & { let {
ref = $bindable(null),
class: className,
inset = undefined,
children,
...restProps
}: WithoutChild<MenubarPrimitive.SubTriggerProps> & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = MenubarPrimitive.SubTriggerEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script> </script>
<MenubarPrimitive.SubTrigger <MenubarPrimitive.SubTrigger
bind:ref
class={cn( class={cn(
"data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className
)} )}
on:click {...restProps}
{...$$restProps}
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
> >
<slot /> {@render children?.()}
<ChevronRight class="ml-auto h-4 w-4" /> <ChevronRight class="ml-auto size-4" />
</MenubarPrimitive.SubTrigger> </MenubarPrimitive.SubTrigger>

View File

@ -2,22 +2,18 @@
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.TriggerProps; let {
type $$Events = MenubarPrimitive.TriggerEvents; ref = $bindable(null),
class: className,
let className: $$Props["class"] = undefined; ...restProps
export { className as class }; }: MenubarPrimitive.TriggerProps = $props();
</script> </script>
<MenubarPrimitive.Trigger <MenubarPrimitive.Trigger
bind:ref
class={cn( class={cn(
"data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none", "data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none",
className className
)} )}
on:click {...restProps}
on:keydown />
on:pointerenter
{...$$restProps}
>
<slot />
</MenubarPrimitive.Trigger>

View File

@ -2,15 +2,15 @@
import { Menubar as MenubarPrimitive } from "bits-ui"; import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/components/utils.js"; import { cn } from "$lib/components/utils.js";
type $$Props = MenubarPrimitive.Props; let {
ref = $bindable(null),
let className: $$Props["class"] = undefined; class: className,
export { className as class }; ...restProps
}: MenubarPrimitive.RootProps = $props();
</script> </script>
<MenubarPrimitive.Root <MenubarPrimitive.Root
bind:ref
class={cn("bg-background flex h-10 items-center space-x-1 rounded-md border p-1", className)} class={cn("bg-background flex h-10 items-center space-x-1 rounded-md border p-1", className)}
{...$$restProps} {...restProps}
> />
<slot />
</MenubarPrimitive.Root>