[READ-ONLY] Mirror of https://github.com/flo-bit/ui-kit. 🦊 fox ui, svelte 5 and tailwind 4 flo-bit.dev/ui-kit/
svelte tailwindcss ui-components
0

Configure Feed

Select the types of activity you want to include in your feed.

add sheet and radio group

Florian (Apr 17, 2026, 12:44 AM +0200) 860223c1 97290004

+710
+5
.changeset/fiery-balloons-look.md
··· 1 + --- 2 + '@foxui/core': patch 3 + --- 4 + 5 + add sheet, add radio group
+23
apps/docs/src/lib/docs/core/radio-group/Card.svelte
··· 1 + <script> 2 + const options = [ 3 + { label: 'Default', checked: false }, 4 + { label: 'Comfortable', checked: true }, 5 + { label: 'Compact', checked: false } 6 + ]; 7 + </script> 8 + 9 + <div class="flex w-full flex-col gap-2"> 10 + {#each options as option (option.label)} 11 + <div class="flex items-center gap-2"> 12 + <div 13 + class="border-accent-500/40 bg-accent-100/60 dark:border-accent-500/20 dark:bg-accent-950/20 data-[state=checked]:border-accent-500/60 data-[state=checked]:bg-accent-200/60 dark:data-[state=checked]:border-accent-500/30 dark:data-[state=checked]:bg-accent-900/40 flex size-5 items-center justify-center rounded-full border" 14 + data-state={option.checked ? 'checked' : 'unchecked'} 15 + > 16 + {#if option.checked} 17 + <div class="bg-accent-600 dark:bg-accent-400 size-2 rounded-full"></div> 18 + {/if} 19 + </div> 20 + <span class="text-base-900 dark:text-base-50 text-sm font-medium">{option.label}</span> 21 + </div> 22 + {/each} 23 + </div>
+65
apps/docs/src/lib/docs/core/radio-group/Documentation.md
··· 1 + ## Usage 2 + 3 + A set of radio buttons where exactly one can be selected at a time. 4 + 5 + The simplest way is to pass an `options` array — items, labels, and spacing are handled for you: 6 + 7 + ```svelte 8 + <script lang="ts"> 9 + import { RadioGroup } from '@foxui/all'; 10 + 11 + let value = $state('comfortable'); 12 + </script> 13 + 14 + <RadioGroup 15 + bind:value 16 + options={[ 17 + { value: 'default', label: 'Default' }, 18 + { value: 'comfortable', label: 'Comfortable' }, 19 + { value: 'compact', label: 'Compact' } 20 + ]} 21 + /> 22 + ``` 23 + 24 + ## With descriptions 25 + 26 + Each option can carry an optional `description`: 27 + 28 + ```svelte 29 + <RadioGroup 30 + bind:value 31 + options={[ 32 + { value: 'card', label: 'Card', description: 'Pay with a credit or debit card.' }, 33 + { value: 'paypal', label: 'PayPal', description: 'Redirect to PayPal to complete payment.' } 34 + ]} 35 + /> 36 + ``` 37 + 38 + ## Horizontal layout 39 + 40 + ```svelte 41 + <RadioGroup bind:value orientation="horizontal" options={[...]} /> 42 + ``` 43 + 44 + ## Custom rendering 45 + 46 + For full control, skip `options` and compose with `RadioGroupItem`: 47 + 48 + ```svelte 49 + <script lang="ts"> 50 + import { RadioGroup, RadioGroupItem, Label } from '@foxui/all'; 51 + 52 + let value = $state('option-one'); 53 + </script> 54 + 55 + <RadioGroup bind:value> 56 + <label class="flex items-center gap-2"> 57 + <RadioGroupItem value="option-one" id="option-one" /> 58 + <Label for="option-one">Option One</Label> 59 + </label> 60 + <label class="flex items-center gap-2"> 61 + <RadioGroupItem value="option-two" id="option-two" /> 62 + <Label for="option-two">Option Two</Label> 63 + </label> 64 + </RadioGroup> 65 + ```
+47
apps/docs/src/lib/docs/core/radio-group/Example.svelte
··· 1 + <script lang="ts"> 2 + import { RadioGroup, Paragraph, Subheading } from '@foxui/all'; 3 + 4 + let value = $state('comfortable'); 5 + let plan = $state('pro'); 6 + </script> 7 + 8 + <div class="not-prose flex flex-col gap-4"> 9 + <RadioGroup 10 + bind:value 11 + options={[ 12 + { value: 'default', label: 'Default' }, 13 + { value: 'comfortable', label: 'Comfortable' }, 14 + { value: 'compact', label: 'Compact', disabled: true } 15 + ]} 16 + /> 17 + </div> 18 + 19 + <Paragraph>Selected: <code>{value}</code></Paragraph> 20 + 21 + <Subheading>With descriptions</Subheading> 22 + 23 + <div class="not-prose flex flex-col gap-4"> 24 + <RadioGroup 25 + bind:value={plan} 26 + options={[ 27 + { 28 + value: 'hobby', 29 + label: 'Hobby', 30 + description: 'Free forever. Great for small side projects.' 31 + }, 32 + { 33 + value: 'pro', 34 + label: 'Pro', 35 + description: '$12/month. Unlimited projects and team seats.' 36 + }, 37 + { 38 + value: 'enterprise', 39 + label: 'Enterprise', 40 + description: 'Custom pricing with SSO and priority support.', 41 + disabled: true 42 + } 43 + ]} 44 + /> 45 + </div> 46 + 47 + <Paragraph>Plan: <code>{plan}</code></Paragraph>
+86
apps/docs/src/lib/docs/core/radio-group/api.ts
··· 1 + import type { APISchema } from '$lib/types/schema'; 2 + 3 + export default [ 4 + { 5 + title: 'RadioGroup', 6 + description: 7 + 'A set of radio buttons where exactly one can be selected. Pass an `options` array for the common case, or use `RadioGroupItem` as children for custom layouts.', 8 + props: { 9 + value: { 10 + type: 'string', 11 + description: 'The currently selected value.', 12 + default: "''", 13 + bindable: true 14 + }, 15 + options: { 16 + type: 'RadioGroupOption[]', 17 + description: 18 + 'Array of options rendered as radio items with labels. Each option is `{ value, label?, description?, disabled? }`. Omit to compose children manually.' 19 + }, 20 + variant: { 21 + type: { type: 'enum', definition: "'primary' | 'secondary'" }, 22 + description: 'Color variant applied to every item.', 23 + default: "'primary'" 24 + }, 25 + sizeVariant: { 26 + type: { type: 'enum', definition: "'default' | 'sm' | 'lg'" }, 27 + description: 'Size applied to every item.', 28 + default: "'default'" 29 + }, 30 + orientation: { 31 + type: { type: 'enum', definition: "'vertical' | 'horizontal'" }, 32 + description: 'Layout direction when rendering the options array.', 33 + default: "'vertical'" 34 + }, 35 + disabled: { 36 + type: 'boolean', 37 + description: 'Disable the entire group.' 38 + }, 39 + onValueChange: { 40 + type: { type: 'function', definition: '(value: string) => void' }, 41 + description: 'Callback when the selected value changes.' 42 + }, 43 + children: { 44 + type: 'Snippet', 45 + description: 'Custom content (e.g. `RadioGroupItem`s). Ignored if `options` is provided.' 46 + }, 47 + class: { 48 + type: 'string', 49 + description: 'Additional CSS classes on the group container.' 50 + } 51 + } 52 + }, 53 + { 54 + title: 'RadioGroupItem', 55 + description: 'A single radio item. Use when composing a custom layout.', 56 + props: { 57 + value: { 58 + type: 'string', 59 + description: 'The value represented by this item.', 60 + required: true 61 + }, 62 + variant: { 63 + type: { type: 'enum', definition: "'primary' | 'secondary'" }, 64 + description: 'Color variant.', 65 + default: "'primary'" 66 + }, 67 + sizeVariant: { 68 + type: { type: 'enum', definition: "'default' | 'sm' | 'lg'" }, 69 + description: 'Size of the radio circle.', 70 + default: "'default'" 71 + }, 72 + disabled: { 73 + type: 'boolean', 74 + description: 'Disable this item.' 75 + }, 76 + id: { 77 + type: 'string', 78 + description: 'HTML id used to associate an external `<Label for={id}>`.' 79 + }, 80 + class: { 81 + type: 'string', 82 + description: 'Additional CSS classes.' 83 + } 84 + } 85 + } 86 + ] satisfies APISchema[];
+21
apps/docs/src/lib/docs/core/radio-group/index.ts
··· 1 + import Docs from './Documentation.md'; 2 + import Example from './Example.svelte'; 3 + import Card from './Card.svelte'; 4 + import api from './api'; 5 + 6 + export default { 7 + slug: 'radio-group', 8 + title: 'Radio Group', 9 + docs: Docs, 10 + example: Example, 11 + card: Card, 12 + api, 13 + sources: [ 14 + { 15 + href: 'https://bits-ui.com/docs/components/radio-group', 16 + label: 'bits-ui radio-group', 17 + package: 'bits-ui', 18 + component: 'RadioGroup' 19 + } 20 + ] 21 + };
+21
apps/docs/src/lib/docs/core/sheet/Card.svelte
··· 1 + <script> 2 + import { Button } from '@foxui/all'; 3 + import { X } from '@foxui/all/icons'; 4 + </script> 5 + 6 + <div class="absolute inset-0 h-full w-full"> 7 + <div 8 + class="bg-base-100 dark:bg-base-900 border-base-200 dark:border-base-800 absolute inset-y-0 right-0 flex h-full w-28 flex-col border-l p-3 shadow-lg" 9 + > 10 + <div class="text-base-900 dark:text-base-50 mt-6 text-sm font-semibold">Are you sure</div> 11 + 12 + <div class="text-base-900 dark:text-base-500 absolute top-2 right-2 p-1"> 13 + <X size={12} strokeWidth={2} /> 14 + </div> 15 + 16 + <div class="mt-8 flex flex-col gap-1.5"> 17 + <Button variant="primary" size="sm" tabindex={-1}>Save</Button> 18 + <Button variant="secondary" size="sm" tabindex={-1}>Cancel</Button> 19 + </div> 20 + </div> 21 + </div>
+37
apps/docs/src/lib/docs/core/sheet/Documentation.md
··· 1 + ## Usage 2 + 3 + A `Sheet` slides in from any edge of the screen — useful for forms, filters, or navigation that complements the main content. 4 + 5 + The API mirrors `Modal`: one component, `bind:open`, plus optional `title`, `description`, and a `footer` snippet for action buttons. 6 + 7 + ```svelte 8 + <Button onclick={() => (open = true)} variant="secondary">Open</Button> 9 + 10 + <Sheet 11 + bind:open 12 + side="right" 13 + title="Edit profile" 14 + description="Make changes to your profile here." 15 + > 16 + <!-- main content --> 17 + 18 + {#snippet footer()} 19 + <Button variant="secondary" onclick={() => (open = false)}>Cancel</Button> 20 + <Button onclick={() => (open = false)}>Save changes</Button> 21 + {/snippet} 22 + </Sheet> 23 + ``` 24 + 25 + ## Side 26 + 27 + Use the `side` prop to pick which edge the sheet slides in from. Values: `top`, `right` (default), `bottom`, `left`. 28 + 29 + ## Width / height 30 + 31 + Override the default size with the `class` prop: 32 + 33 + ```svelte 34 + <Sheet bind:open class="sm:max-w-lg"> 35 + <!-- wider sheet on larger screens --> 36 + </Sheet> 37 + ```
+45
apps/docs/src/lib/docs/core/sheet/Example.svelte
··· 1 + <script lang="ts"> 2 + import { Button, Sheet, Input, Label } from '@foxui/all'; 3 + 4 + let open = $state(false); 5 + let side: 'top' | 'right' | 'bottom' | 'left' = $state('right'); 6 + 7 + const sides = ['top', 'right', 'bottom', 'left'] as const; 8 + </script> 9 + 10 + <div class="flex flex-wrap gap-2"> 11 + {#each sides as s (s)} 12 + <Button 13 + variant="secondary" 14 + onclick={() => { 15 + side = s; 16 + open = true; 17 + }} 18 + > 19 + Open {s} 20 + </Button> 21 + {/each} 22 + </div> 23 + 24 + <Sheet 25 + bind:open 26 + {side} 27 + title="Edit profile" 28 + description="Make changes to your profile here. Click save when you're done." 29 + > 30 + <div class="flex flex-col gap-4 py-2"> 31 + <div class="flex flex-col gap-2"> 32 + <Label for="sheet-name">Name</Label> 33 + <Input id="sheet-name" value="Pedro Duarte" /> 34 + </div> 35 + <div class="flex flex-col gap-2"> 36 + <Label for="sheet-username">Username</Label> 37 + <Input id="sheet-username" value="@peduarte" /> 38 + </div> 39 + </div> 40 + 41 + {#snippet footer()} 42 + <Button variant="secondary" onclick={() => (open = false)}>Cancel</Button> 43 + <Button onclick={() => (open = false)}>Save changes</Button> 44 + {/snippet} 45 + </Sheet>
+70
apps/docs/src/lib/docs/core/sheet/api.ts
··· 1 + import type { APISchema } from '$lib/types/schema'; 2 + 3 + export default [ 4 + { 5 + title: 'Sheet', 6 + description: 7 + 'A dialog that slides in from the edge of the screen. Built on top of the bits-ui Dialog primitive.', 8 + props: { 9 + open: { 10 + type: 'boolean', 11 + description: 'Whether the sheet is open.', 12 + default: 'false', 13 + bindable: true 14 + }, 15 + side: { 16 + type: { type: 'enum', definition: "'top' | 'right' | 'bottom' | 'left'" }, 17 + description: 'Which edge of the screen the sheet slides in from.', 18 + default: "'right'" 19 + }, 20 + title: { 21 + type: 'string', 22 + description: 'Optional title rendered in the sheet header.' 23 + }, 24 + description: { 25 + type: 'string', 26 + description: 'Optional description rendered in the sheet header.' 27 + }, 28 + children: { 29 + type: 'Snippet', 30 + description: 'The main content of the sheet.', 31 + required: true 32 + }, 33 + footer: { 34 + type: 'Snippet', 35 + description: 'Optional footer snippet, typically containing action buttons.' 36 + }, 37 + closeButton: { 38 + type: 'boolean', 39 + description: 'Whether to show the close button in the top-right corner.', 40 + default: 'true' 41 + }, 42 + interactOutsideBehavior: { 43 + type: { type: 'enum', definition: "'close' | 'ignore'" }, 44 + description: 'How the sheet responds to clicks outside its content.', 45 + default: "'close'" 46 + }, 47 + onOpenAutoFocus: { 48 + type: { type: 'function', definition: '(event: Event) => void' }, 49 + description: 50 + 'Callback when focus is moved into the sheet on open. Call event.preventDefault() to prevent default focus behavior.' 51 + }, 52 + contentProps: { 53 + type: 'Dialog.ContentProps', 54 + description: 'Additional props to pass to the underlying Dialog.Content component.' 55 + }, 56 + onOpenChange: { 57 + type: { type: 'function', definition: '(open: boolean) => void' }, 58 + description: 'A callback invoked when the open state changes.' 59 + }, 60 + onOpenChangeComplete: { 61 + type: { type: 'function', definition: '(open: boolean) => void' }, 62 + description: 'A callback invoked after the open/close transition completes.' 63 + }, 64 + class: { 65 + type: 'string', 66 + description: 'Additional CSS classes to apply to the sheet content.' 67 + } 68 + } 69 + } 70 + ] satisfies APISchema[];
+21
apps/docs/src/lib/docs/core/sheet/index.ts
··· 1 + import Docs from './Documentation.md'; 2 + import Example from './Example.svelte'; 3 + import Card from './Card.svelte'; 4 + import api from './api'; 5 + 6 + export default { 7 + slug: 'sheet', 8 + title: 'Sheet', 9 + docs: Docs, 10 + example: Example, 11 + card: Card, 12 + api, 13 + sources: [ 14 + { 15 + href: 'https://bits-ui.com/docs/components/dialog', 16 + label: 'bits-ui dialog', 17 + package: 'bits-ui', 18 + component: 'Dialog' 19 + } 20 + ] 21 + };
+2
packages/core/src/lib/components/index.ts
··· 20 20 export * from './paragraph'; 21 21 export * from './popover'; 22 22 export * from './prose'; 23 + export * from './radio-group'; 23 24 export * from './scroll-area'; 24 25 export * from './select'; 26 + export * from './sheet'; 25 27 export * from './sidebar'; 26 28 export * from './slider'; 27 29 export * from './sonner';
+12
packages/core/src/lib/components/radio-group/index.ts
··· 1 + export { default as RadioGroup } from './radio-group.svelte'; 2 + export { default as RadioGroupItem } from './radio-group-item.svelte'; 3 + export type { 4 + RadioGroupProps, 5 + RadioGroupOption 6 + } from './radio-group.svelte'; 7 + export type { 8 + RadioGroupItemProps, 9 + RadioGroupItemVariant, 10 + RadioGroupItemSize 11 + } from './radio-group-item.svelte'; 12 + export { radioGroupItemVariants } from './radio-group-item.svelte';
+65
packages/core/src/lib/components/radio-group/radio-group-item.svelte
··· 1 + <script lang="ts" module> 2 + import { RadioGroup as RadioGroupPrimitive, type WithoutChildrenOrChild } from 'bits-ui'; 3 + import { type VariantProps, tv } from 'tailwind-variants'; 4 + import { cn } from '../../utils'; 5 + 6 + export const radioGroupItemVariants = tv({ 7 + base: 'peer cursor-pointer box-content relative shrink-0 inline-flex items-center justify-center rounded-full border transition-colors duration-100 outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:opacity-40 data-disabled:grayscale', 8 + variants: { 9 + variant: { 10 + primary: 11 + 'border-accent-500/40 bg-accent-100/60 focus-visible:outline-accent-500 data-[state=checked]:border-accent-500/60 data-[state=checked]:bg-accent-200/60 dark:border-accent-500/20 dark:bg-accent-950/20 dark:data-[state=checked]:border-accent-500/30 dark:data-[state=checked]:bg-accent-900/40', 12 + secondary: 13 + 'border-base-300 bg-base-100/80 focus-visible:outline-base-900 dark:focus-visible:outline-base-100 data-[state=checked]:border-base-400 data-[state=checked]:bg-base-200/80 dark:border-base-600/40 dark:bg-base-950/40 dark:data-[state=checked]:border-base-500/80 dark:data-[state=checked]:bg-base-500/30' 14 + }, 15 + sizeVariant: { 16 + default: 'size-5', 17 + sm: 'size-4', 18 + lg: 'size-6' 19 + } 20 + }, 21 + defaultVariants: { 22 + variant: 'primary', 23 + sizeVariant: 'default' 24 + } 25 + }); 26 + 27 + export type RadioGroupItemVariant = VariantProps<typeof radioGroupItemVariants>['variant']; 28 + export type RadioGroupItemSize = VariantProps<typeof radioGroupItemVariants>['sizeVariant']; 29 + 30 + export type RadioGroupItemProps = WithoutChildrenOrChild<RadioGroupPrimitive.ItemProps> & { 31 + variant?: RadioGroupItemVariant; 32 + sizeVariant?: RadioGroupItemSize; 33 + }; 34 + </script> 35 + 36 + <script lang="ts"> 37 + let { 38 + ref = $bindable(null), 39 + class: className, 40 + variant = 'primary', 41 + sizeVariant = 'default', 42 + ...restProps 43 + }: RadioGroupItemProps = $props(); 44 + </script> 45 + 46 + <RadioGroupPrimitive.Item 47 + bind:ref 48 + class={cn(radioGroupItemVariants({ variant, sizeVariant }), className)} 49 + {...restProps} 50 + > 51 + {#snippet children({ checked })} 52 + {#if checked} 53 + {@const dotSize = sizeVariant === 'sm' ? 'size-1.5' : sizeVariant === 'lg' ? 'size-2.5' : 'size-2'} 54 + <div 55 + class={cn( 56 + 'rounded-full', 57 + dotSize, 58 + variant === 'primary' 59 + ? 'bg-accent-600 dark:bg-accent-400' 60 + : 'bg-base-900 dark:bg-base-100' 61 + )} 62 + ></div> 63 + {/if} 64 + {/snippet} 65 + </RadioGroupPrimitive.Item>
+88
packages/core/src/lib/components/radio-group/radio-group.svelte
··· 1 + <script lang="ts" module> 2 + import { RadioGroup as RadioGroupPrimitive, type WithoutChild } from 'bits-ui'; 3 + import type { Snippet } from 'svelte'; 4 + import { cn } from '../../utils'; 5 + import RadioGroupItem, { 6 + type RadioGroupItemVariant, 7 + type RadioGroupItemSize 8 + } from './radio-group-item.svelte'; 9 + import { Label } from '../label'; 10 + 11 + export type RadioGroupOption = { 12 + value: string; 13 + label?: string; 14 + description?: string; 15 + disabled?: boolean; 16 + }; 17 + 18 + export type RadioGroupProps = WithoutChild<RadioGroupPrimitive.RootProps> & { 19 + options?: RadioGroupOption[]; 20 + variant?: RadioGroupItemVariant; 21 + sizeVariant?: RadioGroupItemSize; 22 + orientation?: 'vertical' | 'horizontal'; 23 + children?: Snippet; 24 + class?: string; 25 + }; 26 + </script> 27 + 28 + <script lang="ts"> 29 + let { 30 + ref = $bindable(null), 31 + value = $bindable(''), 32 + options, 33 + variant = 'primary', 34 + sizeVariant = 'default', 35 + orientation = 'vertical', 36 + class: className, 37 + children, 38 + ...restProps 39 + }: RadioGroupProps = $props(); 40 + 41 + let uid = $props.id(); 42 + </script> 43 + 44 + <RadioGroupPrimitive.Root 45 + bind:ref 46 + bind:value 47 + class={cn( 48 + 'flex gap-3', 49 + orientation === 'vertical' ? 'flex-col' : 'flex-row flex-wrap', 50 + className 51 + )} 52 + {...restProps} 53 + > 54 + {#if options} 55 + {#each options as option (option.value)} 56 + {@const id = `${uid}-${option.value}`} 57 + <div class={cn('flex gap-3', option.description ? 'items-start' : 'items-center')}> 58 + <RadioGroupItem 59 + {id} 60 + value={option.value} 61 + disabled={option.disabled} 62 + {variant} 63 + {sizeVariant} 64 + class={option.description ? 'mt-0.5' : ''} 65 + /> 66 + {#if option.label || option.description} 67 + <div 68 + class={cn( 69 + 'flex flex-col gap-0.5', 70 + option.disabled && 'pointer-events-none opacity-40' 71 + )} 72 + > 73 + {#if option.label} 74 + <Label for={id} class="cursor-pointer leading-none">{option.label}</Label> 75 + {/if} 76 + {#if option.description} 77 + <span class="text-base-600 dark:text-base-400 text-xs leading-snug"> 78 + {option.description} 79 + </span> 80 + {/if} 81 + </div> 82 + {/if} 83 + </div> 84 + {/each} 85 + {:else} 86 + {@render children?.()} 87 + {/if} 88 + </RadioGroupPrimitive.Root>
+100
packages/core/src/lib/components/sheet/Sheet.svelte
··· 1 + <script lang="ts" module> 2 + import { Dialog, type WithoutChild } from 'bits-ui'; 3 + import type { Snippet } from 'svelte'; 4 + import { X } from '@jis3r/icons'; 5 + import { cn } from '../../utils.js'; 6 + 7 + export type SheetSide = 'top' | 'right' | 'bottom' | 'left'; 8 + 9 + export type SheetProps = Dialog.RootProps & { 10 + side?: SheetSide; 11 + title?: string; 12 + description?: string; 13 + footer?: Snippet; 14 + interactOutsideBehavior?: 'close' | 'ignore'; 15 + closeButton?: boolean; 16 + contentProps?: WithoutChild<Dialog.ContentProps>; 17 + 18 + class?: string; 19 + 20 + onOpenAutoFocus?: (event: Event) => void; 21 + }; 22 + </script> 23 + 24 + <script lang="ts"> 25 + let { 26 + open = $bindable(false), 27 + side = 'right', 28 + title, 29 + description, 30 + footer, 31 + children, 32 + contentProps, 33 + interactOutsideBehavior = 'close', 34 + closeButton = true, 35 + class: className, 36 + onOpenAutoFocus, 37 + ...restProps 38 + }: SheetProps = $props(); 39 + </script> 40 + 41 + <Dialog.Root bind:open {...restProps}> 42 + <Dialog.Portal> 43 + <Dialog.Overlay 44 + class="motion-safe:data-[state=open]:animate-in motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-base-200/10 dark:bg-base-900/10 fixed inset-0 z-50 backdrop-blur-sm" 45 + /> 46 + <Dialog.Content 47 + {onOpenAutoFocus} 48 + {interactOutsideBehavior} 49 + {...contentProps} 50 + data-side={side} 51 + class={cn( 52 + 'motion-safe:data-[state=open]:animate-in motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 53 + 'border-base-200 bg-base-100 dark:border-base-700 dark:bg-base-800 fixed z-50 flex flex-col gap-4 shadow-lg backdrop-blur-xl', 54 + 'data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:sm:max-w-sm', 55 + 'data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:sm:max-w-sm', 56 + 'data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:border-b', 57 + 'data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:border-t', 58 + 'data-[side=right]:data-[state=open]:slide-in-from-right data-[side=right]:data-[state=closed]:slide-out-to-right', 59 + 'data-[side=left]:data-[state=open]:slide-in-from-left data-[side=left]:data-[state=closed]:slide-out-to-left', 60 + 'data-[side=top]:data-[state=open]:slide-in-from-top data-[side=top]:data-[state=closed]:slide-out-to-top', 61 + 'data-[side=bottom]:data-[state=open]:slide-in-from-bottom data-[side=bottom]:data-[state=closed]:slide-out-to-bottom', 62 + className 63 + )} 64 + > 65 + {#if title || description} 66 + <div class="flex flex-col gap-1 p-6 pb-2"> 67 + {#if title} 68 + <Dialog.Title class="text-base-900 dark:text-base-100 text-lg font-semibold"> 69 + {title} 70 + </Dialog.Title> 71 + {/if} 72 + {#if description} 73 + <Dialog.Description class="text-base-600 dark:text-base-400 text-sm"> 74 + {description} 75 + </Dialog.Description> 76 + {/if} 77 + </div> 78 + {/if} 79 + 80 + <div class="flex-1 overflow-y-auto px-6 {title || description ? '' : 'pt-6'}"> 81 + {@render children?.()} 82 + </div> 83 + 84 + {#if footer} 85 + <div class="flex flex-col-reverse gap-2 p-6 pt-2 sm:flex-row sm:justify-end"> 86 + {@render footer()} 87 + </div> 88 + {/if} 89 + 90 + {#if closeButton} 91 + <Dialog.Close 92 + class="text-base-900 dark:text-base-500 hover:text-base-800 dark:hover:text-base-200 hover:bg-base-200 dark:hover:bg-base-800 focus:outline-base-900 dark:focus:outline-base-50 focus:bg-base-200 dark:focus:bg-base-800 focus:text-base-800 dark:focus:text-base-200 rounded-ui-sm absolute top-3 right-3 cursor-pointer p-1 transition-colors focus:outline-2 focus:outline-offset-2" 93 + > 94 + <X size={16} /> 95 + <span class="sr-only">Close</span> 96 + </Dialog.Close> 97 + {/if} 98 + </Dialog.Content> 99 + </Dialog.Portal> 100 + </Dialog.Root>
+2
packages/core/src/lib/components/sheet/index.ts
··· 1 + export { default as Sheet } from './Sheet.svelte'; 2 + export type { SheetProps, SheetSide } from './Sheet.svelte';