@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,132 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
|
|
4
|
+
const THEMES = ['latte', 'frappe', 'macchiato', 'mocha'];
|
|
5
|
+
const DARK_THEMES = ['frappe', 'macchiato', 'mocha'];
|
|
6
|
+
const ACCENTS = [
|
|
7
|
+
'rosewater', 'flamingo', 'pink', 'mauve', 'red', 'maroon', 'peach',
|
|
8
|
+
'yellow', 'green', 'teal', 'sky', 'sapphire', 'blue', 'lavender'
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
let currentFlavor = $state('mocha'); // UI 预览用的口味
|
|
12
|
+
let appliedFlavor = $state(''); // 实际应用的口味
|
|
13
|
+
let appliedAccent = $state(''); // 实际应用的强调色
|
|
14
|
+
|
|
15
|
+
function setTheme(theme: string, accent: string) {
|
|
16
|
+
const root = document.documentElement;
|
|
17
|
+
|
|
18
|
+
// 1. 彻底清除旧的主题类名
|
|
19
|
+
THEMES.forEach(t => root.classList.remove(`theme-${t}`));
|
|
20
|
+
ACCENTS.forEach(a => root.classList.remove(`accent-${a}`));
|
|
21
|
+
|
|
22
|
+
// 2. 应用新的主题与强调色类名
|
|
23
|
+
root.classList.add(`theme-${theme}`);
|
|
24
|
+
root.classList.add(`accent-${accent}`);
|
|
25
|
+
|
|
26
|
+
// 3. 同步亮/暗模式 (Basecoat 变量依赖 .dark)
|
|
27
|
+
if (DARK_THEMES.includes(theme)) {
|
|
28
|
+
root.classList.add('dark');
|
|
29
|
+
} else {
|
|
30
|
+
root.classList.remove('dark');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
appliedFlavor = theme;
|
|
34
|
+
appliedAccent = accent;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
localStorage.setItem('catppuccin-theme', theme);
|
|
38
|
+
localStorage.setItem('catppuccin-accent', accent);
|
|
39
|
+
localStorage.removeItem('basecoat-mode');
|
|
40
|
+
} catch (e) {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resetTheme() {
|
|
44
|
+
const root = document.documentElement;
|
|
45
|
+
const wasDark = root.classList.contains('dark');
|
|
46
|
+
|
|
47
|
+
THEMES.forEach(t => root.classList.remove(`theme-${t}`));
|
|
48
|
+
ACCENTS.forEach(a => root.classList.remove(`accent-${a}`));
|
|
49
|
+
|
|
50
|
+
localStorage.removeItem('catppuccin-theme');
|
|
51
|
+
localStorage.removeItem('catppuccin-accent');
|
|
52
|
+
localStorage.setItem('basecoat-mode', wasDark ? 'dark' : 'light');
|
|
53
|
+
|
|
54
|
+
// 恢复到系统/手动模式默认
|
|
55
|
+
if (wasDark) {
|
|
56
|
+
root.classList.add('dark');
|
|
57
|
+
currentFlavor = 'mocha';
|
|
58
|
+
} else {
|
|
59
|
+
root.classList.remove('dark');
|
|
60
|
+
currentFlavor = 'latte';
|
|
61
|
+
}
|
|
62
|
+
appliedFlavor = '';
|
|
63
|
+
appliedAccent = '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onMount(() => {
|
|
67
|
+
const savedTheme = localStorage.getItem('catppuccin-theme');
|
|
68
|
+
const savedAccent = localStorage.getItem('catppuccin-accent');
|
|
69
|
+
if (savedTheme && savedAccent) {
|
|
70
|
+
currentFlavor = savedTheme;
|
|
71
|
+
appliedFlavor = savedTheme;
|
|
72
|
+
appliedAccent = savedAccent;
|
|
73
|
+
// 确保初始化时类名也同步
|
|
74
|
+
setTheme(savedTheme, savedAccent);
|
|
75
|
+
} else {
|
|
76
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
77
|
+
currentFlavor = isDark ? 'mocha' : 'latte';
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 预览色计算
|
|
82
|
+
const previewColors = $derived.by(() => {
|
|
83
|
+
const colors: Record<string, string> = { background: `var(--ctp-${currentFlavor}-base)` };
|
|
84
|
+
ACCENTS.forEach(accent => {
|
|
85
|
+
colors[accent] = `var(--ctp-${currentFlavor}-${accent})`;
|
|
86
|
+
});
|
|
87
|
+
return colors;
|
|
88
|
+
});
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div class="catppuccin-theme-switcher w-full max-w-sm space-y-6">
|
|
92
|
+
<!-- Flavors: 仅更新预览状态 -->
|
|
93
|
+
<div class="button-group w-full" data-orientation="horizontal">
|
|
94
|
+
{#each THEMES as theme}
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
class={['btn btn-sm capitalize flex-1', currentFlavor === theme ? 'btn-primary' : 'btn-ghost'].join(' ')}
|
|
98
|
+
onclick={() => currentFlavor = theme}
|
|
99
|
+
>
|
|
100
|
+
{theme}
|
|
101
|
+
</button>
|
|
102
|
+
{/each}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- Accents: 点击应用主题 -->
|
|
106
|
+
<div
|
|
107
|
+
class="grid grid-cols-7 gap-3 place-items-center p-4 rounded-xl border border-border/50 transition-colors duration-300 shadow-inner"
|
|
108
|
+
style:background-color={previewColors.background}
|
|
109
|
+
>
|
|
110
|
+
{#each ACCENTS as accent}
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
class={['size-6 rounded-full border border-white/20 shadow-sm transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-ring relative flex items-center justify-center',
|
|
114
|
+
appliedAccent === accent && appliedFlavor === currentFlavor ? 'ring-2 ring-white ring-offset-2 scale-110' : ''
|
|
115
|
+
].join(' ')}
|
|
116
|
+
title={accent}
|
|
117
|
+
style:background-color={previewColors[accent]}
|
|
118
|
+
onclick={() => setTheme(currentFlavor, accent)}
|
|
119
|
+
>
|
|
120
|
+
{#if appliedAccent === accent && appliedFlavor === currentFlavor}
|
|
121
|
+
<svg class="text-white drop-shadow-sm" style="width: 12px; height: 12px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
122
|
+
{/if}
|
|
123
|
+
</button>
|
|
124
|
+
{/each}
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div class="flex justify-center">
|
|
128
|
+
<button type="button" class="theme-reset btn btn-sm btn-ghost text-muted-foreground hover:text-destructive w-full" onclick={resetTheme}>
|
|
129
|
+
Reset Theme
|
|
130
|
+
</button>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
checked = $bindable(false),
|
|
4
|
+
class: className = '',
|
|
5
|
+
...rest
|
|
6
|
+
}: {
|
|
7
|
+
checked?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
} = $props();
|
|
11
|
+
|
|
12
|
+
const finalClass = $derived(['input', className].filter(Boolean).join(' '));
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<input type="checkbox" class={finalClass} bind:checked {...rest} />
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
@import "../../../../ultra/src/css/parts/components/checkbox.css";
|
|
19
|
+
@reference "../reference.css";
|
|
20
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
open = $bindable(false),
|
|
6
|
+
trigger,
|
|
7
|
+
children,
|
|
8
|
+
class: className = '',
|
|
9
|
+
...rest
|
|
10
|
+
}: {
|
|
11
|
+
open?: boolean;
|
|
12
|
+
trigger: Snippet<{ open: boolean }>;
|
|
13
|
+
children: Snippet;
|
|
14
|
+
class?: string;
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
} = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div class={['collapsible', className].filter(Boolean).join(' ')} {...rest}>
|
|
20
|
+
<div
|
|
21
|
+
role="button"
|
|
22
|
+
tabindex="0"
|
|
23
|
+
onclick={() => open = !open}
|
|
24
|
+
onkeydown={(e) => e.key === 'Enter' && (open = !open)}
|
|
25
|
+
>
|
|
26
|
+
{@render trigger({ open })}
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
{#if open}
|
|
30
|
+
<div class="collapsible-content">
|
|
31
|
+
{@render children()}
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
@import "../../../../ultra/src/css/parts/components/collapsible.css";
|
|
38
|
+
@reference "../reference.css";
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
trigger: Snippet<{
|
|
5
|
+
open: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
children: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
declare const Collapsible: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
12
|
+
type Collapsible = ReturnType<typeof Collapsible>;
|
|
13
|
+
export default Collapsible;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
value = $bindable(''),
|
|
6
|
+
placeholder = 'Type a command or search...',
|
|
7
|
+
children,
|
|
8
|
+
class: className = '',
|
|
9
|
+
onselect,
|
|
10
|
+
...rest
|
|
11
|
+
}: {
|
|
12
|
+
value?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
class?: string;
|
|
16
|
+
onselect?: (value: string) => void;
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
let container: HTMLDivElement;
|
|
21
|
+
let activeIndex = $state(-1);
|
|
22
|
+
|
|
23
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
24
|
+
const items = container?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])') as NodeListOf<HTMLElement>;
|
|
25
|
+
if (!items.length) return;
|
|
26
|
+
|
|
27
|
+
switch (event.key) {
|
|
28
|
+
case 'ArrowDown':
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
activeIndex = (activeIndex + 1) % items.length;
|
|
31
|
+
items[activeIndex]?.focus();
|
|
32
|
+
break;
|
|
33
|
+
case 'ArrowUp':
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
activeIndex = (activeIndex - 1 + items.length) % items.length;
|
|
36
|
+
items[activeIndex]?.focus();
|
|
37
|
+
break;
|
|
38
|
+
case 'Enter':
|
|
39
|
+
if (activeIndex >= 0) {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
items[activeIndex]?.click();
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
49
|
+
<div
|
|
50
|
+
bind:this={container}
|
|
51
|
+
class={['command bg-background flex flex-col overflow-hidden', className].filter(Boolean).join(' ')}
|
|
52
|
+
onkeydown={handleKeyDown}
|
|
53
|
+
{...rest}
|
|
54
|
+
>
|
|
55
|
+
<header class="flex items-center border-b px-3">
|
|
56
|
+
<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="opacity-50 mr-2 shrink-0"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
57
|
+
<input
|
|
58
|
+
type="text"
|
|
59
|
+
bind:value
|
|
60
|
+
{placeholder}
|
|
61
|
+
class="flex h-10 w-full bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-none focus:ring-0"
|
|
62
|
+
onkeydown={(e) => {
|
|
63
|
+
if (e.key === 'ArrowDown') {
|
|
64
|
+
handleKeyDown(e);
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
</header>
|
|
69
|
+
|
|
70
|
+
<div role="menu" class="max-h-40 overflow-y-auto overflow-x-hidden p-1 scrollbar">
|
|
71
|
+
{@render children?.()}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
@import "../../../../ultra/src/css/parts/components/command.css";
|
|
77
|
+
@reference "../reference.css";
|
|
78
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
value?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
class?: string;
|
|
7
|
+
onselect?: (value: string) => void;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
};
|
|
10
|
+
declare const Command: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
11
|
+
type Command = ReturnType<typeof Command>;
|
|
12
|
+
export default Command;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
import flatpickr from 'flatpickr';
|
|
4
|
+
import { Mandarin } from 'flatpickr/dist/l10n/zh.js';
|
|
5
|
+
import { MandarinTraditional } from 'flatpickr/dist/l10n/zh-tw.js';
|
|
6
|
+
import type { Instance } from 'flatpickr/dist/types/instance';
|
|
7
|
+
import type { Options } from 'flatpickr/dist/types/options';
|
|
8
|
+
|
|
9
|
+
// Custom English configurations
|
|
10
|
+
const EnglishFull = {
|
|
11
|
+
weekdays: {
|
|
12
|
+
shorthand: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
13
|
+
longhand: [
|
|
14
|
+
"Sunday",
|
|
15
|
+
"Monday",
|
|
16
|
+
"Tuesday",
|
|
17
|
+
"Wednesday",
|
|
18
|
+
"Thursday",
|
|
19
|
+
"Friday",
|
|
20
|
+
"Saturday",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
months: {
|
|
24
|
+
shorthand: [
|
|
25
|
+
"January",
|
|
26
|
+
"February",
|
|
27
|
+
"March",
|
|
28
|
+
"April",
|
|
29
|
+
"May",
|
|
30
|
+
"June",
|
|
31
|
+
"July",
|
|
32
|
+
"August",
|
|
33
|
+
"September",
|
|
34
|
+
"October",
|
|
35
|
+
"November",
|
|
36
|
+
"December",
|
|
37
|
+
],
|
|
38
|
+
longhand: [
|
|
39
|
+
"January",
|
|
40
|
+
"February",
|
|
41
|
+
"March",
|
|
42
|
+
"April",
|
|
43
|
+
"May",
|
|
44
|
+
"June",
|
|
45
|
+
"July",
|
|
46
|
+
"August",
|
|
47
|
+
"September",
|
|
48
|
+
"October",
|
|
49
|
+
"November",
|
|
50
|
+
"December",
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const EnglishShort = {
|
|
56
|
+
weekdays: {
|
|
57
|
+
shorthand: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
58
|
+
longhand: [
|
|
59
|
+
"Sunday",
|
|
60
|
+
"Monday",
|
|
61
|
+
"Tuesday",
|
|
62
|
+
"Wednesday",
|
|
63
|
+
"Thursday",
|
|
64
|
+
"Friday",
|
|
65
|
+
"Saturday",
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
months: {
|
|
69
|
+
shorthand: [
|
|
70
|
+
"Jan",
|
|
71
|
+
"Feb",
|
|
72
|
+
"Mar",
|
|
73
|
+
"Apr",
|
|
74
|
+
"May",
|
|
75
|
+
"Jun",
|
|
76
|
+
"Jul",
|
|
77
|
+
"Aug",
|
|
78
|
+
"Sep",
|
|
79
|
+
"Oct",
|
|
80
|
+
"Nov",
|
|
81
|
+
"Dec",
|
|
82
|
+
],
|
|
83
|
+
longhand: [
|
|
84
|
+
"Jan",
|
|
85
|
+
"Feb",
|
|
86
|
+
"Mar",
|
|
87
|
+
"Apr",
|
|
88
|
+
"May",
|
|
89
|
+
"Jun",
|
|
90
|
+
"Jul",
|
|
91
|
+
"Aug",
|
|
92
|
+
"Sep",
|
|
93
|
+
"Oct",
|
|
94
|
+
"Nov",
|
|
95
|
+
"Dec",
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Locale configurations
|
|
101
|
+
const locales = {
|
|
102
|
+
'zh': Mandarin,
|
|
103
|
+
'zh-tw': MandarinTraditional,
|
|
104
|
+
'en-full': EnglishFull,
|
|
105
|
+
'en-short': EnglishShort,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let {
|
|
109
|
+
value = $bindable(''),
|
|
110
|
+
options = {},
|
|
111
|
+
placeholder = 'Select date',
|
|
112
|
+
inline = false,
|
|
113
|
+
locale = 'en-short',
|
|
114
|
+
class: className = '',
|
|
115
|
+
...rest
|
|
116
|
+
}: {
|
|
117
|
+
value?: string | Date | Date[];
|
|
118
|
+
options?: Options;
|
|
119
|
+
placeholder?: string;
|
|
120
|
+
inline?: boolean;
|
|
121
|
+
locale?: string;
|
|
122
|
+
class?: string;
|
|
123
|
+
[key: string]: any;
|
|
124
|
+
} = $props();
|
|
125
|
+
|
|
126
|
+
let element: HTMLElement | undefined = $state();
|
|
127
|
+
let instance: Instance;
|
|
128
|
+
|
|
129
|
+
const finalClass = $derived(
|
|
130
|
+
inline
|
|
131
|
+
? ['datepicker', className].filter(Boolean).join(' ')
|
|
132
|
+
: ['input datepicker', className].filter(Boolean).join(' ')
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
onMount(() => {
|
|
136
|
+
if (element) {
|
|
137
|
+
// Get locale configuration
|
|
138
|
+
const localeConfig = locales[locale] || EnglishShort;
|
|
139
|
+
|
|
140
|
+
instance = flatpickr(element, {
|
|
141
|
+
...options,
|
|
142
|
+
inline,
|
|
143
|
+
defaultDate: value,
|
|
144
|
+
locale: localeConfig,
|
|
145
|
+
shorthandCurrentMonth: locale === "en-short", // Only use shorthand for English abbreviated
|
|
146
|
+
onChange: (selectedDates, dateStr) => {
|
|
147
|
+
value = dateStr;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
onDestroy(() => {
|
|
154
|
+
instance?.destroy();
|
|
155
|
+
});
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
{#if inline}
|
|
159
|
+
<div bind:this={element} class={finalClass} {...rest}></div>
|
|
160
|
+
{:else}
|
|
161
|
+
<input
|
|
162
|
+
bind:this={element}
|
|
163
|
+
type="text"
|
|
164
|
+
class={finalClass}
|
|
165
|
+
{placeholder}
|
|
166
|
+
{...rest}
|
|
167
|
+
/>
|
|
168
|
+
{/if}
|
|
169
|
+
|
|
170
|
+
<style>
|
|
171
|
+
@reference "../reference.css";
|
|
172
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Options } from 'flatpickr/dist/types/options';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
value?: string | Date | Date[];
|
|
4
|
+
options?: Options;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
inline?: boolean;
|
|
7
|
+
locale?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
declare const DatePicker: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
12
|
+
type DatePicker = ReturnType<typeof DatePicker>;
|
|
13
|
+
export default DatePicker;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
open = $bindable(false),
|
|
7
|
+
title,
|
|
8
|
+
description,
|
|
9
|
+
children,
|
|
10
|
+
footer,
|
|
11
|
+
role = 'dialog',
|
|
12
|
+
class: className = '',
|
|
13
|
+
...rest
|
|
14
|
+
}: {
|
|
15
|
+
open?: boolean;
|
|
16
|
+
title?: string | Snippet;
|
|
17
|
+
description?: string | Snippet;
|
|
18
|
+
children: Snippet;
|
|
19
|
+
footer?: Snippet;
|
|
20
|
+
role?: 'dialog' | 'alertdialog';
|
|
21
|
+
class?: string;
|
|
22
|
+
[key: string]: any;
|
|
23
|
+
} = $props();
|
|
24
|
+
|
|
25
|
+
let dialogElement: HTMLDialogElement;
|
|
26
|
+
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (open) {
|
|
29
|
+
dialogElement?.showModal();
|
|
30
|
+
} else {
|
|
31
|
+
dialogElement?.close();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const handleClose = () => {
|
|
36
|
+
open = false;
|
|
37
|
+
};
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
41
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
42
|
+
<dialog
|
|
43
|
+
bind:this={dialogElement}
|
|
44
|
+
class={['dialog', className].filter(Boolean).join(' ')}
|
|
45
|
+
{role}
|
|
46
|
+
onclose={handleClose}
|
|
47
|
+
onclick={(e) => {
|
|
48
|
+
// Only close on backdrop click for regular dialog, not alertdialog
|
|
49
|
+
if (role !== 'alertdialog' && e.target === dialogElement) {
|
|
50
|
+
handleClose();
|
|
51
|
+
}
|
|
52
|
+
}}
|
|
53
|
+
{...rest}
|
|
54
|
+
>
|
|
55
|
+
<!-- Only show close button for regular dialogs, not alert dialogs -->
|
|
56
|
+
{#if role !== 'alertdialog'}
|
|
57
|
+
<button class="close" aria-label="Close" onclick={handleClose}>
|
|
58
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
59
|
+
</button>
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
{#if title || description}
|
|
63
|
+
<header>
|
|
64
|
+
{#if title}
|
|
65
|
+
<h2>
|
|
66
|
+
{#if typeof title === 'string'}{title}{:else}{@render title()}{/if}
|
|
67
|
+
</h2>
|
|
68
|
+
{/if}
|
|
69
|
+
{#if description}
|
|
70
|
+
<p>
|
|
71
|
+
{#if typeof description === 'string'}{description}{:else}{@render description()}{/if}
|
|
72
|
+
</p>
|
|
73
|
+
{/if}
|
|
74
|
+
</header>
|
|
75
|
+
{/if}
|
|
76
|
+
|
|
77
|
+
<section>
|
|
78
|
+
{@render children()}
|
|
79
|
+
</section>
|
|
80
|
+
|
|
81
|
+
{#if footer}
|
|
82
|
+
<footer>
|
|
83
|
+
{@render footer()}
|
|
84
|
+
</footer>
|
|
85
|
+
{/if}
|
|
86
|
+
</dialog>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
@import "../../../../ultra/src/css/parts/components/dialog.css";
|
|
90
|
+
@reference "../reference.css";
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
title?: string | Snippet;
|
|
5
|
+
description?: string | Snippet;
|
|
6
|
+
children: Snippet;
|
|
7
|
+
footer?: Snippet;
|
|
8
|
+
role?: 'dialog' | 'alertdialog';
|
|
9
|
+
class?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
|
12
|
+
declare const Dialog: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
13
|
+
type Dialog = ReturnType<typeof Dialog>;
|
|
14
|
+
export default Dialog;
|