yaxa-svelte 1.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/assets/favicon.svg +15 -0
- package/dist/components/elements/Avatar.svelte +106 -0
- package/dist/components/elements/Avatar.svelte.d.ts +40 -0
- package/dist/components/elements/AvatarGroup.svelte +18 -0
- package/dist/components/elements/AvatarGroup.svelte.d.ts +8 -0
- package/dist/components/elements/Badge.svelte +202 -0
- package/dist/components/elements/Badge.svelte.d.ts +75 -0
- package/dist/components/elements/Button.svelte +300 -0
- package/dist/components/elements/Button.svelte.d.ts +109 -0
- package/dist/components/elements/ButtonGroup.svelte +22 -0
- package/dist/components/elements/ButtonGroup.svelte.d.ts +9 -0
- package/dist/components/elements/Chip.svelte +117 -0
- package/dist/components/elements/Chip.svelte.d.ts +15 -0
- package/dist/components/elements/DataTable.svelte +222 -0
- package/dist/components/elements/DataTable.svelte.d.ts +43 -0
- package/dist/components/elements/Icon.svelte +87 -0
- package/dist/components/elements/Icon.svelte.d.ts +10 -0
- package/dist/components/elements/Kbd.svelte +37 -0
- package/dist/components/elements/Kbd.svelte.d.ts +32 -0
- package/dist/components/elements/Link.svelte +63 -0
- package/dist/components/elements/Link.svelte.d.ts +15 -0
- package/dist/components/elements/Logo.svelte +77 -0
- package/dist/components/elements/Logo.svelte.d.ts +9 -0
- package/dist/components/elements/Meter.svelte +87 -0
- package/dist/components/elements/Meter.svelte.d.ts +14 -0
- package/dist/components/elements/Progress.svelte +63 -0
- package/dist/components/elements/Progress.svelte.d.ts +49 -0
- package/dist/components/elements/Skeleton.svelte +12 -0
- package/dist/components/elements/Skeleton.svelte.d.ts +6 -0
- package/dist/components/elements/Spinner.svelte +39 -0
- package/dist/components/elements/Spinner.svelte.d.ts +7 -0
- package/dist/components/forms/Checkbox.svelte +77 -0
- package/dist/components/forms/Checkbox.svelte.d.ts +17 -0
- package/dist/components/forms/ColorPicker.svelte +142 -0
- package/dist/components/forms/ColorPicker.svelte.d.ts +10 -0
- package/dist/components/forms/Form.svelte +84 -0
- package/dist/components/forms/Form.svelte.d.ts +14 -0
- package/dist/components/forms/FormField.svelte +56 -0
- package/dist/components/forms/FormField.svelte.d.ts +14 -0
- package/dist/components/forms/Input.svelte +138 -0
- package/dist/components/forms/Input.svelte.d.ts +58 -0
- package/dist/components/forms/RadioGroup.svelte +74 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
- package/dist/components/forms/Select.svelte +91 -0
- package/dist/components/forms/Select.svelte.d.ts +50 -0
- package/dist/components/forms/Slider.svelte +37 -0
- package/dist/components/forms/Slider.svelte.d.ts +12 -0
- package/dist/components/forms/Switch.svelte +81 -0
- package/dist/components/forms/Switch.svelte.d.ts +53 -0
- package/dist/components/forms/Textarea.svelte +73 -0
- package/dist/components/forms/Textarea.svelte.d.ts +64 -0
- package/dist/components/layout/Card.svelte +89 -0
- package/dist/components/layout/Card.svelte.d.ts +64 -0
- package/dist/components/layout/Container.svelte +36 -0
- package/dist/components/layout/Container.svelte.d.ts +37 -0
- package/dist/components/layout/Divider.svelte +45 -0
- package/dist/components/layout/Divider.svelte.d.ts +11 -0
- package/dist/components/layout/Footer.svelte +96 -0
- package/dist/components/layout/Footer.svelte.d.ts +10 -0
- package/dist/components/layout/Header.svelte +175 -0
- package/dist/components/layout/Header.svelte.d.ts +12 -0
- package/dist/components/layout/Section.svelte +62 -0
- package/dist/components/layout/Section.svelte.d.ts +14 -0
- package/dist/components/layout/YaxaApp.svelte +48 -0
- package/dist/components/layout/YaxaApp.svelte.d.ts +12 -0
- package/dist/components/navigation/Breadcrumb.svelte +58 -0
- package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
- package/dist/components/navigation/CommandPalette.svelte +201 -0
- package/dist/components/navigation/CommandPalette.svelte.d.ts +19 -0
- package/dist/components/navigation/DropdownMenu.svelte +75 -0
- package/dist/components/navigation/DropdownMenu.svelte.d.ts +20 -0
- package/dist/components/navigation/Pagination.svelte +106 -0
- package/dist/components/navigation/Pagination.svelte.d.ts +10 -0
- package/dist/components/navigation/Tabs.svelte +91 -0
- package/dist/components/navigation/Tabs.svelte.d.ts +18 -0
- package/dist/components/overlays/Accordion.svelte +54 -0
- package/dist/components/overlays/Accordion.svelte.d.ts +16 -0
- package/dist/components/overlays/Alert.svelte +110 -0
- package/dist/components/overlays/Alert.svelte.d.ts +37 -0
- package/dist/components/overlays/ContextMenu.svelte +67 -0
- package/dist/components/overlays/ContextMenu.svelte.d.ts +21 -0
- package/dist/components/overlays/Modal.svelte +99 -0
- package/dist/components/overlays/Modal.svelte.d.ts +40 -0
- package/dist/components/overlays/Popover.svelte +33 -0
- package/dist/components/overlays/Popover.svelte.d.ts +11 -0
- package/dist/components/overlays/Slideover.svelte +102 -0
- package/dist/components/overlays/Slideover.svelte.d.ts +37 -0
- package/dist/components/overlays/Tooltip.svelte +39 -0
- package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
- package/dist/components/saas/AuthCard.svelte +273 -0
- package/dist/components/saas/AuthCard.svelte.d.ts +16 -0
- package/dist/components/saas/PricingCard.svelte +166 -0
- package/dist/components/saas/PricingCard.svelte.d.ts +19 -0
- package/dist/components/saas/PricingTable.svelte +139 -0
- package/dist/components/saas/PricingTable.svelte.d.ts +11 -0
- package/dist/components/saas/SubscriptionCard.svelte +162 -0
- package/dist/components/saas/SubscriptionCard.svelte.d.ts +16 -0
- package/dist/components/saas/UserMenu.svelte +137 -0
- package/dist/components/saas/UserMenu.svelte.d.ts +19 -0
- package/dist/components/seo/Favicons.svelte +22 -0
- package/dist/components/seo/Favicons.svelte.d.ts +8 -0
- package/dist/components/seo/OgImage.svelte +61 -0
- package/dist/components/seo/OgImage.svelte.d.ts +12 -0
- package/dist/components/seo/Seo.svelte +170 -0
- package/dist/components/seo/Seo.svelte.d.ts +21 -0
- package/dist/composables/useAuth.svelte.d.ts +23 -0
- package/dist/composables/useAuth.svelte.js +157 -0
- package/dist/composables/useClipboard.svelte.d.ts +9 -0
- package/dist/composables/useClipboard.svelte.js +36 -0
- package/dist/composables/useColorMode.svelte.d.ts +8 -0
- package/dist/composables/useColorMode.svelte.js +20 -0
- package/dist/composables/useDebounce.svelte.d.ts +3 -0
- package/dist/composables/useDebounce.svelte.js +17 -0
- package/dist/composables/useMediaQuery.svelte.d.ts +3 -0
- package/dist/composables/useMediaQuery.svelte.js +17 -0
- package/dist/composables/useShortcuts.svelte.d.ts +10 -0
- package/dist/composables/useShortcuts.svelte.js +50 -0
- package/dist/composables/useToast.d.ts +19 -0
- package/dist/composables/useToast.js +34 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +92 -0
- package/dist/server/auth/hook.d.ts +14 -0
- package/dist/server/auth/hook.js +36 -0
- package/dist/server/auth/index.d.ts +39 -0
- package/dist/server/auth/index.js +69 -0
- package/dist/server/db/index.d.ts +18 -0
- package/dist/server/db/index.js +43 -0
- package/dist/server/db/schema-pg.d.ts +977 -0
- package/dist/server/db/schema-pg.js +83 -0
- package/dist/server/db/schema-sqlite.d.ts +1045 -0
- package/dist/server/db/schema-sqlite.js +83 -0
- package/dist/server/db/types.d.ts +44 -0
- package/dist/server/db/types.js +1 -0
- package/dist/server/email/index.d.ts +27 -0
- package/dist/server/email/index.js +79 -0
- package/dist/server/polar/index.d.ts +29 -0
- package/dist/server/polar/index.js +49 -0
- package/dist/server/polar/webhook.d.ts +16 -0
- package/dist/server/polar/webhook.js +118 -0
- package/dist/site/config.d.ts +63 -0
- package/dist/site/config.js +48 -0
- package/dist/site/context.d.ts +11 -0
- package/dist/site/context.js +23 -0
- package/dist/site/hook.d.ts +15 -0
- package/dist/site/hook.js +38 -0
- package/dist/site/manifest.d.ts +6 -0
- package/dist/site/manifest.js +32 -0
- package/dist/site/og.d.ts +14 -0
- package/dist/site/og.js +116 -0
- package/dist/site/robots.d.ts +10 -0
- package/dist/site/robots.js +54 -0
- package/dist/site/schema.d.ts +67 -0
- package/dist/site/schema.js +67 -0
- package/dist/site/seo-helpers.d.ts +36 -0
- package/dist/site/seo-helpers.js +21 -0
- package/dist/site/sitemap-xsl.d.ts +6 -0
- package/dist/site/sitemap-xsl.js +164 -0
- package/dist/site/sitemap.d.ts +20 -0
- package/dist/site/sitemap.js +61 -0
- package/dist/styles/yaxa.css +84 -0
- package/dist/theme/theme.svelte.d.ts +74 -0
- package/dist/theme/theme.svelte.js +421 -0
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.js +3 -0
- package/dist/vite/index.d.ts +20 -0
- package/dist/vite/index.js +29 -0
- package/package.json +108 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface MenuItem {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
shortcut?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
separator?: boolean;
|
|
8
|
+
destructive?: boolean;
|
|
9
|
+
onSelect?: () => void;
|
|
10
|
+
}
|
|
11
|
+
import { DropdownMenu } from 'bits-ui';
|
|
12
|
+
import type { Snippet } from 'svelte';
|
|
13
|
+
interface Props {
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
trigger?: Snippet;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const DropdownMenu: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type DropdownMenu = ReturnType<typeof DropdownMenu>;
|
|
20
|
+
export default DropdownMenu;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../elements/Button.svelte';
|
|
3
|
+
import Icon from '../elements/Icon.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
page?: number;
|
|
7
|
+
total: number;
|
|
8
|
+
pageSize?: number;
|
|
9
|
+
siblingCount?: number;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
page = $bindable(1),
|
|
15
|
+
total,
|
|
16
|
+
pageSize = 10,
|
|
17
|
+
siblingCount = 1,
|
|
18
|
+
class: className = ''
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
let totalPages = $derived(Math.max(1, Math.ceil(total / pageSize)));
|
|
22
|
+
|
|
23
|
+
let paginationRange = $derived.by(() => {
|
|
24
|
+
const range: (number | string)[] = [];
|
|
25
|
+
const totalNumbers = siblingCount * 2 + 3;
|
|
26
|
+
const totalBlocks = totalNumbers + 2;
|
|
27
|
+
|
|
28
|
+
if (totalPages <= totalBlocks) {
|
|
29
|
+
for (let i = 1; i <= totalPages; i++) range.push(i);
|
|
30
|
+
return range;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const leftSiblingIndex = Math.max(page - siblingCount, 1);
|
|
34
|
+
const rightSiblingIndex = Math.min(page + siblingCount, totalPages);
|
|
35
|
+
|
|
36
|
+
const shouldShowLeftDots = leftSiblingIndex > 2;
|
|
37
|
+
const shouldShowRightDots = rightSiblingIndex < totalPages - 2;
|
|
38
|
+
|
|
39
|
+
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
40
|
+
const leftItemCount = 3 + 2 * siblingCount;
|
|
41
|
+
for (let i = 1; i <= leftItemCount; i++) range.push(i);
|
|
42
|
+
range.push('...');
|
|
43
|
+
range.push(totalPages);
|
|
44
|
+
} else if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
45
|
+
const rightItemCount = 3 + 2 * siblingCount;
|
|
46
|
+
range.push(1);
|
|
47
|
+
range.push('...');
|
|
48
|
+
for (let i = totalPages - rightItemCount + 1; i <= totalPages; i++) range.push(i);
|
|
49
|
+
} else if (shouldShowLeftDots && shouldShowRightDots) {
|
|
50
|
+
range.push(1);
|
|
51
|
+
range.push('...');
|
|
52
|
+
for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) range.push(i);
|
|
53
|
+
range.push('...');
|
|
54
|
+
range.push(totalPages);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return range;
|
|
58
|
+
});
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<nav aria-label="Pagination" class="flex items-center gap-1 {className}">
|
|
62
|
+
<!-- Previous -->
|
|
63
|
+
<Button
|
|
64
|
+
variant="outline"
|
|
65
|
+
color="neutral"
|
|
66
|
+
size="sm"
|
|
67
|
+
square
|
|
68
|
+
disabled={page <= 1}
|
|
69
|
+
onclick={() => (page = Math.max(1, page - 1))}
|
|
70
|
+
aria-label="Previous page"
|
|
71
|
+
>
|
|
72
|
+
<Icon name="chevron-left" size="xs" />
|
|
73
|
+
</Button>
|
|
74
|
+
|
|
75
|
+
<!-- Page Numbers -->
|
|
76
|
+
{#each paginationRange as p}
|
|
77
|
+
{#if typeof p === 'number'}
|
|
78
|
+
<Button
|
|
79
|
+
variant={page === p ? 'solid' : 'ghost'}
|
|
80
|
+
color={page === p ? 'primary' : 'neutral'}
|
|
81
|
+
size="sm"
|
|
82
|
+
square
|
|
83
|
+
onclick={() => (page = p)}
|
|
84
|
+
aria-label="Page {p}"
|
|
85
|
+
aria-current={page === p ? 'page' : undefined}
|
|
86
|
+
>
|
|
87
|
+
{p}
|
|
88
|
+
</Button>
|
|
89
|
+
{:else}
|
|
90
|
+
<span class="px-2 text-xs text-neutral-400 select-none">...</span>
|
|
91
|
+
{/if}
|
|
92
|
+
{/each}
|
|
93
|
+
|
|
94
|
+
<!-- Next -->
|
|
95
|
+
<Button
|
|
96
|
+
variant="outline"
|
|
97
|
+
color="neutral"
|
|
98
|
+
size="sm"
|
|
99
|
+
square
|
|
100
|
+
disabled={page >= totalPages}
|
|
101
|
+
onclick={() => (page = Math.min(totalPages, page + 1))}
|
|
102
|
+
aria-label="Next page"
|
|
103
|
+
>
|
|
104
|
+
<Icon name="chevron-right" size="xs" />
|
|
105
|
+
</Button>
|
|
106
|
+
</nav>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
page?: number;
|
|
3
|
+
total: number;
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
siblingCount?: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const Pagination: import("svelte").Component<Props, {}, "page">;
|
|
9
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
10
|
+
export default Pagination;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface TabItem {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
badge?: string;
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import type { Snippet } from 'svelte';
|
|
13
|
+
import Icon from '../elements/Icon.svelte';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
items: TabItem[];
|
|
17
|
+
value?: string;
|
|
18
|
+
variant?: 'pill' | 'underline' | 'segmented';
|
|
19
|
+
class?: string;
|
|
20
|
+
children?: Snippet;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
items,
|
|
25
|
+
value = $bindable(items[0]?.value || ''),
|
|
26
|
+
variant = 'segmented',
|
|
27
|
+
class: className = '',
|
|
28
|
+
children
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="w-full">
|
|
33
|
+
<div
|
|
34
|
+
class="flex {variant === 'segmented'
|
|
35
|
+
? 'rounded-xl border border-neutral-200/60 bg-neutral-100 p-1 dark:border-neutral-800/60 dark:bg-neutral-900'
|
|
36
|
+
: variant === 'underline'
|
|
37
|
+
? 'gap-6 border-b border-neutral-200 dark:border-neutral-800'
|
|
38
|
+
: 'gap-2'} {className}"
|
|
39
|
+
role="tablist"
|
|
40
|
+
>
|
|
41
|
+
{#each items as tab}
|
|
42
|
+
{@const isSelected = value === tab.value}
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
role="tab"
|
|
46
|
+
aria-selected={isSelected}
|
|
47
|
+
disabled={tab.disabled}
|
|
48
|
+
onclick={() => (value = tab.value)}
|
|
49
|
+
class="inline-flex items-center justify-center gap-2 text-sm font-medium transition-all duration-150 select-none {tab.disabled
|
|
50
|
+
? 'cursor-not-allowed opacity-50'
|
|
51
|
+
: 'cursor-pointer'} {variant === 'segmented'
|
|
52
|
+
? `rounded-lg px-3 py-1.5 ${
|
|
53
|
+
isSelected
|
|
54
|
+
? 'bg-white text-neutral-900 shadow-xs dark:bg-neutral-800 dark:text-white'
|
|
55
|
+
: 'text-neutral-600 hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-white'
|
|
56
|
+
}`
|
|
57
|
+
: variant === 'underline'
|
|
58
|
+
? `-mb-px border-b-2 pt-1 pb-3 ${
|
|
59
|
+
isSelected
|
|
60
|
+
? 'border-primary-600 font-semibold text-primary-600 dark:text-primary-400'
|
|
61
|
+
: 'border-transparent text-neutral-500 hover:border-neutral-300 hover:text-neutral-800 dark:text-neutral-400 dark:hover:text-neutral-200'
|
|
62
|
+
}`
|
|
63
|
+
: `rounded-lg px-3 py-1.5 ${
|
|
64
|
+
isSelected
|
|
65
|
+
? 'bg-primary-600 text-white shadow-xs'
|
|
66
|
+
: 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-300 dark:hover:bg-neutral-700'
|
|
67
|
+
}`}"
|
|
68
|
+
>
|
|
69
|
+
{#if tab.icon}
|
|
70
|
+
<Icon name={tab.icon} size="xs" />
|
|
71
|
+
{/if}
|
|
72
|
+
<span>{tab.label}</span>
|
|
73
|
+
{#if tab.badge}
|
|
74
|
+
<span
|
|
75
|
+
class="rounded px-1.5 py-0.5 text-[10px] font-semibold {isSelected
|
|
76
|
+
? 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200'
|
|
77
|
+
: 'bg-neutral-200 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-300'}"
|
|
78
|
+
>
|
|
79
|
+
{tab.badge}
|
|
80
|
+
</span>
|
|
81
|
+
{/if}
|
|
82
|
+
</button>
|
|
83
|
+
{/each}
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{#if children}
|
|
87
|
+
<div class="mt-4">
|
|
88
|
+
{@render children()}
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TabItem {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
badge?: string;
|
|
7
|
+
}
|
|
8
|
+
import type { Snippet } from 'svelte';
|
|
9
|
+
interface Props {
|
|
10
|
+
items: TabItem[];
|
|
11
|
+
value?: string;
|
|
12
|
+
variant?: 'pill' | 'underline' | 'segmented';
|
|
13
|
+
class?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
declare const Tabs: import("svelte").Component<Props, {}, "value">;
|
|
17
|
+
type Tabs = ReturnType<typeof Tabs>;
|
|
18
|
+
export default Tabs;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface AccordionItem {
|
|
3
|
+
value: string;
|
|
4
|
+
title: string;
|
|
5
|
+
content?: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { Accordion } from 'bits-ui';
|
|
13
|
+
import Icon from '../elements/Icon.svelte';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
items: AccordionItem[];
|
|
17
|
+
type?: 'single' | 'multiple';
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { items, type = 'single', class: className = '' }: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<Accordion.Root
|
|
25
|
+
{type}
|
|
26
|
+
class="w-full divide-y divide-neutral-200 dark:divide-neutral-800 {className}"
|
|
27
|
+
>
|
|
28
|
+
{#each items as item}
|
|
29
|
+
<Accordion.Item value={item.value} disabled={item.disabled} class="py-1">
|
|
30
|
+
<Accordion.Header>
|
|
31
|
+
<Accordion.Trigger
|
|
32
|
+
class="flex w-full items-center justify-between py-4 text-left text-sm font-medium text-neutral-900 transition-all hover:underline dark:text-white [&[data-state=open]>svg]:rotate-180"
|
|
33
|
+
>
|
|
34
|
+
<div class="flex items-center gap-2.5">
|
|
35
|
+
{#if item.icon}
|
|
36
|
+
<Icon name={item.icon} size="xs" class="text-neutral-500" />
|
|
37
|
+
{/if}
|
|
38
|
+
<span>{item.title}</span>
|
|
39
|
+
</div>
|
|
40
|
+
<Icon
|
|
41
|
+
name="chevron-down"
|
|
42
|
+
size="xs"
|
|
43
|
+
class="text-neutral-500 transition-transform duration-200"
|
|
44
|
+
/>
|
|
45
|
+
</Accordion.Trigger>
|
|
46
|
+
</Accordion.Header>
|
|
47
|
+
<Accordion.Content
|
|
48
|
+
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden pb-4 text-sm text-neutral-600 dark:text-neutral-400"
|
|
49
|
+
>
|
|
50
|
+
{item.content}
|
|
51
|
+
</Accordion.Content>
|
|
52
|
+
</Accordion.Item>
|
|
53
|
+
{/each}
|
|
54
|
+
</Accordion.Root>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface AccordionItem {
|
|
2
|
+
value: string;
|
|
3
|
+
title: string;
|
|
4
|
+
content?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
import { Accordion } from 'bits-ui';
|
|
9
|
+
interface Props {
|
|
10
|
+
items: AccordionItem[];
|
|
11
|
+
type?: 'single' | 'multiple';
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const Accordion: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type Accordion = ReturnType<typeof Accordion>;
|
|
16
|
+
export default Accordion;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { tv, type VariantProps } from '../../utils/cn';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
export const alertVariants = tv({
|
|
6
|
+
base: 'relative w-full rounded-xl border p-4 transition-all duration-150',
|
|
7
|
+
variants: {
|
|
8
|
+
color: {
|
|
9
|
+
info: 'border-sky-200 bg-sky-50/70 text-sky-900 dark:border-sky-900/50 dark:bg-sky-950/30 dark:text-sky-200',
|
|
10
|
+
success:
|
|
11
|
+
'border-emerald-200 bg-emerald-50/70 text-emerald-900 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-200',
|
|
12
|
+
warning:
|
|
13
|
+
'border-amber-200 bg-amber-50/70 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200',
|
|
14
|
+
error:
|
|
15
|
+
'border-rose-200 bg-rose-50/70 text-rose-900 dark:border-rose-900/50 dark:bg-rose-950/30 dark:text-rose-200'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
color: 'info'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type AlertProps = VariantProps<typeof alertVariants> & {
|
|
24
|
+
title?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
icon?: string;
|
|
27
|
+
closable?: boolean;
|
|
28
|
+
onclose?: () => void;
|
|
29
|
+
actions?: Snippet;
|
|
30
|
+
class?: string;
|
|
31
|
+
children?: Snippet;
|
|
32
|
+
};
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import Icon from '../elements/Icon.svelte';
|
|
37
|
+
|
|
38
|
+
let {
|
|
39
|
+
color = 'info',
|
|
40
|
+
title,
|
|
41
|
+
description,
|
|
42
|
+
icon,
|
|
43
|
+
closable = false,
|
|
44
|
+
onclose,
|
|
45
|
+
actions,
|
|
46
|
+
class: className = '',
|
|
47
|
+
children
|
|
48
|
+
}: AlertProps = $props();
|
|
49
|
+
|
|
50
|
+
let visible = $state(true);
|
|
51
|
+
|
|
52
|
+
let defaultIcon = $derived.by(() => {
|
|
53
|
+
if (icon) return icon;
|
|
54
|
+
switch (color) {
|
|
55
|
+
case 'success':
|
|
56
|
+
return 'check';
|
|
57
|
+
case 'warning':
|
|
58
|
+
case 'error':
|
|
59
|
+
return 'alert';
|
|
60
|
+
case 'info':
|
|
61
|
+
default:
|
|
62
|
+
return 'info';
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function handleClose() {
|
|
67
|
+
visible = false;
|
|
68
|
+
if (onclose) onclose();
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
{#if visible}
|
|
73
|
+
<div class={alertVariants({ color, class: className })} role="alert">
|
|
74
|
+
<div class="flex items-start gap-3">
|
|
75
|
+
<div class="mt-0.5 shrink-0">
|
|
76
|
+
<Icon name={defaultIcon} size="sm" />
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="flex-1">
|
|
80
|
+
{#if title}
|
|
81
|
+
<h5 class="text-base font-semibold">{title}</h5>
|
|
82
|
+
{/if}
|
|
83
|
+
{#if description}
|
|
84
|
+
<div class="mt-1 text-sm opacity-90">{description}</div>
|
|
85
|
+
{/if}
|
|
86
|
+
{#if children}
|
|
87
|
+
<div class="mt-1 text-sm">
|
|
88
|
+
{@render children()}
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
{#if actions}
|
|
92
|
+
<div class="mt-3 flex items-center gap-2">
|
|
93
|
+
{@render actions()}
|
|
94
|
+
</div>
|
|
95
|
+
{/if}
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{#if closable}
|
|
99
|
+
<button
|
|
100
|
+
type="button"
|
|
101
|
+
onclick={handleClose}
|
|
102
|
+
class="rounded-lg p-1 opacity-70 transition-opacity hover:opacity-100"
|
|
103
|
+
aria-label="Dismiss alert"
|
|
104
|
+
>
|
|
105
|
+
<Icon name="cross" size="xs" />
|
|
106
|
+
</button>
|
|
107
|
+
{/if}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
{/if}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type VariantProps } from '../../utils/cn';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export declare const alertVariants: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
color: {
|
|
5
|
+
info: "border-sky-200 bg-sky-50/70 text-sky-900 dark:border-sky-900/50 dark:bg-sky-950/30 dark:text-sky-200";
|
|
6
|
+
success: "border-emerald-200 bg-emerald-50/70 text-emerald-900 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-200";
|
|
7
|
+
warning: "border-amber-200 bg-amber-50/70 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200";
|
|
8
|
+
error: "border-rose-200 bg-rose-50/70 text-rose-900 dark:border-rose-900/50 dark:bg-rose-950/30 dark:text-rose-200";
|
|
9
|
+
};
|
|
10
|
+
}, undefined, "relative w-full rounded-xl border p-4 transition-all duration-150", {
|
|
11
|
+
color: {
|
|
12
|
+
info: "border-sky-200 bg-sky-50/70 text-sky-900 dark:border-sky-900/50 dark:bg-sky-950/30 dark:text-sky-200";
|
|
13
|
+
success: "border-emerald-200 bg-emerald-50/70 text-emerald-900 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-200";
|
|
14
|
+
warning: "border-amber-200 bg-amber-50/70 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200";
|
|
15
|
+
error: "border-rose-200 bg-rose-50/70 text-rose-900 dark:border-rose-900/50 dark:bg-rose-950/30 dark:text-rose-200";
|
|
16
|
+
};
|
|
17
|
+
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
18
|
+
color: {
|
|
19
|
+
info: "border-sky-200 bg-sky-50/70 text-sky-900 dark:border-sky-900/50 dark:bg-sky-950/30 dark:text-sky-200";
|
|
20
|
+
success: "border-emerald-200 bg-emerald-50/70 text-emerald-900 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-200";
|
|
21
|
+
warning: "border-amber-200 bg-amber-50/70 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200";
|
|
22
|
+
error: "border-rose-200 bg-rose-50/70 text-rose-900 dark:border-rose-900/50 dark:bg-rose-950/30 dark:text-rose-200";
|
|
23
|
+
};
|
|
24
|
+
}, undefined>>;
|
|
25
|
+
export type AlertProps = VariantProps<typeof alertVariants> & {
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
icon?: string;
|
|
29
|
+
closable?: boolean;
|
|
30
|
+
onclose?: () => void;
|
|
31
|
+
actions?: Snippet;
|
|
32
|
+
class?: string;
|
|
33
|
+
children?: Snippet;
|
|
34
|
+
};
|
|
35
|
+
declare const Alert: import("svelte").Component<AlertProps, {}, "">;
|
|
36
|
+
type Alert = ReturnType<typeof Alert>;
|
|
37
|
+
export default Alert;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { ContextMenu } from 'bits-ui';
|
|
4
|
+
import Icon from '../elements/Icon.svelte';
|
|
5
|
+
import Kbd from '../elements/Kbd.svelte';
|
|
6
|
+
|
|
7
|
+
export interface ContextMenuItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
shortcut?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
destructive?: boolean;
|
|
14
|
+
separator?: boolean;
|
|
15
|
+
onSelect?: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
items?: ContextMenuItem[];
|
|
20
|
+
class?: string;
|
|
21
|
+
children: Snippet;
|
|
22
|
+
content?: Snippet;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { items = [], class: className = '', children, content }: Props = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<ContextMenu.Root>
|
|
29
|
+
<ContextMenu.Trigger class="w-full">
|
|
30
|
+
{@render children()}
|
|
31
|
+
</ContextMenu.Trigger>
|
|
32
|
+
|
|
33
|
+
<ContextMenu.Portal>
|
|
34
|
+
<ContextMenu.Content
|
|
35
|
+
class="z-50 min-w-45 rounded-xl border border-neutral-200/80 bg-white/95 p-1.5 shadow-xl backdrop-blur-md transition-all focus:outline-none dark:border-neutral-800/80 dark:bg-[#18181b]/95 {className}"
|
|
36
|
+
sideOffset={5}
|
|
37
|
+
>
|
|
38
|
+
{#if content}
|
|
39
|
+
{@render content()}
|
|
40
|
+
{:else}
|
|
41
|
+
{#each items as item}
|
|
42
|
+
{#if item.separator}
|
|
43
|
+
<ContextMenu.Separator class="my-1 h-px bg-neutral-200 dark:bg-neutral-800" />
|
|
44
|
+
{:else}
|
|
45
|
+
<ContextMenu.Item
|
|
46
|
+
disabled={item.disabled}
|
|
47
|
+
onSelect={item.onSelect}
|
|
48
|
+
class="group flex cursor-pointer items-center justify-between rounded-lg px-2.5 py-1.5 text-sm font-medium text-neutral-700 transition-colors outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-40 data-highlighted:bg-neutral-100 data-highlighted:text-neutral-900 dark:text-neutral-300 dark:data-highlighted:bg-neutral-800 dark:data-highlighted:text-white {item.destructive
|
|
49
|
+
? 'text-red-600 data-highlighted:bg-red-50 dark:text-red-400 dark:data-highlighted:bg-red-950/40'
|
|
50
|
+
: ''}"
|
|
51
|
+
>
|
|
52
|
+
<div class="flex items-center gap-2">
|
|
53
|
+
{#if item.icon}
|
|
54
|
+
<Icon name={item.icon} size="xs" />
|
|
55
|
+
{/if}
|
|
56
|
+
<span>{item.label}</span>
|
|
57
|
+
</div>
|
|
58
|
+
{#if item.shortcut}
|
|
59
|
+
<Kbd value={item.shortcut} size="xs" class="ml-auto" />
|
|
60
|
+
{/if}
|
|
61
|
+
</ContextMenu.Item>
|
|
62
|
+
{/if}
|
|
63
|
+
{/each}
|
|
64
|
+
{/if}
|
|
65
|
+
</ContextMenu.Content>
|
|
66
|
+
</ContextMenu.Portal>
|
|
67
|
+
</ContextMenu.Root>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { ContextMenu } from 'bits-ui';
|
|
3
|
+
export interface ContextMenuItem {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
shortcut?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
destructive?: boolean;
|
|
10
|
+
separator?: boolean;
|
|
11
|
+
onSelect?: () => void;
|
|
12
|
+
}
|
|
13
|
+
interface Props {
|
|
14
|
+
items?: ContextMenuItem[];
|
|
15
|
+
class?: string;
|
|
16
|
+
children: Snippet;
|
|
17
|
+
content?: Snippet;
|
|
18
|
+
}
|
|
19
|
+
declare const ContextMenu: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type ContextMenu = ReturnType<typeof ContextMenu>;
|
|
21
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { tv, type VariantProps } from '../../utils/cn';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
export const modalVariants = tv({
|
|
6
|
+
base: 'fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-neutral-200 bg-white p-6 shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 dark:border-neutral-800 dark:bg-neutral-900',
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: 'max-w-sm',
|
|
10
|
+
md: 'max-w-lg',
|
|
11
|
+
lg: 'max-w-2xl',
|
|
12
|
+
xl: 'max-w-4xl',
|
|
13
|
+
full: 'max-w-[calc(100vw-2rem)] h-[calc(100vh-2rem)]'
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
size: 'md'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type ModalProps = VariantProps<typeof modalVariants> & {
|
|
22
|
+
open?: boolean;
|
|
23
|
+
title?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
trigger?: Snippet;
|
|
26
|
+
header?: Snippet;
|
|
27
|
+
footer?: Snippet;
|
|
28
|
+
children?: Snippet;
|
|
29
|
+
class?: string;
|
|
30
|
+
};
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<script lang="ts">
|
|
34
|
+
import { Dialog } from 'bits-ui';
|
|
35
|
+
import Icon from '../elements/Icon.svelte';
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
open = $bindable(false),
|
|
39
|
+
title,
|
|
40
|
+
description,
|
|
41
|
+
size = 'md',
|
|
42
|
+
trigger,
|
|
43
|
+
header,
|
|
44
|
+
footer,
|
|
45
|
+
children,
|
|
46
|
+
class: className = ''
|
|
47
|
+
}: ModalProps = $props();
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<Dialog.Root bind:open>
|
|
51
|
+
{#if trigger}
|
|
52
|
+
<Dialog.Trigger class="inline-flex">
|
|
53
|
+
{@render trigger()}
|
|
54
|
+
</Dialog.Trigger>
|
|
55
|
+
{/if}
|
|
56
|
+
|
|
57
|
+
<Dialog.Portal>
|
|
58
|
+
<Dialog.Overlay
|
|
59
|
+
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-neutral-900/60 backdrop-blur-xs duration-200"
|
|
60
|
+
/>
|
|
61
|
+
<Dialog.Content class={modalVariants({ size, class: className })}>
|
|
62
|
+
{#if header}
|
|
63
|
+
{@render header()}
|
|
64
|
+
{:else if title || description}
|
|
65
|
+
<div class="mb-4">
|
|
66
|
+
{#if title}
|
|
67
|
+
<Dialog.Title class="text-lg font-bold text-neutral-900 dark:text-white">
|
|
68
|
+
{title}
|
|
69
|
+
</Dialog.Title>
|
|
70
|
+
{/if}
|
|
71
|
+
{#if description}
|
|
72
|
+
<Dialog.Description class="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
|
|
73
|
+
{description}
|
|
74
|
+
</Dialog.Description>
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
|
|
79
|
+
{#if children}
|
|
80
|
+
<div class="my-2">
|
|
81
|
+
{@render children()}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
84
|
+
|
|
85
|
+
{#if footer}
|
|
86
|
+
<div class="mt-6 flex justify-end gap-3">
|
|
87
|
+
{@render footer()}
|
|
88
|
+
</div>
|
|
89
|
+
{/if}
|
|
90
|
+
|
|
91
|
+
<Dialog.Close
|
|
92
|
+
class="absolute top-4 right-4 rounded-lg p-1 text-neutral-400 hover:bg-neutral-100 hover:text-neutral-600 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
|
|
93
|
+
aria-label="Close dialog"
|
|
94
|
+
>
|
|
95
|
+
<Icon name="cross" size="sm" />
|
|
96
|
+
</Dialog.Close>
|
|
97
|
+
</Dialog.Content>
|
|
98
|
+
</Dialog.Portal>
|
|
99
|
+
</Dialog.Root>
|