@poodle64/ui 2026.8.15 → 2026.9.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/README.md +367 -14
- package/dist/components/ui/button/button.svelte +13 -8
- package/dist/components/ui/checkbox/checkbox.svelte +23 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
- package/dist/components/ui/form/form-button.svelte +7 -0
- package/dist/components/ui/form/form-button.svelte.d.ts +4 -0
- package/dist/components/ui/form/form-description.svelte +18 -0
- package/dist/components/ui/form/form-description.svelte.d.ts +4 -0
- package/dist/components/ui/form/form-element-field.svelte +25 -0
- package/dist/components/ui/form/form-element-field.svelte.d.ts +28 -0
- package/dist/components/ui/form/form-field-errors.svelte +31 -0
- package/dist/components/ui/form/form-field-errors.svelte.d.ts +8 -0
- package/dist/components/ui/form/form-field.svelte +25 -0
- package/dist/components/ui/form/form-field.svelte.d.ts +28 -0
- package/dist/components/ui/form/form-fieldset.svelte +16 -0
- package/dist/components/ui/form/form-fieldset.svelte.d.ts +27 -0
- package/dist/components/ui/form/form-label.svelte +25 -0
- package/dist/components/ui/form/form-label.svelte.d.ts +4 -0
- package/dist/components/ui/form/form-legend.svelte +17 -0
- package/dist/components/ui/form/form-legend.svelte.d.ts +4 -0
- package/dist/components/ui/form/index.d.ts +11 -0
- package/dist/components/ui/form/index.js +13 -0
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/page-header/page-header.svelte +70 -33
- package/dist/components/ui/page-header/page-header.svelte.d.ts +4 -0
- package/dist/components/ui/switch/index.d.ts +2 -2
- package/dist/components/ui/switch/index.js +1 -1
- package/dist/components/ui/switch/switch.svelte +38 -5
- package/dist/components/ui/switch/switch.svelte.d.ts +7 -1
- package/dist/components/ui/tabs/tabs-trigger.svelte +1 -1
- package/dist/format.d.ts +222 -0
- package/dist/format.js +422 -0
- package/dist/styles.css +310 -15
- package/package.json +16 -2
- package/registry/component-map.json +74 -4
- package/registry/component-map.md +18 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import type { FormPathLeaves } from 'sveltekit-superforms';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import type { WithElementRef } from '../../../utils.js';
|
|
5
|
+
declare function $$render<T extends Record<string, unknown>, U extends FormPathLeaves<T>>(): {
|
|
6
|
+
props: Omit<WithElementRef<HTMLAttributes<HTMLDivElement>>, "children"> & FormPrimitive.ElementFieldProps<T, U, any>;
|
|
7
|
+
exports: {};
|
|
8
|
+
bindings: "ref";
|
|
9
|
+
slots: {};
|
|
10
|
+
events: {};
|
|
11
|
+
};
|
|
12
|
+
declare class __sveltets_Render<T extends Record<string, unknown>, U extends FormPathLeaves<T>> {
|
|
13
|
+
props(): ReturnType<typeof $$render<T, U>>['props'];
|
|
14
|
+
events(): ReturnType<typeof $$render<T, U>>['events'];
|
|
15
|
+
slots(): ReturnType<typeof $$render<T, U>>['slots'];
|
|
16
|
+
bindings(): "ref";
|
|
17
|
+
exports(): {};
|
|
18
|
+
}
|
|
19
|
+
interface $$IsomorphicComponent {
|
|
20
|
+
new <T extends Record<string, unknown>, U extends FormPathLeaves<T>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, U>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, U>['props']>, ReturnType<__sveltets_Render<T, U>['events']>, ReturnType<__sveltets_Render<T, U>['slots']>> & {
|
|
21
|
+
$$bindings?: ReturnType<__sveltets_Render<T, U>['bindings']>;
|
|
22
|
+
} & ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
23
|
+
<T extends Record<string, unknown>, U extends FormPathLeaves<T>>(internal: unknown, props: ReturnType<__sveltets_Render<T, U>['props']> & {}): ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
24
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
25
|
+
}
|
|
26
|
+
declare const FormElementField: $$IsomorphicComponent;
|
|
27
|
+
type FormElementField<T extends Record<string, unknown>, U extends FormPathLeaves<T>> = InstanceType<typeof FormElementField<T, U>>;
|
|
28
|
+
export default FormElementField;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as FormPrimitive from 'formsnap';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
errorClasses,
|
|
10
|
+
children: childrenProp,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithoutChild<FormPrimitive.FieldErrorsProps> & {
|
|
13
|
+
errorClasses?: string | undefined | null;
|
|
14
|
+
} = $props();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<FormPrimitive.FieldErrors
|
|
18
|
+
bind:ref
|
|
19
|
+
class={cn('text-destructive text-sm font-medium', className)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{#snippet children({ errors, errorProps })}
|
|
23
|
+
{#if childrenProp}
|
|
24
|
+
{@render childrenProp({ errors, errorProps })}
|
|
25
|
+
{:else}
|
|
26
|
+
{#each errors as error (error)}
|
|
27
|
+
<div {...errorProps} class={cn(errorClasses)}>{error}</div>
|
|
28
|
+
{/each}
|
|
29
|
+
{/if}
|
|
30
|
+
{/snippet}
|
|
31
|
+
</FormPrimitive.FieldErrors>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
3
|
+
type $$ComponentProps = WithoutChild<FormPrimitive.FieldErrorsProps> & {
|
|
4
|
+
errorClasses?: string | undefined | null;
|
|
5
|
+
};
|
|
6
|
+
declare const FormFieldErrors: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
7
|
+
type FormFieldErrors = ReturnType<typeof FormFieldErrors>;
|
|
8
|
+
export default FormFieldErrors;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
|
|
2
|
+
import * as FormPrimitive from 'formsnap';
|
|
3
|
+
import type { FormPath } from 'sveltekit-superforms';
|
|
4
|
+
import { cn } from '../../../utils.js';
|
|
5
|
+
import type { WithElementRef, WithoutChildren } from '../../../utils.js';
|
|
6
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
class: className,
|
|
11
|
+
form,
|
|
12
|
+
name,
|
|
13
|
+
children: childrenProp,
|
|
14
|
+
...restProps
|
|
15
|
+
}: FormPrimitive.FieldProps<T, U> &
|
|
16
|
+
WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<FormPrimitive.Field {form} {name}>
|
|
20
|
+
{#snippet children({ constraints, errors, tainted, value })}
|
|
21
|
+
<div bind:this={ref} data-slot="form-item" class={cn('space-y-2', className)} {...restProps}>
|
|
22
|
+
{@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
|
|
23
|
+
</div>
|
|
24
|
+
{/snippet}
|
|
25
|
+
</FormPrimitive.Field>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import type { FormPath } from 'sveltekit-superforms';
|
|
3
|
+
import type { WithElementRef } from '../../../utils.js';
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
5
|
+
declare function $$render<T extends Record<string, unknown>, U extends FormPath<T>>(): {
|
|
6
|
+
props: FormPrimitive.FieldProps<T, U, any> & Omit<WithElementRef<HTMLAttributes<HTMLDivElement>>, "children">;
|
|
7
|
+
exports: {};
|
|
8
|
+
bindings: "ref";
|
|
9
|
+
slots: {};
|
|
10
|
+
events: {};
|
|
11
|
+
};
|
|
12
|
+
declare class __sveltets_Render<T extends Record<string, unknown>, U extends FormPath<T>> {
|
|
13
|
+
props(): ReturnType<typeof $$render<T, U>>['props'];
|
|
14
|
+
events(): ReturnType<typeof $$render<T, U>>['events'];
|
|
15
|
+
slots(): ReturnType<typeof $$render<T, U>>['slots'];
|
|
16
|
+
bindings(): "ref";
|
|
17
|
+
exports(): {};
|
|
18
|
+
}
|
|
19
|
+
interface $$IsomorphicComponent {
|
|
20
|
+
new <T extends Record<string, unknown>, U extends FormPath<T>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, U>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, U>['props']>, ReturnType<__sveltets_Render<T, U>['events']>, ReturnType<__sveltets_Render<T, U>['slots']>> & {
|
|
21
|
+
$$bindings?: ReturnType<__sveltets_Render<T, U>['bindings']>;
|
|
22
|
+
} & ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
23
|
+
<T extends Record<string, unknown>, U extends FormPath<T>>(internal: unknown, props: ReturnType<__sveltets_Render<T, U>['props']> & {}): ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
24
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
25
|
+
}
|
|
26
|
+
declare const FormField: $$IsomorphicComponent;
|
|
27
|
+
type FormField<T extends Record<string, unknown>, U extends FormPath<T>> = InstanceType<typeof FormField<T, U>>;
|
|
28
|
+
export default FormField;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
|
|
2
|
+
import * as FormPrimitive from 'formsnap';
|
|
3
|
+
import type { FormPath } from 'sveltekit-superforms';
|
|
4
|
+
import { cn } from '../../../utils.js';
|
|
5
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
class: className,
|
|
10
|
+
form,
|
|
11
|
+
name,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithoutChild<FormPrimitive.FieldsetProps<T, U>> = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<FormPrimitive.Fieldset bind:ref {form} {name} class={cn('space-y-2', className)} {...restProps} />
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import type { FormPath } from 'sveltekit-superforms';
|
|
3
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
4
|
+
declare function $$render<T extends Record<string, unknown>, U extends FormPath<T>>(): {
|
|
5
|
+
props: WithoutChild<FormPrimitive.FieldsetProps<T, U>>;
|
|
6
|
+
exports: {};
|
|
7
|
+
bindings: "ref";
|
|
8
|
+
slots: {};
|
|
9
|
+
events: {};
|
|
10
|
+
};
|
|
11
|
+
declare class __sveltets_Render<T extends Record<string, unknown>, U extends FormPath<T>> {
|
|
12
|
+
props(): ReturnType<typeof $$render<T, U>>['props'];
|
|
13
|
+
events(): ReturnType<typeof $$render<T, U>>['events'];
|
|
14
|
+
slots(): ReturnType<typeof $$render<T, U>>['slots'];
|
|
15
|
+
bindings(): "ref";
|
|
16
|
+
exports(): {};
|
|
17
|
+
}
|
|
18
|
+
interface $$IsomorphicComponent {
|
|
19
|
+
new <T extends Record<string, unknown>, U extends FormPath<T>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, U>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, U>['props']>, ReturnType<__sveltets_Render<T, U>['events']>, ReturnType<__sveltets_Render<T, U>['slots']>> & {
|
|
20
|
+
$$bindings?: ReturnType<__sveltets_Render<T, U>['bindings']>;
|
|
21
|
+
} & ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
22
|
+
<T extends Record<string, unknown>, U extends FormPath<T>>(internal: unknown, props: ReturnType<__sveltets_Render<T, U>['props']> & {}): ReturnType<__sveltets_Render<T, U>['exports']>;
|
|
23
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
24
|
+
}
|
|
25
|
+
declare const FormFieldset: $$IsomorphicComponent;
|
|
26
|
+
type FormFieldset<T extends Record<string, unknown>, U extends FormPath<T>> = InstanceType<typeof FormFieldset<T, U>>;
|
|
27
|
+
export default FormFieldset;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as FormPrimitive from 'formsnap';
|
|
3
|
+
import { Label } from '../label/index.js';
|
|
4
|
+
import { cn } from '../../../utils.js';
|
|
5
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
children,
|
|
10
|
+
class: className,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithoutChild<FormPrimitive.LabelProps> = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<FormPrimitive.Label {...restProps} bind:ref>
|
|
16
|
+
{#snippet child({ props })}
|
|
17
|
+
<Label
|
|
18
|
+
{...props}
|
|
19
|
+
data-slot="form-label"
|
|
20
|
+
class={cn('data-[fs-error]:text-destructive', className)}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</Label>
|
|
24
|
+
{/snippet}
|
|
25
|
+
</FormPrimitive.Label>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as FormPrimitive from 'formsnap';
|
|
3
|
+
import { cn } from '../../../utils.js';
|
|
4
|
+
import type { WithoutChild } from '../../../utils.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithoutChild<FormPrimitive.LegendProps> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<FormPrimitive.Legend
|
|
14
|
+
bind:ref
|
|
15
|
+
class={cn('data-[fs-error]:text-destructive text-sm leading-none font-medium', className)}
|
|
16
|
+
{...restProps}
|
|
17
|
+
/>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import Description from './form-description.svelte';
|
|
3
|
+
import Label from './form-label.svelte';
|
|
4
|
+
import FieldErrors from './form-field-errors.svelte';
|
|
5
|
+
import Field from './form-field.svelte';
|
|
6
|
+
import Fieldset from './form-fieldset.svelte';
|
|
7
|
+
import Legend from './form-legend.svelte';
|
|
8
|
+
import ElementField from './form-element-field.svelte';
|
|
9
|
+
import Button from './form-button.svelte';
|
|
10
|
+
declare const Control: import("svelte").Component<FormPrimitive.ControlProps, {}, "">;
|
|
11
|
+
export { Field, Control, Label, Button, FieldErrors, Description, Fieldset, Legend, ElementField, Field as FormField, Control as FormControl, Description as FormDescription, Label as FormLabel, FieldErrors as FormFieldErrors, Fieldset as FormFieldset, Legend as FormLegend, ElementField as FormElementField, Button as FormButton };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as FormPrimitive from 'formsnap';
|
|
2
|
+
import Description from './form-description.svelte';
|
|
3
|
+
import Label from './form-label.svelte';
|
|
4
|
+
import FieldErrors from './form-field-errors.svelte';
|
|
5
|
+
import Field from './form-field.svelte';
|
|
6
|
+
import Fieldset from './form-fieldset.svelte';
|
|
7
|
+
import Legend from './form-legend.svelte';
|
|
8
|
+
import ElementField from './form-element-field.svelte';
|
|
9
|
+
import Button from './form-button.svelte';
|
|
10
|
+
const Control = FormPrimitive.Control;
|
|
11
|
+
export { Field, Control, Label, Button, FieldErrors, Description, Fieldset, Legend, ElementField,
|
|
12
|
+
//
|
|
13
|
+
Field as FormField, Control as FormControl, Description as FormDescription, Label as FormLabel, FieldErrors as FormFieldErrors, Fieldset as FormFieldset, Legend as FormLegend, ElementField as FormElementField, Button as FormButton };
|
|
@@ -2,7 +2,7 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
|
|
|
2
2
|
type: "file";
|
|
3
3
|
files?: FileList;
|
|
4
4
|
} | {
|
|
5
|
-
type?: "number" | "hidden" | "color" | "submit" | "reset" | "button" | "search" | "checkbox" | "radio" | (string & {}) | "text" | "tel" | "url" | "email" | "date" | "time" | "image" | "datetime-local" | "
|
|
5
|
+
type?: "number" | "month" | "hidden" | "color" | "submit" | "reset" | "button" | "search" | "checkbox" | "radio" | (string & {}) | "text" | "tel" | "url" | "email" | "date" | "time" | "image" | "datetime-local" | "password" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
@@ -27,13 +27,22 @@
|
|
|
27
27
|
* of routed links, and a package with no SvelteKit runtime of its own cannot
|
|
28
28
|
* own those (the same reasoning that makes AppShell take `currentPath` as a
|
|
29
29
|
* prop rather than importing `$app/state`).
|
|
30
|
+
*
|
|
31
|
+
* `icon` and `meta` are snippets for the same reason and earn their place the
|
|
32
|
+
* same way `breadcrumbs` did: three apps had hand-rolled the meta row and two
|
|
33
|
+
* the icon square, one of them across 38 of its 49 page headers in a full
|
|
34
|
+
* local reimplementation of this component. The app supplies the GLYPH and
|
|
35
|
+
* the ITEMS; the package owns the treatment — the tinted square, its size,
|
|
36
|
+
* and the row's spacing — because the treatment is the half that drifts.
|
|
30
37
|
*/
|
|
31
38
|
let {
|
|
32
39
|
eyebrow,
|
|
33
40
|
breadcrumbs,
|
|
41
|
+
icon,
|
|
34
42
|
title,
|
|
35
43
|
subtitle,
|
|
36
44
|
info,
|
|
45
|
+
meta,
|
|
37
46
|
actions,
|
|
38
47
|
ref = $bindable(null),
|
|
39
48
|
class: className,
|
|
@@ -43,10 +52,14 @@
|
|
|
43
52
|
eyebrow?: string;
|
|
44
53
|
/** The trail above the title — the app's own routed links. */
|
|
45
54
|
breadcrumbs?: Snippet;
|
|
55
|
+
/** The glyph alone. The tinted square around it is this component's. */
|
|
56
|
+
icon?: Snippet;
|
|
46
57
|
/** Omit for a header that is a breadcrumb bar with actions. */
|
|
47
58
|
title?: string;
|
|
48
59
|
subtitle?: string;
|
|
49
60
|
info?: string;
|
|
61
|
+
/** Facts about the page, under the title: badges, dates, a status. */
|
|
62
|
+
meta?: Snippet;
|
|
50
63
|
actions?: Snippet;
|
|
51
64
|
} = $props();
|
|
52
65
|
</script>
|
|
@@ -56,43 +69,67 @@
|
|
|
56
69
|
class={cn('mb-6 flex flex-wrap items-start justify-between gap-4', className)}
|
|
57
70
|
{...restProps}
|
|
58
71
|
>
|
|
59
|
-
<div class="min-w-0">
|
|
60
|
-
{#if
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
<div class="flex min-w-0 items-start gap-3">
|
|
73
|
+
{#if icon}
|
|
74
|
+
<!-- aria-hidden: the glyph restates the title beside it, so a reader
|
|
75
|
+
already has the fact. `shrink-0` so it never collapses when the
|
|
76
|
+
title is long, and the square is the package's rather than the
|
|
77
|
+
app's precisely so two apps cannot pick two sizes for it. -->
|
|
78
|
+
<span
|
|
79
|
+
class="bg-primary/10 text-primary mt-0.5 inline-flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg]:size-5"
|
|
80
|
+
aria-hidden="true"
|
|
81
|
+
>
|
|
82
|
+
{@render icon()}
|
|
83
|
+
</span>
|
|
69
84
|
{/if}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
{
|
|
74
|
-
</
|
|
75
|
-
|
|
85
|
+
<div class="min-w-0">
|
|
86
|
+
{#if breadcrumbs}
|
|
87
|
+
<div class="text-muted-foreground mb-1.5 flex min-w-0 items-center text-sm">
|
|
88
|
+
{@render breadcrumbs()}
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
{#if eyebrow}
|
|
92
|
+
<div class="text-primary text-2xs tracking-eyebrow mb-1 font-medium uppercase">
|
|
93
|
+
{eyebrow}
|
|
94
|
+
</div>
|
|
95
|
+
{/if}
|
|
96
|
+
{#if title}
|
|
97
|
+
<div class="flex items-center gap-2">
|
|
98
|
+
<h1 class="font-display text-display leading-tight font-semibold tracking-[-0.02em]">
|
|
99
|
+
{title}
|
|
100
|
+
</h1>
|
|
101
|
+
{#if info}
|
|
102
|
+
<InfoTip
|
|
103
|
+
text={info}
|
|
104
|
+
class="text-muted-foreground/50 hover:text-muted-foreground mt-0.5 size-4 transition-colors"
|
|
105
|
+
/>
|
|
106
|
+
{/if}
|
|
107
|
+
</div>
|
|
108
|
+
{:else if info}
|
|
109
|
+
<!-- Title-less, but the page still has something to explain. The tip
|
|
110
|
+
keeps a row of its own rather than being dropped along with the
|
|
111
|
+
heading it usually sits beside. -->
|
|
112
|
+
<div class="flex items-center gap-2">
|
|
76
113
|
<InfoTip
|
|
77
114
|
text={info}
|
|
78
|
-
class="text-muted-foreground/50 hover:text-muted-foreground
|
|
115
|
+
class="text-muted-foreground/50 hover:text-muted-foreground size-4 transition-colors"
|
|
79
116
|
/>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
</div>
|
|
118
|
+
{/if}
|
|
119
|
+
{#if subtitle}
|
|
120
|
+
<p class="text-muted-foreground mt-1.5 line-clamp-1 max-w-4xl text-sm">{subtitle}</p>
|
|
121
|
+
{/if}
|
|
122
|
+
{#if meta}
|
|
123
|
+
<!-- `min-w-0` for the reason the actions row carries it: this is a flex
|
|
124
|
+
item and would otherwise floor at its own min-content width, which
|
|
125
|
+
wrapping cannot get below. -->
|
|
126
|
+
<div
|
|
127
|
+
class="text-muted-foreground mt-2 flex min-w-0 flex-wrap items-center gap-x-4 gap-y-1.5 text-xs"
|
|
128
|
+
>
|
|
129
|
+
{@render meta()}
|
|
130
|
+
</div>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
96
133
|
</div>
|
|
97
134
|
{#if actions}
|
|
98
135
|
<!--
|
|
@@ -6,10 +6,14 @@ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
|
6
6
|
eyebrow?: string;
|
|
7
7
|
/** The trail above the title — the app's own routed links. */
|
|
8
8
|
breadcrumbs?: Snippet;
|
|
9
|
+
/** The glyph alone. The tinted square around it is this component's. */
|
|
10
|
+
icon?: Snippet;
|
|
9
11
|
/** Omit for a header that is a breadcrumb bar with actions. */
|
|
10
12
|
title?: string;
|
|
11
13
|
subtitle?: string;
|
|
12
14
|
info?: string;
|
|
15
|
+
/** Facts about the page, under the title: badges, dates, a status. */
|
|
16
|
+
meta?: Snippet;
|
|
13
17
|
actions?: Snippet;
|
|
14
18
|
};
|
|
15
19
|
declare const PageHeader: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import Root from './switch.svelte';
|
|
2
|
-
export { Root, Root as Switch };
|
|
1
|
+
import Root, { type SwitchSize } from './switch.svelte';
|
|
2
|
+
export { Root, Root as Switch, type SwitchSize };
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** The two track sizes. `sm` lines up with a `size="sm"` control row. */
|
|
3
|
+
export type SwitchSize = 'default' | 'sm';
|
|
4
|
+
</script>
|
|
5
|
+
|
|
1
6
|
<script lang="ts">
|
|
2
7
|
import { Switch as SwitchPrimitive } from 'bits-ui';
|
|
3
8
|
import { cn, type WithoutChildrenOrChild } from '../../../utils.js';
|
|
@@ -5,9 +10,23 @@
|
|
|
5
10
|
let {
|
|
6
11
|
ref = $bindable(null),
|
|
7
12
|
checked = $bindable(false),
|
|
13
|
+
size = 'default',
|
|
8
14
|
class: className,
|
|
9
15
|
...restProps
|
|
10
|
-
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
|
|
16
|
+
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> & { size?: SwitchSize } = $props();
|
|
17
|
+
|
|
18
|
+
// Track, thumb and travel are one decision, so they are one table rather
|
|
19
|
+
// than three prop reads at three call sites. The travel is not a picked
|
|
20
|
+
// number: the track carries a 2px transparent border, so the thumb's run is
|
|
21
|
+
// (width - 4) - thumb, which is 16px on the default and 12px on `sm`.
|
|
22
|
+
const TRACK = {
|
|
23
|
+
default: 'h-5 w-9',
|
|
24
|
+
sm: 'h-4 w-7'
|
|
25
|
+
} as const;
|
|
26
|
+
const THUMB = {
|
|
27
|
+
default: 'size-4 data-checked:translate-x-4',
|
|
28
|
+
sm: 'size-3 data-checked:translate-x-3'
|
|
29
|
+
} as const;
|
|
11
30
|
</script>
|
|
12
31
|
|
|
13
32
|
<!--
|
|
@@ -17,23 +36,37 @@
|
|
|
17
36
|
ball stays light on the dark-first theme. The track colour comes from the
|
|
18
37
|
project tokens. An app with a non-standard root font-size (a dense console
|
|
19
38
|
running an 18px root, say) compensates in its own override layer, not here.
|
|
39
|
+
|
|
40
|
+
The track is 20px tall (16px at `sm`), under WCAG 2.5.8's 24px minimum, so a
|
|
41
|
+
transparent `::after` skirt lifts the pointer target over it. It grows the
|
|
42
|
+
BLOCK axis only: the track is already 36px wide, so there is nothing to win
|
|
43
|
+
horizontally and an inline skirt would reach into the label beside it.
|
|
44
|
+
|
|
45
|
+
The skirt is inset from the PADDING box, and this track carries a 2px
|
|
46
|
+
transparent border, so its real reach is the inset minus 2 on each side —
|
|
47
|
+
which is why the number here is not the number in the target. The target is
|
|
48
|
+
measured in `harness/drive.mjs`, not calculated.
|
|
20
49
|
-->
|
|
21
50
|
<SwitchPrimitive.Root
|
|
22
51
|
bind:ref
|
|
23
52
|
bind:checked
|
|
24
53
|
class={cn(
|
|
25
|
-
'peer inline-flex
|
|
54
|
+
'peer relative inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors',
|
|
55
|
+
'after:absolute after:inset-x-0 after:-inset-y-2',
|
|
26
56
|
'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
|
57
|
+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 aria-invalid:ring-3',
|
|
27
58
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
28
|
-
'data-
|
|
59
|
+
'data-checked:bg-primary data-unchecked:bg-input',
|
|
60
|
+
TRACK[size],
|
|
29
61
|
className
|
|
30
62
|
)}
|
|
31
63
|
{...restProps}
|
|
32
64
|
>
|
|
33
65
|
<SwitchPrimitive.Thumb
|
|
34
66
|
class={cn(
|
|
35
|
-
'bg-background dark:bg-foreground pointer-events-none block
|
|
36
|
-
'data-
|
|
67
|
+
'bg-background dark:bg-foreground pointer-events-none block rounded-full shadow-lg ring-0 transition-transform',
|
|
68
|
+
'data-unchecked:translate-x-0',
|
|
69
|
+
THUMB[size]
|
|
37
70
|
)}
|
|
38
71
|
/>
|
|
39
72
|
</SwitchPrimitive.Root>
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
/** The two track sizes. `sm` lines up with a `size="sm"` control row. */
|
|
2
|
+
export type SwitchSize = 'default' | 'sm';
|
|
1
3
|
import { Switch as SwitchPrimitive } from 'bits-ui';
|
|
2
|
-
|
|
4
|
+
import { type WithoutChildrenOrChild } from '../../../utils.js';
|
|
5
|
+
type $$ComponentProps = WithoutChildrenOrChild<SwitchPrimitive.RootProps> & {
|
|
6
|
+
size?: SwitchSize;
|
|
7
|
+
};
|
|
8
|
+
declare const Switch: import("svelte").Component<$$ComponentProps, {}, "ref" | "checked">;
|
|
3
9
|
type Switch = ReturnType<typeof Switch>;
|
|
4
10
|
export default Switch;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
bind:ref
|
|
10
10
|
data-slot="tabs-trigger"
|
|
11
11
|
class={cn(
|
|
12
|
-
"data-
|
|
12
|
+
"data-active:bg-background dark:data-active:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 dark:data-active:border-input dark:data-active:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-active:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
13
13
|
className
|
|
14
14
|
)}
|
|
15
15
|
{...restProps}
|