zabi-components 5.0.15 → 5.0.18
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 +175 -23
- package/dist/SSRSafe.svelte +0 -3
- package/dist/atoms/Badge.svelte +32 -47
- package/dist/atoms/Badge.svelte.d.ts +6 -5
- package/dist/atoms/Button.svelte +18 -17
- package/dist/atoms/Button.svelte.d.ts +3 -2
- package/dist/atoms/Card.svelte +74 -23
- package/dist/atoms/Card.svelte.d.ts +3 -1
- package/dist/atoms/CardContent.svelte +24 -0
- package/dist/atoms/CardContent.svelte.d.ts +8 -0
- package/dist/atoms/CardDescription.svelte +24 -0
- package/dist/atoms/CardDescription.svelte.d.ts +8 -0
- package/dist/atoms/CardFooter.svelte +24 -0
- package/dist/atoms/CardFooter.svelte.d.ts +8 -0
- package/dist/atoms/CardHeader.svelte +24 -0
- package/dist/atoms/CardHeader.svelte.d.ts +8 -0
- package/dist/atoms/CardTitle.svelte +33 -0
- package/dist/atoms/CardTitle.svelte.d.ts +9 -0
- package/dist/atoms/Checkbox.svelte +19 -20
- package/dist/atoms/CodeBlock.svelte +4 -7
- package/dist/atoms/ColorPicker.svelte +33 -65
- package/dist/atoms/FeatureCard.svelte +2 -12
- package/dist/atoms/FeatureCard.svelte.d.ts +0 -8
- package/dist/atoms/Heading.svelte +0 -2
- package/dist/atoms/Input.svelte +8 -17
- package/dist/atoms/Input.svelte.d.ts +3 -2
- package/dist/atoms/Progress.svelte +1 -4
- package/dist/atoms/Select.svelte +37 -28
- package/dist/atoms/Skeleton.svelte +1 -1
- package/dist/atoms/Textarea.svelte +5 -11
- package/dist/atoms/Textarea.svelte.d.ts +3 -1
- package/dist/atoms/ThemeToggle.svelte +5 -12
- package/dist/atoms/Toast.svelte +1 -2
- package/dist/atoms/Toggle.svelte +2 -10
- package/dist/atoms/Tooltip.svelte +123 -26
- package/dist/atoms/index.d.ts +5 -0
- package/dist/atoms/index.js +5 -1
- package/dist/components/atoms/index.d.ts +5 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/index.js +0 -23
- package/dist/molecules/Alert.svelte +9 -13
- package/dist/molecules/Alert.svelte.d.ts +2 -1
- package/dist/molecules/ComponentDemo.svelte +55 -64
- package/dist/molecules/ContactForm.svelte +21 -12
- package/dist/molecules/Dropdown.svelte +104 -8
- package/dist/molecules/Dropdown.svelte.d.ts +1 -0
- package/dist/molecules/Form.svelte +0 -1
- package/dist/molecules/ImageUpload.svelte +2 -6
- package/dist/molecules/Modal.svelte +30 -6
- package/dist/molecules/SlideUp.svelte +2 -4
- package/dist/molecules/Tabs.svelte +2 -4
- package/dist/molecules/index.js +0 -1
- package/dist/organisms/Navbar.svelte +1 -8
- package/dist/organisms/Navigation.svelte +0 -6
- package/dist/organisms/index.js +0 -1
- package/dist/routes/lib/variant-utils.ts +7 -5
- package/dist/zabi-components-colors.css +287 -211
- package/dist/zabi-components-theme-dark-only.css +158 -0
- package/dist/zabi-components-theme-dark.css +160 -0
- package/dist/zabi-components-theme-only.css +235 -0
- package/dist/zabi-components-theme.css +237 -0
- package/dist/zabi-components.css +575 -360
- package/package.json +15 -5
package/dist/atoms/index.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export { default as Toggle } from './Toggle.svelte';
|
|
|
2
2
|
export { default as Badge } from './Badge.svelte';
|
|
3
3
|
export { default as Button } from './Button.svelte';
|
|
4
4
|
export { default as Card } from './Card.svelte';
|
|
5
|
+
export { default as CardHeader } from './CardHeader.svelte';
|
|
6
|
+
export { default as CardContent } from './CardContent.svelte';
|
|
7
|
+
export { default as CardFooter } from './CardFooter.svelte';
|
|
8
|
+
export { default as CardTitle } from './CardTitle.svelte';
|
|
9
|
+
export { default as CardDescription } from './CardDescription.svelte';
|
|
5
10
|
export { default as Input } from './Input.svelte';
|
|
6
11
|
export { default as Textarea } from './Textarea.svelte';
|
|
7
12
|
export { default as Select } from './Select.svelte';
|
package/dist/atoms/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
// Export atom components - All SSR-safe components
|
|
2
1
|
export { default as Toggle } from './Toggle.svelte';
|
|
3
2
|
export { default as Badge } from './Badge.svelte';
|
|
4
3
|
export { default as Button } from './Button.svelte';
|
|
5
4
|
export { default as Card } from './Card.svelte';
|
|
5
|
+
export { default as CardHeader } from './CardHeader.svelte';
|
|
6
|
+
export { default as CardContent } from './CardContent.svelte';
|
|
7
|
+
export { default as CardFooter } from './CardFooter.svelte';
|
|
8
|
+
export { default as CardTitle } from './CardTitle.svelte';
|
|
9
|
+
export { default as CardDescription } from './CardDescription.svelte';
|
|
6
10
|
export { default as Input } from './Input.svelte';
|
|
7
11
|
export { default as Textarea } from './Textarea.svelte';
|
|
8
12
|
export { default as Select } from './Select.svelte';
|
|
@@ -2,6 +2,11 @@ export { default as Toggle } from './Toggle.svelte';
|
|
|
2
2
|
export { default as Badge } from './Badge.svelte';
|
|
3
3
|
export { default as Button } from './Button.svelte';
|
|
4
4
|
export { default as Card } from './Card.svelte';
|
|
5
|
+
export { default as CardHeader } from './CardHeader.svelte';
|
|
6
|
+
export { default as CardContent } from './CardContent.svelte';
|
|
7
|
+
export { default as CardFooter } from './CardFooter.svelte';
|
|
8
|
+
export { default as CardTitle } from './CardTitle.svelte';
|
|
9
|
+
export { default as CardDescription } from './CardDescription.svelte';
|
|
5
10
|
export { default as Input } from './Input.svelte';
|
|
6
11
|
export { default as Textarea } from './Textarea.svelte';
|
|
7
12
|
export { default as Select } from './Select.svelte';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEpE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEtE,eAAO,MAAM,QAAQ,GAAI,SAAQ,MAAa,KAAG,MAMhD,CAAC;AAEF,eAAO,MAAM,EAAE,GAAI,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,EAAE,KAAG,MAClC,CAAC;AAEtC,eAAO,MAAM,WAAW,GAAI,MAAM,eAAe,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAGrE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,OAG7C,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,GAAG,KAAG,OAE7C,CAAC;AAEF,eAAO,MAAM,SAAS,QAAO,OAE5B,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,MAAM,GAAG,SAEtC,CAAC;AAEF,eAAO,MAAM,YAAY,QAAO,QAAQ,GAAG,SAE1C,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,OAAO,GAAG,SAE7C,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAQ,MAAa,KAAG,MAKlD,CAAC;AAEF,OAAO,EACH,GAAG,EACH,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,GAAG,EACH,SAAS,EACT,SAAS,EACT,QAAQ,EACX,MAAM,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/molecules/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/molecules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/organisms/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/organisms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
// Zabi Components - Clean & Simple Svelte Component Library
|
|
2
|
-
// Main library entry point - All components in one place
|
|
3
|
-
// ============================================================================
|
|
4
|
-
// ATOM COMPONENTS - Basic UI building blocks
|
|
5
|
-
// ============================================================================
|
|
6
|
-
// Form Controls - All SSR-safe components
|
|
7
1
|
export { default as Toggle } from './atoms/Toggle.svelte';
|
|
8
2
|
export { default as Badge } from './atoms/Badge.svelte';
|
|
9
3
|
export { default as Button } from './atoms/Button.svelte';
|
|
@@ -14,9 +8,6 @@ export { default as Select } from './atoms/Select.svelte';
|
|
|
14
8
|
export { default as ColorPicker } from './atoms/ColorPicker.svelte';
|
|
15
9
|
export { default as Heading } from './atoms/Heading.svelte';
|
|
16
10
|
export { default as ThemeToggle } from './atoms/ThemeToggle.svelte';
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// MOLECULE COMPONENTS - Composite UI elements
|
|
19
|
-
// ============================================================================
|
|
20
11
|
export { default as Alert } from './molecules/Alert.svelte';
|
|
21
12
|
export { default as ComponentDemo } from './molecules/ComponentDemo.svelte';
|
|
22
13
|
export { default as ContactForm } from './molecules/ContactForm.svelte';
|
|
@@ -26,15 +17,8 @@ export { default as ImageUpload } from './molecules/ImageUpload.svelte';
|
|
|
26
17
|
export { default as Modal } from './molecules/Modal.svelte';
|
|
27
18
|
export { default as SlideUp } from './molecules/SlideUp.svelte';
|
|
28
19
|
export { default as Tabs } from './molecules/Tabs.svelte';
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// ORGANISM COMPONENTS - Complex UI sections
|
|
31
|
-
// ============================================================================
|
|
32
20
|
export { default as Navbar } from './organisms/Navbar.svelte';
|
|
33
21
|
export { default as Navigation } from './organisms/Navigation.svelte';
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// UTILITIES - Helper functions and utilities
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Simple utility functions
|
|
38
22
|
export const createId = (prefix = 'id') => {
|
|
39
23
|
if (typeof window !== 'undefined') {
|
|
40
24
|
return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -44,12 +28,10 @@ export const createId = (prefix = 'id') => {
|
|
|
44
28
|
}
|
|
45
29
|
};
|
|
46
30
|
export const cn = (...classes) => classes.filter(Boolean).join(' ');
|
|
47
|
-
// Form utilities
|
|
48
31
|
export const getFormData = (form) => {
|
|
49
32
|
const formData = new FormData(form);
|
|
50
33
|
return Object.fromEntries(formData.entries());
|
|
51
34
|
};
|
|
52
|
-
// Validation utilities
|
|
53
35
|
export const validateEmail = (email) => {
|
|
54
36
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
55
37
|
return emailRegex.test(email);
|
|
@@ -57,7 +39,6 @@ export const validateEmail = (email) => {
|
|
|
57
39
|
export const validateRequired = (value) => {
|
|
58
40
|
return value !== null && value !== undefined && value !== '';
|
|
59
41
|
};
|
|
60
|
-
// SSR-safe utilities
|
|
61
42
|
export const isBrowser = () => {
|
|
62
43
|
return typeof window !== 'undefined';
|
|
63
44
|
};
|
|
@@ -70,14 +51,10 @@ export const safeDocument = () => {
|
|
|
70
51
|
export const safeLocalStorage = () => {
|
|
71
52
|
return typeof window !== 'undefined' ? localStorage : undefined;
|
|
72
53
|
};
|
|
73
|
-
// Alias for backward compatibility - separate function to avoid initialization issues
|
|
74
54
|
export const generateId = (prefix = 'id') => {
|
|
75
55
|
if (typeof window !== 'undefined') {
|
|
76
56
|
return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
|
|
77
57
|
}
|
|
78
58
|
return `${prefix}-ssr-${Date.now()}`;
|
|
79
59
|
};
|
|
80
|
-
// ============================================================================
|
|
81
|
-
// ICONS - Re-export commonly used Lucide icons
|
|
82
|
-
// ============================================================================
|
|
83
60
|
export { Sun, Moon, Monitor, Grip, GripVertical, ChevronUp, ChevronDown, ChevronRight, Zap, Briefcase, Clipboard, Settings } from '@lucide/svelte';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ExtendedSemanticVariant } from "../../types/variants.js";
|
|
3
|
+
|
|
2
4
|
interface Props {
|
|
3
|
-
variant?:
|
|
5
|
+
variant?: ExtendedSemanticVariant;
|
|
4
6
|
title?: string;
|
|
5
7
|
message?: string;
|
|
6
8
|
closable?: boolean;
|
|
@@ -20,19 +22,18 @@
|
|
|
20
22
|
}: Props & { children?: any } = $props();
|
|
21
23
|
|
|
22
24
|
function handleDismiss(event: MouseEvent) {
|
|
23
|
-
// Call the parent's onclick handler if provided
|
|
24
25
|
if (onclick) {
|
|
25
26
|
onclick(event);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
let alertClasses = $derived({
|
|
30
|
-
info: "
|
|
31
|
-
success: "
|
|
32
|
-
warning: "
|
|
33
|
-
error: "
|
|
34
|
-
neutral: "
|
|
35
|
-
energetic: "
|
|
31
|
+
info: "text-info border border-info",
|
|
32
|
+
success: "text-success border border-success",
|
|
33
|
+
warning: "text-warning border border-warning",
|
|
34
|
+
error: "text-error border border-error",
|
|
35
|
+
neutral: "text-neutral border border-neutral",
|
|
36
|
+
energetic: "text-energetic border border-energetic",
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
let alertRole = $derived(
|
|
@@ -72,7 +73,6 @@
|
|
|
72
73
|
aria-atomic="true"
|
|
73
74
|
{...restProps}
|
|
74
75
|
>
|
|
75
|
-
<!-- Dismiss button (X in top right) -->
|
|
76
76
|
{#if closable}
|
|
77
77
|
<button
|
|
78
78
|
onclick={handleDismiss}
|
|
@@ -96,14 +96,11 @@
|
|
|
96
96
|
</button>
|
|
97
97
|
{/if}
|
|
98
98
|
|
|
99
|
-
<!-- Content -->
|
|
100
99
|
<div class="flex items-start gap-3 {closable ? 'pr-8' : ''}">
|
|
101
|
-
<!-- Icon -->
|
|
102
100
|
<div class="shrink-0 mt-0.5">
|
|
103
101
|
{@html iconSvg[variant]}
|
|
104
102
|
</div>
|
|
105
103
|
|
|
106
|
-
<!-- Text content -->
|
|
107
104
|
<div class="flex-1 min-w-0">
|
|
108
105
|
{#if title}
|
|
109
106
|
<h4 class="font-semibold text-sm mb-1 text-headline">
|
|
@@ -115,7 +112,6 @@
|
|
|
115
112
|
{message}
|
|
116
113
|
</p>
|
|
117
114
|
{/if}
|
|
118
|
-
<!-- Default slot for custom content -->
|
|
119
115
|
{@render children?.()}
|
|
120
116
|
</div>
|
|
121
117
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { ExtendedSemanticVariant } from "../../types/variants.js";
|
|
1
2
|
interface Props {
|
|
2
|
-
variant?:
|
|
3
|
+
variant?: ExtendedSemanticVariant;
|
|
3
4
|
title?: string;
|
|
4
5
|
message?: string;
|
|
5
6
|
closable?: boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import CodeBlock from "../atoms/CodeBlock.svelte";
|
|
3
|
+
import Card from "../atoms/Card.svelte";
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
title: string;
|
|
@@ -22,75 +23,65 @@
|
|
|
22
23
|
let showCode = $state(false);
|
|
23
24
|
</script>
|
|
24
25
|
|
|
25
|
-
<div
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
stroke="
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
stroke="
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
|
|
78
|
-
/>
|
|
79
|
-
</svg>
|
|
80
|
-
Code
|
|
81
|
-
{/if}
|
|
82
|
-
</button>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
26
|
+
<div class="relative">
|
|
27
|
+
<Card
|
|
28
|
+
size="lg"
|
|
29
|
+
fullWidth={true}
|
|
30
|
+
{title}
|
|
31
|
+
{description}
|
|
32
|
+
className="overflow-hidden {className}"
|
|
33
|
+
{...restProps}
|
|
34
|
+
>
|
|
35
|
+
<button
|
|
36
|
+
onclick={() => (showCode = !showCode)}
|
|
37
|
+
class="absolute top-8 right-8 flex items-center gap-2 px-3 py-1.5 text-sm text-description hover:text-body hover:bg-base-50 rounded-md transition-colors duration-200 z-10"
|
|
38
|
+
aria-label={showCode ? "Show preview" : "Show code"}
|
|
39
|
+
>
|
|
40
|
+
{#if showCode}
|
|
41
|
+
<svg
|
|
42
|
+
class="w-4 h-4"
|
|
43
|
+
fill="none"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
viewBox="0 0 24 24"
|
|
46
|
+
>
|
|
47
|
+
<path
|
|
48
|
+
stroke-linecap="round"
|
|
49
|
+
stroke-linejoin="round"
|
|
50
|
+
stroke-width="2"
|
|
51
|
+
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
52
|
+
/>
|
|
53
|
+
<path
|
|
54
|
+
stroke-linecap="round"
|
|
55
|
+
stroke-linejoin="round"
|
|
56
|
+
stroke-width="2"
|
|
57
|
+
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
58
|
+
/>
|
|
59
|
+
</svg>
|
|
60
|
+
Preview
|
|
61
|
+
{:else}
|
|
62
|
+
<svg
|
|
63
|
+
class="w-4 h-4"
|
|
64
|
+
fill="none"
|
|
65
|
+
stroke="currentColor"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
>
|
|
68
|
+
<path
|
|
69
|
+
stroke-linecap="round"
|
|
70
|
+
stroke-linejoin="round"
|
|
71
|
+
stroke-width="2"
|
|
72
|
+
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
|
|
73
|
+
/>
|
|
74
|
+
</svg>
|
|
75
|
+
Code
|
|
76
|
+
{/if}
|
|
77
|
+
</button>
|
|
85
78
|
|
|
86
|
-
<!-- Content -->
|
|
87
|
-
<div class="p-6">
|
|
88
79
|
{#if showCode}
|
|
89
80
|
<CodeBlock {code} {language} />
|
|
90
81
|
{:else}
|
|
91
|
-
<div class="min-h-[100px] flex items-center justify-center">
|
|
82
|
+
<div class="min-h-[100px] flex items-center justify-center w-full">
|
|
92
83
|
{@render children?.()}
|
|
93
84
|
</div>
|
|
94
85
|
{/if}
|
|
95
|
-
</
|
|
86
|
+
</Card>
|
|
96
87
|
</div>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Textarea from "../atoms/Textarea.svelte";
|
|
5
5
|
import Checkbox from "../atoms/Checkbox.svelte";
|
|
6
6
|
import Button from "../atoms/Button.svelte";
|
|
7
|
+
import Card from "../atoms/Card.svelte";
|
|
7
8
|
import type { ContactFormData } from "../../types/page.types";
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
@@ -33,21 +34,11 @@
|
|
|
33
34
|
subscribe: formData.get("subscribe") === "on" || false,
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
// Form submission is now handled by the parent component
|
|
37
|
-
// through event forwarding. The data is available for processing.
|
|
38
37
|
console.log("Form submitted with data:", data);
|
|
39
38
|
}
|
|
40
39
|
</script>
|
|
41
40
|
|
|
42
|
-
<
|
|
43
|
-
<div class="mb-6">
|
|
44
|
-
<h2 class="text-2xl font-bold text-headline mb-2">Get in Touch</h2>
|
|
45
|
-
<p class="text-description">
|
|
46
|
-
We'd love to hear from you. Send us a message and we'll respond as
|
|
47
|
-
soon as possible.
|
|
48
|
-
</p>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
41
|
+
<Card size="md" fullWidth={true} title="Get in Touch">
|
|
51
42
|
<Form onsubmit={handleFormSubmit} className="space-y-4">
|
|
52
43
|
<div class="space-y-4">
|
|
53
44
|
<Input
|
|
@@ -68,6 +59,24 @@
|
|
|
68
59
|
oninput={(e) =>
|
|
69
60
|
(formData.email = (e.target as HTMLInputElement).value)}
|
|
70
61
|
/>
|
|
62
|
+
<Input
|
|
63
|
+
type="email"
|
|
64
|
+
name="email"
|
|
65
|
+
label="Email"
|
|
66
|
+
placeholder="Enter your email"
|
|
67
|
+
value={formData.email}
|
|
68
|
+
oninput={(e) =>
|
|
69
|
+
(formData.email = (e.target as HTMLInputElement).value)}
|
|
70
|
+
/>
|
|
71
|
+
<Input
|
|
72
|
+
type="email"
|
|
73
|
+
name="email"
|
|
74
|
+
label="Email"
|
|
75
|
+
placeholder="Enter your email"
|
|
76
|
+
value={formData.email}
|
|
77
|
+
oninput={(e) =>
|
|
78
|
+
(formData.email = (e.target as HTMLInputElement).value)}
|
|
79
|
+
/>
|
|
71
80
|
<Textarea
|
|
72
81
|
name="message"
|
|
73
82
|
label="Message"
|
|
@@ -95,4 +104,4 @@
|
|
|
95
104
|
</Button>
|
|
96
105
|
</div>
|
|
97
106
|
</Form>
|
|
98
|
-
</
|
|
107
|
+
</Card>
|
|
@@ -2,17 +2,99 @@
|
|
|
2
2
|
interface Props {
|
|
3
3
|
isOpen?: boolean;
|
|
4
4
|
placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
|
|
5
|
+
ariaLabel?: string;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
let {
|
|
8
9
|
isOpen = false,
|
|
9
10
|
placement = "bottom-start",
|
|
11
|
+
ariaLabel = "Menu",
|
|
10
12
|
children,
|
|
11
13
|
trigger,
|
|
12
14
|
...restProps
|
|
13
15
|
}: Props & { children?: any; trigger?: any } = $props();
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
let dropdownId = `dropdown-${Math.random().toString(36).substr(2, 9)}`;
|
|
18
|
+
let triggerElement: HTMLElement | null = null;
|
|
19
|
+
let menuElement: HTMLElement | null = null;
|
|
20
|
+
|
|
21
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
22
|
+
if (!isOpen) {
|
|
23
|
+
if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
isOpen = true;
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
switch (event.key) {
|
|
31
|
+
case "Escape":
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
isOpen = false;
|
|
34
|
+
triggerElement?.focus();
|
|
35
|
+
break;
|
|
36
|
+
case "ArrowDown":
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
focusNextItem();
|
|
39
|
+
break;
|
|
40
|
+
case "ArrowUp":
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
focusPreviousItem();
|
|
43
|
+
break;
|
|
44
|
+
case "Home":
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
focusFirstItem();
|
|
47
|
+
break;
|
|
48
|
+
case "End":
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
focusLastItem();
|
|
51
|
+
break;
|
|
52
|
+
case "Tab":
|
|
53
|
+
isOpen = false;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getMenuItems(): HTMLElement[] {
|
|
59
|
+
if (!menuElement) return [];
|
|
60
|
+
return Array.from(
|
|
61
|
+
menuElement.querySelectorAll<HTMLElement>('[role="menuitem"], button, a')
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function focusNextItem() {
|
|
66
|
+
const items = getMenuItems();
|
|
67
|
+
const currentIndex = items.findIndex((item) => item === document.activeElement);
|
|
68
|
+
const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
69
|
+
items[nextIndex]?.focus();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function focusPreviousItem() {
|
|
73
|
+
const items = getMenuItems();
|
|
74
|
+
const currentIndex = items.findIndex((item) => item === document.activeElement);
|
|
75
|
+
const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
76
|
+
items[prevIndex]?.focus();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function focusFirstItem() {
|
|
80
|
+
const items = getMenuItems();
|
|
81
|
+
items[0]?.focus();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function focusLastItem() {
|
|
85
|
+
const items = getMenuItems();
|
|
86
|
+
items[items.length - 1]?.focus();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Focus first item when dropdown opens
|
|
90
|
+
$effect(() => {
|
|
91
|
+
if (isOpen && menuElement) {
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
focusFirstItem();
|
|
94
|
+
}, 0);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
16
98
|
const placementClasses = $derived(() => {
|
|
17
99
|
const base = "absolute z-dropdown min-w-[12rem]";
|
|
18
100
|
const positioning = {
|
|
@@ -24,7 +106,6 @@
|
|
|
24
106
|
return `${base} ${positioning[placement]}`;
|
|
25
107
|
});
|
|
26
108
|
|
|
27
|
-
// Get transform classes based on placement and open state
|
|
28
109
|
const transformClasses = $derived(() => {
|
|
29
110
|
if (!isOpen) {
|
|
30
111
|
const hiddenTransform = {
|
|
@@ -38,11 +119,10 @@
|
|
|
38
119
|
return "opacity-100 visible translate-y-0";
|
|
39
120
|
});
|
|
40
121
|
|
|
41
|
-
// Get dropdown content classes
|
|
42
122
|
const dropdownContentClasses = $derived(() => {
|
|
43
123
|
return `
|
|
44
124
|
${placementClasses()}
|
|
45
|
-
bg-
|
|
125
|
+
bg-input
|
|
46
126
|
rounded-lg
|
|
47
127
|
shadow-lg
|
|
48
128
|
border-0
|
|
@@ -51,15 +131,31 @@
|
|
|
51
131
|
duration-200
|
|
52
132
|
ease-in-out
|
|
53
133
|
${transformClasses()}
|
|
54
|
-
|
|
134
|
+
`
|
|
135
|
+
.trim()
|
|
136
|
+
.replace(/\s+/g, " ");
|
|
55
137
|
});
|
|
56
138
|
</script>
|
|
57
139
|
|
|
58
|
-
<div
|
|
59
|
-
|
|
140
|
+
<div
|
|
141
|
+
class="relative inline-block"
|
|
142
|
+
data-placement={placement}
|
|
143
|
+
onkeydown={handleKeydown}
|
|
144
|
+
{...restProps}
|
|
145
|
+
>
|
|
146
|
+
<div bind:this={triggerElement}>
|
|
147
|
+
{@render trigger?.()}
|
|
148
|
+
</div>
|
|
60
149
|
|
|
61
150
|
{#if isOpen}
|
|
62
|
-
<div
|
|
151
|
+
<div
|
|
152
|
+
bind:this={menuElement}
|
|
153
|
+
id={dropdownId}
|
|
154
|
+
class={dropdownContentClasses()}
|
|
155
|
+
role="menu"
|
|
156
|
+
aria-label={ariaLabel}
|
|
157
|
+
tabindex="-1"
|
|
158
|
+
>
|
|
63
159
|
{@render children?.()}
|
|
64
160
|
</div>
|
|
65
161
|
{/if}
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
|
|
31
31
|
const file = input.files[0];
|
|
32
32
|
|
|
33
|
-
// SSR-safe URL creation
|
|
34
33
|
if (typeof URL !== "undefined" && URL.createObjectURL) {
|
|
35
34
|
const url = URL.createObjectURL(file);
|
|
36
35
|
value = url;
|
|
@@ -54,7 +53,6 @@
|
|
|
54
53
|
|
|
55
54
|
<div class="space-y-3">
|
|
56
55
|
{#if value}
|
|
57
|
-
<!-- Image Display -->
|
|
58
56
|
<div class="relative group">
|
|
59
57
|
<img
|
|
60
58
|
src={value}
|
|
@@ -85,9 +83,8 @@
|
|
|
85
83
|
</div>
|
|
86
84
|
</div>
|
|
87
85
|
{:else}
|
|
88
|
-
<!-- Empty State -->
|
|
89
86
|
<div
|
|
90
|
-
class="border-2 border-dashed border-
|
|
87
|
+
class="border-2 border-dashed border-base-200 rounded-lg p-6 text-center hover:border-brand-500 transition-colors {disabled
|
|
91
88
|
? 'cursor-not-allowed opacity-50'
|
|
92
89
|
: 'cursor-pointer'}"
|
|
93
90
|
onclick={triggerFileSelect}
|
|
@@ -99,7 +96,7 @@
|
|
|
99
96
|
>
|
|
100
97
|
<div class="space-y-3">
|
|
101
98
|
<div
|
|
102
|
-
class="w-12 h-12 mx-auto bg-
|
|
99
|
+
class="w-12 h-12 mx-auto bg-base-100 rounded-lg flex items-center justify-center"
|
|
103
100
|
>
|
|
104
101
|
<Image size={24} class="text-description" />
|
|
105
102
|
</div>
|
|
@@ -113,7 +110,6 @@
|
|
|
113
110
|
</div>
|
|
114
111
|
{/if}
|
|
115
112
|
|
|
116
|
-
<!-- Hidden File Input -->
|
|
117
113
|
<input
|
|
118
114
|
bind:this={fileInput}
|
|
119
115
|
type="file"
|