@heyramzi/ui 2.20.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/charts/dashboard/data-table.js +1 -2
- package/dist/components/list-page/filter-section.js +1 -1
- package/dist/components/list-page/list-table.js +5 -6
- package/dist/components/list-page/queue-rail.js +2 -1
- package/dist/components/picker/trigger.js +3 -3
- package/dist/components/segmented/segmented.js +5 -5
- package/dist/components/work-parts/looks.js +1 -1
- package/dist/shadcn/dialog/index.js +3 -2
- package/dist/shadcn/dropdown-menu/menu-item.js +1 -1
- package/dist/shadcn/sheet/index.js +3 -2
- package/dist/shadcn/skeleton/index.js +3 -2
- package/dist/status/index.js +1 -0
- package/dist/work/kanban-drag.js +13 -7
- package/package.json +1 -1
- package/src/ai/confirmation/ConfirmationTitle.svelte +1 -4
- package/src/ai/loading-state/PixelLoader.svelte +1 -1
- package/src/animations/dropdown.css +19 -10
- package/src/animations/modal.css +7 -2
- package/src/charts/charts.css +3 -5
- package/src/charts/dashboard/dashboard-section.svelte +1 -1
- package/src/charts/dashboard/data-table.ts +1 -2
- package/src/charts/dashboard/stat-tile.svelte +1 -1
- package/src/charts/number-chart.css +8 -7
- package/src/charts/pop-value.svelte +68 -0
- package/src/charts/primitives/chart-tooltip.svelte +1 -1
- package/src/charts/reference-line.css +1 -1
- package/src/charts/stat-card.css +4 -4
- package/src/components/copy/copy-button.svelte +11 -10
- package/src/components/data-table/row-arrow.svelte +1 -1
- package/src/components/data-table/row-context-menu.svelte +1 -1
- package/src/components/data-table/sortable-head.svelte +2 -2
- package/src/components/data-table/table-skeleton.svelte +5 -5
- package/src/components/docs/doc-page.svelte +1 -3
- package/src/components/docs/on-this-page.svelte +4 -4
- package/src/components/field-row/field-row.svelte +1 -1
- package/src/components/list-page/detail-empty.svelte +3 -8
- package/src/components/list-page/detail-header.svelte +1 -1
- package/src/components/list-page/detail-row.svelte +2 -1
- package/src/components/list-page/empty-state.svelte +15 -7
- package/src/components/list-page/filter-item.svelte +2 -2
- package/src/components/list-page/filter-popover.svelte +1 -1
- package/src/components/list-page/filter-section.ts +1 -4
- package/src/components/list-page/grouped-list.svelte +37 -8
- package/src/components/list-page/list-page-shell.svelte +1 -1
- package/src/components/list-page/list-table.ts +6 -6
- package/src/components/list-page/master-detail.svelte +1 -1
- package/src/components/list-page/queue-rail.ts +2 -2
- package/src/components/list-page/rail-empty.svelte +1 -1
- package/src/components/list-page/rail-row.svelte +5 -5
- package/src/components/open-in/open-in.svelte +2 -1
- package/src/components/picker/trigger.ts +6 -5
- package/src/components/section-card/section-card.svelte +1 -1
- package/src/components/section-header/section-header.svelte +4 -2
- package/src/components/section-header/section-label.svelte +2 -2
- package/src/components/segmented/animated-tabs.svelte +1 -1
- package/src/components/segmented/section-runs.svelte +1 -1
- package/src/components/segmented/segment.svelte +4 -6
- package/src/components/segmented/segmented-track.svelte +18 -52
- package/src/components/segmented/segmented.ts +5 -5
- package/src/components/table-view/table-view.svelte +43 -24
- package/src/components/tags-input/tags-input.svelte +17 -31
- package/src/components/work-parts/looks.ts +1 -1
- package/src/editor/content.css +3 -3
- package/src/editor/editor.svelte +1 -1
- package/src/inbox/thread-inbox.svelte +2 -9
- package/src/panel/side-panel.svelte +9 -4
- package/src/record/info-list.svelte +7 -8
- package/src/record/project-list.svelte +1 -1
- package/src/record/record-demo.svelte +2 -4
- package/src/record/record-shell.svelte +1 -1
- package/src/record/record-stats.svelte +9 -3
- package/src/record/steps-rail.svelte +7 -11
- package/src/shadcn/accordion/accordion-trigger.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-content.svelte +5 -1
- package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +12 -2
- package/src/shadcn/alert-dialog/alert-dialog-header.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-title.svelte +1 -1
- package/src/shadcn/badge/badge.svelte +3 -1
- package/src/shadcn/bubble/bubble-content.svelte +1 -1
- package/src/shadcn/bubble/bubble.svelte +1 -1
- package/src/shadcn/button/button.svelte +11 -5
- package/src/shadcn/calendar/calendar-month-select.svelte +1 -1
- package/src/shadcn/calendar/calendar-year-select.svelte +1 -1
- package/src/shadcn/calendar/calendar.svelte +1 -1
- package/src/shadcn/card/card.svelte +2 -1
- package/src/shadcn/checkbox/checkbox.svelte +1 -1
- package/src/shadcn/code-block/code-block-actions.svelte +1 -1
- package/src/shadcn/command/command-dialog.svelte +2 -2
- package/src/shadcn/command/command-group.svelte +1 -1
- package/src/shadcn/command/command-input.svelte +1 -1
- package/src/shadcn/command/command-item.svelte +1 -1
- package/src/shadcn/dialog/dialog-body.svelte +22 -0
- package/src/shadcn/dialog/dialog-content.svelte +19 -4
- package/src/shadcn/dialog/dialog-footer.svelte +19 -1
- package/src/shadcn/dialog/dialog-header.svelte +6 -1
- package/src/shadcn/dialog/dialog-title.svelte +2 -1
- package/src/shadcn/dialog/index.ts +3 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/src/shadcn/dropdown-menu/menu-item.ts +1 -1
- package/src/shadcn/field/field-separator.svelte +1 -1
- package/src/shadcn/hover-card/hover-card-content.svelte +1 -1
- package/src/shadcn/input/input.svelte +2 -2
- package/src/shadcn/input-otp/input-otp-slot.svelte +1 -1
- package/src/shadcn/message-scroller/message-scroller-button.svelte +1 -1
- package/src/shadcn/popover/popover-content.svelte +1 -1
- package/src/shadcn/radio-group/radio-group-item.svelte +1 -1
- package/src/shadcn/scroll-area/scroll-area.svelte +1 -1
- package/src/shadcn/select/select-content.svelte +6 -2
- package/src/shadcn/select/select-item.svelte +1 -1
- package/src/shadcn/select/select-trigger.svelte +1 -1
- package/src/shadcn/sheet/index.ts +3 -0
- package/src/shadcn/sheet/sheet-body.svelte +22 -0
- package/src/shadcn/sheet/sheet-content.svelte +27 -5
- package/src/shadcn/sheet/sheet-footer.svelte +14 -2
- package/src/shadcn/sheet/sheet-header.svelte +1 -1
- package/src/shadcn/sheet/sheet-title.svelte +1 -1
- package/src/shadcn/sheet/sheet.css +11 -5
- package/src/shadcn/sidebar/sidebar-group-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-group-label.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-inset.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-button.svelte +2 -2
- package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu.svelte +1 -1
- package/src/shadcn/sidebar/sidebar.svelte +1 -1
- package/src/shadcn/skeleton/index.ts +3 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte +63 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte.test.ts +57 -0
- package/src/shadcn/skeleton/skeleton.svelte +5 -1
- package/src/shadcn/slider/slider.svelte +1 -1
- package/src/shadcn/switch/switch.svelte +2 -2
- package/src/shadcn/table/table-row.svelte +1 -1
- package/src/shadcn/tabs/tabs-list.svelte +1 -1
- package/src/shadcn/tabs/tabs-trigger.svelte +2 -1
- package/src/shadcn/textarea/textarea.svelte +1 -1
- package/src/shadcn/toggle/toggle.svelte +2 -2
- package/src/shadcn/toggle-group/toggle-group.svelte +1 -1
- package/src/shadcn/tooltip/tooltip-content.svelte +1 -1
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +1 -1
- package/src/shadcn/trace/trace.svelte +1 -1
- package/src/status/README.md +1 -1
- package/src/status/index.ts +1 -0
- package/src/status/status-badge.svelte +21 -7
- package/src/status/status-dot.svelte +6 -11
- package/src/status/status-pill.svelte +50 -0
- package/src/thread/thread.svelte +54 -136
- package/src/tokens/craft.css +55 -36
- package/src/tokens/palette.css +16 -9
- package/src/work/demo.svelte +48 -42
- package/src/work/detail/detail-demo.svelte +33 -24
- package/src/work/detail/task-chip.svelte +4 -4
- package/src/work/detail/task-detail.svelte +41 -108
- package/src/work/detail/task-property.svelte +7 -13
- package/src/work/detail/task-thread.svelte +48 -119
- package/src/work/docs/doc-editor-page.svelte +5 -4
- package/src/work/docs/doc-menu.svelte +9 -4
- package/src/work/docs/docs-demo.svelte +4 -4
- package/src/work/docs/edited-chip.svelte +11 -4
- package/src/work/gantt/gantt-bar.svelte +18 -14
- package/src/work/gantt/gantt-grid.svelte +7 -7
- package/src/work/gantt/gantt-group-row.svelte +7 -10
- package/src/work/gantt/gantt-header.svelte +3 -1
- package/src/work/gantt/gantt-task-row.svelte +19 -9
- package/src/work/gantt/gantt.css +4 -5
- package/src/work/inbox/inbox-card.svelte +32 -10
- package/src/work/inbox/inbox-draft.svelte +2 -2
- package/src/work/kanban-column.svelte +7 -15
- package/src/work/kanban-drag.ts +13 -8
- package/src/work/task-gantt.svelte +2 -1
- package/src/work/task-list.svelte +54 -53
- package/src/work/time/time-chip.svelte +16 -19
- package/src/work/time/time-dot.svelte +2 -1
- package/src/work/time/time-ring.svelte +2 -3
- package/src/work/time/time-tracker.svelte +72 -166
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<ScrollAreaPrimitive.Viewport
|
|
30
30
|
bind:ref={viewportRef}
|
|
31
31
|
data-slot="scroll-area-viewport"
|
|
32
|
-
class="cn-scroll-area-viewport
|
|
32
|
+
class="cn-scroll-area-viewport size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:shadow-(--ring-focus) focus-visible:outline-1"
|
|
33
33
|
>
|
|
34
34
|
{@render children?.()}
|
|
35
35
|
</ScrollAreaPrimitive.Viewport>
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
ref = $bindable(null),
|
|
12
12
|
class: className,
|
|
13
13
|
sideOffset = 4,
|
|
14
|
+
// 8px between the list and the viewport edge, as the dropdown and popover keep.
|
|
15
|
+
collisionPadding = 8,
|
|
14
16
|
portalProps,
|
|
15
17
|
children,
|
|
16
18
|
preventScroll = true,
|
|
@@ -24,10 +26,12 @@
|
|
|
24
26
|
<SelectPrimitive.Content
|
|
25
27
|
bind:ref
|
|
26
28
|
{sideOffset}
|
|
29
|
+
{collisionPadding}
|
|
27
30
|
{preventScroll}
|
|
28
31
|
data-slot="select-content"
|
|
29
32
|
class={cn(
|
|
30
|
-
|
|
33
|
+
// 14px around 10px rows inside a 4px inset, so the corners run concentric.
|
|
34
|
+
"t-dropdown bg-popover text-popover-foreground min-w-36 rounded-xl shadow-edge-raised relative isolate z-50 max-h-[var(--bits-floating-available-height,100svh)] max-w-[calc(100vw-1rem)] overflow-x-hidden overflow-y-auto overscroll-contain",
|
|
31
35
|
className,
|
|
32
36
|
)}
|
|
33
37
|
{...restProps}
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
<SelectScrollUpButton />
|
|
36
40
|
<SelectPrimitive.Viewport
|
|
37
41
|
class={cn(
|
|
38
|
-
"h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1",
|
|
42
|
+
"h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1",
|
|
39
43
|
)}
|
|
40
44
|
>
|
|
41
45
|
{@render children?.()}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
{value}
|
|
19
19
|
data-slot="select-item"
|
|
20
20
|
class={cn(
|
|
21
|
-
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-
|
|
21
|
+
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 focus:bg-accent data-highlighted:bg-accent data-highlighted:text-accent-foreground focus:text-accent-foreground relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
22
22
|
className,
|
|
23
23
|
)}
|
|
24
24
|
{...restProps}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
data-slot="select-trigger"
|
|
20
20
|
data-size={size}
|
|
21
21
|
class={cn(
|
|
22
|
-
"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-
|
|
22
|
+
"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 enabled:hover:border-foreground/20 focus-visible:border-ring focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-1 pr-2 pl-2.5 text-sm shadow-field transition-[color,background-color,border-color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:text-[0.8rem] relative data-[size=sm]:after:absolute data-[size=sm]:after:top-1/2 data-[size=sm]:after:left-0 data-[size=sm]:after:h-[max(100%,2rem)] data-[size=sm]:after:w-full data-[size=sm]:after:-translate-y-1/2 *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
23
23
|
className,
|
|
24
24
|
)}
|
|
25
25
|
{...restProps}
|
|
@@ -8,6 +8,7 @@ import Header from "./sheet-header.svelte";
|
|
|
8
8
|
import Footer from "./sheet-footer.svelte";
|
|
9
9
|
import Title from "./sheet-title.svelte";
|
|
10
10
|
import Description from "./sheet-description.svelte";
|
|
11
|
+
import Body from "./sheet-body.svelte";
|
|
11
12
|
|
|
12
13
|
export {
|
|
13
14
|
Root,
|
|
@@ -20,6 +21,7 @@ export {
|
|
|
20
21
|
Footer,
|
|
21
22
|
Title,
|
|
22
23
|
Description,
|
|
24
|
+
Body,
|
|
23
25
|
//
|
|
24
26
|
Root as Sheet,
|
|
25
27
|
Close as SheetClose,
|
|
@@ -31,4 +33,5 @@ export {
|
|
|
31
33
|
Footer as SheetFooter,
|
|
32
34
|
Title as SheetTitle,
|
|
33
35
|
Description as SheetDescription,
|
|
36
|
+
Body as SheetBody,
|
|
34
37
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "../../lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!-- The scrolling middle of a sheet, padded like the header and footer so the three
|
|
14
|
+
share one left edge. It takes the height the header and footer leave. -->
|
|
15
|
+
<div
|
|
16
|
+
bind:this={ref}
|
|
17
|
+
data-slot="sheet-body"
|
|
18
|
+
class={cn("flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto overscroll-contain px-4", className)}
|
|
19
|
+
{...restProps}
|
|
20
|
+
>
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</div>
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
class: className,
|
|
20
20
|
side = "right",
|
|
21
21
|
showCloseButton = true,
|
|
22
|
+
floating,
|
|
22
23
|
portalProps,
|
|
23
24
|
children,
|
|
24
25
|
...restProps
|
|
@@ -26,20 +27,38 @@
|
|
|
26
27
|
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SheetPortal>>;
|
|
27
28
|
side?: Side;
|
|
28
29
|
showCloseButton?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* From `sm` up the sheet floats 8px off the viewport edge at the overlay radius, like
|
|
32
|
+
* SidePanel. On by default for left and right sheets; `floating={false}` keeps one flush.
|
|
33
|
+
* Under `sm` it stays full bleed, since a phone needs the width.
|
|
34
|
+
*/
|
|
35
|
+
floating?: boolean;
|
|
29
36
|
children: Snippet;
|
|
30
37
|
} = $props();
|
|
31
38
|
|
|
39
|
+
// Side sheets float unless a caller opts out; a bottom sheet stays docked.
|
|
40
|
+
const floats = $derived(floating ?? side !== "bottom");
|
|
41
|
+
|
|
32
42
|
// Plain side utilities, not `data-[side=x]:` variants, because the variant outranks a
|
|
33
43
|
// caller's `sm:max-w-xl` and tailwind-merge can't dedupe across modifier sets.
|
|
34
44
|
const SIDE_CLASS: Record<Side, string> = {
|
|
35
45
|
// h-dvh, not h-full: on iOS Safari a fixed panel sizes against the layout
|
|
36
46
|
// viewport, which is taller than the visible area, so h-full hides the footer.
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
// No side border: `shadow-edge-overlay` draws the hairline and the depth.
|
|
48
|
+
right: "inset-y-0 right-0 h-dvh w-3/4 sm:max-w-sm",
|
|
49
|
+
left: "inset-y-0 left-0 h-dvh w-3/4 sm:max-w-sm",
|
|
39
50
|
// Full width, capped at 80vh so the overlay stays visible. The safe-area
|
|
40
51
|
// padding keeps the last row clear of the home indicator.
|
|
41
52
|
bottom:
|
|
42
|
-
"inset-x-0 bottom-0 h-auto max-h-[80vh] w-full rounded-t-
|
|
53
|
+
"inset-x-0 bottom-0 h-auto max-h-[80vh] w-full rounded-t-2xl pb-[env(safe-area-inset-bottom)]",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Floating: an 8px inset (`2`) on the sheet's own sides, the height cut by both gutters,
|
|
57
|
+
// and the overlay radius on every corner (DESIGN.md rule 2).
|
|
58
|
+
const FLOATING_CLASS: Record<Side, string> = {
|
|
59
|
+
right: "sm:inset-y-2 sm:right-2 sm:h-[calc(100dvh-1rem)] sm:rounded-2xl",
|
|
60
|
+
left: "sm:inset-y-2 sm:left-2 sm:h-[calc(100dvh-1rem)] sm:rounded-2xl",
|
|
61
|
+
bottom: "sm:inset-x-2 sm:bottom-2 sm:w-[calc(100%-1rem)] sm:rounded-2xl",
|
|
43
62
|
};
|
|
44
63
|
</script>
|
|
45
64
|
|
|
@@ -49,9 +68,12 @@
|
|
|
49
68
|
bind:ref
|
|
50
69
|
data-slot="sheet-content"
|
|
51
70
|
data-side={side}
|
|
71
|
+
data-floating={floats || undefined}
|
|
72
|
+
data-close={showCloseButton || undefined}
|
|
52
73
|
class={cn(
|
|
53
|
-
"t-sheet bg-
|
|
74
|
+
"t-sheet bg-popover text-popover-foreground fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm shadow-edge-overlay",
|
|
54
75
|
SIDE_CLASS[side],
|
|
76
|
+
floats && FLOATING_CLASS[side],
|
|
55
77
|
className,
|
|
56
78
|
)}
|
|
57
79
|
{...restProps}
|
|
@@ -60,7 +82,7 @@
|
|
|
60
82
|
{#if showCloseButton}
|
|
61
83
|
<SheetPrimitive.Close data-slot="sheet-close">
|
|
62
84
|
{#snippet child({ props })}
|
|
63
|
-
<Button variant="ghost" class="absolute top-3 right-
|
|
85
|
+
<Button variant="ghost" class="absolute top-3 right-2" size="sm" icon {...props}>
|
|
64
86
|
<X size={16} />
|
|
65
87
|
<span class="sr-only">Close</span>
|
|
66
88
|
</Button>
|
|
@@ -6,14 +6,26 @@
|
|
|
6
6
|
ref = $bindable(null),
|
|
7
7
|
class: className,
|
|
8
8
|
children,
|
|
9
|
+
band = false,
|
|
9
10
|
...restProps
|
|
10
|
-
}: WithElementRef<HTMLAttributes<HTMLDivElement>>
|
|
11
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
12
|
+
/**
|
|
13
|
+
* A hairline on top and a quiet surface edge to edge, following the sheet's bottom
|
|
14
|
+
* corners when it floats. A bare `data-band` attribute does the same.
|
|
15
|
+
*/
|
|
16
|
+
band?: boolean;
|
|
17
|
+
} = $props();
|
|
11
18
|
</script>
|
|
12
19
|
|
|
13
20
|
<div
|
|
14
21
|
bind:this={ref}
|
|
15
22
|
data-slot="sheet-footer"
|
|
16
|
-
|
|
23
|
+
data-band={band || undefined}
|
|
24
|
+
class={cn(
|
|
25
|
+
"mt-auto flex flex-col gap-2 p-4",
|
|
26
|
+
"data-band:border-border data-band:bg-muted/50 data-band:border-t data-band:rounded-b-[inherit] data-band:flex-col-reverse data-band:sm:flex-row data-band:sm:justify-end",
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
17
29
|
{...restProps}
|
|
18
30
|
>
|
|
19
31
|
{@render children?.()}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* The sheet slide: transitions.dev panel reveal, owned by the sheet. */
|
|
2
2
|
|
|
3
|
+
/* The same 300ms open and 250ms close as before, named. DESIGN.md hasn't decided
|
|
4
|
+
whether the sheet takes Vaul's 500ms drawer timing, so the values hold. */
|
|
3
5
|
:root {
|
|
4
|
-
--sheet-open-dur:
|
|
5
|
-
--sheet-close-dur:
|
|
6
|
+
--sheet-open-dur: var(--duration-slow);
|
|
7
|
+
--sheet-close-dur: var(--duration-fast);
|
|
6
8
|
--sheet-translate: 100%;
|
|
7
|
-
--sheet-ease:
|
|
9
|
+
--sheet-ease: var(--ease-smooth-out);
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
.t-sheet {
|
|
@@ -41,8 +43,12 @@
|
|
|
41
43
|
opacity var(--sheet-open-dur) var(--sheet-ease);
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
/* Reduced motion keeps the fade and drops the slide (DESIGN.md rule 13). */
|
|
44
47
|
@media (prefers-reduced-motion: reduce) {
|
|
45
|
-
.t-sheet
|
|
46
|
-
|
|
48
|
+
.t-sheet,
|
|
49
|
+
.t-sheet[data-side],
|
|
50
|
+
.t-sheet[data-state] {
|
|
51
|
+
transform: none;
|
|
52
|
+
transition-property: opacity;
|
|
47
53
|
}
|
|
48
54
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const mergedProps = $derived({
|
|
17
17
|
class: cn(
|
|
18
|
-
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 w-5 rounded-md p-0 focus-visible:ring-
|
|
18
|
+
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 w-5 rounded-md p-0 focus-visible:shadow-(--ring-focus) [&>svg]:size-4 flex aspect-square items-center justify-center outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 lg:after:hidden [&>svg]:shrink-0",
|
|
19
19
|
className,
|
|
20
20
|
),
|
|
21
21
|
"data-slot": "sidebar-group-action",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const mergedProps = $derived({
|
|
17
17
|
class: cn(
|
|
18
|
-
"text-sidebar-foreground/90 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 group-data-[collapsible=icon]:pointer-events-none focus-visible:ring-
|
|
18
|
+
"text-sidebar-foreground/90 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 group-data-[collapsible=icon]:pointer-events-none focus-visible:shadow-(--ring-focus) [&>svg]:size-4 flex shrink-0 items-center outline-hidden [&>svg]:shrink-0",
|
|
19
19
|
className,
|
|
20
20
|
),
|
|
21
21
|
"data-slot": "sidebar-group-label",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
bind:this={ref}
|
|
15
15
|
data-slot="sidebar-inset"
|
|
16
16
|
class={cn(
|
|
17
|
-
"bg-background transition-[margin] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] motion-reduce:transition-none lg:peer-data-[variant=inset]:m-2 lg:peer-data-[variant=inset]:ml-0 lg:peer-data-[variant=inset]:rounded-
|
|
17
|
+
"bg-background transition-[margin] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] motion-reduce:transition-none lg:peer-data-[variant=inset]:m-2 lg:peer-data-[variant=inset]:ml-0 lg:peer-data-[variant=inset]:rounded-3xl lg:peer-data-[variant=inset]:bg-card lg:peer-data-[variant=inset]:shadow-card lg:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 relative flex min-h-0 w-full flex-1 flex-col",
|
|
18
18
|
className,
|
|
19
19
|
)}
|
|
20
20
|
{...restProps}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const mergedProps = $derived({
|
|
19
19
|
class: cn(
|
|
20
|
-
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-
|
|
20
|
+
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-foreground/4 hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 aspect-square w-5 rounded-md p-0 peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 focus-visible:shadow-(--ring-focus) [&>svg]:size-4 flex items-center justify-center outline-hidden transition-[background-color,color,transform] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 lg:after:hidden [&>svg]:shrink-0",
|
|
21
21
|
showOnHover &&
|
|
22
22
|
"peer-data-active/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-open:opacity-100 lg:opacity-0",
|
|
23
23
|
className,
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
3
|
|
|
4
4
|
export const sidebarMenuButtonVariants = tv({
|
|
5
|
-
base: "ring-sidebar-ring hover:bg-sidebar-
|
|
5
|
+
base: "ring-sidebar-ring hover:bg-sidebar-foreground/4 hover:text-sidebar-accent-foreground active:bg-sidebar-foreground/8 active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left text-sm transition-[width,height,padding,background-color,color] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:shadow-(--ring-focus) data-active:font-medium peer/menu-button group/menu-button flex w-full items-center overflow-hidden outline-hidden disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
|
-
default: "hover:bg-sidebar-
|
|
8
|
+
default: "hover:bg-sidebar-foreground/4 hover:text-sidebar-accent-foreground",
|
|
9
9
|
outline:
|
|
10
10
|
"bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
|
|
11
11
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
const mergedProps = $derived({
|
|
21
21
|
class: cn(
|
|
22
|
-
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-
|
|
22
|
+
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-foreground/4 hover:text-sidebar-accent-foreground active:bg-sidebar-foreground/8 active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 transition-[background-color,color] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] any-pointer-coarse:h-11 focus-visible:shadow-(--ring-focus) data-[size=md]:text-sm data-[size=sm]:text-xs [&>svg]:size-4 flex min-w-0 -translate-x-px items-center overflow-hidden outline-hidden group-data-[collapsible=icon]:hidden disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:shrink-0",
|
|
23
23
|
className,
|
|
24
24
|
),
|
|
25
25
|
"data-slot": "sidebar-menu-sub-button",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
<div
|
|
101
101
|
data-sidebar="sidebar"
|
|
102
102
|
data-slot="sidebar-inner"
|
|
103
|
-
class="bg-sidebar group-data-[variant=floating]:
|
|
103
|
+
class="bg-sidebar group-data-[variant=floating]:rounded-xl group-data-[variant=floating]:shadow-card flex size-full flex-col"
|
|
104
104
|
>
|
|
105
105
|
{@render children?.()}
|
|
106
106
|
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** DESIGN.md rule 20: a skeleton waits this long, so a fast answer never shows one. */
|
|
3
|
+
export const SHOW_DELAY_MS = 150;
|
|
4
|
+
/** DESIGN.md rule 20: once shown, a skeleton stays this long, so it never blinks. */
|
|
5
|
+
export const MIN_VISIBLE_MS = 300;
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { untrack, type Snippet } from "svelte";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
/** The data is on its way. */
|
|
13
|
+
loading: boolean;
|
|
14
|
+
/** What shows while it loads: one or more `Skeleton`s shaped like the content. */
|
|
15
|
+
skeleton: Snippet;
|
|
16
|
+
/** The content, drawn once loading ends and the skeleton has had its minimum time. */
|
|
17
|
+
children: Snippet;
|
|
18
|
+
/** Ms to wait before the skeleton shows. */
|
|
19
|
+
showDelay?: number;
|
|
20
|
+
/** Ms the skeleton stays once it shows. */
|
|
21
|
+
minVisible?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
loading,
|
|
26
|
+
skeleton,
|
|
27
|
+
children,
|
|
28
|
+
showDelay = SHOW_DELAY_MS,
|
|
29
|
+
minVisible = MIN_VISIBLE_MS,
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
// `wait` draws nothing: the first 150ms of a load, which a fast answer never leaves.
|
|
33
|
+
let phase = $state<"wait" | "skeleton" | "content">(untrack(() => (loading ? "wait" : "content")));
|
|
34
|
+
let shownAt = 0;
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
const isLoading = loading;
|
|
38
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
39
|
+
untrack(() => {
|
|
40
|
+
if (isLoading) {
|
|
41
|
+
if (phase === "skeleton") return;
|
|
42
|
+
phase = "wait";
|
|
43
|
+
timer = setTimeout(() => {
|
|
44
|
+
phase = "skeleton";
|
|
45
|
+
shownAt = performance.now();
|
|
46
|
+
}, showDelay);
|
|
47
|
+
} else if (phase === "skeleton") {
|
|
48
|
+
const left = minVisible - (performance.now() - shownAt);
|
|
49
|
+
if (left > 0) timer = setTimeout(() => (phase = "content"), left);
|
|
50
|
+
else phase = "content";
|
|
51
|
+
} else {
|
|
52
|
+
phase = "content";
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return () => clearTimeout(timer);
|
|
56
|
+
});
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
{#if phase === "skeleton"}
|
|
60
|
+
{@render skeleton()}
|
|
61
|
+
{:else if phase === "content"}
|
|
62
|
+
{@render children()}
|
|
63
|
+
{/if}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createRawSnippet, flushSync, mount, unmount } from "svelte";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
|
|
3
|
+
import SkeletonDelay from "./skeleton-delay.svelte";
|
|
4
|
+
|
|
5
|
+
const skeleton = createRawSnippet(() => ({ render: () => "<i>skeleton</i>" }));
|
|
6
|
+
const children = createRawSnippet(() => ({ render: () => "<b>content</b>" }));
|
|
7
|
+
|
|
8
|
+
function render(loading: boolean) {
|
|
9
|
+
const props = $state({ loading, skeleton, children });
|
|
10
|
+
const target = document.createElement("div");
|
|
11
|
+
const app = mount(SkeletonDelay, { target, props });
|
|
12
|
+
flushSync();
|
|
13
|
+
return {
|
|
14
|
+
shows: () => target.textContent,
|
|
15
|
+
set: (next: boolean) => {
|
|
16
|
+
props.loading = next;
|
|
17
|
+
flushSync();
|
|
18
|
+
},
|
|
19
|
+
wait: (ms: number) => {
|
|
20
|
+
vi.advanceTimersByTime(ms);
|
|
21
|
+
flushSync();
|
|
22
|
+
},
|
|
23
|
+
done: () => void unmount(app),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("SkeletonDelay", () => {
|
|
28
|
+
beforeEach(() => vi.useFakeTimers());
|
|
29
|
+
afterEach(() => vi.useRealTimers());
|
|
30
|
+
|
|
31
|
+
it("shows nothing for a fast answer, then the content", () => {
|
|
32
|
+
const view = render(true);
|
|
33
|
+
expect(view.shows()).toBe("");
|
|
34
|
+
view.wait(100);
|
|
35
|
+
view.set(false);
|
|
36
|
+
expect(view.shows()).toBe("content");
|
|
37
|
+
view.done();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("shows the skeleton after 150ms and holds it 300ms", () => {
|
|
41
|
+
const view = render(true);
|
|
42
|
+
view.wait(150);
|
|
43
|
+
expect(view.shows()).toBe("skeleton");
|
|
44
|
+
view.wait(50);
|
|
45
|
+
view.set(false);
|
|
46
|
+
expect(view.shows()).toBe("skeleton");
|
|
47
|
+
view.wait(250);
|
|
48
|
+
expect(view.shows()).toBe("content");
|
|
49
|
+
view.done();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("draws the content at once when nothing is loading", () => {
|
|
53
|
+
const view = render(false);
|
|
54
|
+
expect(view.shows()).toBe("content");
|
|
55
|
+
view.done();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
<div
|
|
13
13
|
bind:this={ref}
|
|
14
14
|
data-slot="skeleton"
|
|
15
|
-
class={cn(
|
|
15
|
+
class={cn(
|
|
16
|
+
// A foreground wash on light, because `bg-muted` (L 0.94) vanished on the 0.945 canvas.
|
|
17
|
+
"bg-foreground/6 dark:bg-muted rounded-sm animate-pulse motion-reduce:animate-none",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
16
20
|
{...restProps}
|
|
17
21
|
></div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
data-slot="slider-thumb"
|
|
46
46
|
aria-label={thumbLabel}
|
|
47
47
|
aria-valuetext={valueText}
|
|
48
|
-
class="bg-
|
|
48
|
+
class="bg-card block size-5 cursor-grab rounded-full border-2 border-current shadow-[var(--elev-btn)] transition-[transform,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] outline-none hover:scale-110 focus-visible:shadow-(--ring-focus) active:cursor-grabbing data-active:scale-110 data-active:shadow-[var(--elev-raised)] pointer-coarse:size-6 after:absolute after:top-1/2 after:left-1/2 after:size-8 after:-translate-x-1/2 after:-translate-y-1/2 any-pointer-coarse:after:size-11"
|
|
49
49
|
/>
|
|
50
50
|
{/each}
|
|
51
51
|
{/snippet}
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
data-slot="switch"
|
|
20
20
|
data-size={size}
|
|
21
21
|
class={cn(
|
|
22
|
-
"data-checked:bg-primary data-unchecked:bg-input focus-visible:
|
|
22
|
+
"data-checked:bg-primary data-unchecked:bg-input focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent aria-invalid:ring-3 data-[size=default]:h-[18px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-[background-color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] outline-none after:absolute after:-inset-x-3 after:top-1/2 after:h-[max(100%,2rem)] after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
23
23
|
className,
|
|
24
24
|
)}
|
|
25
25
|
{...restProps}
|
|
26
26
|
>
|
|
27
27
|
<SwitchPrimitive.Thumb
|
|
28
28
|
data-slot="switch-thumb"
|
|
29
|
-
class="bg-
|
|
29
|
+
class="bg-card dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground rounded-full group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 pointer-events-none block ring-0 transition-transform duration-[var(--duration-medium)] ease-[var(--ease-smooth-out)] rtl:data-[state=checked]:translate-x-[calc(-100%)]"
|
|
30
30
|
/>
|
|
31
31
|
</SwitchPrimitive.Root>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
bind:this={ref}
|
|
15
15
|
data-slot="table-row"
|
|
16
16
|
class={cn(
|
|
17
|
-
"data-[state=selected]:bg-muted transition-[background-color
|
|
17
|
+
"data-[state=selected]:bg-muted transition-[background-color] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] hover:bg-muted/20",
|
|
18
18
|
className,
|
|
19
19
|
)}
|
|
20
20
|
{...restProps}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
shows the trigger's own active background and never an empty pill. -->
|
|
30
30
|
<span
|
|
31
31
|
aria-hidden="true"
|
|
32
|
-
class="bg-
|
|
32
|
+
class="bg-card dark:bg-input/30 pointer-events-none absolute inset-y-[3px] left-0 z-0 w-0 rounded-[calc(var(--radius-lg)-3px)] border border-transparent opacity-0 shadow-btn transition-[transform,width,opacity] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] will-change-transform motion-reduce:transition-opacity"
|
|
33
33
|
{@attach slidingPill}
|
|
34
34
|
></span>
|
|
35
35
|
{@render children?.()}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
bind:ref
|
|
14
14
|
data-slot="tabs-trigger"
|
|
15
15
|
class={cn(
|
|
16
|
-
|
|
16
|
+
// Muted at rest, foreground on hover and when active, so each state reads stronger than rest.
|
|
17
|
+
"data-[state=active]:bg-card data-[state=active]:text-foreground hover:text-foreground focus-visible:shadow-(--ring-focus) focus-visible:data-[state=active]:shadow-(--ring-focus) dark:data-[state=active]:bg-input/30 text-muted-foreground group-data-[pill=on]/tabs-list:data-[state=active]:border-transparent group-data-[pill=on]/tabs-list:data-[state=active]:bg-transparent group-data-[pill=on]/tabs-list:data-[state=active]:shadow-none dark:group-data-[pill=on]/tabs-list:data-[state=active]:bg-transparent relative z-[1] inline-flex h-full flex-1 items-center justify-center gap-1.5 rounded-[calc(var(--radius-lg)-3px)] border border-transparent px-2 text-sm group-data-[size=sm]/tabs-list:text-[0.8rem] font-medium whitespace-nowrap after:absolute after:top-1/2 after:left-0 after:h-[max(100%,2rem)] after:w-full after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] transition-[color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-btn [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
17
18
|
className,
|
|
18
19
|
)}
|
|
19
20
|
{...restProps}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
bind:this={ref}
|
|
16
16
|
data-slot={dataSlot}
|
|
17
17
|
class={cn(
|
|
18
|
-
"border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-
|
|
18
|
+
"border-input dark:bg-input/30 enabled:hover:border-foreground/20 focus-visible:border-ring focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border bg-transparent px-2.5 py-2 text-base shadow-field transition-[color,border-color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] aria-invalid:ring-3 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
19
19
|
className,
|
|
20
20
|
)}
|
|
21
21
|
bind:value
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import { type VariantProps, tv } from "tailwind-variants";
|
|
3
3
|
|
|
4
4
|
export const toggleVariants = tv({
|
|
5
|
-
base: "relative after:absolute after:top-1/2 after:left-1/2 after:h-[max(100%,2rem)] after:w-[max(100%,2rem)] after:-translate-x-1/2 after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] any-pointer-coarse:after:w-[max(100%,2.75rem)] hover:bg-muted hover:text-muted-foreground data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:
|
|
5
|
+
base: "relative after:absolute after:top-1/2 after:left-1/2 after:h-[max(100%,2rem)] after:w-[max(100%,2rem)] after:-translate-x-1/2 after:-translate-y-1/2 any-pointer-coarse:after:h-[max(100%,2.75rem)] any-pointer-coarse:after:w-[max(100%,2.75rem)] hover:bg-muted hover:text-muted-foreground data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[background-color,color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
8
|
default: "bg-transparent",
|
|
9
9
|
outline:
|
|
10
|
-
"border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-
|
|
10
|
+
"border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-btn",
|
|
11
11
|
},
|
|
12
12
|
size: {
|
|
13
13
|
// Same steps as Button: sm 28, default 32, lg 36. 40 went with the scale.
|
|
@@ -59,7 +59,7 @@ get along, so we shut typescript up by casting `value` to `never`.
|
|
|
59
59
|
data-spacing={spacing}
|
|
60
60
|
style={`--gap: ${spacing}`}
|
|
61
61
|
class={cn(
|
|
62
|
-
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-
|
|
62
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-btn",
|
|
63
63
|
className,
|
|
64
64
|
)}
|
|
65
65
|
{...restProps}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{sideOffset}
|
|
28
28
|
{side}
|
|
29
29
|
class={cn(
|
|
30
|
-
"t-dropdown z-50 w-fit max-w-xs rounded-lg
|
|
30
|
+
"t-dropdown z-50 w-fit max-w-xs rounded-lg bg-popover px-2.5 py-1.5 text-xs text-popover-foreground shadow-edge-raised select-none outline-none",
|
|
31
31
|
"has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm",
|
|
32
32
|
className,
|
|
33
33
|
)}
|