@lanrenbang/basecoat-ultra-svelte 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/dist/components/Accordion.svelte +64 -0
- package/dist/components/Accordion.svelte.d.ts +14 -0
- package/dist/components/Alert.svelte +51 -0
- package/dist/components/Alert.svelte.d.ts +12 -0
- package/dist/components/Avatar.svelte +57 -0
- package/dist/components/Avatar.svelte.d.ts +13 -0
- package/dist/components/Badge.svelte +30 -0
- package/dist/components/Badge.svelte.d.ts +10 -0
- package/dist/components/Breadcrumb.svelte +54 -0
- package/dist/components/Breadcrumb.svelte.d.ts +14 -0
- package/dist/components/Button.svelte +38 -0
- package/dist/components/Button.svelte.d.ts +12 -0
- package/dist/components/ButtonGroup.svelte +26 -0
- package/dist/components/ButtonGroup.svelte.d.ts +10 -0
- package/dist/components/Card.svelte +67 -0
- package/dist/components/Card.svelte.d.ts +13 -0
- package/dist/components/Carousel.svelte +142 -0
- package/dist/components/Carousel.svelte.d.ts +11 -0
- package/dist/components/CatppuccinThemeSwitcher.svelte +132 -0
- package/dist/components/CatppuccinThemeSwitcher.svelte.d.ts +3 -0
- package/dist/components/Checkbox.svelte +20 -0
- package/dist/components/Checkbox.svelte.d.ts +8 -0
- package/dist/components/Collapsible.svelte +39 -0
- package/dist/components/Collapsible.svelte.d.ts +13 -0
- package/dist/components/Command.svelte +78 -0
- package/dist/components/Command.svelte.d.ts +12 -0
- package/dist/components/DatePicker.svelte +172 -0
- package/dist/components/DatePicker.svelte.d.ts +13 -0
- package/dist/components/Dialog.svelte +91 -0
- package/dist/components/Dialog.svelte.d.ts +14 -0
- package/dist/components/Drawer.svelte +127 -0
- package/dist/components/Drawer.svelte.d.ts +12 -0
- package/dist/components/DropdownMenu.svelte +62 -0
- package/dist/components/DropdownMenu.svelte.d.ts +14 -0
- package/dist/components/Empty.svelte +58 -0
- package/dist/components/Empty.svelte.d.ts +12 -0
- package/dist/components/Input.svelte +22 -0
- package/dist/components/Input.svelte.d.ts +9 -0
- package/dist/components/InputOTP.svelte +189 -0
- package/dist/components/InputOTP.svelte.d.ts +12 -0
- package/dist/components/Item.svelte +64 -0
- package/dist/components/Item.svelte.d.ts +14 -0
- package/dist/components/Kbd.svelte +28 -0
- package/dist/components/Kbd.svelte.d.ts +9 -0
- package/dist/components/Label.svelte +30 -0
- package/dist/components/Label.svelte.d.ts +10 -0
- package/dist/components/Pagination.svelte +120 -0
- package/dist/components/Pagination.svelte.d.ts +35 -0
- package/dist/components/Popover.svelte +68 -0
- package/dist/components/Popover.svelte.d.ts +16 -0
- package/dist/components/Progress.svelte +26 -0
- package/dist/components/Progress.svelte.d.ts +9 -0
- package/dist/components/Radio.svelte +22 -0
- package/dist/components/Radio.svelte.d.ts +9 -0
- package/dist/components/Resizable.svelte +66 -0
- package/dist/components/Resizable.svelte.d.ts +13 -0
- package/dist/components/Select.svelte +183 -0
- package/dist/components/Select.svelte.d.ts +16 -0
- package/dist/components/Separator.svelte +19 -0
- package/dist/components/Separator.svelte.d.ts +8 -0
- package/dist/components/Sheet.svelte +182 -0
- package/dist/components/Sheet.svelte.d.ts +13 -0
- package/dist/components/Skeleton.svelte +27 -0
- package/dist/components/Skeleton.svelte.d.ts +8 -0
- package/dist/components/Slider.svelte +38 -0
- package/dist/components/Slider.svelte.d.ts +11 -0
- package/dist/components/Spinner.svelte +28 -0
- package/dist/components/Spinner.svelte.d.ts +8 -0
- package/dist/components/Switch.svelte +20 -0
- package/dist/components/Switch.svelte.d.ts +8 -0
- package/dist/components/Table.svelte +61 -0
- package/dist/components/Table.svelte.d.ts +13 -0
- package/dist/components/Tabs.svelte +97 -0
- package/dist/components/Tabs.svelte.d.ts +15 -0
- package/dist/components/Textarea.svelte +22 -0
- package/dist/components/Textarea.svelte.d.ts +9 -0
- package/dist/components/Toast.svelte +73 -0
- package/dist/components/Toast.svelte.d.ts +3 -0
- package/dist/components/Toggle.svelte +69 -0
- package/dist/components/Toggle.svelte.d.ts +13 -0
- package/dist/components/ToggleGroup.svelte +69 -0
- package/dist/components/ToggleGroup.svelte.d.ts +12 -0
- package/dist/components/Tooltip.svelte +32 -0
- package/dist/components/Tooltip.svelte.d.ts +11 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +47 -0
- package/dist/reference.css +2 -0
- package/package.json +70 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
items = [],
|
|
6
|
+
multiple = false,
|
|
7
|
+
class: className = '',
|
|
8
|
+
...rest
|
|
9
|
+
}: {
|
|
10
|
+
items: Array<{ title: string | Snippet; content: string | Snippet; open?: boolean }>;
|
|
11
|
+
multiple?: boolean;
|
|
12
|
+
class?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
// Track open state for each item - use $derived for reactive initialization
|
|
17
|
+
let openStates = $state<boolean[]>([]);
|
|
18
|
+
|
|
19
|
+
// Initialize open states when items change
|
|
20
|
+
$effect(() => {
|
|
21
|
+
if (openStates.length !== items.length) {
|
|
22
|
+
openStates = items.map(item => item.open ?? false);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function handleToggle(index: number) {
|
|
27
|
+
if (multiple) {
|
|
28
|
+
// Multiple mode: just toggle the clicked item
|
|
29
|
+
openStates[index] = !openStates[index];
|
|
30
|
+
} else {
|
|
31
|
+
// Single mode: close all others, toggle clicked
|
|
32
|
+
openStates = openStates.map((_, i) => i === index ? !openStates[index] : false);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<section class={['accordion', className].filter(Boolean).join(' ')} {...rest}>
|
|
38
|
+
{#each items as item, index}
|
|
39
|
+
<details class="group border-b last:border-b-0" open={openStates[index]}>
|
|
40
|
+
<summary
|
|
41
|
+
class="w-full flex items-center justify-between py-4 text-sm font-medium hover:underline cursor-pointer outline-none"
|
|
42
|
+
onclick={(e) => { e.preventDefault(); handleToggle(index); }}
|
|
43
|
+
>
|
|
44
|
+
{#if typeof item.title === 'string'}
|
|
45
|
+
{item.title}
|
|
46
|
+
{:else}
|
|
47
|
+
{@render item.title()}
|
|
48
|
+
{/if}
|
|
49
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="transition-transform duration-200 group-open:rotate-180 text-muted-foreground"><path d="m6 9 6 6 6-6"/></svg>
|
|
50
|
+
</summary>
|
|
51
|
+
<div class="pb-4 text-sm text-muted-foreground">
|
|
52
|
+
{#if typeof item.content === 'string'}
|
|
53
|
+
{item.content}
|
|
54
|
+
{:else}
|
|
55
|
+
{@render item.content()}
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
</details>
|
|
59
|
+
{/each}
|
|
60
|
+
</section>
|
|
61
|
+
|
|
62
|
+
<style>
|
|
63
|
+
@reference "../reference.css";
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
items: Array<{
|
|
4
|
+
title: string | Snippet;
|
|
5
|
+
content: string | Snippet;
|
|
6
|
+
open?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
multiple?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
|
12
|
+
declare const Accordion: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type Accordion = ReturnType<typeof Accordion>;
|
|
14
|
+
export default Accordion;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
icon,
|
|
7
|
+
title,
|
|
8
|
+
variant = 'default',
|
|
9
|
+
class: className = '',
|
|
10
|
+
...rest
|
|
11
|
+
}: {
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
icon?: Snippet;
|
|
14
|
+
title?: Snippet | string;
|
|
15
|
+
variant?: 'default' | 'destructive';
|
|
16
|
+
class?: string;
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
const baseClass = $derived(variant === 'destructive' ? 'alert-destructive' : 'alert');
|
|
21
|
+
const finalClass = $derived([baseClass, className]
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.join(' '));
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div role="alert" class={finalClass} {...rest}>
|
|
27
|
+
{#if icon}
|
|
28
|
+
{@render icon()}
|
|
29
|
+
{/if}
|
|
30
|
+
|
|
31
|
+
{#if title}
|
|
32
|
+
{#if typeof title === 'string'}
|
|
33
|
+
<strong data-title>{title}</strong>
|
|
34
|
+
{:else}
|
|
35
|
+
<div data-title>
|
|
36
|
+
{@render title()}
|
|
37
|
+
</div>
|
|
38
|
+
{/if}
|
|
39
|
+
{/if}
|
|
40
|
+
|
|
41
|
+
{#if children}
|
|
42
|
+
<section>
|
|
43
|
+
{@render children()}
|
|
44
|
+
</section>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
@import "../../../../ultra/src/css/parts/components/alert.css";
|
|
50
|
+
@reference "../reference.css";
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
icon?: Snippet;
|
|
5
|
+
title?: Snippet | string;
|
|
6
|
+
variant?: 'default' | 'destructive';
|
|
7
|
+
class?: string;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
};
|
|
10
|
+
declare const Alert: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type Alert = ReturnType<typeof Alert>;
|
|
12
|
+
export default Alert;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
src,
|
|
6
|
+
alt = 'Avatar',
|
|
7
|
+
fallback,
|
|
8
|
+
class: className = '',
|
|
9
|
+
size = 'md',
|
|
10
|
+
rounded = 'full',
|
|
11
|
+
...rest
|
|
12
|
+
}: {
|
|
13
|
+
src?: string;
|
|
14
|
+
alt?: string;
|
|
15
|
+
fallback?: Snippet | string;
|
|
16
|
+
class?: string;
|
|
17
|
+
size?: 'sm' | 'md' | 'lg' | string;
|
|
18
|
+
rounded?: 'full' | 'lg' | 'md' | 'sm' | 'none';
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
const sizeMap: Record<string, string> = {
|
|
23
|
+
sm: 'size-8',
|
|
24
|
+
md: 'size-10',
|
|
25
|
+
lg: 'size-12'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const roundedMap: Record<string, string> = {
|
|
29
|
+
full: 'rounded-full',
|
|
30
|
+
lg: 'rounded-lg',
|
|
31
|
+
md: 'rounded-md',
|
|
32
|
+
sm: 'rounded-sm',
|
|
33
|
+
none: 'rounded-none'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const sizeClass = $derived(sizeMap[size] || size);
|
|
37
|
+
const roundedClass = $derived(roundedMap[rounded] || rounded);
|
|
38
|
+
|
|
39
|
+
const finalClass = $derived([
|
|
40
|
+
'avatar overflow-hidden bg-muted flex items-center justify-center shrink-0 object-cover',
|
|
41
|
+
sizeClass,
|
|
42
|
+
roundedClass,
|
|
43
|
+
className
|
|
44
|
+
].filter(Boolean).join(' '));
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<div class={finalClass} {...rest}>
|
|
48
|
+
{#if src}
|
|
49
|
+
<img {src} {alt} class="h-full w-full object-cover" />
|
|
50
|
+
{:else if fallback}
|
|
51
|
+
{#if typeof fallback === 'string'}
|
|
52
|
+
<span class="text-xs font-medium uppercase">{fallback}</span>
|
|
53
|
+
{:else}
|
|
54
|
+
{@render fallback()}
|
|
55
|
+
{/if}
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
src?: string;
|
|
4
|
+
alt?: string;
|
|
5
|
+
fallback?: Snippet | string;
|
|
6
|
+
class?: string;
|
|
7
|
+
size?: 'sm' | 'md' | 'lg' | string;
|
|
8
|
+
rounded?: 'full' | 'lg' | 'md' | 'sm' | 'none';
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
declare const Avatar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Avatar = ReturnType<typeof Avatar>;
|
|
13
|
+
export default Avatar;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
variant = 'primary',
|
|
7
|
+
class: className = '',
|
|
8
|
+
...rest
|
|
9
|
+
}: {
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline';
|
|
12
|
+
class?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const baseClass = 'badge';
|
|
17
|
+
const variantClass = $derived(variant !== 'primary' ? `badge-${variant}` : '');
|
|
18
|
+
const finalClass = $derived([baseClass, variantClass, className]
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(' '));
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<span class={finalClass} {...rest}>
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</span>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
@import "../../../../ultra/src/css/parts/components/badge.css";
|
|
29
|
+
@reference "../reference.css";
|
|
30
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline';
|
|
5
|
+
class?: string;
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
};
|
|
8
|
+
declare const Badge: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type Badge = ReturnType<typeof Badge>;
|
|
10
|
+
export default Badge;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
items = [],
|
|
6
|
+
separator = '/',
|
|
7
|
+
class: className = '',
|
|
8
|
+
...rest
|
|
9
|
+
}: {
|
|
10
|
+
items: Array<{ label: string | Snippet; href?: string; active?: boolean }>;
|
|
11
|
+
separator?: string | Snippet;
|
|
12
|
+
class?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const finalClass = $derived(['flex text-sm text-muted-foreground', className].filter(Boolean).join(' '));
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<nav aria-label="Breadcrumb" class={finalClass} {...rest}>
|
|
20
|
+
<ol class="flex items-center gap-2">
|
|
21
|
+
{#each items as item, i}
|
|
22
|
+
{#if i > 0}
|
|
23
|
+
<li>
|
|
24
|
+
<span class="opacity-50">
|
|
25
|
+
{#if typeof separator === 'string'}
|
|
26
|
+
{separator}
|
|
27
|
+
{:else}
|
|
28
|
+
{@render separator()}
|
|
29
|
+
{/if}
|
|
30
|
+
</span>
|
|
31
|
+
</li>
|
|
32
|
+
{/if}
|
|
33
|
+
<li>
|
|
34
|
+
{#if item.href && !item.active}
|
|
35
|
+
<a href={item.href} class="hover:text-foreground transition-colors">
|
|
36
|
+
{#if typeof item.label === 'string'}
|
|
37
|
+
{item.label}
|
|
38
|
+
{:else}
|
|
39
|
+
{@render item.label()}
|
|
40
|
+
{/if}
|
|
41
|
+
</a>
|
|
42
|
+
{:else}
|
|
43
|
+
<span class={item.active ? 'text-foreground font-medium' : ''}>
|
|
44
|
+
{#if typeof item.label === 'string'}
|
|
45
|
+
{item.label}
|
|
46
|
+
{:else}
|
|
47
|
+
{@render item.label()}
|
|
48
|
+
{/if}
|
|
49
|
+
</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</li>
|
|
52
|
+
{/each}
|
|
53
|
+
</ol>
|
|
54
|
+
</nav>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
items: Array<{
|
|
4
|
+
label: string | Snippet;
|
|
5
|
+
href?: string;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
separator?: string | Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
|
12
|
+
declare const Breadcrumb: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type Breadcrumb = ReturnType<typeof Breadcrumb>;
|
|
14
|
+
export default Breadcrumb;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
// Svelte 5 Runes: 使用 $props() 接收属性
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
type = 'button',
|
|
8
|
+
variant = 'primary',
|
|
9
|
+
size = 'md',
|
|
10
|
+
class: className = '',
|
|
11
|
+
...rest
|
|
12
|
+
}: {
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
type?: 'button' | 'submit' | 'reset';
|
|
15
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
|
|
16
|
+
size?: 'sm' | 'md' | 'lg' | 'icon';
|
|
17
|
+
class?: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
} = $props();
|
|
20
|
+
|
|
21
|
+
// 使用 $derived 确保属性更新时类名同步更新
|
|
22
|
+
const baseClass = 'btn';
|
|
23
|
+
const variantClass = $derived(variant !== 'primary' ? `btn-${variant}` : '');
|
|
24
|
+
const sizeClass = $derived(size !== 'md' ? `btn-${size}` : '');
|
|
25
|
+
|
|
26
|
+
const finalClass = $derived([baseClass, variantClass, sizeClass, className]
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.join(' '));
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<button {type} class={finalClass} {...rest}>
|
|
32
|
+
{@render children?.()}
|
|
33
|
+
</button>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
@import "../../../../ultra/src/css/parts/components/button.css";
|
|
37
|
+
@reference "../reference.css";
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
type?: 'button' | 'submit' | 'reset';
|
|
5
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
|
|
6
|
+
size?: 'sm' | 'md' | 'lg' | 'icon';
|
|
7
|
+
class?: string;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
};
|
|
10
|
+
declare const Button: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type Button = ReturnType<typeof Button>;
|
|
12
|
+
export default Button;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
orientation = 'horizontal',
|
|
7
|
+
class: className = '',
|
|
8
|
+
...rest
|
|
9
|
+
}: {
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
orientation?: 'horizontal' | 'vertical';
|
|
12
|
+
class?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const finalClass = $derived(['button-group', className].filter(Boolean).join(' '));
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div class={finalClass} data-orientation={orientation} {...rest}>
|
|
20
|
+
{@render children?.()}
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
@import "../../../../ultra/src/css/parts/components/button-group.css";
|
|
25
|
+
@reference "../reference.css";
|
|
26
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
orientation?: 'horizontal' | 'vertical';
|
|
5
|
+
class?: string;
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
};
|
|
8
|
+
declare const ButtonGroup: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type ButtonGroup = ReturnType<typeof ButtonGroup>;
|
|
10
|
+
export default ButtonGroup;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
header,
|
|
7
|
+
footer,
|
|
8
|
+
title,
|
|
9
|
+
description,
|
|
10
|
+
class: className = '',
|
|
11
|
+
...rest
|
|
12
|
+
}: {
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
header?: Snippet;
|
|
15
|
+
footer?: Snippet;
|
|
16
|
+
title?: Snippet | string;
|
|
17
|
+
description?: Snippet | string;
|
|
18
|
+
class?: string;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
const finalClass = $derived(['card', className]
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join(' '));
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<article class={finalClass} {...rest}>
|
|
28
|
+
{#if header || title || description}
|
|
29
|
+
<header>
|
|
30
|
+
{#if header}
|
|
31
|
+
{@render header()}
|
|
32
|
+
{:else}
|
|
33
|
+
{#if title}
|
|
34
|
+
{#if typeof title === 'string'}
|
|
35
|
+
<h2>{title}</h2>
|
|
36
|
+
{:else}
|
|
37
|
+
{@render title()}
|
|
38
|
+
{/if}
|
|
39
|
+
{/if}
|
|
40
|
+
{#if description}
|
|
41
|
+
{#if typeof description === 'string'}
|
|
42
|
+
<p>{description}</p>
|
|
43
|
+
{:else}
|
|
44
|
+
{@render description()}
|
|
45
|
+
{/if}
|
|
46
|
+
{/if}
|
|
47
|
+
{/if}
|
|
48
|
+
</header>
|
|
49
|
+
{/if}
|
|
50
|
+
|
|
51
|
+
{#if children}
|
|
52
|
+
<section>
|
|
53
|
+
{@render children()}
|
|
54
|
+
</section>
|
|
55
|
+
{/if}
|
|
56
|
+
|
|
57
|
+
{#if footer}
|
|
58
|
+
<footer>
|
|
59
|
+
{@render footer()}
|
|
60
|
+
</footer>
|
|
61
|
+
{/if}
|
|
62
|
+
</article>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
@import "../../../../ultra/src/css/parts/components/card.css";
|
|
66
|
+
@reference "../reference.css";
|
|
67
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
header?: Snippet;
|
|
5
|
+
footer?: Snippet;
|
|
6
|
+
title?: Snippet | string;
|
|
7
|
+
description?: Snippet | string;
|
|
8
|
+
class?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
declare const Card: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Card = ReturnType<typeof Card>;
|
|
13
|
+
export default Card;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
orientation = 'horizontal',
|
|
7
|
+
class: className = '',
|
|
8
|
+
children,
|
|
9
|
+
showButtons = true,
|
|
10
|
+
...rest
|
|
11
|
+
}: {
|
|
12
|
+
orientation?: 'horizontal' | 'vertical';
|
|
13
|
+
class?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
showButtons?: boolean;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
} = $props();
|
|
18
|
+
|
|
19
|
+
let scrollContainer: HTMLDivElement;
|
|
20
|
+
let canScrollPrev = $state(false);
|
|
21
|
+
let canScrollNext = $state(true);
|
|
22
|
+
|
|
23
|
+
function getScrollAmount() {
|
|
24
|
+
if (!scrollContainer) return 0;
|
|
25
|
+
const firstItem = scrollContainer.querySelector('.carousel-item') as HTMLElement;
|
|
26
|
+
if (!firstItem) {
|
|
27
|
+
return orientation === 'vertical' ? scrollContainer.offsetHeight : scrollContainer.offsetWidth;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const style = window.getComputedStyle(firstItem);
|
|
31
|
+
if (orientation === 'vertical') {
|
|
32
|
+
const itemHeight = firstItem.offsetHeight;
|
|
33
|
+
const marginTop = parseFloat(style.marginTop);
|
|
34
|
+
const marginBottom = parseFloat(style.marginBottom);
|
|
35
|
+
return itemHeight + marginTop + marginBottom;
|
|
36
|
+
} else {
|
|
37
|
+
const itemWidth = firstItem.offsetWidth;
|
|
38
|
+
const marginLeft = parseFloat(style.marginLeft);
|
|
39
|
+
const marginRight = parseFloat(style.marginRight);
|
|
40
|
+
return itemWidth + marginLeft + marginRight;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function scroll(direction: 'prev' | 'next') {
|
|
45
|
+
if (!scrollContainer) return;
|
|
46
|
+
const amount = getScrollAmount();
|
|
47
|
+
const value = direction === 'next' ? amount : -amount;
|
|
48
|
+
|
|
49
|
+
if (orientation === 'vertical') {
|
|
50
|
+
scrollContainer.scrollBy({ top: value, behavior: 'smooth' });
|
|
51
|
+
} else {
|
|
52
|
+
scrollContainer.scrollBy({ left: value, behavior: 'smooth' });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function updateButtons() {
|
|
57
|
+
if (!scrollContainer) return;
|
|
58
|
+
|
|
59
|
+
const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = scrollContainer;
|
|
60
|
+
|
|
61
|
+
if (orientation === 'vertical') {
|
|
62
|
+
canScrollPrev = scrollTop > 1;
|
|
63
|
+
canScrollNext = scrollTop < scrollHeight - clientHeight - 1;
|
|
64
|
+
} else {
|
|
65
|
+
canScrollPrev = scrollLeft > 1;
|
|
66
|
+
canScrollNext = scrollLeft < scrollWidth - clientWidth - 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
onMount(() => {
|
|
71
|
+
updateButtons();
|
|
72
|
+
|
|
73
|
+
// Use ResizeObserver to update buttons when content changes
|
|
74
|
+
const resizeObserver = new ResizeObserver(updateButtons);
|
|
75
|
+
resizeObserver.observe(scrollContainer);
|
|
76
|
+
|
|
77
|
+
return () => resizeObserver.disconnect();
|
|
78
|
+
});
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<div
|
|
82
|
+
class={['carousel relative', className].filter(Boolean).join(' ')}
|
|
83
|
+
data-orientation={orientation}
|
|
84
|
+
{...rest}
|
|
85
|
+
>
|
|
86
|
+
{#if showButtons && orientation === 'horizontal'}
|
|
87
|
+
<button
|
|
88
|
+
class="carousel-previous absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
|
|
89
|
+
onclick={() => scroll('prev')}
|
|
90
|
+
disabled={!canScrollPrev}
|
|
91
|
+
aria-label="Previous slide"
|
|
92
|
+
>
|
|
93
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
|
94
|
+
</button>
|
|
95
|
+
{/if}
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
bind:this={scrollContainer}
|
|
99
|
+
class={[
|
|
100
|
+
'carousel-content flex overflow-hidden scroll-smooth',
|
|
101
|
+
orientation === 'vertical' ? 'flex-col -mt-4 pb-4' : 'flex-row -ml-4'
|
|
102
|
+
].join(' ')}
|
|
103
|
+
onscroll={updateButtons}
|
|
104
|
+
>
|
|
105
|
+
{@render children?.()}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{#if showButtons && orientation === 'horizontal'}
|
|
109
|
+
<button
|
|
110
|
+
class="carousel-next absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
|
|
111
|
+
onclick={() => scroll('next')}
|
|
112
|
+
disabled={!canScrollNext}
|
|
113
|
+
aria-label="Next slide"
|
|
114
|
+
>
|
|
115
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
|
116
|
+
</button>
|
|
117
|
+
{/if}
|
|
118
|
+
|
|
119
|
+
{#if showButtons && orientation === 'vertical'}
|
|
120
|
+
<button
|
|
121
|
+
class="carousel-previous absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
|
|
122
|
+
onclick={() => scroll('prev')}
|
|
123
|
+
disabled={!canScrollPrev}
|
|
124
|
+
aria-label="Previous slide"
|
|
125
|
+
>
|
|
126
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>
|
|
127
|
+
</button>
|
|
128
|
+
<button
|
|
129
|
+
class="carousel-next absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
|
|
130
|
+
onclick={() => scroll('next')}
|
|
131
|
+
disabled={!canScrollNext}
|
|
132
|
+
aria-label="Next slide"
|
|
133
|
+
>
|
|
134
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
135
|
+
</button>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<style>
|
|
140
|
+
@import "../../../../ultra/src/css/parts/custom/carousel.css";
|
|
141
|
+
@reference "../reference.css";
|
|
142
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
orientation?: 'horizontal' | 'vertical';
|
|
4
|
+
class?: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
showButtons?: boolean;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
};
|
|
9
|
+
declare const Carousel: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type Carousel = ReturnType<typeof Carousel>;
|
|
11
|
+
export default Carousel;
|