zabi-components 4.0.2 → 5.0.2
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 +2 -2
- package/dist/SSRSafe.svelte +47 -0
- package/dist/SSRSafe.svelte.d.ts +11 -0
- package/dist/atoms/Badge.svelte +76 -0
- package/dist/atoms/Badge.svelte.d.ts +9 -0
- package/dist/atoms/Button.svelte +72 -0
- package/dist/atoms/Button.svelte.d.ts +13 -0
- package/dist/atoms/Card.svelte +59 -0
- package/dist/atoms/Card.svelte.d.ts +12 -0
- package/dist/atoms/Checkbox.svelte +60 -0
- package/dist/atoms/Checkbox.svelte.d.ts +10 -0
- package/dist/atoms/CodeBlock.svelte +125 -0
- package/dist/atoms/CodeBlock.svelte.d.ts +8 -0
- package/dist/atoms/ColorPicker.svelte +171 -0
- package/dist/atoms/ColorPicker.svelte.d.ts +9 -0
- package/dist/atoms/FeatureCard.svelte +74 -0
- package/dist/atoms/FeatureCard.svelte.d.ts +17 -0
- package/dist/atoms/Heading.svelte +5 -0
- package/dist/atoms/Heading.svelte.d.ts +7 -0
- package/dist/atoms/Input.svelte +98 -0
- package/dist/atoms/Input.svelte.d.ts +14 -0
- package/dist/atoms/OptimizedImage.svelte +32 -0
- package/dist/atoms/OptimizedImage.svelte.d.ts +13 -0
- package/dist/atoms/Progress.svelte +61 -0
- package/dist/atoms/Progress.svelte.d.ts +9 -0
- package/dist/atoms/Select.svelte +85 -0
- package/dist/atoms/Select.svelte.d.ts +16 -0
- package/dist/atoms/Skeleton.svelte +20 -0
- package/dist/atoms/Skeleton.svelte.d.ts +8 -0
- package/dist/atoms/Textarea.svelte +116 -0
- package/dist/atoms/Textarea.svelte.d.ts +14 -0
- package/dist/atoms/ThemeToggle.svelte +89 -0
- package/dist/atoms/ThemeToggle.svelte.d.ts +7 -0
- package/dist/atoms/Toast.svelte +58 -0
- package/dist/atoms/Toast.svelte.d.ts +9 -0
- package/dist/atoms/Toggle.svelte +94 -0
- package/dist/atoms/Toggle.svelte.d.ts +12 -0
- package/dist/atoms/Tooltip.svelte +188 -0
- package/dist/atoms/Tooltip.svelte.d.ts +12 -0
- package/dist/atoms/index.d.ts +4 -15
- package/dist/atoms/index.js +10 -23
- package/dist/components/atoms/index.d.ts +10 -0
- package/dist/components/atoms/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +32 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +1 -0
- package/dist/components/molecules/index.d.ts.map +1 -0
- package/dist/components/organisms/index.d.ts +1 -0
- package/dist/components/organisms/index.d.ts.map +1 -0
- package/dist/index.d.ts +31 -15
- package/dist/index.js +75 -0
- package/dist/molecules/Alert.svelte +114 -0
- package/dist/molecules/Alert.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +96 -0
- package/dist/molecules/ComponentDemo.svelte.d.ts +13 -0
- package/dist/molecules/ContactForm.svelte +98 -0
- package/dist/molecules/ContactForm.svelte.d.ts +10 -0
- package/dist/molecules/Dropdown.svelte +128 -0
- package/dist/molecules/Dropdown.svelte.d.ts +11 -0
- package/dist/molecules/Form.svelte +37 -0
- package/dist/molecules/Form.svelte.d.ts +12 -0
- package/dist/molecules/ImageUpload.svelte +112 -0
- package/dist/molecules/ImageUpload.svelte.d.ts +14 -0
- package/dist/molecules/Modal.svelte +85 -0
- package/dist/molecules/Modal.svelte.d.ts +13 -0
- package/dist/molecules/SlideUp.svelte +73 -0
- package/dist/molecules/SlideUp.svelte.d.ts +12 -0
- package/dist/molecules/Tabs.svelte +68 -0
- package/dist/molecules/Tabs.svelte.d.ts +17 -0
- package/dist/molecules/index.d.ts +0 -11
- package/dist/molecules/index.js +2 -13
- package/dist/organisms/Navbar.svelte +99 -0
- package/dist/organisms/Navbar.svelte.d.ts +14 -0
- package/dist/organisms/Navigation.svelte +48 -0
- package/dist/organisms/Navigation.svelte.d.ts +15 -0
- package/dist/organisms/index.d.ts +0 -4
- package/dist/organisms/index.js +2 -5
- package/dist/routes/lib/ssr-safe.ts +57 -0
- package/dist/routes/lib/variant-utils.ts +98 -0
- package/dist/zabi-components.css +399 -1
- package/package.json +41 -17
- package/dist/CodeBlock-pT5-FgZd.js +0 -431
- package/dist/ContactForm-CHI_jeFY.js +0 -580
- package/dist/FeatureCard-iBFGbgVE.js +0 -656
- package/dist/Navigation-BOT-UHg0.js +0 -104
- package/dist/ThemeToggle-BlNPkAQy.js +0 -48
- package/dist/index/index.js +0 -63
- package/dist/molecules/KeyValueForm.types.d.ts +0 -11
- package/dist/ssr-safe-bsWGK4V4.js +0 -2232
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -1
- package/dist/vite.svg +0 -1
package/README.md
CHANGED
|
@@ -500,7 +500,7 @@ All components will automatically switch to their dark mode variants without any
|
|
|
500
500
|
message={string}
|
|
501
501
|
closable={boolean}
|
|
502
502
|
className={string}
|
|
503
|
-
|
|
503
|
+
onclick={(e) => console.log('Alert closed', e)}
|
|
504
504
|
>
|
|
505
505
|
Custom Alert Content
|
|
506
506
|
</Alert>
|
|
@@ -679,7 +679,7 @@ Form components use **controlled components** pattern instead of two-way binding
|
|
|
679
679
|
```svelte
|
|
680
680
|
<Input bind:value={inputValue} />
|
|
681
681
|
<Button on:click={handleClick}>Click</Button>
|
|
682
|
-
<Modal bind:isOpen
|
|
682
|
+
<Modal bind:isOpen onclick={handleClose} />
|
|
683
683
|
```
|
|
684
684
|
|
|
685
685
|
**After (v2.1.x):**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!-- SSR-safe wrapper component for zabi-components -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { onMount } from "svelte";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
fallback?: any;
|
|
7
|
+
component?: any;
|
|
8
|
+
props?: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
fallback = null,
|
|
13
|
+
component,
|
|
14
|
+
props = {},
|
|
15
|
+
children,
|
|
16
|
+
} = $props<Props & { children?: any }>();
|
|
17
|
+
|
|
18
|
+
let mounted = $state(false);
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
mounted = true;
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
{#if mounted && component}
|
|
26
|
+
{@const Component = component}
|
|
27
|
+
<Component {...props}>
|
|
28
|
+
{@render children?.()}
|
|
29
|
+
</Component>
|
|
30
|
+
{:else if fallback}
|
|
31
|
+
{@const FallbackComponent = fallback}
|
|
32
|
+
<FallbackComponent {...props}>
|
|
33
|
+
{@render children?.()}
|
|
34
|
+
</FallbackComponent>
|
|
35
|
+
{:else}
|
|
36
|
+
<!-- Fallback content for SSR -->
|
|
37
|
+
<div class="zabi-ssr-fallback">
|
|
38
|
+
{@render children?.()}
|
|
39
|
+
</div>
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.zabi-ssr-fallback {
|
|
44
|
+
/* Minimal styling for SSR fallback */
|
|
45
|
+
display: contents;
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
fallback?: any;
|
|
3
|
+
component?: any;
|
|
4
|
+
props?: Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
type $$ComponentProps = Props & {
|
|
7
|
+
children?: any;
|
|
8
|
+
};
|
|
9
|
+
declare const SSRSafe: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type SSRSafe = ReturnType<typeof SSRSafe>;
|
|
11
|
+
export default SSRSafe;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Badge Component - SSR-safe and robust
|
|
3
|
+
|
|
4
|
+
A simple badge component with proper SSR handling and defensive programming
|
|
5
|
+
to prevent DOM access errors during hydration and rapid state changes.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { Check, AlertTriangle, X, Info } from "@lucide/svelte";
|
|
9
|
+
// Props using Svelte 5 runes
|
|
10
|
+
let {
|
|
11
|
+
variant = "default",
|
|
12
|
+
size = "md",
|
|
13
|
+
text = "",
|
|
14
|
+
showIcon = true,
|
|
15
|
+
}: {
|
|
16
|
+
variant?: "default" | "success" | "warning" | "error" | "info";
|
|
17
|
+
size?: "sm" | "md" | "lg";
|
|
18
|
+
text: string;
|
|
19
|
+
showIcon?: boolean;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
// Class computation using conditional logic with full class names
|
|
23
|
+
const classes = $derived(() => {
|
|
24
|
+
const baseClasses =
|
|
25
|
+
"inline-flex items-center font-medium border rounded-md";
|
|
26
|
+
|
|
27
|
+
// Size classes - using full class names
|
|
28
|
+
const sizeClass =
|
|
29
|
+
size === "sm"
|
|
30
|
+
? "px-2 py-0.5 text-xs"
|
|
31
|
+
: size === "lg"
|
|
32
|
+
? "px-4 py-2 text-base"
|
|
33
|
+
: "px-3 py-1 text-sm"; // default md
|
|
34
|
+
|
|
35
|
+
// Variant classes - using semantic color system
|
|
36
|
+
const variantClass =
|
|
37
|
+
variant === "success"
|
|
38
|
+
? "bg-surface-secondary border-secondary text-success"
|
|
39
|
+
: variant === "warning"
|
|
40
|
+
? "bg-surface-secondary border-secondary text-warning"
|
|
41
|
+
: variant === "error"
|
|
42
|
+
? "bg-surface-secondary border-secondary text-error"
|
|
43
|
+
: variant === "info"
|
|
44
|
+
? "bg-surface-secondary border-secondary text-body"
|
|
45
|
+
: "bg-surface-secondary border-secondary text-body"; // default
|
|
46
|
+
|
|
47
|
+
return `${baseClasses} ${sizeClass} ${variantClass}`.trim();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Icon size based on badge size
|
|
51
|
+
const iconSize = $derived(() => {
|
|
52
|
+
return size === "sm" ? 12 : size === "lg" ? 20 : 16; // default md
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Icon spacing class
|
|
56
|
+
const iconSpacingClass = $derived(() => {
|
|
57
|
+
return size === "sm" ? "mr-1" : size === "lg" ? "mr-2" : "mr-1.5"; // default md
|
|
58
|
+
});
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<span class={classes()}>
|
|
62
|
+
{#if showIcon}
|
|
63
|
+
{#if variant === "success"}
|
|
64
|
+
<Check size={iconSize()} class={iconSpacingClass()} />
|
|
65
|
+
{:else if variant === "warning"}
|
|
66
|
+
<AlertTriangle size={iconSize()} class={iconSpacingClass()} />
|
|
67
|
+
{:else if variant === "error"}
|
|
68
|
+
<X size={iconSize()} class={iconSpacingClass()} />
|
|
69
|
+
{:else if variant === "info"}
|
|
70
|
+
<Info size={iconSize()} class={iconSpacingClass()} />
|
|
71
|
+
{:else}
|
|
72
|
+
<Info size={iconSize()} class={iconSpacingClass()} />
|
|
73
|
+
{/if}
|
|
74
|
+
{/if}
|
|
75
|
+
{text}
|
|
76
|
+
</span>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
variant?: "default" | "success" | "warning" | "error" | "info";
|
|
3
|
+
size?: "sm" | "md" | "lg";
|
|
4
|
+
text: string;
|
|
5
|
+
showIcon?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const Badge: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type Badge = ReturnType<typeof Badge>;
|
|
9
|
+
export default Badge;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?:
|
|
6
|
+
| "primary"
|
|
7
|
+
| "secondary"
|
|
8
|
+
| "danger"
|
|
9
|
+
| "success"
|
|
10
|
+
| "ghost"
|
|
11
|
+
| "brand";
|
|
12
|
+
size?: "sm" | "md" | "lg";
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
type?: "button" | "submit" | "reset";
|
|
15
|
+
text?: string;
|
|
16
|
+
onclick?: (event: MouseEvent) => void;
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
variant = "primary",
|
|
22
|
+
size = "md",
|
|
23
|
+
disabled = false,
|
|
24
|
+
type = "button",
|
|
25
|
+
text = "",
|
|
26
|
+
onclick,
|
|
27
|
+
children,
|
|
28
|
+
...restProps
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
// Size classes using full class names
|
|
32
|
+
const sizeClass = $derived(() => {
|
|
33
|
+
return size === "sm"
|
|
34
|
+
? "px-3 py-1.5 text-sm font-medium"
|
|
35
|
+
: size === "lg"
|
|
36
|
+
? "px-6 py-3 text-base font-semibold"
|
|
37
|
+
: "px-4 py-2 text-sm font-medium"; // default md
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Variant classes using full class names in conditionals
|
|
41
|
+
const variantClass = $derived(() => {
|
|
42
|
+
return variant === "primary"
|
|
43
|
+
? "bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-400"
|
|
44
|
+
: variant === "secondary"
|
|
45
|
+
? "bg-gray-600 text-white hover:bg-gray-700 active:bg-gray-800 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:bg-gray-400"
|
|
46
|
+
: variant === "danger"
|
|
47
|
+
? "bg-red-600 text-white hover:bg-red-700 active:bg-red-800 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:bg-red-400"
|
|
48
|
+
: variant === "success"
|
|
49
|
+
? "bg-green-600 text-white hover:bg-green-700 active:bg-green-800 focus:ring-2 focus:ring-green-500 focus:ring-offset-2 disabled:bg-green-400"
|
|
50
|
+
: variant === "ghost"
|
|
51
|
+
? "bg-transparent text-gray-700 hover:bg-gray-100 active:bg-gray-200 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:text-gray-400"
|
|
52
|
+
: variant === "brand"
|
|
53
|
+
? "bg-purple-600 text-white hover:bg-purple-700 active:bg-purple-800 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 disabled:bg-purple-400"
|
|
54
|
+
: "bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-400"; // default primary
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Button classes using Badge pattern
|
|
58
|
+
const buttonClasses = $derived(() => {
|
|
59
|
+
const baseClasses =
|
|
60
|
+
"inline-flex items-center justify-center rounded-md transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none focus:outline-none focus:ring-2 focus:ring-offset-2 active:scale-95 transform-gpu shadow-sm hover:shadow-md";
|
|
61
|
+
|
|
62
|
+
return `${baseClasses} ${sizeClass()} ${variantClass()}`.trim();
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<button {type} class={buttonClasses()} {disabled} {onclick} {...restProps}>
|
|
67
|
+
{#if text}
|
|
68
|
+
{text}
|
|
69
|
+
{:else if children}
|
|
70
|
+
{@render children()}
|
|
71
|
+
{/if}
|
|
72
|
+
</button>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
variant?: "primary" | "secondary" | "danger" | "success" | "ghost" | "brand";
|
|
4
|
+
size?: "sm" | "md" | "lg";
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
type?: "button" | "submit" | "reset";
|
|
7
|
+
text?: string;
|
|
8
|
+
onclick?: (event: MouseEvent) => void;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const Button: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Button = ReturnType<typeof Button>;
|
|
13
|
+
export default Button;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
title?: string;
|
|
6
|
+
image?: string;
|
|
7
|
+
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
8
|
+
size?: "sm" | "md" | "lg";
|
|
9
|
+
fullWidth?: boolean;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
title = "",
|
|
15
|
+
image = "",
|
|
16
|
+
onclick,
|
|
17
|
+
size = "md",
|
|
18
|
+
fullWidth = false,
|
|
19
|
+
children,
|
|
20
|
+
...restProps
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const sizeClass = $derived(() => {
|
|
24
|
+
return size === "sm" ? "p-3" : size === "lg" ? "p-6" : "p-4"; // default md
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const cardClasses = $derived(() => {
|
|
28
|
+
const baseClasses =
|
|
29
|
+
"bg-surface rounded-lg transition-all duration-200 hover:shadow-adaptive-md min-w-64";
|
|
30
|
+
const interactiveClasses = onclick
|
|
31
|
+
? "cursor-pointer hover:bg-surface-hover hover:border-focus"
|
|
32
|
+
: "";
|
|
33
|
+
const widthClasses = fullWidth ? "w-full" : "";
|
|
34
|
+
|
|
35
|
+
return `${baseClasses} ${interactiveClasses} ${widthClasses} ${sizeClass()}`.trim();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const titleClasses = $derived(
|
|
39
|
+
() => "text-lg font-semibold mb-2 text-headline",
|
|
40
|
+
);
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div class={cardClasses()} {onclick} {...restProps}>
|
|
44
|
+
{#if image}
|
|
45
|
+
<img
|
|
46
|
+
src={image}
|
|
47
|
+
alt={title}
|
|
48
|
+
class="w-full h-48 object-cover rounded-md mb-4"
|
|
49
|
+
/>
|
|
50
|
+
{/if}
|
|
51
|
+
|
|
52
|
+
{#if title}
|
|
53
|
+
<h3 class={titleClasses()}>{title}</h3>
|
|
54
|
+
{/if}
|
|
55
|
+
|
|
56
|
+
{#if children}
|
|
57
|
+
{@render children()}
|
|
58
|
+
{/if}
|
|
59
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
title?: string;
|
|
4
|
+
image?: string;
|
|
5
|
+
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
6
|
+
size?: "sm" | "md" | "lg";
|
|
7
|
+
fullWidth?: boolean;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
declare const Card: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Card = ReturnType<typeof Card>;
|
|
12
|
+
export default Card;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
// SSR-safe ID generation
|
|
4
|
+
function generateId(prefix: string = "id"): string {
|
|
5
|
+
if (typeof window !== "undefined") {
|
|
6
|
+
return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
|
|
7
|
+
} else {
|
|
8
|
+
return `${prefix}-ssr-${Date.now()}`;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
checked?: boolean;
|
|
14
|
+
name?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
label?: string;
|
|
17
|
+
onchange?: (event: Event) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
checked = false,
|
|
22
|
+
name = "",
|
|
23
|
+
disabled = false,
|
|
24
|
+
label = "",
|
|
25
|
+
...restProps
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
// Generate unique ID - SSR safe (call directly, not in $state)
|
|
29
|
+
const checkboxId = generateId("checkbox");
|
|
30
|
+
|
|
31
|
+
// Checkbox classes using Badge pattern
|
|
32
|
+
const checkboxClasses = $derived(
|
|
33
|
+
() =>
|
|
34
|
+
"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function handleChange(event: Event) {
|
|
38
|
+
const target = event.target as HTMLInputElement;
|
|
39
|
+
checked = target.checked;
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div class="flex items-center gap-2">
|
|
44
|
+
<input
|
|
45
|
+
type="checkbox"
|
|
46
|
+
id={checkboxId}
|
|
47
|
+
{name}
|
|
48
|
+
{checked}
|
|
49
|
+
{disabled}
|
|
50
|
+
class={checkboxClasses()}
|
|
51
|
+
onchange={handleChange}
|
|
52
|
+
{...restProps}
|
|
53
|
+
/>
|
|
54
|
+
{#if label}
|
|
55
|
+
<label
|
|
56
|
+
for={checkboxId}
|
|
57
|
+
class="text-sm font-medium cursor-pointer text-label">{label}</label
|
|
58
|
+
>
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
checked?: boolean;
|
|
3
|
+
name?: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
label?: string;
|
|
6
|
+
onchange?: (event: Event) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const Checkbox: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
10
|
+
export default Checkbox;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
code: string;
|
|
4
|
+
language?: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
showCopyButton?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
code,
|
|
11
|
+
language = "svelte",
|
|
12
|
+
className = "",
|
|
13
|
+
showCopyButton = true,
|
|
14
|
+
...restProps
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
let copied = $state(false);
|
|
18
|
+
|
|
19
|
+
async function copyToClipboard() {
|
|
20
|
+
try {
|
|
21
|
+
await navigator.clipboard.writeText(code);
|
|
22
|
+
copied = true;
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
copied = false;
|
|
25
|
+
}, 2000);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error("Failed to copy code:", err);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
class="code-block relative bg-gray-900 rounded-lg overflow-hidden {className}"
|
|
34
|
+
{...restProps}
|
|
35
|
+
>
|
|
36
|
+
<!-- Header -->
|
|
37
|
+
<div
|
|
38
|
+
class="flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700"
|
|
39
|
+
>
|
|
40
|
+
<div class="flex items-center gap-2">
|
|
41
|
+
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
|
42
|
+
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
|
|
43
|
+
<div class="w-3 h-3 rounded-full bg-green-500"></div>
|
|
44
|
+
</div>
|
|
45
|
+
{#if showCopyButton}
|
|
46
|
+
<button
|
|
47
|
+
onclick={copyToClipboard}
|
|
48
|
+
class="flex items-center gap-2 px-3 py-1 text-xs text-gray-300 hover:text-white hover:bg-gray-700 rounded transition-colors duration-200"
|
|
49
|
+
aria-label="Copy code to clipboard"
|
|
50
|
+
>
|
|
51
|
+
{#if copied}
|
|
52
|
+
<svg
|
|
53
|
+
class="w-4 h-4"
|
|
54
|
+
fill="currentColor"
|
|
55
|
+
viewBox="0 0 20 20"
|
|
56
|
+
>
|
|
57
|
+
<path
|
|
58
|
+
fill-rule="evenodd"
|
|
59
|
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
60
|
+
clip-rule="evenodd"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
Copied!
|
|
64
|
+
{:else}
|
|
65
|
+
<svg
|
|
66
|
+
class="w-4 h-4"
|
|
67
|
+
fill="none"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
viewBox="0 0 24 24"
|
|
70
|
+
>
|
|
71
|
+
<path
|
|
72
|
+
stroke-linecap="round"
|
|
73
|
+
stroke-linejoin="round"
|
|
74
|
+
stroke-width="2"
|
|
75
|
+
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
76
|
+
/>
|
|
77
|
+
</svg>
|
|
78
|
+
Copy
|
|
79
|
+
{/if}
|
|
80
|
+
</button>
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<!-- Code content -->
|
|
85
|
+
<pre class="p-4 overflow-x-auto text-sm text-gray-100 leading-relaxed"><code
|
|
86
|
+
class="language-{language}">{@html code}</code
|
|
87
|
+
></pre>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<style>
|
|
91
|
+
.code-block {
|
|
92
|
+
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Basic syntax highlighting for common languages */
|
|
96
|
+
:global(.language-svelte .token.tag),
|
|
97
|
+
:global(.language-html .token.tag) {
|
|
98
|
+
color: #f92672;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:global(.language-svelte .token.attr-name),
|
|
102
|
+
:global(.language-html .token.attr-name) {
|
|
103
|
+
color: #a6e22e;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:global(.language-svelte .token.attr-value),
|
|
107
|
+
:global(.language-html .token.attr-value) {
|
|
108
|
+
color: #e6db74;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:global(.language-javascript .token.keyword),
|
|
112
|
+
:global(.language-typescript .token.keyword) {
|
|
113
|
+
color: #66d9ef;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:global(.language-javascript .token.string),
|
|
117
|
+
:global(.language-typescript .token.string) {
|
|
118
|
+
color: #e6db74;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:global(.language-javascript .token.function),
|
|
122
|
+
:global(.language-typescript .token.function) {
|
|
123
|
+
color: #a6e22e;
|
|
124
|
+
}
|
|
125
|
+
</style>
|