@heyramzi/ui 6.1.0 → 8.0.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/COMPONENTS.md +32 -40
- package/README.md +10 -4
- package/package.json +31 -29
- package/scripts/sync-shadcn.mjs +29 -16
- package/scripts/sync-transitions.mjs +11 -11
- package/src/ai/ai-button/AiButton.svelte +86 -0
- package/src/ai/ai-button/index.ts +4 -0
- package/src/ai/confirmation/Confirmation.svelte +8 -8
- package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
- package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
- package/src/ai/generation-progress/index.ts +7 -0
- package/src/ai/index.ts +16 -24
- package/src/ai/prompt-input/PromptInput.svelte +19 -1
- package/src/ai/prompt-input/PromptInputSubmit.svelte +9 -3
- package/src/ai/reasoning/Reasoning.svelte +11 -8
- package/src/ambient.d.ts +4 -0
- package/src/animations/README.md +17 -17
- package/src/animations/index.css +3 -14
- package/src/animations/upstream-coverage.test.ts +1 -1
- package/src/components/copy/clipboard.test.ts +206 -0
- package/src/components/copy/clipboard.ts +49 -0
- package/src/components/copy/copied.svelte.ts +39 -0
- package/src/components/copy/copy-button.svelte +175 -0
- package/src/components/copy/copy-button.test.ts +110 -0
- package/src/components/copy/index.ts +3 -0
- package/src/components/data-table/data-table.test.ts +134 -0
- package/src/components/data-table/data-table.ts +77 -0
- package/src/components/data-table/index.ts +20 -0
- package/src/components/data-table/infinite-scroll.svelte +69 -0
- package/src/components/data-table/pagination.svelte +62 -0
- package/src/components/data-table/row-arrow.svelte +28 -0
- package/src/components/data-table/row-context-menu.svelte +106 -0
- package/src/components/data-table/row-context-menu.test.ts +18 -0
- package/src/components/data-table/sortable-head.svelte +56 -0
- package/src/components/data-table/table-skeleton.svelte +83 -0
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
- package/src/components/hold-to-confirm/index.ts +1 -0
- package/src/components/icon/icon.svelte +23 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-flag/language-flag.svelte +60 -0
- package/src/components/list-page/detail-empty.svelte +50 -0
- package/src/components/list-page/detail-header.svelte +107 -0
- package/src/components/list-page/detail-panel.svelte +90 -0
- package/src/components/list-page/detail-row.svelte +36 -0
- package/src/components/list-page/detail-stack.svelte +33 -0
- package/src/components/list-page/empty-state.svelte +91 -0
- package/src/components/list-page/filter-item.svelte +49 -0
- package/src/components/list-page/filter-popover.svelte +88 -0
- package/src/components/list-page/filter-section.ts +12 -0
- package/src/components/list-page/grouped-list.svelte +116 -0
- package/src/components/list-page/index.ts +28 -0
- package/src/components/list-page/list-page-shell.svelte +195 -0
- package/src/components/list-page/list-table.ts +19 -0
- package/src/components/list-page/master-detail.svelte +206 -0
- package/src/components/list-page/master-detail.test.ts +37 -0
- package/src/components/list-page/notice-bar.svelte +68 -0
- package/src/components/list-page/prev-next.svelte +104 -0
- package/src/components/list-page/queue-rail.ts +25 -0
- package/src/components/list-page/rail-body.svelte +29 -0
- package/src/components/list-page/rail-empty.svelte +51 -0
- package/src/components/list-page/rail-head.svelte +49 -0
- package/src/components/list-page/rail-row.svelte +163 -0
- package/src/components/list-page/section-host.ts +24 -0
- package/src/components/list-page/section-label.svelte +39 -0
- package/src/components/list-page/step.test.ts +21 -0
- package/src/components/list-page/step.ts +10 -0
- package/src/components/list-page/toolbar-search.svelte +63 -0
- package/src/components/list-page/toolbar-trigger.ts +23 -0
- package/src/components/list-page/types.ts +1 -0
- package/src/components/locale-switch/index.ts +1 -0
- package/src/components/locale-switch/locale-switch.svelte +59 -0
- package/src/components/mark/index.ts +1 -0
- package/src/components/mark/mark.svelte +70 -0
- package/src/components/open-in/index.ts +6 -0
- package/src/components/open-in/open-in.svelte +106 -0
- package/src/components/picker/index.ts +4 -0
- package/src/components/picker/mark-picker.svelte +84 -0
- package/src/components/picker/picker.svelte +143 -0
- package/src/components/picker/trigger.ts +42 -0
- package/src/components/picker/types.ts +26 -0
- package/src/components/segmented/animated-tabs.svelte +175 -0
- package/src/components/segmented/collapse.test.ts +49 -0
- package/src/components/segmented/collapse.ts +39 -0
- package/src/components/segmented/index.ts +41 -0
- package/src/components/segmented/section-runs.svelte +79 -0
- package/src/components/segmented/segment.svelte +106 -0
- package/src/components/segmented/segmented-filter.svelte +125 -0
- package/src/components/segmented/segmented-nav.svelte +82 -0
- package/src/components/segmented/segmented-overflow.svelte +85 -0
- package/src/components/segmented/segmented-track.svelte +181 -0
- package/src/components/segmented/segmented.test.ts +20 -0
- package/src/components/segmented/segmented.ts +80 -0
- package/src/components/star-rating-input/index.ts +9 -0
- package/src/components/star-rating-input/star-rating-input.svelte +256 -0
- package/src/components/svg/index.ts +1 -0
- package/src/components/svg/svg.svelte +9 -0
- package/src/effects/border-beam/BorderBeam.svelte +1 -1
- package/src/effects/image-comparison/ImageComparison.css +42 -8
- package/src/effects/image-comparison/ImageComparison.svelte +138 -21
- package/src/effects/image-comparison/compare.test.ts +17 -0
- package/src/effects/image-comparison/compare.ts +5 -0
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
- package/src/effects/magic-card/MagicCard.css +47 -0
- package/src/effects/magic-card/MagicCard.svelte +57 -0
- package/src/effects/marquee/Marquee.css +27 -1
- package/src/effects/marquee/Marquee.svelte +67 -3
- package/src/effects/reveal/index.ts +2 -1
- package/src/effects/reveal/observe.test.ts +60 -0
- package/src/effects/reveal/observe.ts +33 -1
- package/src/effects/reveal/types.ts +7 -0
- package/src/effects/theme-toggle/ThemeToggle.css +199 -6
- package/src/effects/theme-toggle/ThemeToggle.svelte +51 -26
- package/src/gallery.test.ts +52 -0
- package/src/index.ts +5 -1
- package/src/lib/panel.ts +7 -1
- package/src/lib/sliding-pill.ts +68 -0
- package/src/lib/theme-transition.ts +41 -13
- package/src/lib/tone.ts +48 -0
- package/src/shadcn/accordion/accordion-trigger.svelte +15 -6
- package/src/shadcn/alert/alert.svelte +3 -1
- package/src/shadcn/badge/badge.svelte +8 -7
- package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
- package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +2 -2
- package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
- package/src/shadcn/calendar/calendar-next-button.svelte +2 -2
- package/src/shadcn/calendar/calendar-prev-button.svelte +2 -2
- package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
- package/src/shadcn/calendar/calendar.svelte +6 -0
- package/src/shadcn/code-block/code-block-actions.svelte +23 -0
- package/src/shadcn/code-block/code-block-content.svelte +55 -0
- package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
- package/src/shadcn/code-block/code-block-footer.svelte +20 -0
- package/src/shadcn/code-block/code-block-header.svelte +23 -0
- package/src/shadcn/code-block/code-block-line.svelte +71 -0
- package/src/shadcn/code-block/code-block-title.svelte +23 -0
- package/src/shadcn/code-block/code-block.svelte +47 -0
- package/src/shadcn/code-block/index.ts +42 -0
- package/src/shadcn/command/index.ts +3 -0
- package/src/shadcn/field/index.ts +3 -0
- package/src/shadcn/field/simple-field.svelte +71 -0
- package/src/shadcn/field/simple-field.test.ts +47 -0
- package/src/shadcn/hooks/highlighter.ts +132 -0
- package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
- package/src/shadcn/kbd/index.ts +10 -0
- package/src/shadcn/kbd/kbd-group.svelte +20 -0
- package/src/shadcn/kbd/kbd.svelte +23 -0
- package/src/shadcn/message-scroller/index.ts +28 -0
- package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
- package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
- package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
- package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
- package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
- package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
- package/src/shadcn/radio-group/radio-group-item.svelte +3 -2
- package/src/shadcn/sheet/sheet-content.svelte +1 -0
- package/src/shadcn/sheet/sheet.css +48 -0
- package/src/shadcn/sonner/sonner.svelte +7 -10
- package/src/shadcn/tabs/tabs-list.svelte +13 -2
- package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
- package/src/shadcn/toggle-group/toggle-group.svelte +10 -3
- package/src/shadcn/trace/index.ts +29 -0
- package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible.svelte +20 -0
- package/src/shadcn/trace/trace-description.svelte +20 -0
- package/src/shadcn/trace/trace-icon.svelte +24 -0
- package/src/shadcn/trace/trace-line.svelte +31 -0
- package/src/shadcn/trace/trace-title.svelte +20 -0
- package/src/shadcn/trace/trace.svelte +23 -0
- package/src/ai/composer/Composer.css +0 -108
- package/src/ai/composer/Composer.svelte +0 -111
- package/src/ai/composer/index.ts +0 -10
- package/src/ai/conversation/Conversation.svelte +0 -28
- package/src/ai/conversation/ConversationContent.svelte +0 -35
- package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
- package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
- package/src/ai/conversation/index.ts +0 -33
- package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
- package/src/animations/accordion.css +0 -58
- package/src/animations/icon-swap.css +0 -43
- package/src/animations/notification-badge.css +0 -81
- package/src/animations/panel.css +0 -86
- package/src/animations/reasoning-stream.css +0 -49
- package/src/animations/skeleton-reveal.css +0 -97
- package/src/animations/streaming-text.css +0 -31
- package/src/animations/tabs-sliding.css +0 -83
- package/src/animations/texts-reveal.css +0 -70
- package/src/animations/tooltip.css +0 -72
- package/src/animations/view-transition-reveal.ts +0 -74
- package/src/charts/animated-value.css +0 -7
- package/src/charts/animated-value.svelte +0 -110
- package/src/charts/area-chart.css +0 -29
- package/src/charts/area-chart.svelte +0 -195
- package/src/charts/bar-chart.css +0 -119
- package/src/charts/bar-chart.svelte +0 -269
- package/src/charts/charts.css +0 -201
- package/src/charts/crosshair.svelte +0 -35
- package/src/charts/donut-chart.css +0 -56
- package/src/charts/donut-chart.svelte +0 -158
- package/src/charts/edge-fade.svelte +0 -43
- package/src/charts/edge-fade.ts +0 -16
- package/src/charts/figure.css +0 -100
- package/src/charts/figure.svelte +0 -122
- package/src/charts/gauge-chart.css +0 -94
- package/src/charts/gauge-chart.svelte +0 -303
- package/src/charts/hover-band.test.ts +0 -30
- package/src/charts/hover-band.ts +0 -21
- package/src/charts/hover-focus.svelte +0 -85
- package/src/charts/index.ts +0 -30
- package/src/charts/legend.css +0 -131
- package/src/charts/legend.svelte +0 -112
- package/src/charts/line-chart.css +0 -26
- package/src/charts/line-chart.svelte +0 -256
- package/src/charts/motion.ts +0 -19
- package/src/charts/number-chart.css +0 -168
- package/src/charts/number-chart.svelte +0 -177
- package/src/charts/primitives/chart-container.svelte +0 -57
- package/src/charts/primitives/chart-style.svelte +0 -36
- package/src/charts/primitives/chart-tooltip.svelte +0 -161
- package/src/charts/primitives/chart-utils.ts +0 -54
- package/src/charts/primitives/index.ts +0 -11
- package/src/charts/reveal.svelte +0 -61
- package/src/charts/ring-chart.css +0 -96
- package/src/charts/ring-chart.svelte +0 -158
- package/src/charts/sparkline.css +0 -45
- package/src/charts/sparkline.svelte +0 -140
- package/src/charts/stat-card.css +0 -103
- package/src/charts/stat-card.svelte +0 -267
- package/src/charts/time-series-chart.css +0 -28
- package/src/charts/time-series-chart.svelte +0 -179
- package/src/charts/trend-pill.css +0 -38
- package/src/charts/trend-pill.svelte +0 -48
- package/src/lib/hooks/is-mobile.svelte.ts +0 -11
- package/src/lib/utils.ts +0 -45
- package/src/showcase/AiSection.svelte +0 -157
- package/src/showcase/AnimationsSection.svelte +0 -1658
- package/src/showcase/ChartsSection.svelte +0 -458
- package/src/showcase/EffectsSection.svelte +0 -152
- package/src/showcase/ShadcnSection.svelte +0 -27
- package/src/showcase/ShowcaseImport.svelte +0 -126
- package/src/showcase/ShowcaseList.svelte +0 -132
- package/src/showcase/ShowcaseNav.svelte +0 -247
- package/src/showcase/ShowcaseSection.svelte +0 -112
- package/src/showcase/ShowcaseShell.svelte +0 -55
- package/src/showcase/ShowcaseStage.svelte +0 -342
- package/src/showcase/ShowcaseVariant.svelte +0 -109
- package/src/showcase/assets/image-reveal/art-1.jpg +0 -0
- package/src/showcase/assets/image-reveal/art-2.jpg +0 -0
- package/src/showcase/assets/image-reveal/art-3.jpg +0 -0
- package/src/showcase/check.svg +0 -1
- package/src/showcase/chevron-arms.svg +0 -2
- package/src/showcase/chevron-down.svg +0 -1
- package/src/showcase/copy.svg +0 -2
- package/src/showcase/demos/ai/confirmation.svelte +0 -59
- package/src/showcase/demos/ai/conversation.svelte +0 -59
- package/src/showcase/demos/ai/loading-state.svelte +0 -19
- package/src/showcase/demos/ai/prompt-input.svelte +0 -57
- package/src/showcase/demos/ai/reasoning.svelte +0 -19
- package/src/showcase/demos/ai/thinking-dots.svelte +0 -15
- package/src/showcase/demos/ai/voice-waveform.svelte +0 -24
- package/src/showcase/demos/effects/animated-beam.svelte +0 -50
- package/src/showcase/demos/effects/animated-group.svelte +0 -47
- package/src/showcase/demos/effects/border-beam-default.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-line.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-mono.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-sunset.svelte +0 -20
- package/src/showcase/demos/effects/image-comparison.svelte +0 -25
- package/src/showcase/demos/effects/image-reveal.svelte +0 -49
- package/src/showcase/demos/effects/in-view.svelte +0 -41
- package/src/showcase/demos/effects/integrations-hub.svelte +0 -82
- package/src/showcase/demos/effects/magnetic.svelte +0 -31
- package/src/showcase/demos/effects/marquee.svelte +0 -33
- package/src/showcase/demos/effects/progressive-blur.svelte +0 -47
- package/src/showcase/demos/effects/spotlight.svelte +0 -40
- package/src/showcase/demos/effects/text-effect.svelte +0 -55
- package/src/showcase/demos/shadcn/accordion.svelte +0 -45
- package/src/showcase/demos/shadcn/alert-dialog.svelte +0 -23
- package/src/showcase/demos/shadcn/alert.svelte +0 -30
- package/src/showcase/demos/shadcn/attachment.svelte +0 -71
- package/src/showcase/demos/shadcn/avatar.svelte +0 -30
- package/src/showcase/demos/shadcn/badge.svelte +0 -26
- package/src/showcase/demos/shadcn/breadcrumb.svelte +0 -34
- package/src/showcase/demos/shadcn/bubble.svelte +0 -36
- package/src/showcase/demos/shadcn/button.svelte +0 -11
- package/src/showcase/demos/shadcn/calendar.svelte +0 -8
- package/src/showcase/demos/shadcn/card.svelte +0 -39
- package/src/showcase/demos/shadcn/checkbox.svelte +0 -39
- package/src/showcase/demos/shadcn/collapsible.svelte +0 -20
- package/src/showcase/demos/shadcn/command.svelte +0 -48
- package/src/showcase/demos/shadcn/context-menu.svelte +0 -54
- package/src/showcase/demos/shadcn/demo-harness.svelte +0 -12
- package/src/showcase/demos/shadcn/demos.test.ts +0 -20
- package/src/showcase/demos/shadcn/dialog.svelte +0 -38
- package/src/showcase/demos/shadcn/dropdown-menu.svelte +0 -59
- package/src/showcase/demos/shadcn/empty.svelte +0 -29
- package/src/showcase/demos/shadcn/field.svelte +0 -115
- package/src/showcase/demos/shadcn/hover-card.svelte +0 -32
- package/src/showcase/demos/shadcn/index.ts +0 -358
- package/src/showcase/demos/shadcn/input-otp.svelte +0 -19
- package/src/showcase/demos/shadcn/input.svelte +0 -5
- package/src/showcase/demos/shadcn/label.svelte +0 -11
- package/src/showcase/demos/shadcn/marker.svelte +0 -30
- package/src/showcase/demos/shadcn/message.svelte +0 -75
- package/src/showcase/demos/shadcn/popover.svelte +0 -36
- package/src/showcase/demos/shadcn/progress.svelte +0 -13
- package/src/showcase/demos/shadcn/radio-group.svelte +0 -19
- package/src/showcase/demos/shadcn/scroll-area.svelte +0 -18
- package/src/showcase/demos/shadcn/select.svelte +0 -29
- package/src/showcase/demos/shadcn/separator.svelte +0 -12
- package/src/showcase/demos/shadcn/sheet.svelte +0 -48
- package/src/showcase/demos/shadcn/skeleton.svelte +0 -11
- package/src/showcase/demos/shadcn/slider.svelte +0 -7
- package/src/showcase/demos/shadcn/sonner.svelte +0 -18
- package/src/showcase/demos/shadcn/spinner.svelte +0 -11
- package/src/showcase/demos/shadcn/switch.svelte +0 -9
- package/src/showcase/demos/shadcn/table.svelte +0 -76
- package/src/showcase/demos/shadcn/tabs.svelte +0 -62
- package/src/showcase/demos/shadcn/textarea.svelte +0 -5
- package/src/showcase/demos/shadcn/toggle-group.svelte +0 -18
- package/src/showcase/demos/shadcn/toggle.svelte +0 -14
- package/src/showcase/demos/shadcn/tooltip.svelte +0 -13
- package/src/showcase/heart.svg +0 -1
- package/src/showcase/index.ts +0 -38
- package/src/showcase/kit.test.ts +0 -72
- package/src/showcase/kit.ts +0 -17
- package/src/showcase/plus.svg +0 -1
- package/src/showcase/showcase-shell.css +0 -91
- package/src/showcase/stacked-section.css +0 -15
- package/src/showcase/success-check.svg +0 -1
- package/src/showcase/types.ts +0 -29
- package/src/svg-raw.d.ts +0 -7
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
6
|
+
import * as Empty from "../../shadcn/empty/index.js";
|
|
7
|
+
import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
8
|
+
|
|
9
|
+
// =========================
|
|
10
|
+
// TYPES
|
|
11
|
+
// =========================
|
|
12
|
+
interface Props {
|
|
13
|
+
icon?: PhosphorIcon;
|
|
14
|
+
iconWeight?: IconWeight;
|
|
15
|
+
title: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
actionLabel?: string;
|
|
18
|
+
actionHref?: string;
|
|
19
|
+
onAction?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// =========================
|
|
23
|
+
// PROPS
|
|
24
|
+
// =========================
|
|
25
|
+
let {
|
|
26
|
+
icon: Icon,
|
|
27
|
+
iconWeight,
|
|
28
|
+
title,
|
|
29
|
+
description,
|
|
30
|
+
actionLabel,
|
|
31
|
+
actionHref,
|
|
32
|
+
onAction,
|
|
33
|
+
}: Props = $props();
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<!-- =========================== -->
|
|
37
|
+
<!-- MARKUP -->
|
|
38
|
+
<!-- =========================== -->
|
|
39
|
+
<!-- The four parts arrive in reading order. The stagger is what says the page
|
|
40
|
+
finished loading and is simply empty. -->
|
|
41
|
+
<Empty.Root class="t-empty py-16">
|
|
42
|
+
<Empty.Header class="max-w-md">
|
|
43
|
+
{#if Icon}
|
|
44
|
+
<Empty.Media class="mb-4 size-16 rounded-2xl bg-brand-indigo/10 text-brand-indigo">
|
|
45
|
+
<Icon weight={iconWeight} class="size-8" />
|
|
46
|
+
</Empty.Media>
|
|
47
|
+
{/if}
|
|
48
|
+
<Empty.Title class="font-heading text-lg font-semibold">{title}</Empty.Title>
|
|
49
|
+
{#if description}
|
|
50
|
+
<Empty.Description>{description}</Empty.Description>
|
|
51
|
+
{/if}
|
|
52
|
+
</Empty.Header>
|
|
53
|
+
{#if actionLabel && (actionHref || onAction)}
|
|
54
|
+
<Empty.Content>
|
|
55
|
+
{#if actionHref}
|
|
56
|
+
<Button href={actionHref}>{actionLabel}</Button>
|
|
57
|
+
{:else}
|
|
58
|
+
<Button onclick={onAction}>{actionLabel}</Button>
|
|
59
|
+
{/if}
|
|
60
|
+
</Empty.Content>
|
|
61
|
+
{/if}
|
|
62
|
+
</Empty.Root>
|
|
63
|
+
|
|
64
|
+
<!-- =========================== -->
|
|
65
|
+
<!-- STYLE -->
|
|
66
|
+
<!-- =========================== -->
|
|
67
|
+
<style>
|
|
68
|
+
/* Never `:nth-child` here: one such rule makes every DOM insert on the page restyle its later siblings. */
|
|
69
|
+
:global(.t-empty) > :global(*) {
|
|
70
|
+
animation: t-empty-in var(--duration-very-slow) var(--ease-smooth-out) both;
|
|
71
|
+
animation-delay: calc((sibling-index() - 1) * var(--duration-stagger));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@keyframes -global-t-empty-in {
|
|
75
|
+
from {
|
|
76
|
+
opacity: 0;
|
|
77
|
+
transform: translateY(var(--distance-small));
|
|
78
|
+
}
|
|
79
|
+
to {
|
|
80
|
+
opacity: 1;
|
|
81
|
+
transform: none;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
:global(.t-empty) > :global(*) {
|
|
87
|
+
animation-duration: 1ms;
|
|
88
|
+
animation-delay: 0ms;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import { Checkbox } from "../../shadcn/checkbox/index.js";
|
|
7
|
+
import { Check } from "phosphor-svelte";
|
|
8
|
+
import { cn } from "@heyramzi/utils";
|
|
9
|
+
|
|
10
|
+
// =========================
|
|
11
|
+
// TYPES
|
|
12
|
+
// =========================
|
|
13
|
+
type Props = {
|
|
14
|
+
checked: boolean;
|
|
15
|
+
onclick: () => void;
|
|
16
|
+
variant?: "checkbox" | "radio";
|
|
17
|
+
children: Snippet;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// =========================
|
|
21
|
+
// PROPS
|
|
22
|
+
// =========================
|
|
23
|
+
let { checked, onclick, variant = "checkbox", children }: Props = $props();
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- =========================== -->
|
|
27
|
+
<!-- MARKUP -->
|
|
28
|
+
<!-- =========================== -->
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
class="flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-muted"
|
|
32
|
+
{onclick}
|
|
33
|
+
>
|
|
34
|
+
{#if variant === "radio"}
|
|
35
|
+
<span
|
|
36
|
+
class={cn(
|
|
37
|
+
"grid size-3.5 shrink-0 place-items-center rounded-full border transition-colors",
|
|
38
|
+
checked ? "border-primary bg-primary text-primary-foreground" : "border-border",
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
{#if checked}
|
|
42
|
+
<Check class="size-2.5" weight="bold" />
|
|
43
|
+
{/if}
|
|
44
|
+
</span>
|
|
45
|
+
{:else}
|
|
46
|
+
<Checkbox {checked} />
|
|
47
|
+
{/if}
|
|
48
|
+
{@render children()}
|
|
49
|
+
</button>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import * as Popover from "../../shadcn/popover/index.js";
|
|
7
|
+
import { Funnel as FunnelIcon } from "phosphor-svelte";
|
|
8
|
+
import { cn } from "@heyramzi/utils";
|
|
9
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
10
|
+
import { toolbarTriggerClass } from "./toolbar-trigger.js";
|
|
11
|
+
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
12
|
+
|
|
13
|
+
// phosphor-svelte v2 (resolved here) and v3 (what consumers run) differ only in
|
|
14
|
+
// Component generics. The cast bridges that type-only gap.
|
|
15
|
+
const DefaultFunnelIcon = FunnelIcon as unknown as PhosphorIcon;
|
|
16
|
+
|
|
17
|
+
// =========================
|
|
18
|
+
// TYPES
|
|
19
|
+
// =========================
|
|
20
|
+
type Props = {
|
|
21
|
+
label: string;
|
|
22
|
+
icon?: PhosphorIcon;
|
|
23
|
+
count?: number;
|
|
24
|
+
active?: boolean;
|
|
25
|
+
onClear?: () => void;
|
|
26
|
+
align?: "start" | "center" | "end";
|
|
27
|
+
width?: `w-${string}`;
|
|
28
|
+
open?: boolean;
|
|
29
|
+
clearLabel?: string;
|
|
30
|
+
triggerClass?: string;
|
|
31
|
+
contentClass?: string;
|
|
32
|
+
children: Snippet;
|
|
33
|
+
trailing?: Snippet;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// =========================
|
|
37
|
+
// PROPS
|
|
38
|
+
// =========================
|
|
39
|
+
let {
|
|
40
|
+
label,
|
|
41
|
+
icon: IconComponent = DefaultFunnelIcon,
|
|
42
|
+
count = 0,
|
|
43
|
+
active,
|
|
44
|
+
onClear,
|
|
45
|
+
align = "start",
|
|
46
|
+
width = "w-52",
|
|
47
|
+
open = $bindable(false),
|
|
48
|
+
clearLabel = "Clear",
|
|
49
|
+
triggerClass,
|
|
50
|
+
contentClass,
|
|
51
|
+
children,
|
|
52
|
+
trailing,
|
|
53
|
+
}: Props = $props();
|
|
54
|
+
|
|
55
|
+
// =========================
|
|
56
|
+
// DERIVED
|
|
57
|
+
// =========================
|
|
58
|
+
let hasCount = $derived(count > 0);
|
|
59
|
+
let isActive = $derived(active ?? hasCount);
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<!-- =========================== -->
|
|
63
|
+
<!-- MARKUP -->
|
|
64
|
+
<!-- =========================== -->
|
|
65
|
+
<Popover.Root bind:open>
|
|
66
|
+
<Popover.Trigger class={toolbarTriggerClass({ active: isActive, className: triggerClass })}>
|
|
67
|
+
<IconComponent class="size-3.5" />
|
|
68
|
+
<span>{label}</span>
|
|
69
|
+
{#if trailing}
|
|
70
|
+
{@render trailing()}
|
|
71
|
+
{/if}
|
|
72
|
+
{#if hasCount}
|
|
73
|
+
<span
|
|
74
|
+
class="ml-0.5 inline-flex size-4 items-center justify-center rounded-full bg-brand-indigo/15 text-[10px] font-semibold text-brand-indigo dark:text-brand-indigo-light"
|
|
75
|
+
>
|
|
76
|
+
{count}
|
|
77
|
+
</span>
|
|
78
|
+
{/if}
|
|
79
|
+
</Popover.Trigger>
|
|
80
|
+
<Popover.Content class={cn("p-2", width, contentClass)} {align}>
|
|
81
|
+
{@render children()}
|
|
82
|
+
{#if onClear && hasCount}
|
|
83
|
+
<Button variant="ghost" size="sm" class="mt-2 w-full text-xs text-muted-foreground" onclick={onClear}>
|
|
84
|
+
{clearLabel}
|
|
85
|
+
</Button>
|
|
86
|
+
{/if}
|
|
87
|
+
</Popover.Content>
|
|
88
|
+
</Popover.Root>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cn } from "@heyramzi/utils";
|
|
2
|
+
|
|
3
|
+
// The header for each control group in a toolbar popover, in one place so
|
|
4
|
+
// sections can't drift in size or weight.
|
|
5
|
+
|
|
6
|
+
// `first:` drops the leading header's top padding, since the popover pads itself.
|
|
7
|
+
export function filterSectionClass(className?: string): string {
|
|
8
|
+
return cn(
|
|
9
|
+
"px-2 pt-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground first:pt-1",
|
|
10
|
+
className,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts" generics="TGroup">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { untrack, type Snippet } from "svelte";
|
|
6
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
7
|
+
import { CaretDown } from "phosphor-svelte";
|
|
8
|
+
import { cn } from "@heyramzi/utils";
|
|
9
|
+
|
|
10
|
+
// =========================
|
|
11
|
+
// TYPES
|
|
12
|
+
// =========================
|
|
13
|
+
type Props = {
|
|
14
|
+
groups: TGroup[];
|
|
15
|
+
getKey: (group: TGroup) => string;
|
|
16
|
+
defaultCollapsed?: string[];
|
|
17
|
+
/** WHY: a page that drives its own keyboard cursor has to know which rows
|
|
18
|
+
are on screen, and the collapsed set is the only thing that says so. */
|
|
19
|
+
onCollapsedChange?: (keys: string[]) => void;
|
|
20
|
+
header: Snippet<[TGroup, { collapsed: boolean }]>;
|
|
21
|
+
/** Controls beside the header, outside its toggle button so a click on
|
|
22
|
+
one doesn't collapse the group. Shown on hover of the header row. */
|
|
23
|
+
actions?: Snippet<[TGroup]>;
|
|
24
|
+
rows: Snippet<[TGroup]>;
|
|
25
|
+
class?: string;
|
|
26
|
+
onGroupDragOver?: (e: DragEvent, group: TGroup) => void;
|
|
27
|
+
onGroupDragLeave?: (e: DragEvent) => void;
|
|
28
|
+
onGroupDrop?: (e: DragEvent, group: TGroup) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// =========================
|
|
32
|
+
// PROPS
|
|
33
|
+
// =========================
|
|
34
|
+
let {
|
|
35
|
+
groups,
|
|
36
|
+
getKey,
|
|
37
|
+
defaultCollapsed = [],
|
|
38
|
+
onCollapsedChange,
|
|
39
|
+
header,
|
|
40
|
+
actions,
|
|
41
|
+
rows,
|
|
42
|
+
class: className,
|
|
43
|
+
onGroupDragOver,
|
|
44
|
+
onGroupDragLeave,
|
|
45
|
+
onGroupDrop,
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
// =========================
|
|
49
|
+
// STATE
|
|
50
|
+
// =========================
|
|
51
|
+
const collapsed = new SvelteSet<string>(untrack(() => defaultCollapsed));
|
|
52
|
+
|
|
53
|
+
// =========================
|
|
54
|
+
// FUNCTIONS
|
|
55
|
+
// =========================
|
|
56
|
+
function toggle(key: string) {
|
|
57
|
+
if (collapsed.has(key)) {
|
|
58
|
+
collapsed.delete(key);
|
|
59
|
+
} else {
|
|
60
|
+
collapsed.add(key);
|
|
61
|
+
}
|
|
62
|
+
onCollapsedChange?.([...collapsed]);
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<!-- =========================== -->
|
|
67
|
+
<!-- MARKUP -->
|
|
68
|
+
<!-- =========================== -->
|
|
69
|
+
<div class={cn("flex w-full flex-col", className)}>
|
|
70
|
+
{#each groups as group, index (getKey(group))}
|
|
71
|
+
{@const key = getKey(group)}
|
|
72
|
+
{@const isCollapsed = collapsed.has(key)}
|
|
73
|
+
<div
|
|
74
|
+
class={cn(index > 0 && "mt-3 border-t border-border/50 pt-3")}
|
|
75
|
+
role={onGroupDragOver || onGroupDrop ? "group" : undefined}
|
|
76
|
+
ondragover={onGroupDragOver ? (e) => onGroupDragOver(e, group) : undefined}
|
|
77
|
+
ondragleave={onGroupDragLeave}
|
|
78
|
+
ondrop={onGroupDrop ? (e) => onGroupDrop(e, group) : undefined}
|
|
79
|
+
>
|
|
80
|
+
<div class="group/header flex items-center gap-1 rounded-lg transition-colors hover:bg-muted/60">
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
class="flex min-w-0 flex-1 cursor-pointer items-center gap-2.5 rounded-lg px-3 py-2 text-left"
|
|
84
|
+
onclick={() => toggle(key)}
|
|
85
|
+
aria-expanded={!isCollapsed}
|
|
86
|
+
>
|
|
87
|
+
<CaretDown
|
|
88
|
+
class={cn(
|
|
89
|
+
"size-3 shrink-0 text-muted-foreground/60 transition-transform duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)]",
|
|
90
|
+
isCollapsed && "-rotate-90",
|
|
91
|
+
)}
|
|
92
|
+
weight="bold"
|
|
93
|
+
/>
|
|
94
|
+
{@render header(group, { collapsed: isCollapsed })}
|
|
95
|
+
</button>
|
|
96
|
+
{#if actions}
|
|
97
|
+
<div
|
|
98
|
+
class="flex shrink-0 items-center gap-1 pe-2 opacity-0 transition-opacity group-hover/header:opacity-100 focus-within:opacity-100 any-pointer-coarse:opacity-100"
|
|
99
|
+
>
|
|
100
|
+
{@render actions(group)}
|
|
101
|
+
</div>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
{#if !isCollapsed}
|
|
106
|
+
<div>
|
|
107
|
+
{@render rows(group)}
|
|
108
|
+
</div>
|
|
109
|
+
{/if}
|
|
110
|
+
</div>
|
|
111
|
+
{/each}
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- No entrance motion. Four stacked animations made the last group of a
|
|
115
|
+
40-row board arrive two seconds late. Working surfaces don't animate in. -->
|
|
116
|
+
<!-- No `overflow: hidden` either: it clipped anything a row opened. -->
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export { default as DetailEmpty } from "./detail-empty.svelte";
|
|
2
|
+
export { default as DetailHeader } from "./detail-header.svelte";
|
|
3
|
+
export { default as DetailPanel } from "./detail-panel.svelte";
|
|
4
|
+
export { default as DetailRow } from "./detail-row.svelte";
|
|
5
|
+
export { default as DetailStack } from "./detail-stack.svelte";
|
|
6
|
+
export { default as EmptyState } from "./empty-state.svelte";
|
|
7
|
+
export { default as FilterItem } from "./filter-item.svelte";
|
|
8
|
+
export { default as PrevNext } from "./prev-next.svelte";
|
|
9
|
+
export { stepTo } from "./step.js";
|
|
10
|
+
export { default as NoticeBar } from "./notice-bar.svelte";
|
|
11
|
+
export { default as RailBody } from "./rail-body.svelte";
|
|
12
|
+
export { default as RailEmpty } from "./rail-empty.svelte";
|
|
13
|
+
export { default as RailHead } from "./rail-head.svelte";
|
|
14
|
+
export { default as RailRow } from "./rail-row.svelte";
|
|
15
|
+
export { default as SectionLabel } from "./section-label.svelte";
|
|
16
|
+
export { queueRailClass, queueRailWidth } from "./queue-rail.js";
|
|
17
|
+
export { default as FilterPopover } from "./filter-popover.svelte";
|
|
18
|
+
export { filterSectionClass } from "./filter-section.js";
|
|
19
|
+
export { default as GroupedList } from "./grouped-list.svelte";
|
|
20
|
+
export { default as ListPageShell } from "./list-page-shell.svelte";
|
|
21
|
+
export { default as MasterDetail } from "./master-detail.svelte";
|
|
22
|
+
// The segmented control isn't re-exported here. Two import paths drifted, so
|
|
23
|
+
// its one home is `@heyramzi/ui/components/segmented`.
|
|
24
|
+
export { default as ToolbarSearch } from "./toolbar-search.svelte";
|
|
25
|
+
export { toolbarTriggerClass } from "./toolbar-trigger.js";
|
|
26
|
+
export { listTable } from "./list-table.js";
|
|
27
|
+
export type { ViewMode } from "./types.js";
|
|
28
|
+
export { getSectionHost, setSectionHost, type SectionHost } from "./section-host.js";
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import EmptyState from "./empty-state.svelte";
|
|
7
|
+
import { cn } from "@heyramzi/utils";
|
|
8
|
+
import { pageGutterClass } from "../../lib/layout.js";
|
|
9
|
+
import { getSectionHost } from "./section-host.js";
|
|
10
|
+
import SectionRuns from "../segmented/section-runs.svelte";
|
|
11
|
+
import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
12
|
+
|
|
13
|
+
// =========================
|
|
14
|
+
// TYPES
|
|
15
|
+
// =========================
|
|
16
|
+
type EmptyConfig = {
|
|
17
|
+
icon?: PhosphorIcon;
|
|
18
|
+
iconWeight?: IconWeight;
|
|
19
|
+
title: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
actionLabel?: string;
|
|
22
|
+
onAction?: () => void;
|
|
23
|
+
actionHref?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type Props = {
|
|
27
|
+
isEmpty?: boolean;
|
|
28
|
+
emptyState?: EmptyConfig;
|
|
29
|
+
hideToolbarWhenEmpty?: boolean;
|
|
30
|
+
maxWidth?: string | false;
|
|
31
|
+
fillHeight?: boolean;
|
|
32
|
+
containerClass?: string;
|
|
33
|
+
toolbarClass?: string;
|
|
34
|
+
tabs?: Snippet;
|
|
35
|
+
search?: Snippet;
|
|
36
|
+
filters?: Snippet;
|
|
37
|
+
rightActions?: Snippet;
|
|
38
|
+
summary?: Snippet;
|
|
39
|
+
content?: Snippet;
|
|
40
|
+
empty?: Snippet;
|
|
41
|
+
extra?: Snippet;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// =========================
|
|
45
|
+
// PROPS
|
|
46
|
+
// =========================
|
|
47
|
+
let {
|
|
48
|
+
isEmpty = false,
|
|
49
|
+
emptyState,
|
|
50
|
+
hideToolbarWhenEmpty = false,
|
|
51
|
+
maxWidth = "1600px",
|
|
52
|
+
fillHeight = false,
|
|
53
|
+
containerClass,
|
|
54
|
+
toolbarClass,
|
|
55
|
+
tabs,
|
|
56
|
+
search,
|
|
57
|
+
filters,
|
|
58
|
+
rightActions,
|
|
59
|
+
summary,
|
|
60
|
+
content,
|
|
61
|
+
empty,
|
|
62
|
+
extra,
|
|
63
|
+
}: Props = $props();
|
|
64
|
+
|
|
65
|
+
// =========================
|
|
66
|
+
// STATE
|
|
67
|
+
// =========================
|
|
68
|
+
// Null off an app shell, where the toolbar just has no section strip to draw.
|
|
69
|
+
const section = getSectionHost();
|
|
70
|
+
|
|
71
|
+
// =========================
|
|
72
|
+
// DERIVED
|
|
73
|
+
// =========================
|
|
74
|
+
let hideChrome = $derived(isEmpty && hideToolbarWhenEmpty);
|
|
75
|
+
// WHY the section strip counts: on a page whose only chrome is the section
|
|
76
|
+
// nav, this row is what draws it, and skipping the row would drop the tabs.
|
|
77
|
+
let hasToolbar = $derived(
|
|
78
|
+
Boolean(search || filters || tabs || rightActions || section?.strip) && !hideChrome,
|
|
79
|
+
);
|
|
80
|
+
// Whether anything holds the left of the row. With nothing there, the tabs
|
|
81
|
+
// take that edge instead of floating mid-row beside the actions.
|
|
82
|
+
let hasLeading = $derived(Boolean(search || filters || section?.strip));
|
|
83
|
+
|
|
84
|
+
// =========================
|
|
85
|
+
// EFFECTS
|
|
86
|
+
// =========================
|
|
87
|
+
// Claiming stops the app shell drawing its own strip above this page.
|
|
88
|
+
// Unmount releases it, so shell-less pages get the standalone row back.
|
|
89
|
+
$effect(() => {
|
|
90
|
+
if (!hasToolbar) return;
|
|
91
|
+
return section?.claim();
|
|
92
|
+
});
|
|
93
|
+
// The shared gutter from `lib/layout.ts`. A typed one put the search field
|
|
94
|
+
// 8px left of the section tabs above it.
|
|
95
|
+
let outerClass = $derived(
|
|
96
|
+
cn(
|
|
97
|
+
`flex flex-col gap-4 py-4 ${pageGutterClass}`,
|
|
98
|
+
fillHeight && "min-h-0 flex-1",
|
|
99
|
+
!fillHeight && "flex-1",
|
|
100
|
+
containerClass,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
// flex-1 lets a snippet pin a footer with mt-auto. min-w-0 keeps wide
|
|
104
|
+
// tables and chip rows from causing horizontal scroll.
|
|
105
|
+
let innerClass = $derived(
|
|
106
|
+
maxWidth === false
|
|
107
|
+
? cn("flex min-w-0 flex-1 flex-col gap-4", fillHeight && "min-h-0")
|
|
108
|
+
: cn(
|
|
109
|
+
"mx-auto flex w-full min-w-0 flex-1 flex-col gap-4",
|
|
110
|
+
fillHeight && "min-h-0",
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
let innerStyle = $derived(maxWidth ? `max-width: ${maxWidth}` : undefined);
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!-- =========================== -->
|
|
117
|
+
<!-- MARKUP -->
|
|
118
|
+
<!-- =========================== -->
|
|
119
|
+
<div class={outerClass}>
|
|
120
|
+
<div class={innerClass} style={innerStyle}>
|
|
121
|
+
{#if hasToolbar}
|
|
122
|
+
<!-- One row on every list page: search and filters left, then tabs,
|
|
123
|
+
view toggle and primary action right. -->
|
|
124
|
+
<!-- Without room, `order` keeps the actions beside search and drops
|
|
125
|
+
filters to a scrolling line below. flex-wrap stays on at every width. -->
|
|
126
|
+
<!-- A container query, not a breakpoint: an 834px iPad read as desktop
|
|
127
|
+
and clipped a 733px toolbar into a 538px column. -->
|
|
128
|
+
<!-- Sticky, because the section tabs live here. `-mx-4 px-4` covers the
|
|
129
|
+
gutter, `-mt-4 py-4` pulls the top padding inside the sticky box. -->
|
|
130
|
+
<div
|
|
131
|
+
class="@container/toolbar sticky top-0 z-20 -mx-4 -mt-4 bg-background/95 px-4 py-4 backdrop-blur supports-[backdrop-filter]:bg-background/70"
|
|
132
|
+
>
|
|
133
|
+
<div class={cn("flex flex-wrap items-center gap-2", toolbarClass)}>
|
|
134
|
+
<!-- The section tabs lead: which page comes before which slice of it.
|
|
135
|
+
One row replaces two rows that held four controls. -->
|
|
136
|
+
{#if section?.strip}
|
|
137
|
+
<SectionRuns
|
|
138
|
+
strip={section.strip}
|
|
139
|
+
class="order-first w-full max-w-full @3xl/toolbar:w-auto"
|
|
140
|
+
/>
|
|
141
|
+
{/if}
|
|
142
|
+
{#if search}
|
|
143
|
+
<div class="order-1 flex min-w-0 flex-1 items-center @3xl/toolbar:flex-none">
|
|
144
|
+
{@render search()}
|
|
145
|
+
</div>
|
|
146
|
+
{/if}
|
|
147
|
+
{#if tabs || rightActions}
|
|
148
|
+
<!-- Shrinks and wraps, not shrink-0: /tasks needs 610px here, and
|
|
149
|
+
shrink-0 clipped its last control off an iPad screen. -->
|
|
150
|
+
<div
|
|
151
|
+
class={cn(
|
|
152
|
+
"order-2 flex min-w-0 flex-wrap items-center gap-2 @3xl/toolbar:order-3",
|
|
153
|
+
hasLeading ? "@3xl/toolbar:ms-auto" : "flex-1",
|
|
154
|
+
)}
|
|
155
|
+
>
|
|
156
|
+
{#if tabs}
|
|
157
|
+
<div class={cn("flex min-w-0 items-center", !hasLeading && "me-auto")}>
|
|
158
|
+
{@render tabs()}
|
|
159
|
+
</div>
|
|
160
|
+
{/if}
|
|
161
|
+
{#if rightActions}
|
|
162
|
+
{@render rightActions()}
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
{/if}
|
|
166
|
+
{#if filters}
|
|
167
|
+
<div
|
|
168
|
+
class="no-scrollbar order-3 flex w-full max-w-full min-w-0 items-center gap-2 overflow-x-auto @3xl/toolbar:order-2 @3xl/toolbar:w-auto"
|
|
169
|
+
>
|
|
170
|
+
{@render filters()}
|
|
171
|
+
</div>
|
|
172
|
+
{/if}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
|
|
177
|
+
{#if summary && !hideChrome}
|
|
178
|
+
{@render summary()}
|
|
179
|
+
{/if}
|
|
180
|
+
|
|
181
|
+
{#if extra && !hideChrome}
|
|
182
|
+
{@render extra()}
|
|
183
|
+
{/if}
|
|
184
|
+
|
|
185
|
+
{#if isEmpty}
|
|
186
|
+
{#if empty}
|
|
187
|
+
{@render empty()}
|
|
188
|
+
{:else if emptyState}
|
|
189
|
+
<EmptyState {...emptyState} />
|
|
190
|
+
{/if}
|
|
191
|
+
{:else if content}
|
|
192
|
+
{@render content()}
|
|
193
|
+
{/if}
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { cn } from "@heyramzi/utils";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The list-page table rhythm, Attio's: one 40px row, a hairline under each, a flat hover.
|
|
5
|
+
*/
|
|
6
|
+
export const listTable = {
|
|
7
|
+
/** The header row: 36px, a hairline under it. On the cells: `thead` zeroes its row's border. */
|
|
8
|
+
headRow: (className?: string) =>
|
|
9
|
+
cn(
|
|
10
|
+
"hover:translate-y-0 hover:bg-transparent hover:shadow-none [&>th]:h-9 [&>th]:border-b [&>th]:border-border",
|
|
11
|
+
className,
|
|
12
|
+
),
|
|
13
|
+
/** A body row. The shared row lifts on hover; a flush list row does not. */
|
|
14
|
+
row: (className?: string) =>
|
|
15
|
+
cn(
|
|
16
|
+
"border-b border-border/50 last:border-b-0 hover:translate-y-0 hover:bg-muted/40 hover:shadow-none [&>td]:h-10 [&>td]:py-0",
|
|
17
|
+
className,
|
|
18
|
+
),
|
|
19
|
+
};
|