@nucel/ui 0.1.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 +235 -0
- package/package.json +88 -0
- package/src/lib/components/ui/Backdrop.svelte +19 -0
- package/src/lib/components/ui/CountBadge.svelte +40 -0
- package/src/lib/components/ui/EmptyState.svelte +68 -0
- package/src/lib/components/ui/KbdShortcut.svelte +20 -0
- package/src/lib/components/ui/MarkdownRenderer.svelte +85 -0
- package/src/lib/components/ui/ProgressRing.svelte +61 -0
- package/src/lib/components/ui/ProviderIcon.svelte +57 -0
- package/src/lib/components/ui/ReviewBadge.svelte +54 -0
- package/src/lib/components/ui/Sparkline.svelte +61 -0
- package/src/lib/components/ui/StatusBadge.svelte +32 -0
- package/src/lib/components/ui/StatusDot.svelte +65 -0
- package/src/lib/components/ui/TabBar.svelte +127 -0
- package/src/lib/components/ui/VerticalSeparator.svelte +9 -0
- package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
- package/src/lib/components/ui/accordion/accordion-item.svelte +17 -0
- package/src/lib/components/ui/accordion/accordion-trigger.svelte +32 -0
- package/src/lib/components/ui/accordion/accordion.svelte +16 -0
- package/src/lib/components/ui/accordion/index.ts +16 -0
- package/src/lib/components/ui/avatar/avatar-fallback.svelte +17 -0
- package/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
- package/src/lib/components/ui/avatar/avatar.svelte +19 -0
- package/src/lib/components/ui/avatar/index.ts +13 -0
- package/src/lib/components/ui/badge/badge.svelte +49 -0
- package/src/lib/components/ui/badge/index.ts +2 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +20 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +21 -0
- package/src/lib/components/ui/breadcrumb/index.ts +25 -0
- package/src/lib/components/ui/button/button.svelte +82 -0
- package/src/lib/components/ui/button/index.ts +17 -0
- package/src/lib/components/ui/card/card-action.svelte +20 -0
- package/src/lib/components/ui/card/card-content.svelte +15 -0
- package/src/lib/components/ui/card/card-description.svelte +20 -0
- package/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/src/lib/components/ui/card/card-header.svelte +23 -0
- package/src/lib/components/ui/card/card-title.svelte +20 -0
- package/src/lib/components/ui/card/card.svelte +23 -0
- package/src/lib/components/ui/card/index.ts +25 -0
- package/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
- package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/src/lib/components/ui/collapsible/collapsible.svelte +11 -0
- package/src/lib/components/ui/collapsible/index.ts +13 -0
- package/src/lib/components/ui/dialog/dialog-close.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog-content.svelte +45 -0
- package/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/src/lib/components/ui/dialog/dialog-trigger.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog.svelte +7 -0
- package/src/lib/components/ui/dialog/index.ts +34 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +41 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +29 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +31 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/index.ts +54 -0
- package/src/lib/components/ui/input/index.ts +7 -0
- package/src/lib/components/ui/input/input.svelte +52 -0
- package/src/lib/components/ui/label/index.ts +7 -0
- package/src/lib/components/ui/label/label.svelte +20 -0
- package/src/lib/components/ui/navigation-menu/index.ts +28 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte +21 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte +22 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte +17 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte +20 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte +17 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte +34 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte +22 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu.svelte +32 -0
- package/src/lib/components/ui/progress/index.ts +1 -0
- package/src/lib/components/ui/progress/progress.svelte +33 -0
- package/src/lib/components/ui/scroll-area/index.ts +10 -0
- package/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte +31 -0
- package/src/lib/components/ui/scroll-area/scroll-area.svelte +43 -0
- package/src/lib/components/ui/select/index.ts +37 -0
- package/src/lib/components/ui/select/select-content.svelte +45 -0
- package/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/src/lib/components/ui/select/select-group.svelte +7 -0
- package/src/lib/components/ui/select/select-item.svelte +38 -0
- package/src/lib/components/ui/select/select-label.svelte +20 -0
- package/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/src/lib/components/ui/select/select.svelte +11 -0
- package/src/lib/components/ui/separator/index.ts +7 -0
- package/src/lib/components/ui/separator/separator.svelte +21 -0
- package/src/lib/components/ui/sheet/index.ts +34 -0
- package/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet-content.svelte +62 -0
- package/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
- package/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
- package/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet.svelte +7 -0
- package/src/lib/components/ui/skeleton/index.ts +1 -0
- package/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/src/lib/components/ui/sonner/index.ts +1 -0
- package/src/lib/components/ui/sonner/sonner.svelte +10 -0
- package/src/lib/components/ui/tabs/index.ts +16 -0
- package/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/src/lib/components/ui/tabs/tabs-list.svelte +16 -0
- package/src/lib/components/ui/tabs/tabs-trigger.svelte +20 -0
- package/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/src/lib/components/ui/textarea/index.ts +7 -0
- package/src/lib/components/ui/textarea/textarea.svelte +23 -0
- package/src/lib/components/ui/toggle/index.ts +13 -0
- package/src/lib/components/ui/toggle/toggle.svelte +52 -0
- package/src/lib/components/ui/toggle-group/index.ts +10 -0
- package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +35 -0
- package/src/lib/components/ui/toggle-group/toggle-group.svelte +65 -0
- package/src/lib/components/ui/tooltip/index.ts +19 -0
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/src/lib/index.ts +244 -0
- package/src/lib/utils/cn.test.ts +993 -0
- package/src/lib/utils/cn.ts +6 -0
- package/src/lib/utils.ts +12 -0
- package/src/styles.css +127 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '../../index.js';
|
|
3
|
+
|
|
4
|
+
let { status, reviewer }: { status: string; reviewer: string } = $props();
|
|
5
|
+
|
|
6
|
+
let icon = $derived(
|
|
7
|
+
status === 'approved' ? 'check' : status === 'changes_requested' ? 'x' : 'comment',
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
let label = $derived(
|
|
11
|
+
status === 'approved'
|
|
12
|
+
? 'approved'
|
|
13
|
+
: status === 'changes_requested'
|
|
14
|
+
? 'requested changes'
|
|
15
|
+
: 'commented',
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const variantMap: Record<string, string> = {
|
|
19
|
+
approved: 'border-success/30 bg-success/10 text-success',
|
|
20
|
+
changes_requested: 'border-destructive/30 bg-destructive/10 text-destructive',
|
|
21
|
+
commented: '',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const variant = $derived(status === 'commented' ? 'secondary' : 'outline');
|
|
25
|
+
const extraClass = $derived(variantMap[status] ?? '');
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<Badge {variant} class="inline-flex items-center gap-1.5 rounded-full {extraClass}">
|
|
29
|
+
{#if icon === 'check'}
|
|
30
|
+
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
32
|
+
</svg>
|
|
33
|
+
{:else if icon === 'x'}
|
|
34
|
+
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
35
|
+
<path
|
|
36
|
+
stroke-linecap="round"
|
|
37
|
+
stroke-linejoin="round"
|
|
38
|
+
stroke-width="2"
|
|
39
|
+
d="M6 18L18 6M6 6l12 12"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
{:else}
|
|
43
|
+
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
44
|
+
<path
|
|
45
|
+
stroke-linecap="round"
|
|
46
|
+
stroke-linejoin="round"
|
|
47
|
+
stroke-width="2"
|
|
48
|
+
d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"
|
|
49
|
+
/>
|
|
50
|
+
</svg>
|
|
51
|
+
{/if}
|
|
52
|
+
<span class="font-medium">{reviewer}</span>
|
|
53
|
+
<span>{label}</span>
|
|
54
|
+
</Badge>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
ref = $bindable(null),
|
|
6
|
+
data,
|
|
7
|
+
color = 'stroke-primary',
|
|
8
|
+
fillColor = 'fill-primary/10',
|
|
9
|
+
width = 80,
|
|
10
|
+
height = 24,
|
|
11
|
+
class: className,
|
|
12
|
+
...restProps
|
|
13
|
+
}: {
|
|
14
|
+
ref?: SVGSVGElement | null;
|
|
15
|
+
data: number[];
|
|
16
|
+
color?: string;
|
|
17
|
+
fillColor?: string;
|
|
18
|
+
width?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
class?: string;
|
|
21
|
+
} = $props();
|
|
22
|
+
|
|
23
|
+
const points = $derived.by(() => {
|
|
24
|
+
if (data.length === 0) return '';
|
|
25
|
+
const max = Math.max(...data, 1);
|
|
26
|
+
const step = width / Math.max(data.length - 1, 1);
|
|
27
|
+
return data.map((v, i) => `${i * step},${height - (v / max) * (height - 2) - 1}`).join(' ');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const areaPoints = $derived.by(() => {
|
|
31
|
+
if (data.length === 0) return '';
|
|
32
|
+
const max = Math.max(...data, 1);
|
|
33
|
+
const step = width / Math.max(data.length - 1, 1);
|
|
34
|
+
const linePoints = data
|
|
35
|
+
.map((v, i) => `${i * step},${height - (v / max) * (height - 2) - 1}`)
|
|
36
|
+
.join(' ');
|
|
37
|
+
return `0,${height} ${linePoints} ${(data.length - 1) * step},${height}`;
|
|
38
|
+
});
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<svg
|
|
42
|
+
bind:this={ref}
|
|
43
|
+
viewBox="0 0 {width} {height}"
|
|
44
|
+
class={cn('overflow-visible', className)}
|
|
45
|
+
style="width: {width}px; height: {height}px;"
|
|
46
|
+
{...restProps}
|
|
47
|
+
>
|
|
48
|
+
{#if data.length > 1}
|
|
49
|
+
<polygon points={areaPoints} class={fillColor} />
|
|
50
|
+
<polyline
|
|
51
|
+
{points}
|
|
52
|
+
fill="none"
|
|
53
|
+
class={color}
|
|
54
|
+
stroke-width="1.5"
|
|
55
|
+
stroke-linecap="round"
|
|
56
|
+
stroke-linejoin="round"
|
|
57
|
+
/>
|
|
58
|
+
{:else if data.length === 1}
|
|
59
|
+
<line x1="0" y1={height / 2} x2={width} y2={height / 2} class={color} stroke-width="1.5" />
|
|
60
|
+
{/if}
|
|
61
|
+
</svg>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '../../index.js';
|
|
3
|
+
import StatusDot from './StatusDot.svelte';
|
|
4
|
+
|
|
5
|
+
let { status, label }: { status: string; label?: string } = $props();
|
|
6
|
+
|
|
7
|
+
const displayLabel = $derived(label ?? status);
|
|
8
|
+
|
|
9
|
+
const classes: Record<string, string> = {
|
|
10
|
+
open: 'border-success/30 bg-success/10 text-success',
|
|
11
|
+
closed: '',
|
|
12
|
+
merged: 'border-purple-500/30 bg-purple-500/10 text-purple-400',
|
|
13
|
+
running: 'border-blue-500/30 bg-blue-500/10 text-blue-400',
|
|
14
|
+
active: 'border-blue-500/30 bg-blue-500/10 text-blue-400',
|
|
15
|
+
passed: 'border-success/30 bg-success/10 text-success',
|
|
16
|
+
succeeded: 'border-success/30 bg-success/10 text-success',
|
|
17
|
+
failed: 'border-destructive/30 bg-destructive/10 text-destructive',
|
|
18
|
+
error: 'border-destructive/30 bg-destructive/10 text-destructive',
|
|
19
|
+
cancelled: '',
|
|
20
|
+
paused: '',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const variant = $derived(
|
|
24
|
+
status === 'closed' || status === 'cancelled' || status === 'paused' ? 'secondary' : 'outline',
|
|
25
|
+
);
|
|
26
|
+
const extraClass = $derived(classes[status] ?? '');
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<Badge {variant} class="inline-flex items-center gap-1 {extraClass}">
|
|
30
|
+
<StatusDot {status} />
|
|
31
|
+
<span>{displayLabel}</span>
|
|
32
|
+
</Badge>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
type Status =
|
|
6
|
+
| 'open'
|
|
7
|
+
| 'closed'
|
|
8
|
+
| 'running'
|
|
9
|
+
| 'succeeded'
|
|
10
|
+
| 'failed'
|
|
11
|
+
| 'active'
|
|
12
|
+
| 'paused'
|
|
13
|
+
| 'merged'
|
|
14
|
+
| 'error'
|
|
15
|
+
| 'warning'
|
|
16
|
+
| 'passed'
|
|
17
|
+
| 'cancelled'
|
|
18
|
+
| 'terminated'
|
|
19
|
+
| string;
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
ref = $bindable(null),
|
|
23
|
+
color,
|
|
24
|
+
status,
|
|
25
|
+
animated = false,
|
|
26
|
+
size = 'md',
|
|
27
|
+
class: className,
|
|
28
|
+
...restProps
|
|
29
|
+
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> & {
|
|
30
|
+
color?: string;
|
|
31
|
+
status?: Status;
|
|
32
|
+
animated?: boolean;
|
|
33
|
+
size?: 'xs' | 'sm' | 'md';
|
|
34
|
+
} = $props();
|
|
35
|
+
|
|
36
|
+
const colorMap: Record<string, string> = {
|
|
37
|
+
open: 'bg-success',
|
|
38
|
+
active: 'bg-success',
|
|
39
|
+
succeeded: 'bg-success',
|
|
40
|
+
passed: 'bg-success',
|
|
41
|
+
closed: 'bg-muted-foreground',
|
|
42
|
+
paused: 'bg-muted-foreground',
|
|
43
|
+
terminated: 'bg-muted-foreground',
|
|
44
|
+
running: 'bg-blue-500',
|
|
45
|
+
merged: 'bg-purple-500',
|
|
46
|
+
failed: 'bg-destructive',
|
|
47
|
+
error: 'bg-destructive',
|
|
48
|
+
warning: 'bg-yellow-500',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const resolvedColor = $derived(
|
|
52
|
+
color ?? (status ? (colorMap[status] ?? 'bg-muted-foreground') : 'bg-muted-foreground'),
|
|
53
|
+
);
|
|
54
|
+
const isAnimated = $derived(animated || status === 'running');
|
|
55
|
+
const sizeClass = $derived(size === 'xs' ? 'h-1 w-1' : size === 'sm' ? 'h-1.5 w-1.5' : 'h-2 w-2');
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<span bind:this={ref} class="relative flex {sizeClass} shrink-0 {cn(className)}" {...restProps}>
|
|
59
|
+
{#if isAnimated}
|
|
60
|
+
<span
|
|
61
|
+
class="absolute inline-flex h-full w-full animate-ping rounded-full {resolvedColor} opacity-50"
|
|
62
|
+
></span>
|
|
63
|
+
{/if}
|
|
64
|
+
<span class="relative inline-flex {sizeClass} rounded-full {resolvedColor}"></span>
|
|
65
|
+
</span>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
|
|
4
|
+
type TabItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon?: Component;
|
|
8
|
+
count?: number;
|
|
9
|
+
closable?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
items,
|
|
14
|
+
selected,
|
|
15
|
+
onselect,
|
|
16
|
+
onclose,
|
|
17
|
+
variant = 'underline',
|
|
18
|
+
orientation = 'horizontal',
|
|
19
|
+
size = 'sm',
|
|
20
|
+
class: className = '',
|
|
21
|
+
}: {
|
|
22
|
+
items: TabItem[];
|
|
23
|
+
selected: string;
|
|
24
|
+
onselect: (id: string) => void;
|
|
25
|
+
onclose?: (id: string) => void;
|
|
26
|
+
variant?: 'underline' | 'pill' | 'fill';
|
|
27
|
+
orientation?: 'horizontal' | 'vertical';
|
|
28
|
+
size?: 'xs' | 'sm';
|
|
29
|
+
class?: string;
|
|
30
|
+
} = $props();
|
|
31
|
+
|
|
32
|
+
const isVertical = $derived(orientation === 'vertical');
|
|
33
|
+
|
|
34
|
+
const containerClass = $derived.by(() => {
|
|
35
|
+
const base = isVertical ? 'flex flex-col gap-0.5' : 'flex items-center gap-0';
|
|
36
|
+
return `${base} ${className}`;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const itemClass = $derived.by(() => {
|
|
40
|
+
const sizeStyles = size === 'xs' ? 'text-[10px] px-2 py-1' : 'text-xs px-3 py-1.5';
|
|
41
|
+
|
|
42
|
+
if (variant === 'pill') {
|
|
43
|
+
return {
|
|
44
|
+
base: `rounded-full font-medium transition-all duration-150 ${sizeStyles}`,
|
|
45
|
+
active: 'bg-primary/15 text-primary font-medium',
|
|
46
|
+
inactive: 'text-muted-foreground hover:text-foreground',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (variant === 'fill') {
|
|
51
|
+
const fillSize = isVertical
|
|
52
|
+
? `w-full text-xs px-2 py-1.5 rounded-md`
|
|
53
|
+
: `${sizeStyles} rounded`;
|
|
54
|
+
return {
|
|
55
|
+
base: `font-medium transition-colors ${fillSize}`,
|
|
56
|
+
active: 'bg-accent text-foreground',
|
|
57
|
+
inactive: 'text-muted-foreground hover:text-foreground',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// underline (default)
|
|
62
|
+
return {
|
|
63
|
+
base: `relative font-medium transition-colors shrink-0 ${sizeStyles}`,
|
|
64
|
+
active: 'text-foreground',
|
|
65
|
+
inactive: 'text-muted-foreground hover:text-foreground',
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<div class={containerClass} role="tablist" aria-orientation={orientation}>
|
|
71
|
+
{#each items as item (item.id)}
|
|
72
|
+
{@const isActive = selected === item.id}
|
|
73
|
+
<button
|
|
74
|
+
role="tab"
|
|
75
|
+
aria-selected={isActive}
|
|
76
|
+
class="group flex items-center gap-1.5 {itemClass.base} {isActive
|
|
77
|
+
? itemClass.active
|
|
78
|
+
: itemClass.inactive}"
|
|
79
|
+
onclick={() => onselect(item.id)}
|
|
80
|
+
>
|
|
81
|
+
{#if item.icon}
|
|
82
|
+
{@const Icon = item.icon}
|
|
83
|
+
<Icon class={size === 'xs' ? 'h-3 w-3' : 'h-3.5 w-3.5'} />
|
|
84
|
+
{/if}
|
|
85
|
+
{item.label}
|
|
86
|
+
{#if item.count !== undefined && item.count > 0}
|
|
87
|
+
{#if variant === 'underline'}
|
|
88
|
+
<span class="bg-primary/10 text-primary rounded-full px-1.5 text-[10px] font-bold"
|
|
89
|
+
>{item.count}</span
|
|
90
|
+
>
|
|
91
|
+
{:else}
|
|
92
|
+
<span class="bg-muted ml-1 rounded-full px-1.5 text-[10px] tabular-nums"
|
|
93
|
+
>{item.count}</span
|
|
94
|
+
>
|
|
95
|
+
{/if}
|
|
96
|
+
{/if}
|
|
97
|
+
{#if item.closable && onclose}
|
|
98
|
+
<span
|
|
99
|
+
class="hover:bg-muted ml-0.5 hidden h-3 w-3 items-center justify-center rounded-sm group-hover:inline-flex"
|
|
100
|
+
role="button"
|
|
101
|
+
tabindex="0"
|
|
102
|
+
onclick={(e) => {
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
onclose?.(item.id);
|
|
105
|
+
}}
|
|
106
|
+
onkeydown={(e) => {
|
|
107
|
+
if (e.key === 'Enter') {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
onclose?.(item.id);
|
|
110
|
+
}
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<svg
|
|
114
|
+
class="h-2 w-2"
|
|
115
|
+
viewBox="0 0 24 24"
|
|
116
|
+
fill="none"
|
|
117
|
+
stroke="currentColor"
|
|
118
|
+
stroke-width="2"><path d="M18 6L6 18M6 6l12 12" /></svg
|
|
119
|
+
>
|
|
120
|
+
</span>
|
|
121
|
+
{/if}
|
|
122
|
+
{#if variant === 'underline' && isActive}
|
|
123
|
+
<span class="bg-primary absolute right-2 bottom-0 left-2 h-0.5 rounded-full"></span>
|
|
124
|
+
{/if}
|
|
125
|
+
</button>
|
|
126
|
+
{/each}
|
|
127
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Accordion as AccordionPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn, type WithoutChild } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<AccordionPrimitive.Content
|
|
14
|
+
bind:ref
|
|
15
|
+
data-slot="accordion-content"
|
|
16
|
+
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
<div class={cn('pt-0 pb-4', className)}>
|
|
20
|
+
{@render children?.()}
|
|
21
|
+
</div>
|
|
22
|
+
</AccordionPrimitive.Content>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Accordion as AccordionPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: AccordionPrimitive.ItemProps = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<AccordionPrimitive.Item
|
|
13
|
+
bind:ref
|
|
14
|
+
data-slot="accordion-item"
|
|
15
|
+
class={cn('border-b last:border-b-0', className)}
|
|
16
|
+
{...restProps}
|
|
17
|
+
/>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Accordion as AccordionPrimitive } from 'bits-ui';
|
|
3
|
+
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
4
|
+
import { cn, type WithoutChild } from '../../../utils.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
level = 3,
|
|
10
|
+
children,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
|
|
13
|
+
level?: AccordionPrimitive.HeaderProps['level'];
|
|
14
|
+
} = $props();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<AccordionPrimitive.Header {level} class="flex">
|
|
18
|
+
<AccordionPrimitive.Trigger
|
|
19
|
+
data-slot="accordion-trigger"
|
|
20
|
+
bind:ref
|
|
21
|
+
class={cn(
|
|
22
|
+
'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-start text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
{...restProps}
|
|
26
|
+
>
|
|
27
|
+
{@render children?.()}
|
|
28
|
+
<ChevronDownIcon
|
|
29
|
+
class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200"
|
|
30
|
+
/>
|
|
31
|
+
</AccordionPrimitive.Trigger>
|
|
32
|
+
</AccordionPrimitive.Header>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Accordion as AccordionPrimitive } from 'bits-ui';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
ref = $bindable(null),
|
|
6
|
+
value = $bindable(),
|
|
7
|
+
...restProps
|
|
8
|
+
}: AccordionPrimitive.RootProps = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<AccordionPrimitive.Root
|
|
12
|
+
bind:ref
|
|
13
|
+
bind:value={value as never}
|
|
14
|
+
data-slot="accordion"
|
|
15
|
+
{...restProps}
|
|
16
|
+
/>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Root from './accordion.svelte';
|
|
2
|
+
import Content from './accordion-content.svelte';
|
|
3
|
+
import Item from './accordion-item.svelte';
|
|
4
|
+
import Trigger from './accordion-trigger.svelte';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
Root,
|
|
8
|
+
Content,
|
|
9
|
+
Item,
|
|
10
|
+
Trigger,
|
|
11
|
+
//
|
|
12
|
+
Root as Accordion,
|
|
13
|
+
Content as AccordionContent,
|
|
14
|
+
Item as AccordionItem,
|
|
15
|
+
Trigger as AccordionTrigger,
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: AvatarPrimitive.FallbackProps = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<AvatarPrimitive.Fallback
|
|
13
|
+
bind:ref
|
|
14
|
+
data-slot="avatar-fallback"
|
|
15
|
+
class={cn('bg-muted flex size-full items-center justify-center rounded-full', className)}
|
|
16
|
+
{...restProps}
|
|
17
|
+
/>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: AvatarPrimitive.ImageProps = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<AvatarPrimitive.Image
|
|
13
|
+
bind:ref
|
|
14
|
+
data-slot="avatar-image"
|
|
15
|
+
class={cn('aspect-square size-full', className)}
|
|
16
|
+
{...restProps}
|
|
17
|
+
/>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
loadingStatus = $bindable('loading'),
|
|
8
|
+
class: className,
|
|
9
|
+
...restProps
|
|
10
|
+
}: AvatarPrimitive.RootProps = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
bind:ref
|
|
15
|
+
bind:loadingStatus
|
|
16
|
+
data-slot="avatar"
|
|
17
|
+
class={cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', className)}
|
|
18
|
+
{...restProps}
|
|
19
|
+
/>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Root from './avatar.svelte';
|
|
2
|
+
import Image from './avatar-image.svelte';
|
|
3
|
+
import Fallback from './avatar-fallback.svelte';
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
Root,
|
|
7
|
+
Image,
|
|
8
|
+
Fallback,
|
|
9
|
+
//
|
|
10
|
+
Root as Avatar,
|
|
11
|
+
Image as AvatarImage,
|
|
12
|
+
Fallback as AvatarFallback,
|
|
13
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { type VariantProps, tv } from 'tailwind-variants';
|
|
3
|
+
|
|
4
|
+
export const badgeVariants = tv({
|
|
5
|
+
base: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3',
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent',
|
|
9
|
+
secondary:
|
|
10
|
+
'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent',
|
|
11
|
+
destructive:
|
|
12
|
+
'bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white',
|
|
13
|
+
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: 'default',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
26
|
+
import { cn, type WithElementRef } from '../../../utils.js';
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
ref = $bindable(null),
|
|
30
|
+
href,
|
|
31
|
+
class: className,
|
|
32
|
+
variant = 'default',
|
|
33
|
+
children,
|
|
34
|
+
...restProps
|
|
35
|
+
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
36
|
+
variant?: BadgeVariant;
|
|
37
|
+
} = $props();
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<svelte:element
|
|
41
|
+
this={href ? 'a' : 'span'}
|
|
42
|
+
bind:this={ref}
|
|
43
|
+
data-slot="badge"
|
|
44
|
+
{href}
|
|
45
|
+
class={cn(badgeVariants({ variant }), className)}
|
|
46
|
+
{...restProps}
|
|
47
|
+
>
|
|
48
|
+
{@render children?.()}
|
|
49
|
+
</svelte:element>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import EllipsisIcon from '@lucide/svelte/icons/ellipsis';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn, type WithElementRef, type WithoutChildren } from '../../../utils.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<span
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="breadcrumb-ellipsis"
|
|
16
|
+
role="presentation"
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
class={cn('flex size-9 items-center justify-center', className)}
|
|
19
|
+
{...restProps}
|
|
20
|
+
>
|
|
21
|
+
<EllipsisIcon class="size-4" />
|
|
22
|
+
<span class="sr-only">More</span>
|
|
23
|
+
</span>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLLiAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLLiAttributes> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<li
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="breadcrumb-item"
|
|
16
|
+
class={cn('inline-flex items-center gap-1.5', className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</li>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { cn, type WithElementRef } from '../../../utils.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
href = undefined,
|
|
10
|
+
child,
|
|
11
|
+
children,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
14
|
+
child?: Snippet<[{ props: HTMLAnchorAttributes }]>;
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
const attrs = $derived({
|
|
18
|
+
'data-slot': 'breadcrumb-link',
|
|
19
|
+
class: cn('hover:text-foreground transition-colors', className),
|
|
20
|
+
href,
|
|
21
|
+
...restProps,
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
{#if child}
|
|
26
|
+
{@render child({ props: attrs })}
|
|
27
|
+
{:else}
|
|
28
|
+
<a bind:this={ref} {...attrs}>
|
|
29
|
+
{@render children?.()}
|
|
30
|
+
</a>
|
|
31
|
+
{/if}
|