zabi-components 6.0.0 → 7.0.1
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 +137 -1473
- package/dist/atoms/ActionPanel.svelte +116 -0
- package/dist/atoms/ActionPanel.svelte.d.ts +19 -0
- package/dist/atoms/Badge.svelte +1 -1
- package/dist/atoms/Badge.svelte.d.ts +1 -1
- package/dist/atoms/Button.svelte +38 -21
- package/dist/atoms/Button.svelte.d.ts +6 -6
- package/dist/atoms/Card.svelte +12 -3
- package/dist/atoms/Card.svelte.d.ts +2 -2
- package/dist/atoms/CardHeader.svelte +8 -4
- package/dist/atoms/CardHeader.svelte.d.ts +4 -2
- package/dist/atoms/Checkbox.svelte +50 -76
- package/dist/atoms/Checkbox.svelte.d.ts +7 -2
- package/dist/atoms/CodeBlock.svelte +20 -34
- package/dist/atoms/CodeBlock.svelte.d.ts +1 -0
- package/dist/atoms/ColorPicker.svelte +2 -6
- package/dist/atoms/Container.svelte +41 -0
- package/dist/atoms/Container.svelte.d.ts +12 -0
- package/dist/atoms/Divider.svelte +68 -0
- package/dist/atoms/Divider.svelte.d.ts +13 -0
- package/dist/atoms/FeatureCard.svelte +35 -50
- package/dist/atoms/FeatureCard.svelte.d.ts +9 -4
- package/dist/atoms/IconButton.svelte +36 -29
- package/dist/atoms/IconButton.svelte.d.ts +7 -13
- package/dist/atoms/Input.svelte +58 -26
- package/dist/atoms/Input.svelte.d.ts +6 -1
- package/dist/atoms/List.svelte +2 -7
- package/dist/atoms/List.svelte.d.ts +1 -6
- package/dist/atoms/ListItem.svelte +54 -65
- package/dist/atoms/ListItem.svelte.d.ts +7 -13
- package/dist/atoms/ListItemLeading.svelte +19 -0
- package/dist/atoms/ListItemLeading.svelte.d.ts +9 -0
- package/dist/atoms/Progress.svelte +1 -1
- package/dist/atoms/Radio.svelte +51 -0
- package/dist/atoms/Radio.svelte.d.ts +14 -0
- package/dist/atoms/Select.svelte +23 -30
- package/dist/atoms/Select.svelte.d.ts +0 -10
- package/dist/atoms/SelectionControl.svelte +98 -0
- package/dist/atoms/SelectionControl.svelte.d.ts +26 -0
- package/dist/atoms/Skeleton.svelte +39 -15
- package/dist/atoms/Skeleton.svelte.d.ts +7 -3
- package/dist/atoms/Table.svelte +27 -0
- package/dist/atoms/Table.svelte.d.ts +10 -0
- package/dist/atoms/Text.svelte +46 -0
- package/dist/atoms/Text.svelte.d.ts +13 -0
- package/dist/atoms/Textarea.svelte +74 -53
- package/dist/atoms/Textarea.svelte.d.ts +6 -2
- package/dist/atoms/ThemeToggle.svelte +35 -24
- package/dist/atoms/ThemeToggle.svelte.d.ts +1 -0
- package/dist/atoms/Toast.svelte +55 -31
- package/dist/atoms/Toast.svelte.d.ts +4 -1
- package/dist/atoms/Toggle.svelte +37 -22
- package/dist/atoms/Toggle.svelte.d.ts +2 -1
- package/dist/atoms/Tooltip.svelte +97 -14
- package/dist/atoms/Tooltip.svelte.d.ts +6 -5
- package/dist/atoms/index.d.ts +6 -0
- package/dist/atoms/index.js +6 -0
- package/dist/atoms/selection-control.styles.d.ts +25 -0
- package/dist/atoms/selection-control.styles.js +30 -0
- package/dist/components/atoms/index.d.ts +6 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +26 -0
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -0
- package/dist/components/index.d.ts +41 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +10 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +39 -0
- package/dist/components/molecules/toast-store.d.ts.map +1 -0
- package/dist/components/organisms/index.d.ts +2 -1
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/page.types.d.ts +69 -0
- package/dist/components/types/page.types.d.ts.map +1 -0
- package/dist/components/types/page.types.js +1 -0
- package/dist/components/types/variants.d.ts +64 -0
- package/dist/components/types/variants.d.ts.map +1 -0
- package/dist/components/types/variants.js +44 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts +23 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +12 -0
- package/dist/components/util/focus-utils.d.ts.map +1 -0
- package/dist/components/util/ssr-safe.d.ts +17 -0
- package/dist/components/util/ssr-safe.d.ts.map +1 -0
- package/dist/index.d.ts +41 -5
- package/dist/index.js +44 -8
- package/dist/lib/layout-width-tokens.d.ts +17 -0
- package/dist/lib/layout-width-tokens.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Button.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Button.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Input.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Input.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/List.d.ts +3 -0
- package/dist/lib/showcase/component-docs/List.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +3 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts +12 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/index.d.ts +4 -0
- package/dist/lib/showcase/component-docs/index.d.ts.map +1 -0
- package/dist/lib/showcase/components-catalog.d.ts.map +1 -1
- package/dist/lib/showcase/components-showcase-constants.d.ts +26 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -1
- package/dist/molecules/Alert.svelte +50 -23
- package/dist/molecules/Alert.svelte.d.ts +1 -1
- package/dist/molecules/ComponentDemo.svelte +2 -2
- package/dist/molecules/ContactForm.svelte +1 -1
- package/dist/molecules/Dropdown.svelte +99 -109
- package/dist/molecules/Dropdown.svelte.d.ts +12 -6
- package/dist/molecules/EmptyState.svelte +44 -0
- package/dist/molecules/EmptyState.svelte.d.ts +11 -0
- package/dist/molecules/FormField.svelte +89 -0
- package/dist/molecules/FormField.svelte.d.ts +23 -0
- package/dist/molecules/Header.svelte +41 -0
- package/dist/molecules/Header.svelte.d.ts +10 -0
- package/dist/molecules/Modal.svelte +37 -24
- package/dist/molecules/Modal.svelte.d.ts +4 -2
- package/dist/molecules/NavigationMenu.svelte +16 -0
- package/dist/molecules/NavigationMenu.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuContent.svelte +9 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuTrigger.svelte +9 -1
- package/dist/molecules/Page.svelte +16 -0
- package/dist/molecules/Page.svelte.d.ts +8 -0
- package/dist/molecules/PropsTable.svelte +61 -0
- package/dist/molecules/PropsTable.svelte.d.ts +8 -0
- package/dist/molecules/RadioGroup.svelte +197 -0
- package/dist/molecules/RadioGroup.svelte.d.ts +20 -0
- package/dist/molecules/Section.svelte +2 -11
- package/dist/molecules/Section.svelte.d.ts +2 -11
- package/dist/molecules/SidebarBrandHeader.svelte +68 -0
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +10 -0
- package/dist/molecules/SidebarFooter.svelte +119 -0
- package/dist/molecules/SidebarFooter.svelte.d.ts +23 -0
- package/dist/molecules/SidebarNavSection.svelte +49 -0
- package/dist/molecules/SidebarNavSection.svelte.d.ts +14 -0
- package/dist/molecules/SlideUp.svelte +19 -14
- package/dist/molecules/SlideUp.svelte.d.ts +1 -1
- package/dist/molecules/Tabs.svelte +4 -9
- package/dist/molecules/Toaster.svelte +21 -0
- package/dist/molecules/Toaster.svelte.d.ts +6 -0
- package/dist/molecules/ToasterToast.svelte +217 -0
- package/dist/molecules/ToasterToast.svelte.d.ts +7 -0
- package/dist/molecules/index.d.ts +10 -0
- package/dist/molecules/index.js +10 -0
- package/dist/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/molecules/navigation-menu-context.js +18 -0
- package/dist/molecules/toast-store.d.ts +38 -0
- package/dist/molecules/toast-store.js +34 -0
- package/dist/organisms/SidebarAccountPanel.svelte +118 -0
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +20 -0
- package/dist/organisms/SidebarNavigation.svelte +267 -237
- package/dist/organisms/SidebarNavigation.svelte.d.ts +21 -5
- package/dist/organisms/SidebarPanel.svelte +54 -22
- package/dist/organisms/SidebarPanel.svelte.d.ts +3 -0
- package/dist/organisms/{Navbar.svelte → TopNavbar.svelte} +22 -17
- package/dist/organisms/TopNavbar.svelte.d.ts +35 -0
- package/dist/organisms/index.d.ts +2 -1
- package/dist/organisms/index.js +2 -1
- package/dist/routes/lib/focus-utils.d.ts +7 -18
- package/dist/routes/lib/focus-utils.ts +32 -34
- package/dist/routes/lib/ssr-safe.ts +5 -4
- package/dist/routes/lib/variant-utils.ts +48 -39
- package/dist/types/components.d.ts +187 -0
- package/dist/types/components.d.ts.map +1 -0
- package/dist/types/events.d.ts +200 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/index.d.ts +171 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/page.types.d.ts +2 -31
- package/dist/types/page.types.d.ts.map +1 -1
- package/dist/types/page.types.js +1 -0
- package/dist/types/page.types.ts +79 -0
- package/dist/types/variants.d.ts +3 -0
- package/dist/types/variants.d.ts.map +1 -0
- package/dist/types/variants.js +1 -0
- package/dist/types/variants.ts +5 -6
- package/dist/util/fixed-sidebar-flyout.d.ts +22 -0
- package/dist/util/fixed-sidebar-flyout.js +148 -0
- package/dist/util/focus-utils.d.ts +11 -0
- package/dist/util/focus-utils.js +94 -0
- package/dist/util/ssr-safe.d.ts +16 -0
- package/dist/util/ssr-safe.js +48 -0
- package/dist/zabi-components-colors.css +128 -115
- package/dist/zabi-components-theme-dark-only.css +77 -72
- package/dist/zabi-components-theme-dark.css +77 -72
- package/dist/zabi-components-theme-only.css +51 -43
- package/dist/zabi-components-theme.css +51 -43
- package/dist/zabi-components.css +952 -249
- package/docs/lucide-icons.md +24 -0
- package/package.json +22 -9
- package/scripts/fix-lucide-svelte-icon-dts.js +35 -0
- package/dist/organisms/Navbar.svelte.d.ts +0 -41
|
@@ -1,30 +1,54 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
type Variant = "text" | "circle" | "block";
|
|
3
|
+
|
|
2
4
|
interface Props {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
variant?: Variant;
|
|
6
|
+
/** CSS length or px number; omit to use variant default (circle stays square). */
|
|
7
|
+
width?: string | number;
|
|
8
|
+
/** CSS length or px number; omit for variant default height. */
|
|
9
|
+
height?: string | number;
|
|
10
|
+
class?: string;
|
|
6
11
|
"aria-label"?: string;
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
let {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
variant = "text",
|
|
16
|
+
width,
|
|
17
|
+
height,
|
|
18
|
+
class: className = "",
|
|
19
|
+
"aria-label": ariaLabel = "Loading…",
|
|
15
20
|
}: Props = $props();
|
|
16
21
|
|
|
17
|
-
const
|
|
18
|
-
|
|
22
|
+
const radius: Record<Variant, string> = {
|
|
23
|
+
text: "rounded-md",
|
|
24
|
+
circle: "rounded-full",
|
|
25
|
+
block: "rounded-lg",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const defaultShape: Record<Variant, string> = {
|
|
29
|
+
text: "w-full h-3",
|
|
30
|
+
circle: "w-10 aspect-square",
|
|
31
|
+
block: "w-full h-32",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function toSize(value: string | number): string {
|
|
35
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const inlineStyle = $derived(
|
|
39
|
+
[
|
|
40
|
+
width !== undefined ? `width: ${toSize(width)}` : null,
|
|
41
|
+
height !== undefined ? `height: ${toSize(height)}` : null,
|
|
42
|
+
]
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join("; "),
|
|
19
45
|
);
|
|
20
|
-
const styles = $derived(`width: ${width}; height: ${height};`);
|
|
21
46
|
</script>
|
|
22
47
|
|
|
23
48
|
<div
|
|
24
|
-
class={
|
|
25
|
-
style={
|
|
49
|
+
class="animate-pulse bg-surface-2 {defaultShape[variant]} {radius[variant]} {className}"
|
|
50
|
+
style={inlineStyle || undefined}
|
|
51
|
+
role="status"
|
|
26
52
|
aria-busy="true"
|
|
27
53
|
aria-label={ariaLabel}
|
|
28
|
-
role="status"
|
|
29
|
-
{...restProps}
|
|
30
54
|
></div>
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
type Variant = "text" | "circle" | "block";
|
|
1
2
|
interface Props {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
variant?: Variant;
|
|
4
|
+
/** CSS length or px number; omit to use variant default (circle stays square). */
|
|
5
|
+
width?: string | number;
|
|
6
|
+
/** CSS length or px number; omit for variant default height. */
|
|
7
|
+
height?: string | number;
|
|
8
|
+
class?: string;
|
|
5
9
|
"aria-label"?: string;
|
|
6
10
|
}
|
|
7
11
|
declare const Skeleton: import("svelte").Component<Props, {}, "">;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
/** Visible `<caption>` when set. */
|
|
6
|
+
caption?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { caption, class: className = '', children }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div
|
|
15
|
+
class="overflow-x-auto rounded-lg border border-border shadow-sm {className}"
|
|
16
|
+
>
|
|
17
|
+
<table class="w-full min-w-[20rem] border-collapse text-left text-sm text-body">
|
|
18
|
+
{#if caption}
|
|
19
|
+
<caption class="border-b border-border bg-base-50 px-4 py-3 text-left text-headline">
|
|
20
|
+
{caption}
|
|
21
|
+
</caption>
|
|
22
|
+
{/if}
|
|
23
|
+
{#if children}
|
|
24
|
+
{@render children()}
|
|
25
|
+
{/if}
|
|
26
|
+
</table>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Visible `<caption>` when set. */
|
|
4
|
+
caption?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Table: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Table = ReturnType<typeof Table>;
|
|
10
|
+
export default Table;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
type Tone = 'body' | 'description' | 'caption' | 'headline' | 'label' | 'error';
|
|
5
|
+
type Size = 'sm' | 'md' | 'lg';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
as?: 'p' | 'span' | 'div';
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
size?: Size;
|
|
11
|
+
class?: string;
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
as: Tag = 'p',
|
|
17
|
+
tone = 'body',
|
|
18
|
+
size = 'md',
|
|
19
|
+
class: className = '',
|
|
20
|
+
children,
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const toneClass: Record<Tone, string> = {
|
|
24
|
+
body: 'text-body',
|
|
25
|
+
description: 'text-description',
|
|
26
|
+
caption: 'text-caption',
|
|
27
|
+
headline: 'text-headline',
|
|
28
|
+
label: 'text-label',
|
|
29
|
+
error: 'text-error',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const sizeClass: Record<Size, string> = {
|
|
33
|
+
sm: 'text-sm',
|
|
34
|
+
md: 'text-base',
|
|
35
|
+
lg: 'text-lg',
|
|
36
|
+
};
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<svelte:element
|
|
40
|
+
this={Tag}
|
|
41
|
+
class={[toneClass[tone], sizeClass[size], className].filter(Boolean).join(' ')}
|
|
42
|
+
>
|
|
43
|
+
{#if children}
|
|
44
|
+
{@render children()}
|
|
45
|
+
{/if}
|
|
46
|
+
</svelte:element>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type Tone = 'body' | 'description' | 'caption' | 'headline' | 'label' | 'error';
|
|
3
|
+
type Size = 'sm' | 'md' | 'lg';
|
|
4
|
+
interface Props {
|
|
5
|
+
as?: 'p' | 'span' | 'div';
|
|
6
|
+
tone?: Tone;
|
|
7
|
+
size?: Size;
|
|
8
|
+
class?: string;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const Text: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Text = ReturnType<typeof Text>;
|
|
13
|
+
export default Text;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { CheckCircle, AlertTriangle, AlertCircle } from
|
|
3
|
-
import type { SemanticVariant, SizeVariant } from
|
|
4
|
-
import { generateId } from "
|
|
2
|
+
import { CheckCircle, AlertTriangle, AlertCircle } from '@lucide/svelte';
|
|
3
|
+
import type { SemanticVariant, SizeVariant } from '../types/variants.js';
|
|
4
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
|
+
id?: string;
|
|
7
8
|
value?: string;
|
|
8
9
|
name?: string;
|
|
10
|
+
class?: string;
|
|
9
11
|
label?: string;
|
|
12
|
+
hideLabel?: boolean;
|
|
10
13
|
placeholder?: string;
|
|
11
14
|
required?: boolean;
|
|
12
15
|
disabled?: boolean;
|
|
16
|
+
loading?: boolean;
|
|
13
17
|
rows?: number;
|
|
14
18
|
size?: SizeVariant;
|
|
15
19
|
variant?: SemanticVariant;
|
|
@@ -18,95 +22,112 @@
|
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
let {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
id: idProp,
|
|
26
|
+
value = $bindable(''),
|
|
27
|
+
name = '',
|
|
28
|
+
class: className = '',
|
|
29
|
+
label = '',
|
|
30
|
+
hideLabel = false,
|
|
31
|
+
placeholder = '',
|
|
25
32
|
required = false,
|
|
26
33
|
disabled = false,
|
|
34
|
+
loading = false,
|
|
27
35
|
rows = 4,
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
size = 'md',
|
|
37
|
+
variant = 'default',
|
|
38
|
+
message = '',
|
|
30
39
|
oninput,
|
|
31
40
|
...restProps
|
|
32
41
|
}: Props = $props();
|
|
33
42
|
|
|
34
|
-
const
|
|
43
|
+
const fallbackId = generateId('textarea');
|
|
44
|
+
const textareaId = $derived(idProp ?? fallbackId);
|
|
45
|
+
const isDisabled = $derived(disabled || loading);
|
|
35
46
|
|
|
36
47
|
const variantClass = $derived(() => {
|
|
37
|
-
return variant ===
|
|
38
|
-
?
|
|
39
|
-
: variant ===
|
|
40
|
-
?
|
|
41
|
-
: variant ===
|
|
42
|
-
?
|
|
43
|
-
:
|
|
48
|
+
return variant === 'success'
|
|
49
|
+
? 'border-success focus-visible:border-success'
|
|
50
|
+
: variant === 'warning'
|
|
51
|
+
? 'border-warning focus-visible:border-warning'
|
|
52
|
+
: variant === 'error'
|
|
53
|
+
? 'border-error focus-visible:border-error'
|
|
54
|
+
: 'border-input-border';
|
|
44
55
|
});
|
|
45
56
|
|
|
46
57
|
const textareaClasses = $derived(() => {
|
|
47
58
|
const baseClasses =
|
|
48
|
-
|
|
59
|
+
'focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus-visible:outline-none disabled:opacity-50 disabled:cursor-not-allowed resize-y px-4 py-2.5 text-base leading-6';
|
|
49
60
|
|
|
50
|
-
return `${baseClasses} ${variantClass()}`.trim();
|
|
61
|
+
return `${baseClasses} ${variantClass()} ${className}`.trim();
|
|
51
62
|
});
|
|
52
63
|
|
|
53
64
|
const labelClasses = $derived(
|
|
54
|
-
() =>
|
|
65
|
+
() => 'mb-1 block text-sm font-medium text-label',
|
|
55
66
|
);
|
|
56
67
|
|
|
57
68
|
const messageClasses = $derived(() => {
|
|
58
|
-
if (variant ===
|
|
59
|
-
return
|
|
60
|
-
} else if (variant ===
|
|
61
|
-
return
|
|
62
|
-
} else if (variant ===
|
|
63
|
-
return
|
|
69
|
+
if (variant === 'error') {
|
|
70
|
+
return 'mt-1 flex items-center gap-1.5 text-sm text-error';
|
|
71
|
+
} else if (variant === 'success') {
|
|
72
|
+
return 'mt-1 flex items-center gap-1.5 text-sm text-success';
|
|
73
|
+
} else if (variant === 'warning') {
|
|
74
|
+
return 'mt-1 flex items-center gap-1.5 text-sm text-warning';
|
|
64
75
|
}
|
|
65
|
-
return
|
|
76
|
+
return 'mt-1 flex items-center gap-1.5 text-sm text-description';
|
|
66
77
|
});
|
|
67
78
|
|
|
68
79
|
const getIcon = $derived(() => {
|
|
69
|
-
if (variant ===
|
|
70
|
-
if (variant ===
|
|
71
|
-
if (variant ===
|
|
80
|
+
if (variant === 'error') return AlertCircle;
|
|
81
|
+
if (variant === 'success') return CheckCircle;
|
|
82
|
+
if (variant === 'warning') return AlertTriangle;
|
|
72
83
|
return null;
|
|
73
84
|
});
|
|
74
85
|
|
|
75
86
|
function handleInput(event: Event) {
|
|
76
87
|
const target = event.target as HTMLTextAreaElement;
|
|
77
88
|
value = target.value;
|
|
78
|
-
|
|
79
|
-
if (oninput) {
|
|
80
|
-
oninput(event);
|
|
81
|
-
}
|
|
89
|
+
oninput?.(event);
|
|
82
90
|
}
|
|
83
91
|
</script>
|
|
84
92
|
|
|
85
93
|
<div>
|
|
86
|
-
{#if label}
|
|
94
|
+
{#if label && !hideLabel}
|
|
87
95
|
<label for={textareaId} class={labelClasses()}>{label}</label>
|
|
88
96
|
{/if}
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
<div class="relative">
|
|
98
|
+
<textarea
|
|
99
|
+
id={textareaId}
|
|
100
|
+
{name}
|
|
101
|
+
bind:value
|
|
102
|
+
{placeholder}
|
|
103
|
+
{required}
|
|
104
|
+
disabled={isDisabled}
|
|
105
|
+
{rows}
|
|
106
|
+
class={textareaClasses()}
|
|
107
|
+
oninput={handleInput}
|
|
108
|
+
aria-invalid={variant === 'error' ? 'true' : undefined}
|
|
109
|
+
aria-required={required ? 'true' : undefined}
|
|
110
|
+
aria-busy={loading ? 'true' : undefined}
|
|
111
|
+
aria-describedby={message ? `${textareaId}-message` : undefined}
|
|
112
|
+
{...restProps}
|
|
113
|
+
></textarea>
|
|
114
|
+
{#if loading}
|
|
115
|
+
<span
|
|
116
|
+
class="pointer-events-none absolute top-3 right-3 flex items-center text-description"
|
|
117
|
+
aria-hidden="true"
|
|
118
|
+
>
|
|
119
|
+
<span
|
|
120
|
+
class="inline-block size-5 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-70"
|
|
121
|
+
></span>
|
|
122
|
+
</span>
|
|
123
|
+
{/if}
|
|
124
|
+
</div>
|
|
125
|
+
{#if message && variant !== 'default'}
|
|
105
126
|
<p
|
|
106
127
|
id={`${textareaId}-message`}
|
|
107
128
|
class={messageClasses()}
|
|
108
|
-
role={variant ===
|
|
109
|
-
aria-live={variant ===
|
|
129
|
+
role={variant === 'error' ? 'alert' : 'status'}
|
|
130
|
+
aria-live={variant === 'error' ? 'assertive' : 'polite'}
|
|
110
131
|
>
|
|
111
132
|
{#if getIcon()}
|
|
112
133
|
{@const Icon = getIcon()}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import type { SemanticVariant, SizeVariant } from
|
|
1
|
+
import type { SemanticVariant, SizeVariant } from '../types/variants.js';
|
|
2
2
|
interface Props {
|
|
3
|
+
id?: string;
|
|
3
4
|
value?: string;
|
|
4
5
|
name?: string;
|
|
6
|
+
class?: string;
|
|
5
7
|
label?: string;
|
|
8
|
+
hideLabel?: boolean;
|
|
6
9
|
placeholder?: string;
|
|
7
10
|
required?: boolean;
|
|
8
11
|
disabled?: boolean;
|
|
12
|
+
loading?: boolean;
|
|
9
13
|
rows?: number;
|
|
10
14
|
size?: SizeVariant;
|
|
11
15
|
variant?: SemanticVariant;
|
|
12
16
|
message?: string;
|
|
13
17
|
oninput?: (event: Event) => void;
|
|
14
18
|
}
|
|
15
|
-
declare const Textarea: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
declare const Textarea: import("svelte").Component<Props, {}, "value">;
|
|
16
20
|
type Textarea = ReturnType<typeof Textarea>;
|
|
17
21
|
export default Textarea;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import { Sun, Moon } from "@lucide/svelte";
|
|
4
|
+
import { FOCUS_BRAND_CLASS } from "../util/focus-utils.js";
|
|
4
5
|
|
|
5
6
|
function safeLocalStorage(): Storage | undefined {
|
|
6
7
|
return typeof window !== "undefined" ? localStorage : undefined;
|
|
@@ -13,13 +14,15 @@
|
|
|
13
14
|
interface Props {
|
|
14
15
|
size?: "sm" | "md" | "lg";
|
|
15
16
|
variant?: "default" | "ghost" | "outline";
|
|
17
|
+
disabled?: boolean;
|
|
16
18
|
onclick?: (event: Event) => void;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
let {
|
|
21
|
+
let {
|
|
20
22
|
size = "md",
|
|
21
23
|
variant = "default",
|
|
22
|
-
|
|
24
|
+
disabled = false,
|
|
25
|
+
...restProps
|
|
23
26
|
}: Props = $props();
|
|
24
27
|
|
|
25
28
|
let isDark = $state(false);
|
|
@@ -27,25 +30,27 @@
|
|
|
27
30
|
|
|
28
31
|
onMount(() => {
|
|
29
32
|
mounted = true;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const doc = safeDocument();
|
|
34
|
+
if (doc) {
|
|
35
|
+
isDark = doc.documentElement.classList.contains("dark");
|
|
36
|
+
}
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
38
|
-
} catch (e) {
|
|
39
|
-
prefersDark = false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
38
|
+
if (!window.matchMedia) return;
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
41
|
+
const handleSystemChange = (event: MediaQueryListEvent) => {
|
|
42
|
+
const storage = safeLocalStorage();
|
|
43
|
+
if (storage && storage.getItem("theme")) return;
|
|
44
|
+
isDark = event.matches;
|
|
44
45
|
updateTheme();
|
|
45
|
-
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
media.addEventListener("change", handleSystemChange);
|
|
49
|
+
return () => media.removeEventListener("change", handleSystemChange);
|
|
46
50
|
});
|
|
47
51
|
|
|
48
52
|
function toggleTheme(event: Event) {
|
|
53
|
+
if (disabled) return;
|
|
49
54
|
isDark = !isDark;
|
|
50
55
|
updateTheme();
|
|
51
56
|
const storage = safeLocalStorage();
|
|
@@ -63,8 +68,10 @@
|
|
|
63
68
|
if (mounted && doc) {
|
|
64
69
|
if (isDark) {
|
|
65
70
|
doc.documentElement.classList.add("dark");
|
|
71
|
+
doc.documentElement.style.colorScheme = "dark";
|
|
66
72
|
} else {
|
|
67
73
|
doc.documentElement.classList.remove("dark");
|
|
74
|
+
doc.documentElement.style.colorScheme = "light";
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
}
|
|
@@ -90,11 +97,11 @@
|
|
|
90
97
|
|
|
91
98
|
const variantClass = $derived(() => {
|
|
92
99
|
if (variant === "ghost") {
|
|
93
|
-
return "bg-transparent hover:bg-base-100 border-0";
|
|
100
|
+
return "bg-transparent hover:bg-base-100 active:bg-base-200 border-0";
|
|
94
101
|
} else if (variant === "outline") {
|
|
95
|
-
return "bg-base-50 hover:bg-base-100 border border-border";
|
|
102
|
+
return "bg-base-50 hover:bg-base-100 active:bg-base-200 border border-border";
|
|
96
103
|
} else {
|
|
97
|
-
return "bg-base-50 hover:bg-base-100 border-0";
|
|
104
|
+
return "bg-base-50 hover:bg-base-100 active:bg-base-200 border-0";
|
|
98
105
|
}
|
|
99
106
|
});
|
|
100
107
|
|
|
@@ -111,11 +118,12 @@
|
|
|
111
118
|
cursor-pointer
|
|
112
119
|
transition-colors
|
|
113
120
|
duration-200
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
active:scale-[0.96]
|
|
122
|
+
disabled:opacity-50
|
|
123
|
+
disabled:cursor-not-allowed
|
|
124
|
+
disabled:active:scale-100
|
|
125
|
+
disabled:hover:bg-base-50
|
|
126
|
+
${FOCUS_BRAND_CLASS}
|
|
119
127
|
`.trim().replace(/\s+/g, " ");
|
|
120
128
|
});
|
|
121
129
|
</script>
|
|
@@ -125,7 +133,9 @@
|
|
|
125
133
|
onclick={toggleTheme}
|
|
126
134
|
class={buttonClasses()}
|
|
127
135
|
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
|
|
136
|
+
aria-pressed={isDark}
|
|
128
137
|
type="button"
|
|
138
|
+
{disabled}
|
|
129
139
|
{...restProps}
|
|
130
140
|
>
|
|
131
141
|
{#if isDark}
|
|
@@ -136,9 +146,10 @@
|
|
|
136
146
|
</button>
|
|
137
147
|
{:else}
|
|
138
148
|
<button
|
|
139
|
-
class="w-10 h-10 bg-base-50 rounded-lg flex items-center justify-center text-label cursor-pointer"
|
|
149
|
+
class="w-10 h-10 bg-base-50 rounded-lg flex items-center justify-center text-label cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
|
|
140
150
|
aria-label="Theme toggle"
|
|
141
151
|
type="button"
|
|
152
|
+
{disabled}
|
|
142
153
|
{...restProps}
|
|
143
154
|
>
|
|
144
155
|
<Sun size={20} class="text-label" />
|
package/dist/atoms/Toast.svelte
CHANGED
|
@@ -1,57 +1,81 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
interface Props {
|
|
3
3
|
message?: string;
|
|
4
|
-
type?:
|
|
4
|
+
type?: 'success' | 'error' | 'warning' | 'info';
|
|
5
5
|
closable?: boolean;
|
|
6
6
|
onclick?: (event: Event) => void;
|
|
7
|
+
class?: string;
|
|
8
|
+
/** `viewport`: fixed corner; `inline`: block in flow (e.g. demos). */
|
|
9
|
+
layout?: 'viewport' | 'inline';
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
let {
|
|
10
|
-
message =
|
|
11
|
-
type =
|
|
13
|
+
message = '',
|
|
14
|
+
type = 'info',
|
|
12
15
|
closable = true,
|
|
13
16
|
onclick,
|
|
17
|
+
class: className = '',
|
|
18
|
+
layout = 'viewport',
|
|
14
19
|
...restProps
|
|
15
20
|
}: Props = $props();
|
|
16
21
|
|
|
17
22
|
let isVisible = $state(true);
|
|
18
23
|
|
|
19
|
-
const typeClasses = {
|
|
20
|
-
success:
|
|
21
|
-
error:
|
|
22
|
-
warning:
|
|
23
|
-
info:
|
|
24
|
+
const typeClasses: Record<NonNullable<Props['type']>, string> = {
|
|
25
|
+
success: 'border-success bg-card text-success',
|
|
26
|
+
error: 'border-error bg-card text-error',
|
|
27
|
+
warning: 'border-warning bg-card text-warning',
|
|
28
|
+
info: 'border-border bg-card text-body',
|
|
24
29
|
};
|
|
25
30
|
|
|
31
|
+
const cardClasses =
|
|
32
|
+
'box-border w-full min-w-[18rem] max-w-[min(24rem,calc(100vw-2rem))] overflow-hidden rounded-lg border p-4 shadow-lg';
|
|
33
|
+
|
|
26
34
|
function closeToast(event: Event) {
|
|
27
35
|
isVisible = false;
|
|
28
|
-
|
|
29
|
-
onclick(event);
|
|
30
|
-
}
|
|
36
|
+
onclick?.(event);
|
|
31
37
|
}
|
|
32
38
|
</script>
|
|
33
39
|
|
|
40
|
+
{#snippet toastBody()}
|
|
41
|
+
<div class="flex min-w-0 items-start gap-3">
|
|
42
|
+
<div class="min-w-0 max-w-full flex-1 basis-0">
|
|
43
|
+
<p class="text-sm break-words [overflow-wrap:anywhere]">{message}</p>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{#if closable}
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
class="focus-ring shrink-0 cursor-pointer rounded-md text-description hover:text-headline focus:outline-none"
|
|
50
|
+
onclick={closeToast}
|
|
51
|
+
aria-label="Close notification"
|
|
52
|
+
>
|
|
53
|
+
×
|
|
54
|
+
</button>
|
|
55
|
+
{/if}
|
|
56
|
+
</div>
|
|
57
|
+
{/snippet}
|
|
58
|
+
|
|
34
59
|
{#if isVisible}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
{#if layout === 'viewport'}
|
|
61
|
+
<div
|
|
62
|
+
class="pointer-events-none fixed top-4 right-4 left-4 z-toast flex justify-end sm:left-auto"
|
|
63
|
+
>
|
|
64
|
+
<div
|
|
65
|
+
class="pointer-events-auto {cardClasses} {typeClasses[type]} {className}"
|
|
66
|
+
role="alert"
|
|
67
|
+
{...restProps}
|
|
68
|
+
>
|
|
69
|
+
{@render toastBody()}
|
|
43
70
|
</div>
|
|
44
|
-
|
|
45
|
-
{#if closable}
|
|
46
|
-
<button
|
|
47
|
-
type="button"
|
|
48
|
-
class="ml-3 cursor-pointer text-base-400 hover:text-base-600"
|
|
49
|
-
onclick={closeToast}
|
|
50
|
-
aria-label="Close notification"
|
|
51
|
-
>
|
|
52
|
-
×
|
|
53
|
-
</button>
|
|
54
|
-
{/if}
|
|
55
71
|
</div>
|
|
56
|
-
|
|
72
|
+
{:else}
|
|
73
|
+
<div
|
|
74
|
+
class="relative z-toast mx-auto {cardClasses} {typeClasses[type]} {className}"
|
|
75
|
+
role="alert"
|
|
76
|
+
{...restProps}
|
|
77
|
+
>
|
|
78
|
+
{@render toastBody()}
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
57
81
|
{/if}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
interface Props {
|
|
2
2
|
message?: string;
|
|
3
|
-
type?:
|
|
3
|
+
type?: 'success' | 'error' | 'warning' | 'info';
|
|
4
4
|
closable?: boolean;
|
|
5
5
|
onclick?: (event: Event) => void;
|
|
6
|
+
class?: string;
|
|
7
|
+
/** `viewport`: fixed corner; `inline`: block in flow (e.g. demos). */
|
|
8
|
+
layout?: 'viewport' | 'inline';
|
|
6
9
|
}
|
|
7
10
|
declare const Toast: import("svelte").Component<Props, {}, "">;
|
|
8
11
|
type Toast = ReturnType<typeof Toast>;
|