mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-29 12:09:05 +01:00
feat(frontend): restyle ui primitives for v3
This commit is contained in:
@@ -37,12 +37,7 @@
|
||||
<b>{@render children?.()}</b>
|
||||
{/snippet}
|
||||
{#snippet link({ children, options })}
|
||||
<a
|
||||
class="text-black underline dark:text-white"
|
||||
href={options.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<a class="inline-link" href={options.href} target="_blank" rel="noopener noreferrer">
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
<AlertDialogPrimitive.Action
|
||||
bind:ref
|
||||
data-slot="alert-dialog-action"
|
||||
class={cn(buttonVariants({ variant, size }), 'cn-alert-dialog-action', className)}
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
variant = 'outline',
|
||||
variant = 'ghost',
|
||||
size = 'default',
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.CancelProps & {
|
||||
@@ -22,6 +22,6 @@
|
||||
<AlertDialogPrimitive.Cancel
|
||||
bind:ref
|
||||
data-slot="alert-dialog-cancel"
|
||||
class={cn(buttonVariants({ variant, size }), 'cn-alert-dialog-cancel', className)}
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
data-slot="alert-dialog-content"
|
||||
data-size={size}
|
||||
class={cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-4xl p-6 shadow-xl ring-1 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md',
|
||||
'group/alert-dialog-content bg-popover text-popover-foreground border-border shadow-overlay transition-motion fixed top-1/2 left-1/2 z-(--z-overlay) grid max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-xl border px-6 py-5 duration-(--dur-slow) outline-none data-closed:opacity-0 data-closed:duration-(--dur-base) data-closed:ease-in sm:data-[size=default]:max-w-(--dialog-sm) sm:data-[size=sm]:max-w-xs starting:scale-(--pop-scale) starting:opacity-0',
|
||||
'max-sm:before:bg-border-strong max-sm:inset-x-0 max-sm:top-auto max-sm:bottom-0 max-sm:w-full max-sm:translate-0 max-sm:rounded-t-lg max-sm:rounded-b-none max-sm:pt-6 max-sm:pb-[calc(20px+env(safe-area-inset-bottom))] max-sm:before:absolute max-sm:before:top-2 max-sm:before:left-1/2 max-sm:before:h-1 max-sm:before:w-9 max-sm:before:-translate-x-1/2 max-sm:before:rounded-[2px] max-sm:data-closed:translate-y-full max-sm:data-closed:opacity-100 max-sm:starting:translate-y-full max-sm:starting:scale-100 max-sm:starting:opacity-100',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
<AlertDialogPrimitive.Description
|
||||
bind:ref
|
||||
data-slot="alert-dialog-description"
|
||||
class={cn(
|
||||
'text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3',
|
||||
className
|
||||
)}
|
||||
class={cn('text-control text-fg-muted *:[a]:inline-link wrap-anywhere', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-footer"
|
||||
class={cn(
|
||||
'cn-alert-dialog-footer flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
|
||||
'mt-1 flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-header"
|
||||
class={cn(
|
||||
'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
|
||||
className
|
||||
)}
|
||||
class={cn('flex flex-col items-start gap-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-media"
|
||||
class={cn(
|
||||
"bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
|
||||
"bg-fill-muted mb-2 inline-flex size-12 items-center justify-center rounded-full *:[svg:not([class*='size-'])]:size-6",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
bind:ref
|
||||
data-slot="alert-dialog-overlay"
|
||||
class={cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/30 duration-100 supports-backdrop-filter:backdrop-blur-sm',
|
||||
'bg-scrim transition-motion fixed inset-0 isolate z-(--z-overlay) backdrop-blur-[4px] duration-(--dur-slow) data-closed:opacity-0 data-closed:duration-(--dur-base) data-closed:ease-in starting:opacity-0 [@media(prefers-reduced-transparency:reduce)]:backdrop-filter-none!',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
<AlertDialogPrimitive.Title
|
||||
bind:ref
|
||||
data-slot="alert-dialog-title"
|
||||
class={cn(
|
||||
'text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
|
||||
className
|
||||
)}
|
||||
class={cn('font-display text-display-s text-fg wrap-anywhere', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-action"
|
||||
class={cn('absolute top-2.5 right-3', className)}
|
||||
class={cn('absolute top-2 right-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="alert-description"
|
||||
class={cn(
|
||||
'text-muted-foreground [&_a]:hover:text-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4',
|
||||
'text-fg [&_a]:inline-link text-pretty group-has-[>svg]/alert:col-start-2 [&_p:not(:last-child)]:mb-2',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="alert-title"
|
||||
class={cn(
|
||||
'[&_a]:hover:text-foreground font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3',
|
||||
'[&_a]:inline-link font-medium text-(--tone-fg) group-has-[>svg]/alert:col-start-2',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const alertVariants = tv({
|
||||
base: "grid gap-0.5 rounded-2xl border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full",
|
||||
base: 'group/alert relative grid w-full gap-0.5 rounded-sm border border-(--tone-border) bg-(--tone-soft) px-3 py-2.5 text-left text-control text-fg has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:size-3.5 *:[svg]:translate-y-0.75 *:[svg]:text-(--tone-solid)',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-card text-card-foreground',
|
||||
destructive:
|
||||
'bg-red-200 text-red-900 dark:bg-red-900 dark:text-red-100 *:[svg]:text-current',
|
||||
info: '[--tone-border:var(--info-border)] [--tone-fg:var(--info-fg)] [--tone-soft:var(--info-soft)] [--tone-solid:var(--info-solid)]',
|
||||
success:
|
||||
'bg-green-100 text-green-900 dark:bg-green-900 dark:text-green-100 *:[svg]:text-current',
|
||||
info: 'bg-blue-100 text-blue-900 dark:bg-blue-900 dark:text-blue-100 *:[svg]:text-current',
|
||||
warning: 'bg-warning text-warning-foreground border-warning/40 *:[svg]:text-current'
|
||||
'[--tone-border:var(--success-border)] [--tone-fg:var(--success-fg)] [--tone-soft:var(--success-soft)] [--tone-solid:var(--success-solid)]',
|
||||
warning:
|
||||
'[--tone-border:var(--warning-border)] [--tone-fg:var(--warning-fg)] [--tone-soft:var(--warning-soft)] [--tone-solid:var(--warning-solid)]',
|
||||
destructive:
|
||||
'[--tone-border:var(--danger-border)] [--tone-fg:var(--danger-fg)] [--tone-soft:var(--danger-soft)] [--tone-solid:var(--danger-solid)]'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
variant: 'info'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
import { m } from '#lib/paraglide/messages.js';
|
||||
import { LucideX } from '@lucide/svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
@@ -31,7 +32,7 @@
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
variant = 'default',
|
||||
variant = 'info',
|
||||
children,
|
||||
onDismiss,
|
||||
dismissibleId = undefined,
|
||||
@@ -68,14 +69,18 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
class={cn(alertVariants({ variant }), className)}
|
||||
role={variant === 'destructive' ? 'alert' : 'status'}
|
||||
class={cn(alertVariants({ variant }), (dismissibleId || onDismiss) && 'pr-10', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
{#if dismissibleId || onDismiss}
|
||||
<button onclick={dismiss} class="absolute top-2.5 right-3 text-current">
|
||||
<LucideX class="size-4" />
|
||||
<button
|
||||
onclick={dismiss}
|
||||
aria-label={m.close()}
|
||||
class="text-fg-muted hover:bg-fill-hover hover:text-fg focus-visible:outline-ring absolute top-2 right-2 grid size-6 place-items-center rounded-xs transition-colors focus-visible:outline-2"
|
||||
>
|
||||
<LucideX class="size-3.5" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
bind:ref
|
||||
data-slot="avatar-fallback"
|
||||
class={cn(
|
||||
'bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs',
|
||||
'bg-fill-selected text-fg flex size-full items-center justify-center rounded-full text-[length:38cqi] leading-none font-medium',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="avatar-group-count"
|
||||
class={cn(
|
||||
'bg-muted text-muted-foreground ring-background relative flex size-8 shrink-0 items-center justify-center rounded-full text-sm ring-2 group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
|
||||
'bg-fill-selected text-fg ring-background relative flex size-8 shrink-0 items-center justify-center rounded-full text-sm ring-2 group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="avatar-group"
|
||||
class={cn(
|
||||
'cn-avatar-group *:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2',
|
||||
'*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
data-slot="avatar"
|
||||
data-size={size}
|
||||
class={cn(
|
||||
'after:border-border group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten',
|
||||
'group/avatar after:inset-ring-border @container relative flex size-8 shrink-0 rounded-full select-none after:pointer-events-none after:absolute after:inset-0 after:rounded-full after:inset-ring data-[size=lg]:size-10 data-[size=sm]:size-6',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
<script lang="ts" module>
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
const tag =
|
||||
'h-[22px] rounded-xs border border-border px-2 text-micro [&>svg]:size-3! [a]:hover:bg-fill-hover';
|
||||
const status =
|
||||
"gap-1.5 text-control text-fg before:size-2 before:shrink-0 before:rounded-full before:content-['']";
|
||||
|
||||
export const badgeVariants = tv({
|
||||
base: 'h-5 gap-1 rounded-3xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap transition-colors focus-visible:ring-[3px] [&>svg]:pointer-events-none',
|
||||
base: 'group/badge inline-flex w-fit shrink-0 items-center gap-1 overflow-hidden whitespace-nowrap align-middle transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&>svg]:pointer-events-none',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
|
||||
secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
|
||||
destructive:
|
||||
'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
|
||||
outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
|
||||
ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
default: `${tag} bg-fill-muted text-fg`,
|
||||
secondary: `${tag} border-transparent bg-fill-muted text-fg`,
|
||||
outline: `${tag} bg-transparent text-fg`,
|
||||
mono: `${tag} h-5 font-mono text-mono-s`,
|
||||
success: `${status} before:bg-success-solid`,
|
||||
warning: `${status} before:bg-warning-solid`,
|
||||
destructive: `${status} before:bg-danger-solid`,
|
||||
muted: `${status} before:bg-fg-muted`
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
data-slot="button-group-separator"
|
||||
{orientation}
|
||||
class={cn(
|
||||
'bg-input relative self-stretch data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto',
|
||||
'bg-border relative self-stretch data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
const mergedProps = $derived({
|
||||
...restProps,
|
||||
class: cn(
|
||||
"bg-muted gap-2 rounded-4xl border px-2.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
|
||||
"flex items-center gap-2 rounded-sm bg-fill-muted px-3.5 text-control text-fg-muted dark:bg-fg/6 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
),
|
||||
'data-slot': 'button-group-text'
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const buttonGroupVariants = tv({
|
||||
base: "has-[>[data-variant=outline]]:[&>input]:border-border has-[>[data-variant=outline]]:[&>input:focus-visible]:border-ring has-[>[data-variant=outline]]:*:data-[slot=input-group]:border-border has-[>[data-variant=outline]]:[&>[data-slot=input-group]:has(:focus-visible)]:border-ring has-[>[data-variant=outline]]:*:data-[slot=select-trigger]:border-border has-[>[data-variant=outline]]:[&>[data-slot=select-trigger]:focus-visible]:border-ring has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-4xl flex w-fit items-stretch [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
||||
base: "flex w-fit items-stretch gap-px has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-sm [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
||||
variants: {
|
||||
orientation: {
|
||||
horizontal:
|
||||
'[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-4xl! [&>[data-slot]]:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0',
|
||||
'[&>[data-slot]]:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-sm! [&>[data-slot]~[data-slot]]:rounded-l-none',
|
||||
vertical:
|
||||
'[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-4xl! flex-col [&>[data-slot]]:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0'
|
||||
'flex-col [&>[data-slot]]:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-sm! [&>[data-slot]~[data-slot]]:rounded-t-none'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -3,33 +3,39 @@
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
const accentWash = 'bg-primary/12 text-fg before:bg-primary/6';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
base: "group/button relative isolate inline-flex shrink-0 items-center justify-center gap-2 rounded-sm text-control font-medium whitespace-nowrap select-none transition-motion before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:opacity-0 before:transition-opacity before:duration-(--dur-fast) before:ease-out hover:not-disabled:before:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring active:not-disabled:scale-(--press-scale) disabled:cursor-not-allowed disabled:opacity-45 aria-disabled:pointer-events-none aria-disabled:opacity-45 aria-busy:cursor-progress aria-busy:opacity-100 aria-expanded:before:opacity-100 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
outline:
|
||||
'border-border bg-background hover:bg-muted hover:text-foreground dark:hover:bg-input/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
|
||||
ghost:
|
||||
'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
|
||||
destructive:
|
||||
'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
default:
|
||||
'bg-primary text-primary-foreground shadow-[inset_0_0_0_1px_var(--primary-edge)] before:bg-primary-hover',
|
||||
secondary: accentWash,
|
||||
outline: accentWash,
|
||||
ghost: 'text-fg before:bg-fill-hover',
|
||||
destructive: 'bg-danger-fill text-danger-fill-fg before:bg-danger-fill-hover',
|
||||
inverse:
|
||||
'bg-inverse-fg text-inverse before:bg-[color-mix(in_srgb,var(--inverse-fg)_88%,var(--inverse))] focus-visible:outline-inverse-fg',
|
||||
link: 'text-fg before:hidden hover:text-fg-muted active:not-disabled:scale-100'
|
||||
},
|
||||
size: {
|
||||
default:
|
||||
'h-9.5 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5',
|
||||
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
|
||||
sm: 'h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
||||
lg: 'h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
|
||||
icon: 'size-9.5',
|
||||
'icon-xs': "size-6 [&_svg:not([class*='size-'])]:size-3",
|
||||
'icon-sm': 'size-8',
|
||||
'icon-lg': 'size-10'
|
||||
default: 'h-(--h-md) px-4',
|
||||
sm: "h-(--h-sm) px-3 [&_svg:not([class*='size-'])]:size-3.5",
|
||||
icon: 'size-(--h-md)',
|
||||
'icon-sm': 'size-(--h-sm)',
|
||||
'icon-xs': "size-(--h-xs) [&_svg:not([class*='size-'])]:size-3.5"
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
{ variant: 'link', class: 'h-auto px-0' },
|
||||
{
|
||||
variant: 'ghost',
|
||||
size: ['icon', 'icon-sm', 'icon-xs'],
|
||||
class:
|
||||
'text-fg-muted hover:text-fg aria-expanded:text-fg aria-expanded:before:bg-fill-selected'
|
||||
}
|
||||
],
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
@@ -112,18 +118,27 @@
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{type}
|
||||
disabled={disabled || isLoading}
|
||||
aria-busy={isLoading || undefined}
|
||||
onclick={handleOnClick}
|
||||
{...restProps}
|
||||
>
|
||||
<span class="flex w-full items-center justify-center">
|
||||
<!-- Label and spinner share one grid cell, so the width never changes while busy -->
|
||||
<!-- Both spans may shrink below their content, so a truncating label can end at the button's edge -->
|
||||
<span class="inline-grid max-w-full min-w-0 place-items-center">
|
||||
<span
|
||||
class={cn(
|
||||
'col-start-1 row-start-1 inline-flex max-w-full min-w-0 items-center justify-center gap-2 transition-opacity duration-(--dur-fast) ease-out',
|
||||
isLoading && 'opacity-0'
|
||||
)}
|
||||
>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
<Spinner
|
||||
class={cn(
|
||||
'grid overflow-hidden transition-[width,opacity,margin-right] duration-400 ease-[linear(0,0.897_14.4%,1.311_31.2%,1.338_46%,1.054_80.4%,1)]',
|
||||
isLoading ? 'mr-2 w-4 opacity-100' : 'w-0 opacity-0'
|
||||
'col-start-1 row-start-1 size-3.5 transition-opacity duration-(--dur-fast) ease-out',
|
||||
isLoading ? 'delay-60' : 'opacity-0 [animation-play-state:paused]'
|
||||
)}
|
||||
aria-hidden={!isLoading}
|
||||
/>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<CalendarPrimitive.Cell
|
||||
bind:ref
|
||||
class={cn(
|
||||
'relative size-(--cell-size) p-0 text-center text-sm focus-within:z-20 [&:first-child[data-selected]_[data-bits-day]]:rounded-s-(--cell-radius) [&:last-child[data-selected]_[data-bits-day]]:rounded-e-(--cell-radius)',
|
||||
'text-control relative size-(--cell-size) p-0 text-center focus-within:z-20 [&:first-child[data-selected]_[data-bits-day]]:rounded-s-(--cell-radius) [&:last-child[data-selected]_[data-bits-day]]:rounded-e-(--cell-radius)',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
class={cn(
|
||||
'flex size-(--cell-size) flex-col items-center justify-center gap-1 rounded-(--cell-radius) p-0 leading-none font-normal whitespace-nowrap select-none',
|
||||
'[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',
|
||||
'not-data-selected:hover:bg-accent/50 not-data-selected:hover:text-accent-foreground',
|
||||
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground [&[data-today][data-disabled]]:text-muted-foreground',
|
||||
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:text-foreground',
|
||||
'not-data-selected:hover:bg-fill-hover transition-colors duration-(--dur-fast)',
|
||||
'[&[data-today]:not([data-selected])]:bg-fill-muted [&[data-today][data-disabled]]:text-fg-muted [&[data-today]:not([data-selected])]:font-medium',
|
||||
'data-[selected]:bg-primary data-[selected]:text-primary-foreground',
|
||||
// Outside months
|
||||
'[&[data-outside-month]:not([data-selected])]:text-muted-foreground [&[data-outside-month]:not([data-selected])]:hover:text-accent-foreground',
|
||||
'[&[data-outside-month]:not([data-selected],[data-disabled],[data-unavailable])]:text-fg-muted',
|
||||
// Disabled
|
||||
'data-[disabled]:text-muted-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'data-[disabled]:text-fg-subtle data-[disabled]:pointer-events-none',
|
||||
// Unavailable
|
||||
'data-[unavailable]:text-muted-foreground data-[unavailable]:line-through',
|
||||
'data-[unavailable]:text-fg-subtle data-[unavailable]:line-through',
|
||||
// focus
|
||||
'focus:border-ring focus:ring-ring/50 focus:relative',
|
||||
'focus-visible:outline-ring focus-visible:relative focus-visible:outline-2 focus-visible:outline-offset-2',
|
||||
// inner spans
|
||||
'[&>span]:text-xs [&>span]:opacity-70',
|
||||
'[&>span]:text-micro [&>span]:opacity-70',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
|
||||
<CalendarPrimitive.HeadCell
|
||||
bind:ref
|
||||
class={cn(
|
||||
'text-muted-foreground w-(--cell-size) rounded-md text-[0.8rem] font-normal',
|
||||
className
|
||||
)}
|
||||
class={cn('text-caption text-fg-muted w-(--cell-size)', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<CalendarPrimitive.Header
|
||||
bind:ref
|
||||
class={cn(
|
||||
'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium',
|
||||
'text-control flex h-(--cell-size) w-full items-center justify-center gap-1.5 font-medium',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
|
||||
<CalendarPrimitive.Heading
|
||||
bind:ref
|
||||
class={cn('px-(--cell-size) text-sm font-medium', className)}
|
||||
class={cn('text-control px-(--cell-size) font-medium', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<span
|
||||
class={cn(
|
||||
'has-focus:border-ring border-input has-focus:ring-ring/50 relative flex rounded-md border shadow-xs has-focus:ring-[3px]',
|
||||
'bg-fill-muted has-focus-visible:border-ring has-focus-visible:ring-ring-halo relative flex rounded-sm border border-transparent has-focus-visible:ring-3',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -37,7 +37,7 @@
|
||||
{/each}
|
||||
</select>
|
||||
<span
|
||||
class="[&>svg]:text-muted-foreground flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5"
|
||||
class="text-control [&>svg]:text-fg-muted flex h-(--cell-size) items-center gap-1 rounded-sm ps-2 pe-1 font-medium select-none [&>svg]:size-3.5"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{monthItems.find((item) => item.value === value)?.label || selectedMonthItem.label}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<span
|
||||
class={cn(
|
||||
'has-focus:border-ring border-input has-focus:ring-ring/50 relative flex rounded-md border shadow-xs has-focus:ring-[3px]',
|
||||
'bg-fill-muted has-focus-visible:border-ring has-focus-visible:ring-ring-halo relative flex rounded-sm border border-transparent has-focus-visible:ring-3',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -36,7 +36,7 @@
|
||||
{/each}
|
||||
</select>
|
||||
<span
|
||||
class="[&>svg]:text-muted-foreground flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5"
|
||||
class="text-control [&>svg]:text-fg-muted flex h-(--cell-size) items-center gap-1 rounded-sm ps-2 pe-1 font-medium select-none [&>svg]:size-3.5"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{yearItems.find((item) => item.value === value)?.label || selectedYearItem.label}
|
||||
|
||||
@@ -51,7 +51,7 @@ get along, so we shut typescript up by casting `value` to `never`.
|
||||
{weekdayFormat}
|
||||
{disableDaysOutsideMonth}
|
||||
class={cn(
|
||||
'bg-background group/calendar p-3 [--cell-radius:var(--radius-4xl)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',
|
||||
'bg-background group/calendar p-3 [--cell-radius:var(--radius-sm)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',
|
||||
className
|
||||
)}
|
||||
{locale}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.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(
|
||||
'cn-card-action col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,20 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.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 group-data-[size=sm]/card:px-4', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,20 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
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 mt-1 text-sm', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.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 rounded-b-4xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.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(
|
||||
'group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-t-4xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,20 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-title"
|
||||
class={cn('flex flex-row items-center gap-2 text-xl leading-none font-semibold', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
size = 'default',
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { size?: 'default' | 'sm' } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card"
|
||||
data-size={size}
|
||||
class={cn(
|
||||
'bg-card text-card-foreground border-foreground/5 dark:border-foreground/10 group/card flex flex-col gap-6 overflow-hidden rounded-4xl border-1 py-6 text-sm shadow-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-4xl *:[img:last-child]:rounded-b-4xl',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -11,13 +11,16 @@
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||
|
||||
const markClass =
|
||||
'col-start-1 row-start-1 size-3 transition-[opacity,scale] duration-(--dur-base) ease-out';
|
||||
</script>
|
||||
|
||||
<CheckboxPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="checkbox"
|
||||
class={cn(
|
||||
'bg-input/90 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 peer relative flex size-4 shrink-0 items-center justify-center rounded-[5px] border border-transparent transition-shadow outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3',
|
||||
'peer bg-surface text-primary-control-foreground before:bg-primary-control focus-visible:outline-ring relative isolate grid size-4.5 shrink-0 place-items-center rounded-xs shadow-[inset_0_0_0_1px_var(--border-control)] group-has-disabled/field:opacity-45 before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:opacity-0 before:transition-opacity before:duration-(--dur-base) before:ease-out after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-45 aria-invalid:shadow-[inset_0_0_0_1px_var(--danger-solid)] data-checked:before:opacity-100 data-[state=indeterminate]:before:opacity-100',
|
||||
className
|
||||
)}
|
||||
bind:checked
|
||||
@@ -25,15 +28,9 @@
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<div
|
||||
data-slot="checkbox-indicator"
|
||||
class="grid place-content-center text-current transition-none [&>svg]:size-3.5"
|
||||
>
|
||||
{#if checked}
|
||||
<CheckIcon />
|
||||
{:else if indeterminate}
|
||||
<MinusIcon />
|
||||
{/if}
|
||||
</div>
|
||||
<span data-slot="checkbox-indicator" class="grid">
|
||||
<CheckIcon class={cn(markClass, !checked && 'scale-60 opacity-0')} />
|
||||
<MinusIcon class={cn(markClass, (checked || !indeterminate) && 'scale-60 opacity-0')} />
|
||||
</span>
|
||||
{/snippet}
|
||||
</CheckboxPrimitive.Root>
|
||||
|
||||
@@ -33,7 +33,10 @@
|
||||
<Dialog.Description>{description}</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Content
|
||||
class={cn('top-1/3 translate-y-0 overflow-hidden rounded-4xl! p-0 p-0', className)}
|
||||
class={cn(
|
||||
'bg-popover/72 border-fg/6 dark:border-fg/10 [@media(prefers-reduced-transparency:reduce)]:bg-popover! top-[15%] max-w-(--palette-w) translate-y-0 gap-0 overflow-hidden rounded-lg p-0 backdrop-blur-[40px] backdrop-saturate-150 [@media(prefers-reduced-transparency:reduce)]:backdrop-filter-none!',
|
||||
className
|
||||
)}
|
||||
{showCloseButton}
|
||||
{portalProps}
|
||||
>
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
<CommandPrimitive.Empty
|
||||
bind:ref
|
||||
data-slot="command-empty"
|
||||
class={cn('py-6 text-center text-sm', className)}
|
||||
class={cn('text-control text-fg-muted py-6 text-center', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -17,15 +17,12 @@
|
||||
<CommandPrimitive.Group
|
||||
bind:ref
|
||||
data-slot="command-group"
|
||||
class={cn(
|
||||
'text-foreground **:[[cmdk-group-heading]]:text-muted-foreground overflow-hidden px-1 py-2 **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium',
|
||||
className
|
||||
)}
|
||||
class={cn('text-fg overflow-hidden py-1', className)}
|
||||
value={value ?? heading ?? `----${useId()}`}
|
||||
{...restProps}
|
||||
>
|
||||
{#if heading}
|
||||
<CommandPrimitive.GroupHeading class="text-muted-foreground px-2 py-1.5 text-xs font-medium">
|
||||
<CommandPrimitive.GroupHeading class="text-caption text-fg-muted px-2.5 pt-2 pb-1 font-medium">
|
||||
{heading}
|
||||
</CommandPrimitive.GroupHeading>
|
||||
{/if}
|
||||
|
||||
@@ -13,14 +13,11 @@
|
||||
</script>
|
||||
|
||||
<div data-slot="command-input-wrapper" class="p-1 pb-0">
|
||||
<InputGroup.Root class="bg-input/50 h-9">
|
||||
<InputGroup.Root class="bg-fg/5 h-9 rounded-sm">
|
||||
<CommandPrimitive.Input
|
||||
{value}
|
||||
data-slot="command-input"
|
||||
class={cn(
|
||||
'w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
class={cn('w-full outline-hidden disabled:cursor-not-allowed disabled:opacity-50', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
bind:ref
|
||||
data-slot="command-item"
|
||||
class={cn(
|
||||
"group/command-item data-selected:bg-muted data-selected:text-foreground data-selected:*:[svg]:text-foreground relative flex cursor-default items-center gap-2 rounded-full px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group/command-item data-selected:bg-fg/6 text-control [&_svg:not([class*='text-'])]:text-fg-muted relative flex min-h-9 cursor-default items-center gap-2 rounded-sm px-2.5 outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
<CheckIcon
|
||||
class="cn-command-item-indicator ml-auto opacity-0 group-has-[[data-slot=command-shortcut]]/command-item:hidden group-data-[checked=true]/command-item:opacity-100"
|
||||
class="text-fg ml-auto opacity-0 group-has-[[data-slot=command-shortcut]]/command-item:hidden group-data-[checked=true]/command-item:opacity-100"
|
||||
/>
|
||||
</CommandPrimitive.Item>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
bind:ref
|
||||
data-slot="command-item"
|
||||
class={cn(
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-selected:bg-fg/6 text-control [&_svg:not([class*='text-'])]:text-fg-muted relative flex min-h-9 cursor-default items-center gap-2 rounded-sm px-2.5 outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
bind:ref
|
||||
data-slot="command-list"
|
||||
class={cn(
|
||||
'no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none',
|
||||
'max-h-72 scroll-py-1 [scrollbar-width:thin] [scrollbar-color:color-mix(in_srgb,var(--fg)_18%,transparent)_transparent] overflow-x-hidden overflow-y-auto outline-none',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
<CommandPrimitive.Separator
|
||||
bind:ref
|
||||
data-slot="command-separator"
|
||||
class={cn('bg-border/50 my-1.5 h-px', className)}
|
||||
class={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
<span
|
||||
bind:this={ref}
|
||||
data-slot="command-shortcut"
|
||||
class={cn(
|
||||
'text-muted-foreground group-data-selected/command-item:text-foreground ml-auto text-xs tracking-widest',
|
||||
className
|
||||
)}
|
||||
class={cn('text-caption text-fg-muted ml-auto pl-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
bind:value
|
||||
bind:ref
|
||||
data-slot="command"
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-4xl p-1',
|
||||
className
|
||||
)}
|
||||
class={cn('text-popover-foreground flex size-full flex-col overflow-hidden p-1', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -7,28 +7,33 @@
|
||||
import type { ComponentProps } from 'svelte';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import XIcon from '@lucide/svelte/icons/x';
|
||||
import { m } from '#lib/paraglide/messages.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
overlayClass,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
|
||||
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DialogPortal>>;
|
||||
overlayClass?: string;
|
||||
children: Snippet;
|
||||
showCloseButton?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DialogPortal {...portalProps}>
|
||||
<Dialog.Overlay />
|
||||
<Dialog.Overlay class={overlayClass} />
|
||||
<!-- The phone bottom sheet is a set of max-sm overrides, so the centered position and width callers pass in class never reach it -->
|
||||
<DialogPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="dialog-content"
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/5 dark:ring-foreground/10 fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-4xl p-6 text-sm shadow-xl ring-1 duration-100 outline-none sm:max-w-md',
|
||||
'bg-popover text-popover-foreground border-border shadow-overlay transition-motion fixed top-1/2 left-1/2 z-(--z-overlay) grid max-h-[85dvh] w-[calc(100%-2rem)] max-w-(--dialog-md) -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-xl border px-6 py-5 duration-(--dur-slow) outline-none data-closed:opacity-0 data-closed:duration-(--dur-base) data-closed:ease-in has-[>[data-slot=dialog-close]]:*:data-[slot=dialog-header]:pr-6 starting:scale-(--pop-scale) starting:opacity-0',
|
||||
'max-sm:before:bg-border-strong max-sm:inset-x-0 max-sm:top-auto max-sm:bottom-0 max-sm:w-full max-sm:max-w-none max-sm:translate-0 max-sm:rounded-t-lg max-sm:rounded-b-none max-sm:pt-6 max-sm:pb-[calc(20px+env(safe-area-inset-bottom))] max-sm:before:absolute max-sm:before:top-2 max-sm:before:left-1/2 max-sm:before:h-1 max-sm:before:w-9 max-sm:before:-translate-x-1/2 max-sm:before:rounded-[2px] max-sm:data-closed:translate-y-full max-sm:data-closed:opacity-100 max-sm:starting:translate-y-full max-sm:starting:scale-100 max-sm:starting:opacity-100',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
@@ -39,12 +44,12 @@
|
||||
{#snippet child({ props })}
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="bg-secondary absolute top-4 right-4"
|
||||
class="absolute top-3.5 right-3.5 size-7"
|
||||
size="icon-sm"
|
||||
{...props}
|
||||
>
|
||||
<XIcon />
|
||||
<span class="sr-only">Close</span>
|
||||
<span class="sr-only">{m.close()}</span>
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DialogPrimitive.Close>
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
<DialogPrimitive.Description
|
||||
bind:ref
|
||||
data-slot="dialog-description"
|
||||
class={cn(
|
||||
'text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3',
|
||||
className
|
||||
)}
|
||||
class={cn('text-control text-fg-muted *:[a]:inline-link wrap-anywhere', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { m } from '#lib/paraglide/messages.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -18,14 +19,14 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="dialog-footer"
|
||||
class={cn('flex flex-col-reverse gap-2 gap-2 sm:flex-row sm:justify-end', className)}
|
||||
class={cn('mt-1 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
{#if showCloseButton}
|
||||
<DialogPrimitive.Close>
|
||||
{#snippet child({ props })}
|
||||
<Button variant="outline" {...props}>Close</Button>
|
||||
<Button variant="outline" {...props}>{m.close()}</Button>
|
||||
{/snippet}
|
||||
</DialogPrimitive.Close>
|
||||
{/if}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="dialog-header"
|
||||
class={cn('flex flex-col gap-1.5', className)}
|
||||
class={cn('flex flex-col gap-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
bind:ref
|
||||
data-slot="dialog-overlay"
|
||||
class={cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 isolate z-50 bg-black/30 duration-100 supports-backdrop-filter:backdrop-blur-sm',
|
||||
'bg-scrim transition-motion fixed inset-0 isolate z-(--z-overlay) backdrop-blur-[4px] duration-(--dur-slow) data-closed:opacity-0 data-closed:duration-(--dur-base) data-closed:ease-in starting:opacity-0 [@media(prefers-reduced-transparency:reduce)]:backdrop-filter-none!',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
<DialogPrimitive.Title
|
||||
bind:ref
|
||||
data-slot="dialog-title"
|
||||
class={cn('text-base leading-none font-medium', className)}
|
||||
class={cn('font-display text-display-s text-fg wrap-anywhere', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -23,20 +23,20 @@
|
||||
bind:indeterminate
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
class={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none data-inset:pl-9.5 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-highlighted:bg-fg/6 text-control [&_svg:not([class*='text-'])]:text-fg-muted relative flex min-h-9 cursor-pointer items-center gap-2 rounded-sm px-2.5 pr-8 outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-45 data-inset:pl-8.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<span
|
||||
class="pointer-events-none absolute right-2 flex items-center justify-center"
|
||||
class="pointer-events-none absolute right-2.5 flex items-center justify-center"
|
||||
data-slot="dropdown-menu-checkbox-item-indicator"
|
||||
>
|
||||
{#if indeterminate}
|
||||
<MinusIcon />
|
||||
<MinusIcon class="text-fg" />
|
||||
{:else if checked}
|
||||
<CheckIcon />
|
||||
<CheckIcon class="text-fg" />
|
||||
{/if}
|
||||
</span>
|
||||
{@render childrenProp?.()}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{sideOffset}
|
||||
{align}
|
||||
class={cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 dark:ring-foreground/10 text-popover-foreground data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 bg-popover/70 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:data-highlighted:bg-destructive/10! dark:**:data-[variant=destructive]:data-highlighted:bg-destructive/20! relative z-50 min-w-48 animate-none! overflow-x-hidden overflow-y-auto rounded-3xl p-1.5 shadow-lg ring-1 duration-100 outline-none before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 data-closed:overflow-hidden',
|
||||
'text-popover-foreground shadow-popover bg-popover/72 border-fg/6 dark:border-fg/10 [@media(prefers-reduced-transparency:reduce)]:bg-popover! transition-motion z-(--z-popover) max-h-(--bits-dropdown-menu-content-available-height) min-w-50 origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 backdrop-blur-[40px] backdrop-saturate-150 duration-(--dur-base) outline-none data-closed:pointer-events-none data-closed:opacity-0 data-closed:duration-(--dur-fast) data-closed:ease-in starting:scale-(--pop-scale) starting:opacity-0 [@media(prefers-reduced-transparency:reduce)]:backdrop-filter-none!',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-group-heading"
|
||||
data-inset={inset}
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:ps-8', className)}
|
||||
class={cn('text-caption text-fg-muted px-2.5 pt-2 pb-1 font-medium data-inset:pl-8.5', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
class={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground group/dropdown-menu-item relative flex cursor-pointer items-center gap-2.5 rounded-2xl px-3 py-1.5 text-sm font-medium outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-9.5 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group/dropdown-menu-item data-highlighted:bg-fg/6 text-control not-data-[variant=destructive]:[&_svg:not([class*='text-'])]:text-fg-muted data-[variant=destructive]:text-danger-fg data-[variant=destructive]:data-highlighted:bg-danger-soft data-[variant=destructive]:*:[svg]:text-danger-fg relative flex min-h-9 cursor-pointer items-center gap-2 rounded-sm px-2.5 outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-45 data-inset:pl-8.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
class={cn(
|
||||
'text-muted-foreground px-3 py-2.5 text-xs data-inset:pl-9.5 data-[inset]:pl-8',
|
||||
className
|
||||
)}
|
||||
class={cn('text-caption text-fg-muted px-2.5 pt-2 pb-1 font-medium data-inset:pl-8.5', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -15,18 +15,18 @@
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
class={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2.5 rounded-2xl py-2 pr-8 pl-3 text-sm font-medium outline-hidden select-none data-inset:pl-9.5 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-highlighted:bg-fg/6 text-control [&_svg:not([class*='text-'])]:text-fg-muted relative flex min-h-9 cursor-pointer items-center gap-2 rounded-sm px-2.5 pr-8 outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-45 data-inset:pl-8.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked })}
|
||||
<span
|
||||
class="pointer-events-none absolute right-2 flex items-center justify-center"
|
||||
class="pointer-events-none absolute right-2.5 flex items-center justify-center"
|
||||
data-slot="dropdown-menu-radio-item-indicator"
|
||||
>
|
||||
{#if checked}
|
||||
<CheckIcon />
|
||||
<CheckIcon class="text-fg" />
|
||||
{/if}
|
||||
</span>
|
||||
{@render childrenProp?.({ checked })}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
<DropdownMenuPrimitive.Separator
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-separator"
|
||||
class={cn('bg-border/50 -mx-1.5 my-1.5 h-px', className)}
|
||||
class={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
<span
|
||||
bind:this={ref}
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
class={cn(
|
||||
'text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest',
|
||||
className
|
||||
)}
|
||||
class={cn('text-caption text-fg-muted ml-auto pl-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '#lib/utils/style.js';
|
||||
import DropdownMenuPortal from './dropdown-menu-portal.svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -9,12 +10,15 @@
|
||||
}: DropdownMenuPrimitive.SubContentProps = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
class={cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 dark:ring-foreground/10 text-popover-foreground bg-popover/70 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:data-highlighted:bg-destructive/10! dark:**:data-[variant=destructive]:data-highlighted:bg-destructive/20! relative w-auto min-w-36 animate-none! rounded-3xl p-1.5 shadow-lg ring-1 duration-100 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
<!-- The parent menu's backdrop blur and scroll container would otherwise clip the submenu -->
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
class={cn(
|
||||
'text-popover-foreground shadow-popover bg-popover/72 border-fg/6 dark:border-fg/10 [@media(prefers-reduced-transparency:reduce)]:bg-popover! transition-motion z-(--z-popover) w-auto min-w-50 origin-(--bits-menu-content-transform-origin) rounded-md border p-1 backdrop-blur-[40px] backdrop-saturate-150 duration-(--dur-base) outline-none data-closed:pointer-events-none data-closed:opacity-0 data-closed:duration-(--dur-fast) data-closed:ease-in starting:scale-(--pop-scale) starting:opacity-0 [@media(prefers-reduced-transparency:reduce)]:backdrop-filter-none!',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
</DropdownMenuPortal>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
class={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground flex cursor-default items-center gap-2 rounded-2xl px-3 py-1.5 text-sm font-medium outline-hidden select-none data-inset:pl-9.5 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-highlighted:bg-fg/6 text-control [&_svg:not([class*='text-'])]:text-fg-muted data-open:bg-fg/6 relative flex min-h-9 cursor-pointer items-center gap-2 rounded-sm px-2.5 outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-45 data-inset:pl-8.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="empty-content"
|
||||
class={cn(
|
||||
'flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance',
|
||||
className
|
||||
)}
|
||||
class={cn('flex w-full min-w-0 flex-wrap items-center gap-3', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="empty-description"
|
||||
class={cn(
|
||||
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
|
||||
className
|
||||
)}
|
||||
class={cn('text-body text-fg-muted [&>a]:inline-link', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="empty-header"
|
||||
class={cn('flex max-w-sm flex-col items-center gap-2', className)}
|
||||
class={cn('flex flex-col items-start gap-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const emptyMediaVariants = tv({
|
||||
base: 'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
base: 'mb-1 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
icon: "flex size-10 shrink-0 items-center justify-center rounded-xl bg-muted text-foreground [&_svg:not([class*='size-'])]:size-5"
|
||||
icon: "size-10 rounded-md bg-fill-muted text-fg-muted [&_svg:not([class*='size-'])]:size-4.5"
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="empty-title"
|
||||
class={cn('text-xl font-medium tracking-tight', className)}
|
||||
class={cn('text-title font-sans', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="empty"
|
||||
class={cn(
|
||||
'flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-2xl border-dashed p-12 text-center text-balance',
|
||||
'flex w-full max-w-[420px] min-w-0 flex-col items-start gap-4 pt-16 pb-12 text-left',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="field-content"
|
||||
class={cn('group/field-content flex flex-1 flex-col gap-1 leading-snug', className)}
|
||||
class={cn('group/field-content flex flex-1 flex-col gap-0.5', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
bind:this={ref}
|
||||
data-slot="field-description"
|
||||
class={cn(
|
||||
'text-muted-foreground text-left text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
|
||||
'last:mt-0 nth-last-2:-mt-1',
|
||||
'[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
|
||||
'text-caption text-fg-muted text-left [[data-variant=legend]+&]:-mt-1.5',
|
||||
'[&>a]:inline-link',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import type { Snippet } from 'svelte';
|
||||
import CircleAlertIcon from '@lucide/svelte/icons/circle-alert';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -38,9 +39,10 @@
|
||||
bind:this={ref}
|
||||
role="alert"
|
||||
data-slot="field-error"
|
||||
class={cn('text-destructive text-sm font-normal', className)}
|
||||
class={cn('text-caption text-danger-fg flex items-start gap-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<CircleAlertIcon class="mt-[3px] size-3 shrink-0" />
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else if singleErrorMessage}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="field-group"
|
||||
class={cn(
|
||||
'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4',
|
||||
'group/field-group @container/field-group flex w-full flex-col gap-4 data-[slot=checkbox-group]:gap-3',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Label } from '#lib/components/ui/label/index.js';
|
||||
import { m } from '#lib/paraglide/messages.js';
|
||||
import { cn } from '#lib/utils/style.js';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
required = false,
|
||||
optional = false,
|
||||
children,
|
||||
...restProps
|
||||
}: ComponentProps<typeof Label> & {
|
||||
required?: boolean;
|
||||
optional?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
@@ -18,12 +19,14 @@
|
||||
bind:ref
|
||||
data-slot="field-label"
|
||||
class={cn(
|
||||
'has-data-checked:bg-input/30 group/field-label peer/field-label flex w-fit gap-2 leading-snug leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-2xl has-[>[data-slot=field]]:border *:data-[slot=field]:p-4',
|
||||
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
|
||||
required && "after:text-red-500 after:content-['*']",
|
||||
'group/field-label peer/field-label flex w-fit gap-2 font-semibold group-data-[disabled=true]/field:opacity-45',
|
||||
'has-[>[data-slot=field]]:bg-surface hover:has-[>[data-slot=field]]:bg-fill-row-hover has-data-checked:border-border-selected has-[>[data-slot=field]]:has-data-checked:bg-surface-raised hover:has-[>[data-slot=field]]:has-data-checked:bg-surface-raised has-data-checked:shadow-selected has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border has-[>[data-slot=field]]:font-medium has-[>[data-slot=field]]:has-disabled:cursor-not-allowed *:data-[slot=field]:px-4 *:data-[slot=field]:py-3 has-disabled:**:data-[slot=field-content]:opacity-45',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
{#if optional}
|
||||
<span class="text-fg-muted -ms-1 font-normal">{m.field_optional()}</span>
|
||||
{/if}
|
||||
</Label>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
data-slot="field-legend"
|
||||
data-variant={variant}
|
||||
class={cn(
|
||||
'mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
|
||||
'text-fg data-[variant=label]:text-control data-[variant=legend]:text-heading mb-3 data-[variant=label]:font-semibold',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -20,13 +20,16 @@
|
||||
bind:this={ref}
|
||||
data-slot="field-separator"
|
||||
data-content={hasContent}
|
||||
class={cn('relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', className)}
|
||||
class={cn(
|
||||
'text-caption relative -my-2 h-5 group-data-[variant=outline]/field-group:-mb-2',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<Separator class="absolute inset-0 top-1/2" />
|
||||
{#if children}
|
||||
<span
|
||||
class="text-muted-foreground bg-background relative mx-auto block w-fit px-2"
|
||||
class="bg-background text-fg-muted relative mx-auto block w-fit px-2"
|
||||
data-slot="field-separator-content"
|
||||
>
|
||||
{@render children()}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="field-label"
|
||||
class={cn(
|
||||
'flex w-fit items-center gap-2 text-sm leading-snug leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
|
||||
'text-control text-fg flex w-fit items-center gap-2 font-medium group-data-[disabled=true]/field:opacity-45',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const fieldVariants = tv({
|
||||
base: 'data-[invalid=true]:text-destructive gap-3 group/field flex w-full',
|
||||
base: 'group/field flex w-full',
|
||||
variants: {
|
||||
orientation: {
|
||||
vertical: 'cn-field-orientation-vertical flex-col [&>*]:w-full [&>.sr-only]:w-auto',
|
||||
vertical: 'flex-col gap-1.5 [&>*]:w-full [&>.sr-only]:w-auto',
|
||||
horizontal:
|
||||
'cn-field-orientation-horizontal flex-row items-center has-[>[data-slot=field-content]]:items-start [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
|
||||
'flex-row items-center gap-3 has-[>[data-slot=field-content]]:items-start [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
|
||||
responsive:
|
||||
'cn-field-orientation-responsive flex-col @md/field-group:flex-row @md/field-group:items-center @md/field-group:has-[>[data-slot=field-content]]:items-start [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px'
|
||||
'flex-col gap-1.5 @md/field-group:flex-row @md/field-group:items-center @md/field-group:gap-3 @md/field-group:has-[>[data-slot=field-content]]:items-start [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script lang="ts" module>
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
export const inputGroupAddonVariants = tv({
|
||||
base: "text-muted-foreground **:data-[slot=kbd]:bg-muted-foreground/10 h-auto gap-2 py-2 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-3xl **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
|
||||
base: "flex h-auto cursor-text items-center justify-center gap-2 text-control text-fg-muted select-none group-data-[disabled=true]/input-group:opacity-45 [&>svg:not([class*='size-'])]:size-4",
|
||||
variants: {
|
||||
align: {
|
||||
'inline-start': 'pl-3 has-[>button]:-ml-1 has-[>kbd]:-ml-1 order-first',
|
||||
'inline-end': 'pr-3 has-[>button]:-mr-1 has-[>kbd]:-mr-1 order-last',
|
||||
'block-start':
|
||||
'px-3 pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5 order-first w-full justify-start',
|
||||
'block-end':
|
||||
'px-3 pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5 order-last w-full justify-start'
|
||||
'inline-start': 'order-first pl-3 has-[>button]:pl-1',
|
||||
'inline-end': 'order-last pr-3.5 has-[>button]:pr-1',
|
||||
'block-start': 'order-first w-full justify-start px-3.5 pt-2.5',
|
||||
'block-end': 'order-last w-full justify-start px-3.5 pb-2.5'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
const inputGroupButtonVariants = tv({
|
||||
base: 'gap-2 rounded-4xl text-sm flex items-center shadow-none',
|
||||
base: 'flex items-center gap-1.5',
|
||||
variants: {
|
||||
size: {
|
||||
xs: "h-6 gap-1 rounded-xl px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
||||
sm: 'cn-input-group-button-size-sm',
|
||||
'icon-xs': 'size-6 rounded-xl p-0 has-[>svg]:p-0',
|
||||
'icon-sm': 'size-8 p-0 has-[>svg]:p-0'
|
||||
xs: "h-(--h-xs) rounded-xs px-2 [&>svg:not([class*='size-'])]:size-3.5",
|
||||
sm: 'h-(--h-sm) px-2.5',
|
||||
'icon-xs': 'size-(--h-xs) rounded-xs p-0',
|
||||
'icon-sm': 'size-(--h-sm) p-0'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '#lib/utils/style.js';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
import { Input } from '#lib/components/ui/input/index.js';
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
import type { HTMLInputAttributes } from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(),
|
||||
class: className,
|
||||
...props
|
||||
}: ComponentProps<typeof Input> = $props();
|
||||
...restProps
|
||||
}: WithElementRef<HTMLInputAttributes> = $props();
|
||||
</script>
|
||||
|
||||
<Input
|
||||
bind:ref
|
||||
<input
|
||||
bind:this={ref}
|
||||
bind:value
|
||||
data-slot="input-group-control"
|
||||
class={cn(
|
||||
'flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent',
|
||||
'text-body text-fg placeholder:text-fg-muted h-full w-full min-w-0 flex-1 bg-transparent px-3.5 outline-hidden max-sm:text-[16px] [&::-webkit-search-cancel-button]:appearance-none',
|
||||
className
|
||||
)}
|
||||
bind:value
|
||||
{...props}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<span
|
||||
bind:this={ref}
|
||||
class={cn(
|
||||
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
||||
"text-control text-fg-muted flex items-center gap-2 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '#lib/utils/style.js';
|
||||
import { Textarea } from '#lib/components/ui/textarea/index.js';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
import { cn, type WithElementRef, type WithoutChildren } from '#lib/utils/style.js';
|
||||
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(),
|
||||
class: className,
|
||||
...props
|
||||
}: ComponentProps<typeof Textarea> = $props();
|
||||
...restProps
|
||||
}: WithoutChildren<WithElementRef<HTMLTextareaAttributes>> = $props();
|
||||
</script>
|
||||
|
||||
<Textarea
|
||||
bind:ref
|
||||
<textarea
|
||||
bind:this={ref}
|
||||
bind:value
|
||||
data-slot="input-group-control"
|
||||
class={cn(
|
||||
'flex-1 resize-none rounded-none border-0 bg-transparent py-2.5 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent',
|
||||
'text-body text-fg placeholder:text-fg-muted min-h-[86px] w-full flex-1 resize-none bg-transparent px-3.5 py-2.5 outline-hidden max-sm:text-[16px]',
|
||||
className
|
||||
)}
|
||||
bind:value
|
||||
{...props}
|
||||
/>
|
||||
{...restProps}></textarea>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
data-slot="input-group"
|
||||
role="group"
|
||||
class={cn(
|
||||
'group/input-group bg-input/50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 relative flex h-9 w-full min-w-0 items-center rounded-4xl border border-transparent transition-[color,box-shadow,background-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-3xl has-data-[align=block-start]:rounded-3xl has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[textarea]:rounded-2xl has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5',
|
||||
'group/input-group bg-fill-muted text-fg ring-ring-halo hover:bg-fill-hover has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:bg-surface has-[[data-slot][aria-invalid=true]]:border-danger-solid has-[[data-slot][aria-invalid=true]]:ring-danger-solid/20 dark:border-fg/10 dark:bg-fg/6 dark:hover:bg-fg/9 dark:has-[[data-slot=input-group-control]:focus-visible]:border-ring dark:has-[[data-slot=input-group-control]:focus-visible]:bg-fg/7 dark:has-[[data-slot][aria-invalid=true]]:border-danger-solid relative flex h-(--h-md) w-full min-w-0 items-center rounded-sm border border-transparent has-[[data-slot=input-group-control]:focus-visible]:ring-(length:--halo-width) has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=inline-end]]:[&>input]:pr-2.5 has-[>[data-align=inline-start]]:[&>input]:pl-2.5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="input-otp-group"
|
||||
class={cn(
|
||||
'has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive flex items-center gap-1.5 rounded-3xl has-aria-invalid:ring-3',
|
||||
'bg-surface relative flex min-w-0 flex-1 overflow-hidden rounded-sm after:pointer-events-none after:absolute after:inset-0 after:z-1 after:rounded-[inherit] after:shadow-[inset_0_0_0_1px_var(--border-control)]',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
bind:this={ref}
|
||||
data-slot="input-otp-separator"
|
||||
role="separator"
|
||||
class={cn("flex items-center [&_svg:not([class*='size-'])]:size-4", className)}
|
||||
class={cn(
|
||||
"text-fg-muted flex w-7 shrink-0 items-center justify-center [&_svg:not([class*='size-'])]:size-3",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#if children}
|
||||
|
||||
@@ -15,17 +15,15 @@
|
||||
bind:ref
|
||||
data-slot="input-otp-slot"
|
||||
class={cn(
|
||||
'bg-input/50 border-input data-active:border-ring data-active:ring-ring/30 data-active:aria-invalid:ring-destructive/20 dark:data-active:aria-invalid:ring-destructive/40 aria-invalid:border-destructive relative flex h-13 w-10 items-center justify-center rounded-sm border text-sm transition-all outline-none data-active:z-10 data-active:ring-3',
|
||||
'text-code-cell text-fg before:bg-danger-soft aria-invalid:text-danger-fg relative isolate grid h-18 max-w-14 min-w-[30px] flex-1 place-items-center font-mono uppercase tabular-nums before:absolute before:inset-0 before:-z-1 before:opacity-0 before:transition-opacity before:duration-(--dur-fast) before:ease-out after:pointer-events-none after:absolute after:inset-0 after:opacity-0 after:shadow-[inset_0_0_0_1px_var(--ring),inset_0_0_0_calc(1px+var(--halo-width))_var(--ring-halo)] first:after:rounded-l-sm last:after:rounded-r-sm aria-invalid:before:opacity-100 data-active:z-2 data-active:after:opacity-100 [&+&]:shadow-[inset_1px_0_0_var(--border-strong)]',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{cell.char}
|
||||
{#if cell.hasFakeCaret}
|
||||
<div
|
||||
class="cn-input-otp-caret pointer-events-none absolute inset-0 flex items-center justify-center"
|
||||
>
|
||||
<div class="animate-caret-blink bg-foreground bg-foreground h-4 w-px duration-1000"></div>
|
||||
<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||
<div class="animate-caret-blink bg-fg h-5.5 w-px [animation-duration:var(--t-caret)]"></div>
|
||||
</div>
|
||||
{/if}
|
||||
</InputOTPPrimitive.Cell>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
data-lpignore="true"
|
||||
data-1p-ignore="true"
|
||||
class={cn(
|
||||
'cn-input-otp-input flex items-center gap-2 disabled:cursor-not-allowed has-disabled:opacity-50',
|
||||
'flex w-full items-center disabled:cursor-not-allowed has-disabled:opacity-45',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Root from './input.svelte';
|
||||
import Root, { filledFieldClass, readOnlyFieldClass } from './input.svelte';
|
||||
|
||||
export type FormInputEvent<T extends Event = Event> = T & {
|
||||
currentTarget: EventTarget & HTMLInputElement;
|
||||
@@ -6,6 +6,8 @@ export type FormInputEvent<T extends Event = Event> = T & {
|
||||
|
||||
export {
|
||||
Root,
|
||||
filledFieldClass,
|
||||
readOnlyFieldClass,
|
||||
//
|
||||
Root as Input
|
||||
};
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
<script lang="ts" module>
|
||||
// Shared by inputs, textareas and select triggers
|
||||
// The dark: classes repeat the focus and invalid borders because the dark variant sorts after every state variant
|
||||
export const filledFieldClass =
|
||||
'border border-transparent bg-fill-muted text-fg outline-hidden hover:bg-fill-hover focus-visible:border-ring focus-visible:bg-surface focus-visible:ring-(length:--halo-width) focus-visible:ring-ring-halo aria-invalid:border-danger-solid aria-invalid:ring-danger-solid/20 disabled:cursor-not-allowed disabled:opacity-45 dark:border-fg/10 dark:bg-fg/6 dark:hover:bg-fg/9 dark:focus-visible:border-ring dark:focus-visible:bg-fg/7 dark:aria-invalid:border-danger-solid';
|
||||
|
||||
// Read-only fields drop the fill for a hairline so they never look editable
|
||||
export const readOnlyFieldClass =
|
||||
'read-only:border-border read-only:bg-transparent! dark:read-only:border-border';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '#lib/utils/style.js';
|
||||
@@ -18,31 +29,28 @@
|
||||
'data-slot': dataSlot = 'input',
|
||||
...restProps
|
||||
}: Props = $props();
|
||||
|
||||
// Phone inputs use 16px text so iOS does not zoom on focus, and mono values elsewhere use the mono size because mono glyphs run larger than sans ones
|
||||
const inputClass = $derived(
|
||||
cn(
|
||||
filledFieldClass,
|
||||
readOnlyFieldClass,
|
||||
'h-(--h-md) w-full min-w-0 rounded-sm px-3.5 text-body placeholder:text-fg-muted autofill:shadow-[inset_0_0_0_100px_var(--fill-muted)] autofill:[-webkit-text-fill-color:var(--fg)] max-sm:text-[16px] sm:[&.font-mono]:text-mono file:inline-flex file:h-full file:border-0 file:bg-transparent file:font-medium file:text-fg',
|
||||
className
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if type === 'file'}
|
||||
<input
|
||||
bind:this={ref}
|
||||
data-slot={dataSlot}
|
||||
class={cn(
|
||||
'bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 file:text-foreground placeholder:text-muted-foreground h-9 w-full min-w-0 rounded-3xl border border-transparent px-3 py-1 text-base transition-[color,box-shadow,background-color] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm',
|
||||
className
|
||||
)}
|
||||
class={inputClass}
|
||||
type="file"
|
||||
bind:files
|
||||
bind:value
|
||||
{...restProps}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
bind:this={ref}
|
||||
data-slot={dataSlot}
|
||||
class={cn(
|
||||
'bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 file:text-foreground placeholder:text-muted-foreground h-9 w-full min-w-0 rounded-3xl border border-transparent px-3 py-1 text-base transition-[color,box-shadow,background-color] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm',
|
||||
className
|
||||
)}
|
||||
{type}
|
||||
bind:value
|
||||
{...restProps}
|
||||
/>
|
||||
<input bind:this={ref} data-slot={dataSlot} class={inputClass} {type} bind:value {...restProps} />
|
||||
{/if}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="item-content"
|
||||
class={cn(
|
||||
'flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0.5 [&+[data-slot=item-content]]:flex-none',
|
||||
'flex flex-1 flex-col gap-0.5 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="item-description"
|
||||
class={cn(
|
||||
'text-muted-foreground [&>a:hover]:text-primary line-clamp-2 text-left text-sm font-normal [&>a]:underline [&>a]:underline-offset-4',
|
||||
'text-caption text-fg-muted [&>a:hover]:text-fg line-clamp-2 text-left [&>a]:underline [&>a]:underline-offset-4',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
default: 'bg-transparent',
|
||||
icon: "[&_svg:not([class*='size-'])]:size-4",
|
||||
image:
|
||||
'size-10 overflow-hidden rounded-xl group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 group-data-[size=xs]/item:rounded-lg [&_img]:size-full [&_img]:object-cover'
|
||||
'size-10 overflow-hidden rounded-md group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 group-data-[size=xs]/item:rounded-xs [&_img]:size-full [&_img]:object-cover'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="item-title"
|
||||
class={cn(
|
||||
'line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-semibold underline-offset-4',
|
||||
'text-heading line-clamp-1 flex w-fit items-center gap-2 underline-offset-4',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const itemVariants = tv({
|
||||
base: '[a]:hover:bg-muted rounded-2xl border text-sm group/item focus-visible:border-ring focus-visible:ring-ring/50 flex w-full flex-wrap items-center transition-colors duration-100 outline-none focus-visible:ring-[3px] [a]:transition-colors',
|
||||
base: 'group/item flex w-full flex-wrap items-center rounded-md border text-control transition-colors duration-(--dur-fast) outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [a]:hover:bg-fill-row-hover',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'border-transparent',
|
||||
outline: 'border-border',
|
||||
muted: 'bg-muted/50 border-transparent',
|
||||
card: 'bg-card shadow-sm border-transparent',
|
||||
transparent: 'bg-transparent border-transparent'
|
||||
outline: 'border-border bg-surface',
|
||||
card: 'border-border bg-surface',
|
||||
transparent: 'border-transparent bg-transparent'
|
||||
},
|
||||
size: {
|
||||
default: 'gap-3.5 px-4 py-3.5',
|
||||
sm: 'gap-3.5 px-3.5 py-3',
|
||||
xs: 'gap-2.5 px-3 py-2.5 in-data-[slot=dropdown-menu-content]:p-0'
|
||||
default: 'gap-4 px-4 py-3',
|
||||
sm: 'gap-3 px-3 py-2.5',
|
||||
xs: 'gap-2.5 px-3 py-2 in-data-[slot=dropdown-menu-content]:p-0'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
bind:ref
|
||||
data-slot="label"
|
||||
class={cn(
|
||||
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||
'text-control text-fg flex items-center gap-2 font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-45 peer-disabled:cursor-not-allowed peer-disabled:opacity-45',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
aria-hidden="true"
|
||||
data-slot="pagination-ellipsis"
|
||||
class={cn(
|
||||
"flex size-9 items-center items-center justify-center justify-center [&_svg:not([class*='size-'])]:size-4",
|
||||
"text-fg-muted flex size-(--h-sm) items-center justify-center [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user