@spaethtech/svelte-ui 0.19.1-dev.98.32fa323 → 0.20.1-dev.101.530be6a

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.
@@ -14,6 +14,8 @@
14
14
  variant?: AlertVariant;
15
15
  size?: Responsive<Size>;
16
16
  text?: string;
17
+ /** Optional bold heading rendered above the body (`text`/`children`). Icon top-aligns when set. */
18
+ title?: string;
17
19
  /** Faint tinted background in addition to the bordered + coloured text. */
18
20
  filled?: boolean;
19
21
  /** Drop the visible frame while keeping the 1px box: border colour = the alert's own background
@@ -31,6 +33,7 @@
31
33
  variant = "info",
32
34
  size = "md",
33
35
  text = "",
36
+ title,
34
37
  filled = false,
35
38
  borderless = false,
36
39
  icon,
@@ -86,13 +89,13 @@
86
89
  ` color: ${textColor};` +
87
90
  (filled ? ` background-color: ${bgColor};` : ""),
88
91
  );
89
- const has = $derived(!!children || !!text);
92
+ const has = $derived(!!children || !!text || !!title);
90
93
  </script>
91
94
 
92
95
  {#if has}
93
96
  <div
94
97
  role="alert"
95
- class="flex items-center [border-radius:var(--ui-border-radius)] border {responsiveClasses(
98
+ class="flex {title ? 'items-start' : 'items-center'} [border-radius:var(--ui-border-radius)] border {responsiveClasses(
96
99
  size,
97
100
  sizeClass,
98
101
  )} {cls}"
@@ -104,9 +107,12 @@
104
107
  >{@render icon()}</span
105
108
  >
106
109
  {/if}
107
- <span class="min-w-0"
108
- >{#if children}{@render children()}{:else}{text}{/if}</span
109
- >
110
+ <div class="flex min-w-0 flex-col">
111
+ {#if title}<span class="font-semibold">{title}</span>{/if}
112
+ <span class="min-w-0"
113
+ >{#if children}{@render children()}{:else}{text}{/if}</span
114
+ >
115
+ </div>
110
116
  {#if icon && iconPosition === "right"}
111
117
  <span
112
118
  class="ml-auto inline-flex shrink-0 [&_svg]:fill-current {responsiveClasses(
@@ -11,6 +11,8 @@ interface Props {
11
11
  variant?: AlertVariant;
12
12
  size?: Responsive<Size>;
13
13
  text?: string;
14
+ /** Optional bold heading rendered above the body (`text`/`children`). Icon top-aligns when set. */
15
+ title?: string;
14
16
  /** Faint tinted background in addition to the bordered + coloured text. */
15
17
  filled?: boolean;
16
18
  /** Drop the visible frame while keeping the 1px box: border colour = the alert's own background
@@ -2,6 +2,7 @@
2
2
  import { uiI18n } from "../../i18n.js";
3
3
  const i18n = uiI18n();
4
4
  import type { Snippet } from "svelte";
5
+ import type { HTMLAttributes } from "svelte/elements";
5
6
  import Close from "~icons/mdi/close";
6
7
  import type { Size } from "../../types/sizes.js";
7
8
  import { responsiveClasses, type Responsive } from "../../types/responsive.js";
@@ -27,7 +28,10 @@
27
28
  | "danger"
28
29
  | "ghost";
29
30
 
30
- interface Props {
31
+ // Spreads leftover HTML attributes (`class`, `data-*`, `aria-*`, `title`, `id`, event handlers, …)
32
+ // onto the chip's root element. `style` is OMITTED — the chip computes its own themed `style`
33
+ // (fill/border/text `color-mix`); a passthrough would clobber it. Use `class` for overrides.
34
+ interface Props extends Omit<HTMLAttributes<HTMLElement>, "style"> {
31
35
  text?: string;
32
36
  variant?: BadgeVariant;
33
37
  size?: Responsive<Size>;
@@ -55,6 +59,8 @@
55
59
  href,
56
60
  ondismiss,
57
61
  children,
62
+ class: cls = "",
63
+ ...rest
58
64
  }: Props = $props();
59
65
 
60
66
  // Every variant maps to a library-wide colour token — no Tailwind
@@ -128,12 +134,13 @@
128
134
  {#if hasContent}
129
135
  <svelte:element
130
136
  this={href ? "a" : "span"}
137
+ {...rest}
131
138
  {href}
132
139
  class="inline-flex items-center gap-1 border no-underline select-none {href
133
140
  ? 'cursor-pointer hover:brightness-95'
134
141
  : 'cursor-default'} {responsiveClasses(size, sizeClass)} {rounded
135
142
  ? 'rounded-full'
136
- : 'rounded'} transition-[filter,background-color,border-color] duration-150"
143
+ : 'rounded'} transition-[filter,background-color,border-color] duration-150 {cls}"
137
144
  {style}
138
145
  >
139
146
  <span class="truncate">
@@ -1,4 +1,5 @@
1
1
  import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
2
3
  import type { Size } from "../../types/sizes.js";
3
4
  import { type Responsive } from "../../types/responsive.js";
4
5
  /**
@@ -12,7 +13,7 @@ import { type Responsive } from "../../types/responsive.js";
12
13
  * text stays high-contrast against the background.
13
14
  */
14
15
  type BadgeVariant = "neutral" | "default" | "primary" | "secondary" | "success" | "info" | "warning" | "danger" | "ghost";
15
- interface Props {
16
+ interface Props extends Omit<HTMLAttributes<HTMLElement>, "style"> {
16
17
  text?: string;
17
18
  variant?: BadgeVariant;
18
19
  size?: Responsive<Size>;
@@ -17,6 +17,7 @@
17
17
  variant = "neutral",
18
18
  size = "md",
19
19
  separator = true,
20
+ align = "right",
20
21
  class: cls = "",
21
22
  children,
22
23
  ...rest
@@ -25,10 +26,20 @@
25
26
  size?: Responsive<Size>;
26
27
  /** Show the top separator (default true). */
27
28
  separator?: boolean;
29
+ /** Horizontal placement of the footer content (action buttons). Default `"right"` — footers
30
+ * right-align actions. Convenience over `class`; a `class` justify-* still overrides (applied last). */
31
+ align?: "left" | "center" | "right" | "between";
28
32
  class?: string;
29
33
  children?: Snippet;
30
34
  } & Omit<HTMLAttributes<HTMLDivElement>, "class"> = $props();
31
35
 
36
+ const justifyClass: Record<NonNullable<typeof align>, string> = {
37
+ left: "justify-start",
38
+ center: "justify-center",
39
+ right: "justify-end",
40
+ between: "justify-between",
41
+ };
42
+
32
43
  // The body→footer gap (`pad`) is ALWAYS applied, so `separator={false}` still leaves breathing room
33
44
  // above the footer (just no line). `separator={true}` adds the line + a matching margin above it
34
45
  // (`sepGap`), giving the margin → line → pad rhythm.
@@ -44,7 +55,7 @@
44
55
  </script>
45
56
 
46
57
  <div
47
- class="flex items-center justify-end gap-3 {responsiveClasses(size, pad)} {separator
58
+ class="flex items-center {justifyClass[align]} gap-3 {responsiveClasses(size, pad)} {separator
48
59
  ? 'border-t ' + responsiveClasses(size, sepGap)
49
60
  : ''} {cls}"
50
61
  style={separator ? `border-color: ${sepColor}` : ""}
@@ -8,6 +8,9 @@ type $$ComponentProps = {
8
8
  size?: Responsive<Size>;
9
9
  /** Show the top separator (default true). */
10
10
  separator?: boolean;
11
+ /** Horizontal placement of the footer content (action buttons). Default `"right"` — footers
12
+ * right-align actions. Convenience over `class`; a `class` justify-* still overrides (applied last). */
13
+ align?: "left" | "center" | "right" | "between";
11
14
  class?: string;
12
15
  children?: Snippet;
13
16
  } & Omit<HTMLAttributes<HTMLDivElement>, "class">;
@@ -5,8 +5,9 @@
5
5
  import type { Size } from "../types/sizes.js";
6
6
  import type { Responsive } from "../types/responsive.js";
7
7
  import type { Variant } from "../types/variants.js";
8
+ import type { FieldChromeProps } from "./field-chrome.js";
8
9
 
9
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
10
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
10
11
  value: string;
11
12
  class?: string;
12
13
  inputClass?: string;
@@ -2,7 +2,8 @@ import type { HTMLInputAttributes } from "svelte/elements";
2
2
  import type { Size } from "../types/sizes.js";
3
3
  import type { Responsive } from "../types/responsive.js";
4
4
  import type { Variant } from "../types/variants.js";
5
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
5
+ import type { FieldChromeProps } from "./field-chrome.js";
6
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
6
7
  value: string;
7
8
  class?: string;
8
9
  inputClass?: string;
@@ -11,8 +11,9 @@
11
11
  import type { Size } from "../types/sizes.js";
12
12
  import type { Responsive } from "../types/responsive.js";
13
13
  import type { Variant } from "../types/variants.js";
14
+ import type { FieldChromeProps } from "./field-chrome.js";
14
15
 
15
- interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
16
+ interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size">, FieldChromeProps {
16
17
  value: number | null;
17
18
  class?: string;
18
19
  inputClass?: string;
@@ -2,7 +2,8 @@ import type { HTMLInputAttributes } from "svelte/elements";
2
2
  import type { Size } from "../types/sizes.js";
3
3
  import type { Responsive } from "../types/responsive.js";
4
4
  import type { Variant } from "../types/variants.js";
5
- interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
5
+ import type { FieldChromeProps } from "./field-chrome.js";
6
+ interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size">, FieldChromeProps {
6
7
  value: number | null;
7
8
  class?: string;
8
9
  inputClass?: string;
@@ -6,8 +6,9 @@
6
6
  import type { Size } from "../types/sizes.js";
7
7
  import type { Responsive } from "../types/responsive.js";
8
8
  import type { Variant } from "../types/variants.js";
9
+ import type { FieldChromeProps } from "./field-chrome.js";
9
10
 
10
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
11
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
11
12
  value: string;
12
13
  class?: string;
13
14
  inputClass?: string;
@@ -2,7 +2,8 @@ import type { HTMLInputAttributes } from "svelte/elements";
2
2
  import type { Size } from "../types/sizes.js";
3
3
  import type { Responsive } from "../types/responsive.js";
4
4
  import type { Variant } from "../types/variants.js";
5
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
5
+ import type { FieldChromeProps } from "./field-chrome.js";
6
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
6
7
  value: string;
7
8
  class?: string;
8
9
  inputClass?: string;
@@ -6,8 +6,9 @@
6
6
  import type { Size } from "../types/sizes.js";
7
7
  import type { Responsive } from "../types/responsive.js";
8
8
  import type { Variant } from "../types/variants.js";
9
+ import type { FieldChromeProps } from "./field-chrome.js";
9
10
 
10
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
11
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
11
12
  value: string;
12
13
  class?: string;
13
14
  inputClass?: string;
@@ -2,7 +2,8 @@ import type { HTMLInputAttributes } from "svelte/elements";
2
2
  import type { Size } from "../types/sizes.js";
3
3
  import type { Responsive } from "../types/responsive.js";
4
4
  import type { Variant } from "../types/variants.js";
5
- interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
5
+ import type { FieldChromeProps } from "./field-chrome.js";
6
+ interface Props extends Omit<HTMLInputAttributes, "type" | "size">, FieldChromeProps {
6
7
  value: string;
7
8
  class?: string;
8
9
  inputClass?: string;
@@ -0,0 +1,22 @@
1
+ import type { Snippet } from "svelte";
2
+ /**
3
+ * The shared **FieldChrome** vocabulary (`label` / `aside` / `error` / `description`) that `Input`,
4
+ * `TextArea`, and the specialized inputs (`PasswordInput` / `EmailInput` / `NumberInput` /
5
+ * `SearchInput`) all render through `FieldChrome`. See `FieldChrome.spec.md`.
6
+ *
7
+ * The specialized inputs forward `{...restProps}` into `<Input>` at runtime, so these props already
8
+ * WORK on them — they just weren't TYPED (their `Props` derived from `HTMLInputAttributes` alone,
9
+ * which has no `label`/`aside`/`error`/`description`). Extending this interface types them without any
10
+ * runtime change. `required`/`id` are NOT here — they are genuine `HTMLInputAttributes` and come from
11
+ * the base. Keep in sync with the chrome props declared on `Input`/`TextArea`.
12
+ */
13
+ export interface FieldChromeProps {
14
+ /** Renders above the control; `null`/omitted → no label row. */
15
+ label?: string | null;
16
+ /** Right-aligned snippet in the label row. */
17
+ aside?: Snippet;
18
+ /** Inline error row (string → `{@html}`); also drives the shared error state (border + aria-invalid). */
19
+ error?: string | Snippet | null;
20
+ /** Muted help row below the control (string → `{@html}`). */
21
+ description?: string | Snippet | null;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -590,7 +590,11 @@ Status indicator / label.
590
590
  - **Location**: `src/lib/components/Badge/Badge.svelte`
591
591
  - **Axes**: `size`
592
592
  - **Variants**: `default`, `primary`, `success`, `warning`, `danger`, `info`
593
- - **Props**: `text`, `variant`, `size`, `dismissible`, `rounded`, `children`; dispatches `dismiss`
593
+ - **Props**: `text`, `variant`, `size`, `dismissible`, `rounded`, `href`, `children`; dispatches `dismiss`
594
+ - **Attribute passthrough**: leftover HTML attributes — `class` (merged, appended last), `data-*`
595
+ (e.g. a `data-testid` e2e hook), `aria-*`, `id`, `title`, event handlers — spread onto the chip's
596
+ root element. **`style` is not** passed through (the chip computes its own themed `style`); use
597
+ `class` for style overrides.
594
598
 
595
599
  ### NotesEditor
596
600
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaethtech/svelte-ui",
3
- "version": "0.19.1-dev.98.32fa323",
3
+ "version": "0.20.1-dev.101.530be6a",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/spaethtech/svelte-ui.git"