zabi-components 5.0.15 → 5.0.17
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 +51 -0
- package/dist/atoms/Badge.svelte +14 -14
- package/dist/atoms/Button.svelte +8 -8
- package/dist/atoms/Card.svelte +2 -2
- package/dist/atoms/Checkbox.svelte +19 -16
- package/dist/atoms/CodeBlock.svelte +4 -4
- package/dist/atoms/ColorPicker.svelte +4 -4
- package/dist/atoms/FeatureCard.svelte +2 -2
- package/dist/atoms/Input.svelte +5 -5
- package/dist/atoms/Progress.svelte +1 -1
- package/dist/atoms/Select.svelte +37 -14
- package/dist/atoms/Skeleton.svelte +1 -1
- package/dist/atoms/Textarea.svelte +2 -2
- package/dist/atoms/ThemeToggle.svelte +5 -5
- package/dist/atoms/Toast.svelte +1 -1
- package/dist/atoms/Toggle.svelte +2 -4
- package/dist/atoms/Tooltip.svelte +128 -19
- package/dist/molecules/Alert.svelte +13 -7
- package/dist/molecules/ComponentDemo.svelte +57 -64
- package/dist/molecules/Dropdown.svelte +4 -2
- package/dist/molecules/ImageUpload.svelte +2 -2
- package/dist/molecules/Modal.svelte +2 -2
- package/dist/molecules/SlideUp.svelte +2 -2
- package/dist/molecules/Tabs.svelte +2 -2
- package/dist/organisms/Navbar.svelte +1 -1
- package/dist/routes/lib/variant-utils.ts +4 -4
- package/dist/zabi-components-colors.css +185 -177
- package/dist/zabi-components-theme-dark-only.css +149 -0
- package/dist/zabi-components-theme-dark.css +151 -0
- package/dist/zabi-components-theme-only.css +227 -0
- package/dist/zabi-components-theme.css +229 -0
- package/dist/zabi-components.css +370 -331
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -40,6 +40,57 @@ npm install svelte@^5.38.10
|
|
|
40
40
|
npm install @sveltejs/kit@^2.0.0 # Optional, for SvelteKit projects
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## Theme Setup
|
|
44
|
+
|
|
45
|
+
Zabi Components includes a comprehensive theme system with Tailwind CSS v4. You need to import the theme before using components.
|
|
46
|
+
|
|
47
|
+
### Quick Setup
|
|
48
|
+
|
|
49
|
+
**For projects with existing Tailwind CSS:**
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
/* app.css */
|
|
53
|
+
@import "tailwindcss";
|
|
54
|
+
@import 'zabi-components/theme-only';
|
|
55
|
+
@import 'zabi-components/dist/zabi-components.css';
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**For standalone projects (no Tailwind setup):**
|
|
59
|
+
|
|
60
|
+
```css
|
|
61
|
+
/* app.css */
|
|
62
|
+
@import 'zabi-components/theme';
|
|
63
|
+
@import 'zabi-components/dist/zabi-components.css';
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### With Dark Mode
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
@import "tailwindcss";
|
|
70
|
+
@import 'zabi-components/theme-only';
|
|
71
|
+
@import 'zabi-components/theme-dark-only'; /* Add dark mode support */
|
|
72
|
+
@import 'zabi-components/dist/zabi-components.css';
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Extending the Theme
|
|
76
|
+
|
|
77
|
+
You can extend the theme with custom colors and fonts:
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
@import "tailwindcss";
|
|
81
|
+
@import 'zabi-components/theme-only';
|
|
82
|
+
|
|
83
|
+
/* Your custom theme extensions */
|
|
84
|
+
@theme {
|
|
85
|
+
--font-family-title: 'Your Font', sans-serif;
|
|
86
|
+
--color-custom-primary: #ff0000;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@import 'zabi-components/dist/zabi-components.css';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> 📖 **Complete Theme Guide**: See [THEME.md](./THEME.md) for comprehensive documentation on theme usage, customization, dark mode, and advanced patterns.
|
|
93
|
+
|
|
43
94
|
## Import Methods
|
|
44
95
|
|
|
45
96
|
Zabi Components supports multiple import patterns:
|
package/dist/atoms/Badge.svelte
CHANGED
|
@@ -45,18 +45,18 @@
|
|
|
45
45
|
// Note: Using text-white instead of text-inverse to ensure light text in both light and dark modes
|
|
46
46
|
const variantClass =
|
|
47
47
|
variant === "success"
|
|
48
|
-
? "bg-success border-success text-
|
|
48
|
+
? "bg-success border-success text-card"
|
|
49
49
|
: variant === "warning"
|
|
50
|
-
? "bg-warning border-warning text-
|
|
50
|
+
? "bg-warning border-warning text-card"
|
|
51
51
|
: variant === "error"
|
|
52
|
-
? "bg-error border-error text-
|
|
52
|
+
? "bg-error border-error text-card"
|
|
53
53
|
: variant === "info"
|
|
54
|
-
? "bg-info border-info text-
|
|
54
|
+
? "bg-info border-info text-card"
|
|
55
55
|
: variant === "neutral"
|
|
56
|
-
? "bg-neutral border-neutral text-
|
|
56
|
+
? "bg-neutral border-neutral text-card"
|
|
57
57
|
: variant === "energetic"
|
|
58
|
-
? "bg-energetic border-energetic text-
|
|
59
|
-
: "bg-secondary border-secondary text-
|
|
58
|
+
? "bg-energetic border-energetic text-card"
|
|
59
|
+
: "bg-secondary border-secondary text-card"; // default
|
|
60
60
|
|
|
61
61
|
return `${baseClasses} ${sizeClass} ${variantClass}`.trim();
|
|
62
62
|
});
|
|
@@ -75,22 +75,22 @@
|
|
|
75
75
|
<span class={classes()}>
|
|
76
76
|
{#if showIcon}
|
|
77
77
|
{#if variant === "success"}
|
|
78
|
-
<Check size={iconSize()} class="{iconSpacingClass()} text-
|
|
78
|
+
<Check size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
79
79
|
{:else if variant === "warning"}
|
|
80
80
|
<AlertTriangle
|
|
81
81
|
size={iconSize()}
|
|
82
|
-
class="{iconSpacingClass()} text-
|
|
82
|
+
class="{iconSpacingClass()} text-card"
|
|
83
83
|
/>
|
|
84
84
|
{:else if variant === "error"}
|
|
85
|
-
<X size={iconSize()} class="{iconSpacingClass()} text-
|
|
85
|
+
<X size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
86
86
|
{:else if variant === "info"}
|
|
87
|
-
<Info size={iconSize()} class="{iconSpacingClass()} text-
|
|
87
|
+
<Info size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
88
88
|
{:else if variant === "neutral"}
|
|
89
|
-
<Info size={iconSize()} class="{iconSpacingClass()} text-
|
|
89
|
+
<Info size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
90
90
|
{:else if variant === "energetic"}
|
|
91
|
-
<Info size={iconSize()} class="{iconSpacingClass()} text-
|
|
91
|
+
<Info size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
92
92
|
{:else}
|
|
93
|
-
<Info size={iconSize()} class="{iconSpacingClass()} text-
|
|
93
|
+
<Info size={iconSize()} class="{iconSpacingClass()} text-card" />
|
|
94
94
|
{/if}
|
|
95
95
|
{/if}
|
|
96
96
|
{text}
|
package/dist/atoms/Button.svelte
CHANGED
|
@@ -32,28 +32,28 @@
|
|
|
32
32
|
leading: "leading-5",
|
|
33
33
|
tracking: "tracking-[0.1px]",
|
|
34
34
|
radius: "rounded-lg",
|
|
35
|
-
gap: "gap-2"
|
|
35
|
+
gap: "gap-2",
|
|
36
36
|
};
|
|
37
37
|
} else if (size === "lg") {
|
|
38
38
|
return {
|
|
39
|
-
padding: "px-
|
|
40
|
-
text: "text-
|
|
39
|
+
padding: "px-8 py-4",
|
|
40
|
+
text: "text-lg",
|
|
41
41
|
font: "font-normal",
|
|
42
42
|
leading: "leading-8",
|
|
43
43
|
tracking: "tracking-normal",
|
|
44
|
-
radius: "rounded-
|
|
45
|
-
gap: "gap-3"
|
|
44
|
+
radius: "rounded-xl",
|
|
45
|
+
gap: "gap-3",
|
|
46
46
|
};
|
|
47
47
|
} else {
|
|
48
48
|
// default md
|
|
49
49
|
return {
|
|
50
|
-
padding: "px-
|
|
50
|
+
padding: "px-5 py-3",
|
|
51
51
|
text: "text-base",
|
|
52
52
|
font: "font-medium",
|
|
53
53
|
leading: "leading-6",
|
|
54
54
|
tracking: "tracking-[0.15px]",
|
|
55
55
|
radius: "rounded-lg",
|
|
56
|
-
gap: "gap-2"
|
|
56
|
+
gap: "gap-2",
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
});
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
: variant === "danger"
|
|
68
68
|
? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
|
69
69
|
: variant === "ghost"
|
|
70
|
-
? "bg-transparent text-headline hover:bg-
|
|
70
|
+
? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 focus:ring-2 focus:ring-base-500 focus:ring-offset-2 disabled:text-disabled"
|
|
71
71
|
: "bg-action-primary text-inverse hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"; // default primary
|
|
72
72
|
});
|
|
73
73
|
|
package/dist/atoms/Card.svelte
CHANGED
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
const cardClasses = $derived(() => {
|
|
30
30
|
const baseClasses =
|
|
31
|
-
"rounded-lg transition-all duration-200 min-w-64 bg-
|
|
31
|
+
"rounded-lg transition-all duration-200 min-w-64 bg-card shadow-sm";
|
|
32
32
|
const interactiveClasses = onclick
|
|
33
|
-
? "cursor-pointer hover:shadow-lg hover:bg-
|
|
33
|
+
? "cursor-pointer hover:shadow-lg hover:bg-card-hover"
|
|
34
34
|
: "";
|
|
35
35
|
const widthClasses = fullWidth ? "w-full" : "";
|
|
36
36
|
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
// Checkbox container classes matching M3 design
|
|
32
32
|
const checkboxContainerClasses = $derived(() => {
|
|
33
|
-
const baseClasses =
|
|
34
|
-
|
|
33
|
+
const baseClasses =
|
|
34
|
+
"relative inline-flex items-center justify-center w-5 h-5 rounded transition-all duration-200";
|
|
35
|
+
|
|
35
36
|
if (disabled) {
|
|
36
37
|
return `${baseClasses} opacity-50 cursor-not-allowed`;
|
|
37
38
|
}
|
|
@@ -41,13 +42,13 @@
|
|
|
41
42
|
// Checkbox border and background classes matching M3 design
|
|
42
43
|
const checkboxBoxClasses = $derived(() => {
|
|
43
44
|
if (checked) {
|
|
44
|
-
return disabled
|
|
45
|
-
? "border-0 bg-brand-
|
|
46
|
-
: "border-0 bg-brand-
|
|
45
|
+
return disabled
|
|
46
|
+
? "border-0 opacity-50 bg-brand-500"
|
|
47
|
+
: "border-0 bg-brand-500";
|
|
47
48
|
}
|
|
48
49
|
return disabled
|
|
49
|
-
? "border-2 border-
|
|
50
|
-
: "border-2 border-
|
|
50
|
+
? "border-2 border-base-400 bg-transparent"
|
|
51
|
+
: "border-2 border-base-400 bg-transparent";
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
function handleChange(event: Event) {
|
|
@@ -63,7 +64,9 @@
|
|
|
63
64
|
<div class="flex items-center gap-2">
|
|
64
65
|
<label
|
|
65
66
|
for={checkboxId}
|
|
66
|
-
class="flex items-center gap-2 cursor-pointer {disabled
|
|
67
|
+
class="flex items-center gap-2 cursor-pointer {disabled
|
|
68
|
+
? 'opacity-50 cursor-not-allowed'
|
|
69
|
+
: ''}"
|
|
67
70
|
>
|
|
68
71
|
<div class={checkboxContainerClasses()}>
|
|
69
72
|
<input
|
|
@@ -78,16 +81,16 @@
|
|
|
78
81
|
/>
|
|
79
82
|
<div class="absolute inset-0 {checkboxBoxClasses()} rounded"></div>
|
|
80
83
|
{#if checked}
|
|
81
|
-
<svg
|
|
82
|
-
class="absolute w-3 h-3 text-
|
|
83
|
-
fill="none"
|
|
84
|
-
stroke="currentColor"
|
|
84
|
+
<svg
|
|
85
|
+
class="absolute w-3 h-3 text-base-50 pointer-events-none z-10"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
85
88
|
viewBox="0 0 24 24"
|
|
86
89
|
>
|
|
87
|
-
<path
|
|
88
|
-
stroke-linecap="round"
|
|
89
|
-
stroke-linejoin="round"
|
|
90
|
-
stroke-width="2.5"
|
|
90
|
+
<path
|
|
91
|
+
stroke-linecap="round"
|
|
92
|
+
stroke-linejoin="round"
|
|
93
|
+
stroke-width="2.5"
|
|
91
94
|
d="M5 13l4 4L19 7"
|
|
92
95
|
/>
|
|
93
96
|
</svg>
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
</script>
|
|
31
31
|
|
|
32
32
|
<div
|
|
33
|
-
class="code-block relative bg-
|
|
33
|
+
class="code-block relative bg-base-900 rounded-lg overflow-hidden {className}"
|
|
34
34
|
{...restProps}
|
|
35
35
|
>
|
|
36
36
|
<!-- Header -->
|
|
37
37
|
<div
|
|
38
|
-
class="flex items-center justify-between px-4 py-2 bg-
|
|
38
|
+
class="flex items-center justify-between px-4 py-2 bg-base-800 border-b border-base-700"
|
|
39
39
|
>
|
|
40
40
|
<div class="flex items-center gap-2">
|
|
41
41
|
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
{#if showCopyButton}
|
|
46
46
|
<button
|
|
47
47
|
onclick={copyToClipboard}
|
|
48
|
-
class="flex items-center gap-2 px-3 py-1 text-xs text-
|
|
48
|
+
class="flex items-center gap-2 px-3 py-1 text-xs text-base-300 hover:text-white hover:bg-base-700 rounded transition-colors duration-200"
|
|
49
49
|
aria-label="Copy code to clipboard"
|
|
50
50
|
>
|
|
51
51
|
{#if copied}
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
</div>
|
|
83
83
|
|
|
84
84
|
<!-- Code content -->
|
|
85
|
-
<pre class="p-4 overflow-x-auto text-sm text-
|
|
85
|
+
<pre class="p-4 overflow-x-auto text-sm text-base-100 leading-relaxed"><code
|
|
86
86
|
class="language-{language}">{@html code}</code
|
|
87
87
|
></pre>
|
|
88
88
|
</div>
|
|
@@ -68,11 +68,11 @@
|
|
|
68
68
|
// Input classes
|
|
69
69
|
const inputClasses = $derived(() => {
|
|
70
70
|
const baseClasses =
|
|
71
|
-
"w-full px-3 py-2
|
|
71
|
+
"w-full px-3 py-2 bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg text-sm text-body placeholder:text-description transition-colors focus:outline-none focus:ring-2 focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
72
72
|
const stateClasses =
|
|
73
73
|
isValidHex(value) || value === ""
|
|
74
|
-
? "border-
|
|
75
|
-
: "border-red-300 focus:border-red-500 focus:ring-red-
|
|
74
|
+
? "border-base-300 focus:border-brand-500 focus:ring-brand-500"
|
|
75
|
+
: "border-red-300 focus:border-red-500 focus:ring-red-500";
|
|
76
76
|
|
|
77
77
|
return `${baseClasses} ${stateClasses}`.trim();
|
|
78
78
|
});
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
/>
|
|
100
100
|
{#if value && isValidHex(value)}
|
|
101
101
|
<div
|
|
102
|
-
class="w-8 h-8 rounded border-2 border-
|
|
102
|
+
class="w-8 h-8 rounded border-2 border-base-300 shrink-0"
|
|
103
103
|
style="background-color: {value};"
|
|
104
104
|
aria-label="Color preview"
|
|
105
105
|
></div>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
{#if mounted}
|
|
36
36
|
<div
|
|
37
|
-
class="p-6 rounded-lg bg-
|
|
37
|
+
class="p-6 rounded-lg bg-base-50 border border-border hover:border-primary/20 hover:shadow-sm transition-colors duration-200 {className}"
|
|
38
38
|
{...restProps}
|
|
39
39
|
>
|
|
40
40
|
<div class="flex items-start gap-4">
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
{:else}
|
|
55
55
|
<!-- SSR fallback -->
|
|
56
56
|
<div
|
|
57
|
-
class="p-6 rounded-lg bg-
|
|
57
|
+
class="p-6 rounded-lg bg-base-100 border border-base-300 hover:border-base-400 hover:shadow-sm transition-colors duration-200 {className}"
|
|
58
58
|
{...restProps}
|
|
59
59
|
>
|
|
60
60
|
<div class="flex items-start gap-4">
|
package/dist/atoms/Input.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
// SSR-safe ID generation
|
|
5
5
|
function generateId(prefix: string = "id"): string {
|
|
6
6
|
if (typeof window !== "undefined") {
|
|
@@ -46,20 +46,20 @@
|
|
|
46
46
|
return {
|
|
47
47
|
padding: "px-4 py-2",
|
|
48
48
|
text: "text-sm",
|
|
49
|
-
leading: "leading-5"
|
|
49
|
+
leading: "leading-5",
|
|
50
50
|
};
|
|
51
51
|
} else if (size === "lg") {
|
|
52
52
|
return {
|
|
53
53
|
padding: "px-4 py-3",
|
|
54
54
|
text: "text-base",
|
|
55
|
-
leading: "leading-6"
|
|
55
|
+
leading: "leading-6",
|
|
56
56
|
};
|
|
57
57
|
} else {
|
|
58
58
|
// default md
|
|
59
59
|
return {
|
|
60
60
|
padding: "px-4 py-2.5",
|
|
61
61
|
text: "text-base",
|
|
62
|
-
leading: "leading-6"
|
|
62
|
+
leading: "leading-6",
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
});
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
const inputClasses = $derived(() => {
|
|
80
80
|
const sizeStyles = sizeClass();
|
|
81
81
|
const baseClasses =
|
|
82
|
-
"w-full bg-
|
|
82
|
+
"w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
83
83
|
|
|
84
84
|
return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
|
|
85
85
|
});
|
package/dist/atoms/Select.svelte
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Dropdown from "../molecules/Dropdown.svelte";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ChevronDown,
|
|
5
|
+
CheckCircle,
|
|
6
|
+
AlertTriangle,
|
|
7
|
+
AlertCircle,
|
|
8
|
+
} from "@lucide/svelte";
|
|
4
9
|
|
|
5
10
|
// SSR-safe ID generation
|
|
6
11
|
function generateId(prefix: string = "id"): string {
|
|
@@ -48,20 +53,20 @@
|
|
|
48
53
|
return {
|
|
49
54
|
padding: "px-4 py-2",
|
|
50
55
|
text: "text-sm",
|
|
51
|
-
leading: "leading-5"
|
|
56
|
+
leading: "leading-5",
|
|
52
57
|
};
|
|
53
58
|
} else if (size === "lg") {
|
|
54
59
|
return {
|
|
55
60
|
padding: "px-4 py-3",
|
|
56
61
|
text: "text-base",
|
|
57
|
-
leading: "leading-6"
|
|
62
|
+
leading: "leading-6",
|
|
58
63
|
};
|
|
59
64
|
} else {
|
|
60
65
|
// default md
|
|
61
66
|
return {
|
|
62
67
|
padding: "px-4 py-2.5",
|
|
63
68
|
text: "text-base",
|
|
64
|
-
leading: "leading-6"
|
|
69
|
+
leading: "leading-6",
|
|
65
70
|
};
|
|
66
71
|
}
|
|
67
72
|
});
|
|
@@ -81,7 +86,7 @@
|
|
|
81
86
|
const triggerClasses = $derived(() => {
|
|
82
87
|
const sizeStyles = sizeClass();
|
|
83
88
|
const baseClasses =
|
|
84
|
-
"w-full bg-
|
|
89
|
+
"w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between";
|
|
85
90
|
|
|
86
91
|
return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
|
|
87
92
|
});
|
|
@@ -120,7 +125,9 @@
|
|
|
120
125
|
return String(placeholder || "Select an option");
|
|
121
126
|
}
|
|
122
127
|
const selected = options.find((opt) => opt.value === value);
|
|
123
|
-
return selected?.label
|
|
128
|
+
return selected?.label
|
|
129
|
+
? String(selected.label)
|
|
130
|
+
: String(placeholder || "Select an option");
|
|
124
131
|
});
|
|
125
132
|
|
|
126
133
|
// Check if value is empty
|
|
@@ -138,7 +145,7 @@
|
|
|
138
145
|
const syntheticEvent = new Event("change", { bubbles: true });
|
|
139
146
|
Object.defineProperty(syntheticEvent, "target", {
|
|
140
147
|
value: { value: optionValue },
|
|
141
|
-
enumerable: true
|
|
148
|
+
enumerable: true,
|
|
142
149
|
});
|
|
143
150
|
(onchange as (event: Event) => void)(syntheticEvent);
|
|
144
151
|
}
|
|
@@ -152,7 +159,10 @@
|
|
|
152
159
|
|
|
153
160
|
// Close dropdown when clicking outside
|
|
154
161
|
function handleClickOutside(event: MouseEvent) {
|
|
155
|
-
if (
|
|
162
|
+
if (
|
|
163
|
+
isOpen &&
|
|
164
|
+
!(event.target as HTMLElement).closest(".select-container")
|
|
165
|
+
) {
|
|
156
166
|
isOpen = false;
|
|
157
167
|
}
|
|
158
168
|
}
|
|
@@ -172,7 +182,7 @@
|
|
|
172
182
|
<label for={selectId} class={labelClasses()}>{label}</label>
|
|
173
183
|
{/if}
|
|
174
184
|
|
|
175
|
-
<Dropdown
|
|
185
|
+
<Dropdown {isOpen} placement="bottom-start">
|
|
176
186
|
{#snippet trigger()}
|
|
177
187
|
<button
|
|
178
188
|
type="button"
|
|
@@ -184,12 +194,21 @@
|
|
|
184
194
|
aria-expanded={isOpen}
|
|
185
195
|
aria-describedby={message ? `${selectId}-message` : undefined}
|
|
186
196
|
>
|
|
187
|
-
<span
|
|
188
|
-
{isEmpty()
|
|
197
|
+
<span
|
|
198
|
+
class="text-left flex-1 {isEmpty()
|
|
199
|
+
? 'text-description'
|
|
200
|
+
: 'text-body'}"
|
|
201
|
+
>
|
|
202
|
+
{isEmpty()
|
|
203
|
+
? placeholder
|
|
204
|
+
: options.find((opt) => opt.value === value)?.label ||
|
|
205
|
+
placeholder}
|
|
189
206
|
</span>
|
|
190
207
|
<ChevronDown
|
|
191
208
|
size={20}
|
|
192
|
-
class="text-description transition-transform duration-200 {isOpen
|
|
209
|
+
class="text-description transition-transform duration-200 {isOpen
|
|
210
|
+
? 'rotate-180'
|
|
211
|
+
: ''}"
|
|
193
212
|
/>
|
|
194
213
|
</button>
|
|
195
214
|
{/snippet}
|
|
@@ -198,7 +217,11 @@
|
|
|
198
217
|
{#each options as option (option.value)}
|
|
199
218
|
<button
|
|
200
219
|
type="button"
|
|
201
|
-
class="w-full text-left px-4 py-2 text-body hover:bg-
|
|
220
|
+
class="w-full text-left px-4 py-2 text-body hover:bg-base-100 transition-colors rounded-md my-0.5 {option.disabled
|
|
221
|
+
? 'opacity-50 cursor-not-allowed'
|
|
222
|
+
: 'cursor-pointer'} {value === option.value
|
|
223
|
+
? 'bg-base-100'
|
|
224
|
+
: ''}"
|
|
202
225
|
onclick={() => handleOptionClick(option.value)}
|
|
203
226
|
disabled={option.disabled}
|
|
204
227
|
>
|
|
@@ -217,4 +240,4 @@
|
|
|
217
240
|
<span>{message}</span>
|
|
218
241
|
</p>
|
|
219
242
|
{/if}
|
|
220
|
-
</div>
|
|
243
|
+
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
// SSR-safe ID generation
|
|
5
5
|
function generateId(prefix: string = "id"): string {
|
|
6
6
|
if (typeof window !== "undefined") {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
// Textarea classes matching M3 design
|
|
53
53
|
const textareaClasses = $derived(() => {
|
|
54
54
|
const baseClasses =
|
|
55
|
-
"w-full bg-
|
|
55
|
+
"w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed resize-y px-4 py-2.5 text-base leading-6";
|
|
56
56
|
|
|
57
57
|
return `${baseClasses} ${variantClass()}`.trim();
|
|
58
58
|
});
|
|
@@ -95,12 +95,12 @@
|
|
|
95
95
|
// Variant classes using semantic colors
|
|
96
96
|
const variantClass = $derived(() => {
|
|
97
97
|
if (variant === "ghost") {
|
|
98
|
-
return "bg-transparent hover:bg-
|
|
98
|
+
return "bg-transparent hover:bg-base-100 border-0";
|
|
99
99
|
} else if (variant === "outline") {
|
|
100
|
-
return "bg-
|
|
100
|
+
return "bg-base-50 hover:bg-base-100 border border-border";
|
|
101
101
|
} else {
|
|
102
102
|
// default
|
|
103
|
-
return "bg-
|
|
103
|
+
return "bg-base-50 hover:bg-base-100 border-0";
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
106
|
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
focus:ring-2
|
|
122
122
|
focus:ring-brand-500
|
|
123
123
|
focus:ring-offset-2
|
|
124
|
-
focus:ring-offset-
|
|
124
|
+
focus:ring-offset-base-50
|
|
125
125
|
`.trim().replace(/\s+/g, " ");
|
|
126
126
|
});
|
|
127
127
|
</script>
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
{:else}
|
|
144
144
|
<!-- SSR fallback -->
|
|
145
145
|
<button
|
|
146
|
-
class="w-10 h-10 bg-
|
|
146
|
+
class="w-10 h-10 bg-base-50 rounded-lg flex items-center justify-center text-label cursor-pointer"
|
|
147
147
|
aria-label="Theme toggle"
|
|
148
148
|
type="button"
|
|
149
149
|
{...restProps}
|
package/dist/atoms/Toast.svelte
CHANGED
package/dist/atoms/Toggle.svelte
CHANGED
|
@@ -39,21 +39,19 @@
|
|
|
39
39
|
if (onchange) onchange({ checked });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// Toggle button classes matching M3 design
|
|
43
42
|
const toggleButtonClasses = $derived(() =>
|
|
44
43
|
[
|
|
45
44
|
"relative inline-flex w-10 h-6 flex-shrink-0 cursor-pointer rounded-full border-0 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2",
|
|
46
|
-
checked ? "bg-
|
|
45
|
+
checked ? "bg-action-primary" : "bg-base-400",
|
|
47
46
|
disabled && "opacity-50 cursor-not-allowed",
|
|
48
47
|
]
|
|
49
48
|
.filter(Boolean)
|
|
50
49
|
.join(" "),
|
|
51
50
|
);
|
|
52
51
|
|
|
53
|
-
// Toggle thumb classes matching M3 design
|
|
54
52
|
const toggleThumbClasses = $derived(() => {
|
|
55
53
|
const baseClasses =
|
|
56
|
-
"pointer-events-none absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-
|
|
54
|
+
"pointer-events-none absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-card transition-transform duration-200 ease-in-out";
|
|
57
55
|
const positionClasses = checked ? "translate-x-4" : "translate-x-0";
|
|
58
56
|
|
|
59
57
|
return `${baseClasses} ${positionClasses}`.trim();
|