@interstellarforge/matter-svelte 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/Avatar.svelte +25 -0
- package/dist/Avatar.svelte.d.ts +10 -0
- package/dist/Badge.svelte +18 -0
- package/dist/Badge.svelte.d.ts +10 -0
- package/dist/Banner.svelte +25 -0
- package/dist/Banner.svelte.d.ts +11 -0
- package/dist/Button.svelte +44 -0
- package/dist/Button.svelte.d.ts +13 -0
- package/dist/Card.svelte +28 -0
- package/dist/Card.svelte.d.ts +11 -0
- package/dist/Checkbox.svelte +30 -0
- package/dist/Checkbox.svelte.d.ts +10 -0
- package/dist/Dialog.svelte +75 -0
- package/dist/Dialog.svelte.d.ts +14 -0
- package/dist/EmptyState.svelte +19 -0
- package/dist/EmptyState.svelte.d.ts +11 -0
- package/dist/Icon.svelte +35 -0
- package/dist/Icon.svelte.d.ts +12 -0
- package/dist/IconButton.svelte +22 -0
- package/dist/IconButton.svelte.d.ts +14 -0
- package/dist/Input.svelte +40 -0
- package/dist/Input.svelte.d.ts +12 -0
- package/dist/Menu.svelte +127 -0
- package/dist/Menu.svelte.d.ts +10 -0
- package/dist/ProgressBar.svelte +30 -0
- package/dist/ProgressBar.svelte.d.ts +11 -0
- package/dist/Select.svelte +27 -0
- package/dist/Select.svelte.d.ts +10 -0
- package/dist/Switch.svelte +33 -0
- package/dist/Switch.svelte.d.ts +8 -0
- package/dist/Tabs.svelte +78 -0
- package/dist/Tabs.svelte.d.ts +14 -0
- package/dist/Textarea.svelte +21 -0
- package/dist/Textarea.svelte.d.ts +7 -0
- package/dist/Tooltip.svelte +32 -0
- package/dist/Tooltip.svelte.d.ts +11 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +18 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +1 -0
- package/package.json +52 -3
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Interstellarforge
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
5
|
+
/** Full name: used as the accessible name and for the initials fallback. */
|
|
6
|
+
name: string;
|
|
7
|
+
src?: string;
|
|
8
|
+
size?: "sm" | "md" | "lg";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { name, src, size = "md", class: className, ...rest }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const initials = $derived(
|
|
14
|
+
name
|
|
15
|
+
.split(/\s+/)
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.slice(0, 2)
|
|
18
|
+
.map((part) => part.charAt(0))
|
|
19
|
+
.join(""),
|
|
20
|
+
);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<span {...rest} class={["m-avatar", className]} data-size={size} role="img" aria-label={name}>
|
|
24
|
+
{#if src}<img {src} alt="" />{:else}{initials}{/if}
|
|
25
|
+
</span>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
3
|
+
/** Full name: used as the accessible name and for the initials fallback. */
|
|
4
|
+
name: string;
|
|
5
|
+
src?: string;
|
|
6
|
+
size?: "sm" | "md" | "lg";
|
|
7
|
+
}
|
|
8
|
+
declare const Avatar: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Avatar = ReturnType<typeof Avatar>;
|
|
10
|
+
export default Avatar;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
import type { BadgeTone } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
8
|
+
tone?: BadgeTone;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Status must also be readable as text; never rely on the tone colour alone. */
|
|
13
|
+
let { tone = "neutral", class: className, children, ...rest }: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<span {...rest} class={["m-badge", className]} data-tone={tone}>
|
|
17
|
+
{@render children?.()}
|
|
18
|
+
</span>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { BadgeTone } from "./types.ts";
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
tone?: BadgeTone;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Badge: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Badge = ReturnType<typeof Badge>;
|
|
10
|
+
export default Badge;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
import type { BannerTone } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
tone?: BannerTone;
|
|
9
|
+
title?: string;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { tone = "info", title, class: className, children, ...rest }: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<!-- Warnings and errors are announced immediately (alert); the rest politely (status). -->
|
|
17
|
+
<div
|
|
18
|
+
role={tone === "danger" || tone === "warning" ? "alert" : "status"}
|
|
19
|
+
{...rest}
|
|
20
|
+
class={["m-banner", className]}
|
|
21
|
+
data-tone={tone}
|
|
22
|
+
>
|
|
23
|
+
{#if title}<div class="m-banner__title">{title}</div>{/if}
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { BannerTone } from "./types.ts";
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
5
|
+
tone?: BannerTone;
|
|
6
|
+
title?: string;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const Banner: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Banner = ReturnType<typeof Banner>;
|
|
11
|
+
export default Banner;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
import type { ButtonVariant, ControlSize } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLButtonAttributes {
|
|
8
|
+
variant?: ButtonVariant;
|
|
9
|
+
size?: ControlSize;
|
|
10
|
+
/** Keeps the button focusable but blocks clicks while pending. */
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
variant = "secondary",
|
|
17
|
+
size = "md",
|
|
18
|
+
type = "button",
|
|
19
|
+
loading = false,
|
|
20
|
+
class: className,
|
|
21
|
+
onclick,
|
|
22
|
+
children,
|
|
23
|
+
...rest
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<button
|
|
28
|
+
{...rest}
|
|
29
|
+
{type}
|
|
30
|
+
class={["m-button", className]}
|
|
31
|
+
data-variant={variant}
|
|
32
|
+
data-size={size}
|
|
33
|
+
aria-busy={loading ? true : undefined}
|
|
34
|
+
aria-disabled={loading ? true : undefined}
|
|
35
|
+
onclick={(event) => {
|
|
36
|
+
if (loading) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
} else {
|
|
39
|
+
onclick?.(event);
|
|
40
|
+
}
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{@render children?.()}
|
|
44
|
+
</button>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
import type { ButtonVariant, ControlSize } from "./types.ts";
|
|
4
|
+
interface Props extends HTMLButtonAttributes {
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
size?: ControlSize;
|
|
7
|
+
/** Keeps the button focusable but blocks clicks while pending. */
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const Button: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Button = ReturnType<typeof Button>;
|
|
13
|
+
export default Button;
|
package/dist/Card.svelte
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
6
|
+
/** Element to render; default `div`. */
|
|
7
|
+
as?: string;
|
|
8
|
+
padded?: boolean;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
as: tag = "div",
|
|
14
|
+
padded = false,
|
|
15
|
+
class: className,
|
|
16
|
+
children,
|
|
17
|
+
...rest
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<svelte:element
|
|
22
|
+
this={tag}
|
|
23
|
+
{...rest}
|
|
24
|
+
class={["m-card", className]}
|
|
25
|
+
data-padded={padded ? "" : undefined}
|
|
26
|
+
>
|
|
27
|
+
{@render children?.()}
|
|
28
|
+
</svelte:element>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
4
|
+
/** Element to render; default `div`. */
|
|
5
|
+
as?: string;
|
|
6
|
+
padded?: boolean;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const Card: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Card = ReturnType<typeof Card>;
|
|
11
|
+
export default Card;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value"> {
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
invalid = false,
|
|
13
|
+
indeterminate = false,
|
|
14
|
+
checked = $bindable(false),
|
|
15
|
+
class: className,
|
|
16
|
+
children,
|
|
17
|
+
...rest
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<label class={["m-checkbox", className]}>
|
|
22
|
+
<input
|
|
23
|
+
{...rest}
|
|
24
|
+
type="checkbox"
|
|
25
|
+
bind:checked
|
|
26
|
+
{indeterminate}
|
|
27
|
+
aria-invalid={invalid ? true : undefined}
|
|
28
|
+
/>
|
|
29
|
+
<span>{@render children?.()}</span>
|
|
30
|
+
</label>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value"> {
|
|
4
|
+
invalid?: boolean;
|
|
5
|
+
indeterminate?: boolean;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
9
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
10
|
+
export default Checkbox;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
onOpenChange?: (open: boolean) => void;
|
|
7
|
+
/** Visible heading; also the accessible name. */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Accessible name of the close button. */
|
|
10
|
+
closeLabel: string;
|
|
11
|
+
children?: Snippet;
|
|
12
|
+
footer?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
open = $bindable(false),
|
|
17
|
+
onOpenChange,
|
|
18
|
+
title,
|
|
19
|
+
closeLabel,
|
|
20
|
+
children,
|
|
21
|
+
footer,
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
|
|
24
|
+
const titleId = $props.id();
|
|
25
|
+
let dialog: HTMLDialogElement | undefined = $state();
|
|
26
|
+
|
|
27
|
+
/** Modal dialog on the native `<dialog>`: focus trap, Esc and inert background come from the platform. */
|
|
28
|
+
$effect(() => {
|
|
29
|
+
if (!dialog) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (open && !dialog.open) {
|
|
33
|
+
dialog.showModal();
|
|
34
|
+
} else if (!open && dialog.open) {
|
|
35
|
+
dialog.close();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function setOpen(next: boolean) {
|
|
40
|
+
open = next;
|
|
41
|
+
onOpenChange?.(next);
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<dialog
|
|
46
|
+
bind:this={dialog}
|
|
47
|
+
class="m-dialog"
|
|
48
|
+
aria-labelledby={titleId}
|
|
49
|
+
onclose={() => {
|
|
50
|
+
setOpen(false);
|
|
51
|
+
}}
|
|
52
|
+
onclick={(event) => {
|
|
53
|
+
if (event.target === event.currentTarget) {
|
|
54
|
+
setOpen(false);
|
|
55
|
+
}
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<div class="m-dialog__header">
|
|
59
|
+
<h2 id={titleId} class="m-dialog__title">{title}</h2>
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
class="m-button"
|
|
63
|
+
data-variant="ghost"
|
|
64
|
+
data-icon-only=""
|
|
65
|
+
aria-label={closeLabel}
|
|
66
|
+
onclick={() => {
|
|
67
|
+
setOpen(false);
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<span aria-hidden="true">×</span>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="m-dialog__body">{@render children?.()}</div>
|
|
74
|
+
{#if footer}<div class="m-dialog__footer">{@render footer()}</div>{/if}
|
|
75
|
+
</dialog>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
onOpenChange?: (open: boolean) => void;
|
|
5
|
+
/** Visible heading; also the accessible name. */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Accessible name of the close button. */
|
|
8
|
+
closeLabel: string;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
footer?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const Dialog: import("svelte").Component<Props, {}, "open">;
|
|
13
|
+
type Dialog = ReturnType<typeof Dialog>;
|
|
14
|
+
export default Dialog;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
6
|
+
title: string;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
/** Optional call to action, e.g. a `Button`. */
|
|
9
|
+
action?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { title, class: className, children, action, ...rest }: Props = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div {...rest} class={["m-empty", className]}>
|
|
16
|
+
<div class="m-empty__title">{title}</div>
|
|
17
|
+
{#if children}<div>{@render children()}</div>{/if}
|
|
18
|
+
{@render action?.()}
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
4
|
+
title: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
/** Optional call to action, e.g. a `Button`. */
|
|
7
|
+
action?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const EmptyState: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
11
|
+
export default EmptyState;
|
package/dist/Icon.svelte
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { IconDefinition } from "@interstellarforge/matter-icons";
|
|
3
|
+
import type { SVGAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
interface Props extends SVGAttributes<SVGSVGElement> {
|
|
6
|
+
/** A named export of `@interstellarforge/matter-icons`, e.g. `search` (tree-shakeable). */
|
|
7
|
+
icon: IconDefinition;
|
|
8
|
+
size?: number;
|
|
9
|
+
/** Accessible name. Without it the icon is decorative and hidden from assistive technology. */
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { icon, size = 18, label, class: className, ...rest }: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<svg
|
|
17
|
+
{...rest}
|
|
18
|
+
class={["m-icon", className]}
|
|
19
|
+
width={size}
|
|
20
|
+
height={size}
|
|
21
|
+
viewBox="0 0 24 24"
|
|
22
|
+
fill="none"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
stroke-width="1.8"
|
|
25
|
+
stroke-linecap="round"
|
|
26
|
+
stroke-linejoin="round"
|
|
27
|
+
focusable="false"
|
|
28
|
+
role={label ? "img" : undefined}
|
|
29
|
+
aria-label={label}
|
|
30
|
+
aria-hidden={label ? undefined : true}
|
|
31
|
+
>
|
|
32
|
+
<!-- Static markup from our own, SVGO-optimised icon sources. -->
|
|
33
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -- trusted static icon markup -->
|
|
34
|
+
{@html icon.body}
|
|
35
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IconDefinition } from "@interstellarforge/matter-icons";
|
|
2
|
+
import type { SVGAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends SVGAttributes<SVGSVGElement> {
|
|
4
|
+
/** A named export of `@interstellarforge/matter-icons`, e.g. `search` (tree-shakeable). */
|
|
5
|
+
icon: IconDefinition;
|
|
6
|
+
size?: number;
|
|
7
|
+
/** Accessible name. Without it the icon is decorative and hidden from assistive technology. */
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const Icon: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Icon = ReturnType<typeof Icon>;
|
|
12
|
+
export default Icon;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
import Button from "./Button.svelte";
|
|
6
|
+
import type { ButtonVariant, ControlSize } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
interface Props extends Omit<HTMLButtonAttributes, "aria-label"> {
|
|
9
|
+
/** Accessible name; required because the button has no visible text. */
|
|
10
|
+
label: string;
|
|
11
|
+
variant?: ButtonVariant;
|
|
12
|
+
size?: ControlSize;
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let { label, children, ...rest }: Props = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<Button {...rest} aria-label={label} data-icon-only="">
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</Button>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
import type { ButtonVariant, ControlSize } from "./types.ts";
|
|
4
|
+
interface Props extends Omit<HTMLButtonAttributes, "aria-label"> {
|
|
5
|
+
/** Accessible name; required because the button has no visible text. */
|
|
6
|
+
label: string;
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
size?: ControlSize;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const IconButton: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type IconButton = ReturnType<typeof IconButton>;
|
|
14
|
+
export default IconButton;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { IconDefinition } from "@interstellarforge/matter-icons";
|
|
3
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
import Icon from "./Icon.svelte";
|
|
6
|
+
|
|
7
|
+
interface Props extends Omit<HTMLInputAttributes, "value"> {
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Sets `aria-invalid` and the error border. */
|
|
10
|
+
invalid?: boolean;
|
|
11
|
+
/** Decorative leading icon, e.g. `search`. The input still needs its own accessible name. */
|
|
12
|
+
icon?: IconDefinition;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
invalid = false,
|
|
17
|
+
icon,
|
|
18
|
+
value = $bindable(),
|
|
19
|
+
class: className,
|
|
20
|
+
...rest
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#snippet field(cls: unknown)}
|
|
25
|
+
<input
|
|
26
|
+
{...rest}
|
|
27
|
+
bind:value
|
|
28
|
+
class={["m-input", cls]}
|
|
29
|
+
aria-invalid={invalid ? true : undefined}
|
|
30
|
+
/>
|
|
31
|
+
{/snippet}
|
|
32
|
+
|
|
33
|
+
{#if icon}
|
|
34
|
+
<span class={["m-input-icon", className]}>
|
|
35
|
+
<Icon {icon} />
|
|
36
|
+
{@render field(undefined)}
|
|
37
|
+
</span>
|
|
38
|
+
{:else}
|
|
39
|
+
{@render field(className)}
|
|
40
|
+
{/if}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IconDefinition } from "@interstellarforge/matter-icons";
|
|
2
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends Omit<HTMLInputAttributes, "value"> {
|
|
4
|
+
value?: string;
|
|
5
|
+
/** Sets `aria-invalid` and the error border. */
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
/** Decorative leading icon, e.g. `search`. The input still needs its own accessible name. */
|
|
8
|
+
icon?: IconDefinition;
|
|
9
|
+
}
|
|
10
|
+
declare const Input: import("svelte").Component<Props, {}, "value">;
|
|
11
|
+
type Input = ReturnType<typeof Input>;
|
|
12
|
+
export default Input;
|
package/dist/Menu.svelte
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from "svelte";
|
|
3
|
+
|
|
4
|
+
import type { MenuItem } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Visible text of the trigger button. */
|
|
8
|
+
triggerLabel: string;
|
|
9
|
+
items: MenuItem[];
|
|
10
|
+
onSelect: (id: string) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { triggerLabel, items, onSelect }: Props = $props();
|
|
14
|
+
|
|
15
|
+
const menuId = $props.id();
|
|
16
|
+
let root: HTMLDivElement | undefined = $state();
|
|
17
|
+
let trigger: HTMLButtonElement | undefined = $state();
|
|
18
|
+
const itemEls: (HTMLButtonElement | undefined)[] = [];
|
|
19
|
+
let open = $state(false);
|
|
20
|
+
const enabled = $derived(items.flatMap((item, index) => (item.disabled ? [] : [index])));
|
|
21
|
+
|
|
22
|
+
$effect(() => {
|
|
23
|
+
if (!open) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
function onPointerDown(event: PointerEvent) {
|
|
27
|
+
if (!root?.contains(event.target as Node)) {
|
|
28
|
+
open = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
32
|
+
return () => {
|
|
33
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function focusItem(index: number | undefined) {
|
|
38
|
+
if (index !== undefined) {
|
|
39
|
+
itemEls[index]?.focus();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function close() {
|
|
44
|
+
open = false;
|
|
45
|
+
trigger?.focus();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function openAt(position: "first" | "last") {
|
|
49
|
+
open = true;
|
|
50
|
+
await tick();
|
|
51
|
+
focusItem(position === "first" ? enabled[0] : enabled.at(-1));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function onMenuKeyDown(event: KeyboardEvent) {
|
|
55
|
+
const current = itemEls.findIndex((element) => element === document.activeElement);
|
|
56
|
+
const position = enabled.indexOf(current);
|
|
57
|
+
if (event.key === "Escape") {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
close();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const next = {
|
|
63
|
+
ArrowDown: enabled[(position + 1) % enabled.length],
|
|
64
|
+
ArrowUp: enabled[(position - 1 + enabled.length) % enabled.length],
|
|
65
|
+
Home: enabled[0],
|
|
66
|
+
End: enabled.at(-1),
|
|
67
|
+
}[event.key];
|
|
68
|
+
if (next !== undefined) {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
focusItem(next);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (event.key.length === 1) {
|
|
74
|
+
const typed = event.key.toLowerCase();
|
|
75
|
+
focusItem(
|
|
76
|
+
enabled.find(
|
|
77
|
+
(index) => index > current && items[index]?.label.toLowerCase().startsWith(typed),
|
|
78
|
+
) ?? enabled.find((index) => items[index]?.label.toLowerCase().startsWith(typed)),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<div bind:this={root} class="m-menu-wrap">
|
|
85
|
+
<button
|
|
86
|
+
bind:this={trigger}
|
|
87
|
+
type="button"
|
|
88
|
+
class="m-button"
|
|
89
|
+
aria-haspopup="menu"
|
|
90
|
+
aria-expanded={open}
|
|
91
|
+
aria-controls={menuId}
|
|
92
|
+
onclick={() => {
|
|
93
|
+
if (open) {
|
|
94
|
+
open = false;
|
|
95
|
+
} else {
|
|
96
|
+
void openAt("first");
|
|
97
|
+
}
|
|
98
|
+
}}
|
|
99
|
+
onkeydown={(event) => {
|
|
100
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
void openAt(event.key === "ArrowDown" ? "first" : "last");
|
|
103
|
+
}
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{triggerLabel}
|
|
107
|
+
</button>
|
|
108
|
+
<!-- svelte-ignore a11y_interactive_supports_focus -->
|
|
109
|
+
<div id={menuId} role="menu" class="m-menu" hidden={!open} onkeydown={onMenuKeyDown}>
|
|
110
|
+
{#each items as item, index (item.id)}
|
|
111
|
+
<button
|
|
112
|
+
bind:this={itemEls[index]}
|
|
113
|
+
type="button"
|
|
114
|
+
role="menuitem"
|
|
115
|
+
class="m-menu__item"
|
|
116
|
+
tabindex={-1}
|
|
117
|
+
disabled={item.disabled}
|
|
118
|
+
onclick={() => {
|
|
119
|
+
onSelect(item.id);
|
|
120
|
+
close();
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
{item.label}
|
|
124
|
+
</button>
|
|
125
|
+
{/each}
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MenuItem } from "./types.ts";
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Visible text of the trigger button. */
|
|
4
|
+
triggerLabel: string;
|
|
5
|
+
items: MenuItem[];
|
|
6
|
+
onSelect: (id: string) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const Menu: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Menu = ReturnType<typeof Menu>;
|
|
10
|
+
export default Menu;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
5
|
+
/** Accessible name (required). */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Omit for an indeterminate bar. */
|
|
8
|
+
value?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { label, value, max = 100, class: className, style, ...rest }: Props = $props();
|
|
13
|
+
|
|
14
|
+
const percent = $derived(
|
|
15
|
+
value === undefined ? undefined : Math.min(100, Math.max(0, (value / max) * 100)),
|
|
16
|
+
);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
{...rest}
|
|
21
|
+
class={["m-progress", className]}
|
|
22
|
+
role="progressbar"
|
|
23
|
+
aria-label={label}
|
|
24
|
+
aria-valuemin={0}
|
|
25
|
+
aria-valuemax={max}
|
|
26
|
+
aria-valuenow={value}
|
|
27
|
+
style={percent === undefined ? style : `${style ?? ""};--m-progress:${String(percent)}%`}
|
|
28
|
+
>
|
|
29
|
+
<div class="m-progress__bar"></div>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
3
|
+
/** Accessible name (required). */
|
|
4
|
+
label: string;
|
|
5
|
+
/** Omit for an indeterminate bar. */
|
|
6
|
+
value?: number;
|
|
7
|
+
max?: number;
|
|
8
|
+
}
|
|
9
|
+
declare const ProgressBar: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type ProgressBar = ReturnType<typeof ProgressBar>;
|
|
11
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLSelectAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<HTMLSelectAttributes, "value"> {
|
|
6
|
+
value?: string;
|
|
7
|
+
invalid?: boolean;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
invalid = false,
|
|
13
|
+
value = $bindable(),
|
|
14
|
+
class: className,
|
|
15
|
+
children,
|
|
16
|
+
...rest
|
|
17
|
+
}: Props = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<select
|
|
21
|
+
{...rest}
|
|
22
|
+
bind:value
|
|
23
|
+
class={["m-input m-select", className]}
|
|
24
|
+
aria-invalid={invalid ? true : undefined}
|
|
25
|
+
>
|
|
26
|
+
{@render children?.()}
|
|
27
|
+
</select>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLSelectAttributes } from "svelte/elements";
|
|
3
|
+
interface Props extends Omit<HTMLSelectAttributes, "value"> {
|
|
4
|
+
value?: string;
|
|
5
|
+
invalid?: boolean;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Select: import("svelte").Component<Props, {}, "value">;
|
|
9
|
+
type Select = ReturnType<typeof Select>;
|
|
10
|
+
export default Select;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<HTMLButtonAttributes, "role" | "value"> {
|
|
5
|
+
checked?: boolean;
|
|
6
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Needs an accessible name via `aria-label` or `aria-labelledby`. */
|
|
10
|
+
let {
|
|
11
|
+
checked = $bindable(false),
|
|
12
|
+
onCheckedChange,
|
|
13
|
+
class: className,
|
|
14
|
+
onclick,
|
|
15
|
+
...rest
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<button
|
|
20
|
+
{...rest}
|
|
21
|
+
type="button"
|
|
22
|
+
role="switch"
|
|
23
|
+
aria-checked={checked}
|
|
24
|
+
class={["m-switch", className]}
|
|
25
|
+
onclick={(event) => {
|
|
26
|
+
onclick?.(event);
|
|
27
|
+
if (event.defaultPrevented) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
checked = !checked;
|
|
31
|
+
onCheckedChange?.(checked);
|
|
32
|
+
}}
|
|
33
|
+
></button>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
2
|
+
interface Props extends Omit<HTMLButtonAttributes, "role" | "value"> {
|
|
3
|
+
checked?: boolean;
|
|
4
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const Switch: import("svelte").Component<Props, {}, "checked">;
|
|
7
|
+
type Switch = ReturnType<typeof Switch>;
|
|
8
|
+
export default Switch;
|
package/dist/Tabs.svelte
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
import type { TabItem } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
items: TabItem[];
|
|
8
|
+
/** Accessible name of the tab list. */
|
|
9
|
+
label: string;
|
|
10
|
+
value?: string;
|
|
11
|
+
onValueChange?: (value: string) => void;
|
|
12
|
+
/** Renders the panel content for the selected tab. */
|
|
13
|
+
panel: Snippet<[string]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { items, label, value = $bindable(), onValueChange, panel }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const base = $props.id();
|
|
19
|
+
const tabs: Record<string, HTMLButtonElement | undefined> = {};
|
|
20
|
+
const enabled = $derived(items.filter((item) => !item.disabled));
|
|
21
|
+
const selected = $derived(value ?? enabled[0]?.id);
|
|
22
|
+
|
|
23
|
+
function select(id: string) {
|
|
24
|
+
value = id;
|
|
25
|
+
onValueChange?.(id);
|
|
26
|
+
tabs[id]?.focus();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function onkeydown(event: KeyboardEvent) {
|
|
30
|
+
const index = enabled.findIndex((item) => item.id === selected);
|
|
31
|
+
const last = enabled.length - 1;
|
|
32
|
+
const target = {
|
|
33
|
+
ArrowRight: enabled[index === last ? 0 : index + 1],
|
|
34
|
+
ArrowLeft: enabled[index <= 0 ? last : index - 1],
|
|
35
|
+
Home: enabled[0],
|
|
36
|
+
End: enabled[last],
|
|
37
|
+
}[event.key];
|
|
38
|
+
if (target) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
select(target.id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="m-tabs">
|
|
46
|
+
<!-- svelte-ignore a11y_interactive_supports_focus -->
|
|
47
|
+
<div role="tablist" aria-label={label} class="m-tablist" {onkeydown}>
|
|
48
|
+
{#each items as item (item.id)}
|
|
49
|
+
<button
|
|
50
|
+
bind:this={tabs[item.id]}
|
|
51
|
+
type="button"
|
|
52
|
+
role="tab"
|
|
53
|
+
id="{base}-tab-{item.id}"
|
|
54
|
+
class="m-tab"
|
|
55
|
+
aria-selected={item.id === selected}
|
|
56
|
+
aria-controls="{base}-panel-{item.id}"
|
|
57
|
+
tabindex={item.id === selected ? 0 : -1}
|
|
58
|
+
disabled={item.disabled}
|
|
59
|
+
onclick={() => {
|
|
60
|
+
select(item.id);
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
{item.label}
|
|
64
|
+
</button>
|
|
65
|
+
{/each}
|
|
66
|
+
</div>
|
|
67
|
+
{#each items as item (item.id)}
|
|
68
|
+
<div
|
|
69
|
+
role="tabpanel"
|
|
70
|
+
id="{base}-panel-{item.id}"
|
|
71
|
+
class="m-tabpanel"
|
|
72
|
+
aria-labelledby="{base}-tab-{item.id}"
|
|
73
|
+
hidden={item.id !== selected}
|
|
74
|
+
>
|
|
75
|
+
{#if item.id === selected}{@render panel(item.id)}{/if}
|
|
76
|
+
</div>
|
|
77
|
+
{/each}
|
|
78
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { TabItem } from "./types.ts";
|
|
3
|
+
interface Props {
|
|
4
|
+
items: TabItem[];
|
|
5
|
+
/** Accessible name of the tab list. */
|
|
6
|
+
label: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
onValueChange?: (value: string) => void;
|
|
9
|
+
/** Renders the panel content for the selected tab. */
|
|
10
|
+
panel: Snippet<[string]>;
|
|
11
|
+
}
|
|
12
|
+
declare const Tabs: import("svelte").Component<Props, {}, "value">;
|
|
13
|
+
type Tabs = ReturnType<typeof Tabs>;
|
|
14
|
+
export default Tabs;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLTextareaAttributes } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
interface Props extends HTMLTextareaAttributes {
|
|
5
|
+
invalid?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
invalid = false,
|
|
10
|
+
value = $bindable(),
|
|
11
|
+
class: className,
|
|
12
|
+
...rest
|
|
13
|
+
}: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<textarea
|
|
17
|
+
{...rest}
|
|
18
|
+
bind:value
|
|
19
|
+
class={["m-input m-textarea", className]}
|
|
20
|
+
aria-invalid={invalid ? true : undefined}
|
|
21
|
+
></textarea>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HTMLTextareaAttributes } from "svelte/elements";
|
|
2
|
+
interface Props extends HTMLTextareaAttributes {
|
|
3
|
+
invalid?: boolean;
|
|
4
|
+
}
|
|
5
|
+
declare const Textarea: import("svelte").Component<Props, {}, "value">;
|
|
6
|
+
type Textarea = ReturnType<typeof Textarea>;
|
|
7
|
+
export default Textarea;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
content: string;
|
|
6
|
+
/** Receives the props the trigger must spread (`aria-describedby`). */
|
|
7
|
+
children: Snippet<[{ "aria-describedby": string }]>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Opens on hover and focus; Esc dismisses. Placement is fixed to the top in the MVP (no collision handling). */
|
|
11
|
+
let { content, children }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const id = $props.id();
|
|
14
|
+
let open = $state(false);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
18
|
+
<span
|
|
19
|
+
class="m-tooltip-wrap"
|
|
20
|
+
onmouseenter={() => (open = true)}
|
|
21
|
+
onmouseleave={() => (open = false)}
|
|
22
|
+
onfocusin={() => (open = true)}
|
|
23
|
+
onfocusout={() => (open = false)}
|
|
24
|
+
onkeydown={(event) => {
|
|
25
|
+
if (event.key === "Escape") {
|
|
26
|
+
open = false;
|
|
27
|
+
}
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
{@render children({ "aria-describedby": id })}
|
|
31
|
+
<span {id} role="tooltip" class="m-tooltip" hidden={!open}>{content}</span>
|
|
32
|
+
</span>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
content: string;
|
|
4
|
+
/** Receives the props the trigger must spread (`aria-describedby`). */
|
|
5
|
+
children: Snippet<[{
|
|
6
|
+
"aria-describedby": string;
|
|
7
|
+
}]>;
|
|
8
|
+
}
|
|
9
|
+
declare const Tooltip: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Tooltip = ReturnType<typeof Tooltip>;
|
|
11
|
+
export default Tooltip;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { default as Avatar } from "./Avatar.svelte";
|
|
2
|
+
export { default as Badge } from "./Badge.svelte";
|
|
3
|
+
export { default as Banner } from "./Banner.svelte";
|
|
4
|
+
export { default as Button } from "./Button.svelte";
|
|
5
|
+
export { default as Card } from "./Card.svelte";
|
|
6
|
+
export { default as Checkbox } from "./Checkbox.svelte";
|
|
7
|
+
export { default as Dialog } from "./Dialog.svelte";
|
|
8
|
+
export { default as EmptyState } from "./EmptyState.svelte";
|
|
9
|
+
export { default as Icon } from "./Icon.svelte";
|
|
10
|
+
export { default as IconButton } from "./IconButton.svelte";
|
|
11
|
+
export { default as Input } from "./Input.svelte";
|
|
12
|
+
export { default as Menu } from "./Menu.svelte";
|
|
13
|
+
export { default as ProgressBar } from "./ProgressBar.svelte";
|
|
14
|
+
export { default as Select } from "./Select.svelte";
|
|
15
|
+
export { default as Switch } from "./Switch.svelte";
|
|
16
|
+
export { default as Tabs } from "./Tabs.svelte";
|
|
17
|
+
export { default as Textarea } from "./Textarea.svelte";
|
|
18
|
+
export { default as Tooltip } from "./Tooltip.svelte";
|
|
19
|
+
export type { BadgeTone, BannerTone, ButtonVariant, ControlSize, MenuItem, TabItem, } from "./types.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { default as Avatar } from "./Avatar.svelte";
|
|
2
|
+
export { default as Badge } from "./Badge.svelte";
|
|
3
|
+
export { default as Banner } from "./Banner.svelte";
|
|
4
|
+
export { default as Button } from "./Button.svelte";
|
|
5
|
+
export { default as Card } from "./Card.svelte";
|
|
6
|
+
export { default as Checkbox } from "./Checkbox.svelte";
|
|
7
|
+
export { default as Dialog } from "./Dialog.svelte";
|
|
8
|
+
export { default as EmptyState } from "./EmptyState.svelte";
|
|
9
|
+
export { default as Icon } from "./Icon.svelte";
|
|
10
|
+
export { default as IconButton } from "./IconButton.svelte";
|
|
11
|
+
export { default as Input } from "./Input.svelte";
|
|
12
|
+
export { default as Menu } from "./Menu.svelte";
|
|
13
|
+
export { default as ProgressBar } from "./ProgressBar.svelte";
|
|
14
|
+
export { default as Select } from "./Select.svelte";
|
|
15
|
+
export { default as Switch } from "./Switch.svelte";
|
|
16
|
+
export { default as Tabs } from "./Tabs.svelte";
|
|
17
|
+
export { default as Textarea } from "./Textarea.svelte";
|
|
18
|
+
export { default as Tooltip } from "./Tooltip.svelte";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
|
|
2
|
+
export type ControlSize = "sm" | "md" | "lg";
|
|
3
|
+
export type BadgeTone = "neutral" | "accent" | "info" | "success" | "warning" | "danger";
|
|
4
|
+
export interface TabItem {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface MenuItem {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type BannerTone = "info" | "success" | "warning" | "danger";
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interstellarforge/matter-svelte",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Matter components for Svelte 5 (Vite SPA and SvelteKit). Styling comes from @interstellarforge/matter-css.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/interstellarforge/matter.git",
|
|
9
|
+
"directory": "packages/svelte"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"**/*.css"
|
|
17
|
+
],
|
|
18
|
+
"svelte": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"svelte": "./dist/index.js",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"registry": "https://registry.npmjs.org/",
|
|
29
|
+
"access": "public",
|
|
30
|
+
"provenance": true
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"svelte": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@sveltejs/package": "2.5.8",
|
|
37
|
+
"@sveltejs/vite-plugin-svelte": "7.3.1",
|
|
38
|
+
"@testing-library/dom": "10.4.2",
|
|
39
|
+
"@testing-library/svelte": "5.4.2",
|
|
40
|
+
"@testing-library/user-event": "14.6.8",
|
|
41
|
+
"axe-core": "4.14.0",
|
|
42
|
+
"jsdom": "30.1.3",
|
|
43
|
+
"svelte": "5.57.3",
|
|
44
|
+
"svelte-check": "4.7.7",
|
|
45
|
+
"vitest": "4.1.6"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@interstellarforge/matter-icons": "0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "svelte-package -i src -o dist",
|
|
52
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json",
|
|
53
|
+
"test": "vitest run"
|
|
54
|
+
}
|
|
6
55
|
}
|
package/README.md
DELETED