@noxlovette/material 0.9.4 → 0.10.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/dist/components/forms/index.d.ts +1 -0
- package/dist/components/forms/index.js +1 -0
- package/dist/components/forms/slider/Slider.svelte +8 -0
- package/dist/components/forms/slider/types.d.ts +13 -0
- package/dist/components/forms/textarea/Textarea.mdx +63 -0
- package/dist/components/forms/textarea/Textarea.stories.svelte +99 -0
- package/dist/components/forms/textarea/Textarea.stories.svelte.d.ts +19 -0
- package/dist/components/forms/textarea/Textarea.svelte +142 -0
- package/dist/components/forms/textarea/Textarea.svelte.d.ts +13 -0
- package/dist/components/forms/textarea/index.d.ts +3 -0
- package/dist/components/forms/textarea/index.js +6 -0
- package/dist/components/forms/textarea/theme.d.ts +150 -0
- package/dist/components/forms/textarea/theme.js +155 -0
- package/dist/components/forms/textarea/types.d.ts +59 -0
- package/dist/components/forms/textarea/types.js +1 -0
- package/dist/components/misc/ThemeSettings.svelte +1 -1
- package/dist/components/misc/materialAvatarPalette.js +1 -1
- package/dist/components/progress/CircularProgress.svelte +5 -3
- package/dist/components/progress/CircularProgress.svelte.d.ts +2 -1
- package/dist/components/progress/LinearProgress.svelte +5 -2
- package/dist/components/progress/LinearProgress.svelte.d.ts +2 -1
- package/dist/components/progress/WavyLinearProgress.svelte +5 -3
- package/dist/components/progress/WavyLinearProgress.svelte.d.ts +2 -1
- package/dist/components/progress/index.d.ts +1 -0
- package/dist/components/progress/index.js +1 -0
- package/dist/components/progress/types.d.ts +16 -0
- package/dist/components/progress/types.js +1 -0
- package/dist/utils/Theme.svelte +1 -1
- package/dist/utils/ThemeScript.svelte +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- /package/dist/utils/{theme.svelte.d.ts → themeState.svelte.d.ts} +0 -0
- /package/dist/utils/{theme.svelte.js → themeState.svelte.js} +0 -0
|
@@ -6,6 +6,7 @@ export * from './search/index.js';
|
|
|
6
6
|
export * from './select/index.js';
|
|
7
7
|
export * from './slider/index.js';
|
|
8
8
|
export * from './switch/index.js';
|
|
9
|
+
export * from './textarea/index.js';
|
|
9
10
|
export * from './textfield/index.js';
|
|
10
11
|
export * from './toggle-group/index.js';
|
|
11
12
|
export * from './tooltip/index.js';
|
|
@@ -8,6 +8,7 @@ export * from './search/index.js';
|
|
|
8
8
|
export * from './select/index.js';
|
|
9
9
|
export * from './slider/index.js';
|
|
10
10
|
export * from './switch/index.js';
|
|
11
|
+
export * from './textarea/index.js';
|
|
11
12
|
export * from './textfield/index.js';
|
|
12
13
|
export * from './toggle-group/index.js';
|
|
13
14
|
export * from './tooltip/index.js';
|
|
@@ -36,6 +36,9 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
|
|
|
36
36
|
class: className,
|
|
37
37
|
dir,
|
|
38
38
|
id,
|
|
39
|
+
'aria-label': ariaLabel,
|
|
40
|
+
'aria-labelledby': ariaLabelledby,
|
|
41
|
+
'aria-describedby': ariaDescribedby,
|
|
39
42
|
...extra
|
|
40
43
|
}: SliderProps = $props();
|
|
41
44
|
|
|
@@ -216,8 +219,13 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
|
|
|
216
219
|
|
|
217
220
|
<Slider.Thumb index={0}>
|
|
218
221
|
{#snippet child({ props: thumbProps, active })}
|
|
222
|
+
<!-- The thumb is the focusable role="slider" element, so it carries the accessible name. -->
|
|
219
223
|
<div
|
|
220
224
|
{...thumbProps}
|
|
225
|
+
aria-label={ariaLabel}
|
|
226
|
+
aria-labelledby={ariaLabelledby}
|
|
227
|
+
aria-describedby={ariaDescribedby}
|
|
228
|
+
aria-valuetext={format(value)}
|
|
221
229
|
class={cls.handle()}
|
|
222
230
|
data-active={active || undefined}
|
|
223
231
|
style="{start}: {center(pos)}; {extent}: {handlePx}px"
|
|
@@ -54,4 +54,17 @@ export type SliderProps = Omit<SliderVariants, 'active'> & Omit<HTMLAttributes<H
|
|
|
54
54
|
* Orientation of the slider.
|
|
55
55
|
*/
|
|
56
56
|
vertical?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Accessible name for the slider. Applied to the focusable thumb (`role="slider"`), not the
|
|
59
|
+
* root, so screen readers announce it. Required unless `aria-labelledby` is set.
|
|
60
|
+
*/
|
|
61
|
+
'aria-label'?: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Id of the element that labels the slider. Applied to the thumb, like `aria-label`.
|
|
64
|
+
*/
|
|
65
|
+
'aria-labelledby'?: string | null;
|
|
66
|
+
/**
|
|
67
|
+
* Id of the element that describes the slider. Applied to the thumb.
|
|
68
|
+
*/
|
|
69
|
+
'aria-describedby'?: string | null;
|
|
57
70
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as TextareaStories from './Textarea.stories.svelte';
|
|
3
|
+
|
|
4
|
+
<Meta of={TextareaStories} />
|
|
5
|
+
|
|
6
|
+
<Title />
|
|
7
|
+
|
|
8
|
+
<Subtitle>A text field for longer, multi-line input.</Subtitle>
|
|
9
|
+
|
|
10
|
+
[M3 text field guidelines](https://m3.material.io/components/text-fields/guidelines) ·
|
|
11
|
+
`import { Textarea } from '@noxlovette/material';`
|
|
12
|
+
|
|
13
|
+
<Canvas of={TextareaStories.Playground} />
|
|
14
|
+
|
|
15
|
+
<Controls of={TextareaStories.Playground} />
|
|
16
|
+
|
|
17
|
+
## Not an M3 component
|
|
18
|
+
|
|
19
|
+
M3 has no textarea. It has a _multi-line text field_, and that is what this is: the
|
|
20
|
+
[`Textfield`](?path=/docs/forms-textfield--docs) with a `<textarea>` inside. Everything a text
|
|
21
|
+
field does carries over (filled and outlined containers, the floating label and notch, icons,
|
|
22
|
+
supporting text, required, error, disabled). Only these change:
|
|
23
|
+
|
|
24
|
+
| Part | Textfield | Textarea |
|
|
25
|
+
| ------------- | ------------------------- | ------------------------------------------------------- |
|
|
26
|
+
| Height | Fixed 56dp | 56dp minimum, then grows with the content |
|
|
27
|
+
| Resting label | Centered vertically | On the first line of text |
|
|
28
|
+
| Icons | Centered vertically | Beside the first line of text |
|
|
29
|
+
| Filled input | Centered, 16dp top offset | 24dp above the first line, 8dp below the last |
|
|
30
|
+
| Counter | Only with supporting text | Whenever `characterLimit` is set; over the limit errors |
|
|
31
|
+
|
|
32
|
+
Use `Textfield` for anything that fits on one line (names, emails, search). Use `Textarea` for
|
|
33
|
+
free text: messages, descriptions, notes, feedback.
|
|
34
|
+
|
|
35
|
+
<Canvas of={TextareaStories.FilledAndOutlined} />
|
|
36
|
+
|
|
37
|
+
## Height
|
|
38
|
+
|
|
39
|
+
`rows` is the minimum height in lines (default 3). The field grows as the user types, up to
|
|
40
|
+
`maxRows`, and scrolls after that. Leave `maxRows` out to grow without limit, or set it equal to
|
|
41
|
+
`rows` for a fixed-height field. There's no resize handle: the field sizes itself.
|
|
42
|
+
|
|
43
|
+
Growing uses CSS `field-sizing: content`, with a script fallback for browsers without it.
|
|
44
|
+
|
|
45
|
+
<Canvas of={TextareaStories.Growing} />
|
|
46
|
+
|
|
47
|
+
## Character limit
|
|
48
|
+
|
|
49
|
+
`characterLimit` shows a `12 / 160` counter below the field. Going over the limit puts the field
|
|
50
|
+
in its error state but doesn't block typing, so pasted text isn't silently cut. Pass the native
|
|
51
|
+
`maxlength` too if the limit is hard.
|
|
52
|
+
|
|
53
|
+
<Canvas of={TextareaStories.CharacterLimit} />
|
|
54
|
+
|
|
55
|
+
## Accessibility
|
|
56
|
+
|
|
57
|
+
- **Label.** The floating label is a real `<label for>` tied to the textarea's id.
|
|
58
|
+
- **Descriptions.** The supporting text and the counter are linked with `aria-describedby`, added
|
|
59
|
+
to any `aria-describedby` you pass.
|
|
60
|
+
- **Errors.** `error`, or going over `characterLimit`, sets `aria-invalid`. Say what's wrong in
|
|
61
|
+
`supportingText`, not just with color.
|
|
62
|
+
- **Required.** `required` sets `required` and `aria-required`, and shows an asterisk that screen
|
|
63
|
+
readers skip.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import Textarea from './Textarea.svelte';
|
|
4
|
+
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: 'Forms/Textarea',
|
|
7
|
+
component: Textarea,
|
|
8
|
+
argTypes: {
|
|
9
|
+
label: { control: 'text' },
|
|
10
|
+
variant: { control: 'select', options: ['filled', 'outlined'] },
|
|
11
|
+
rows: { control: 'number' },
|
|
12
|
+
maxRows: { control: 'number' },
|
|
13
|
+
disabled: { control: 'boolean' },
|
|
14
|
+
error: { control: 'boolean' },
|
|
15
|
+
required: { control: 'boolean' },
|
|
16
|
+
characterLimit: { control: 'number' }
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
label: 'Message',
|
|
20
|
+
variant: 'outlined',
|
|
21
|
+
rows: 3,
|
|
22
|
+
disabled: false,
|
|
23
|
+
error: false,
|
|
24
|
+
required: false
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<Story name="Playground">
|
|
30
|
+
{#snippet template(args)}
|
|
31
|
+
<div class="p-spacing-300 max-w-md">
|
|
32
|
+
<Textarea {...args} />
|
|
33
|
+
</div>
|
|
34
|
+
{/snippet}
|
|
35
|
+
</Story>
|
|
36
|
+
|
|
37
|
+
<Story name="Filled and outlined" asChild>
|
|
38
|
+
<div class="gap-spacing-300 p-spacing-300 flex max-w-md flex-col">
|
|
39
|
+
<Textarea variant="filled" label="Filled" />
|
|
40
|
+
<Textarea variant="outlined" label="Outlined" />
|
|
41
|
+
<Textarea
|
|
42
|
+
variant="filled"
|
|
43
|
+
label="Filled with value"
|
|
44
|
+
value={'Line one\nLine two\nLine three\nLine four grows the field.'}
|
|
45
|
+
/>
|
|
46
|
+
<Textarea
|
|
47
|
+
variant="outlined"
|
|
48
|
+
label="Outlined with value"
|
|
49
|
+
value={'Line one\nLine two\nLine three\nLine four grows the field.'}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</Story>
|
|
53
|
+
|
|
54
|
+
<Story name="Growing" asChild>
|
|
55
|
+
<div class="gap-spacing-300 p-spacing-300 flex max-w-md flex-col">
|
|
56
|
+
<Textarea label="Grows without limit" rows={2} />
|
|
57
|
+
<Textarea label="Grows to 5 lines, then scrolls" rows={2} maxRows={5} />
|
|
58
|
+
<Textarea label="Fixed at 4 lines" rows={4} maxRows={4} />
|
|
59
|
+
</div>
|
|
60
|
+
</Story>
|
|
61
|
+
|
|
62
|
+
<Story name="Character Limit" asChild>
|
|
63
|
+
<div class="p-spacing-300 max-w-md">
|
|
64
|
+
<Textarea label="Bio" characterLimit={160} value="">
|
|
65
|
+
{#snippet supportingText()}
|
|
66
|
+
Tell people a little about yourself.
|
|
67
|
+
{/snippet}
|
|
68
|
+
</Textarea>
|
|
69
|
+
</div>
|
|
70
|
+
</Story>
|
|
71
|
+
|
|
72
|
+
<Story name="With Icons" asChild>
|
|
73
|
+
<div class="gap-spacing-300 p-spacing-300 flex max-w-md flex-col">
|
|
74
|
+
<Textarea
|
|
75
|
+
label="Notes"
|
|
76
|
+
leadingIconProps={{ name: 'edit_note' }}
|
|
77
|
+
trailingIconProps={{ name: 'close' }}
|
|
78
|
+
/>
|
|
79
|
+
<Textarea
|
|
80
|
+
variant="filled"
|
|
81
|
+
label="Notes"
|
|
82
|
+
leadingIconProps={{ name: 'edit_note' }}
|
|
83
|
+
trailingIconProps={{ name: 'close' }}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</Story>
|
|
87
|
+
|
|
88
|
+
<Story name="States" asChild>
|
|
89
|
+
<div class="gap-spacing-300 p-spacing-300 flex max-w-md flex-col">
|
|
90
|
+
<Textarea label="Required" required />
|
|
91
|
+
<Textarea label="Error" error value="Not quite right">
|
|
92
|
+
{#snippet supportingText()}
|
|
93
|
+
Describe the problem in at least 20 characters.
|
|
94
|
+
{/snippet}
|
|
95
|
+
</Textarea>
|
|
96
|
+
<Textarea label="Disabled" disabled value="Can't edit" />
|
|
97
|
+
<Textarea variant="filled" label="Disabled filled" disabled value="Can't edit" />
|
|
98
|
+
</div>
|
|
99
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Textarea from './Textarea.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const Textarea: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type Textarea = InstanceType<typeof Textarea>;
|
|
19
|
+
export default Textarea;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Multi-line text field.
|
|
4
|
+
|
|
5
|
+
M3 has no textarea spec, only a multi-line text field, so this is the `Textfield` with a
|
|
6
|
+
`<textarea>` inside: the same filled and outlined containers, floating label and supporting
|
|
7
|
+
text. It grows with its content from `rows` to `maxRows` lines, then scrolls.
|
|
8
|
+
|
|
9
|
+
@see https://m3.material.io/components/text-fields/guidelines
|
|
10
|
+
-->
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import clsx from 'clsx';
|
|
13
|
+
import type { Attachment } from 'svelte/attachments';
|
|
14
|
+
import { useId } from 'bits-ui';
|
|
15
|
+
import { Icon } from '../../../utils/index.js';
|
|
16
|
+
import { ButtonIcon } from '../../buttons/index.js';
|
|
17
|
+
import { textarea } from './theme.js';
|
|
18
|
+
import type { TextareaProps } from './types.js';
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
value = $bindable(),
|
|
22
|
+
supportingText,
|
|
23
|
+
leadingIconProps,
|
|
24
|
+
trailingIconProps,
|
|
25
|
+
placeholder = '',
|
|
26
|
+
label,
|
|
27
|
+
id = useId(),
|
|
28
|
+
class: className,
|
|
29
|
+
characterLimit,
|
|
30
|
+
disabled = false,
|
|
31
|
+
error = false,
|
|
32
|
+
required = false,
|
|
33
|
+
variant = 'outlined',
|
|
34
|
+
rows = 3,
|
|
35
|
+
maxRows,
|
|
36
|
+
trailingOnClick,
|
|
37
|
+
trailingIcon,
|
|
38
|
+
...restProps
|
|
39
|
+
}: TextareaProps = $props();
|
|
40
|
+
|
|
41
|
+
const length = $derived(value?.length ?? 0);
|
|
42
|
+
const overLimit = $derived(characterLimit != null && length > characterLimit);
|
|
43
|
+
const invalid = $derived(!!error || overLimit);
|
|
44
|
+
|
|
45
|
+
const cls = $derived(
|
|
46
|
+
textarea({ disabled, error: invalid, variant, leadingIcon: !!leadingIconProps })
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const supportingId = $derived(`${id}-supporting`);
|
|
50
|
+
const counterId = $derived(`${id}-counter`);
|
|
51
|
+
const describedBy = $derived(
|
|
52
|
+
clsx(
|
|
53
|
+
restProps['aria-describedby'],
|
|
54
|
+
supportingText && supportingId,
|
|
55
|
+
characterLimit != null && counterId
|
|
56
|
+
) || undefined
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const sizeStyle = $derived(
|
|
60
|
+
`min-height: ${rows}lh;` + (maxRows != null ? ` max-height: ${Math.max(rows, maxRows)}lh;` : '')
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/* `field-sizing: content` does the growing where it's supported; elsewhere, fit the height to
|
|
64
|
+
the content on every change, including a `value` set from outside. */
|
|
65
|
+
let textareaEl = $state<HTMLTextAreaElement>();
|
|
66
|
+
const fieldSizing = typeof CSS !== 'undefined' && CSS.supports('field-sizing', 'content');
|
|
67
|
+
|
|
68
|
+
const autoGrow: Attachment<HTMLTextAreaElement> = (node) => {
|
|
69
|
+
if (fieldSizing) return;
|
|
70
|
+
void value;
|
|
71
|
+
node.style.height = 'auto';
|
|
72
|
+
node.style.height = `${node.scrollHeight}px`;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/* The wrapper's padding sits outside the textarea; a press there still focuses it. */
|
|
76
|
+
function focusTextarea(event: PointerEvent) {
|
|
77
|
+
if (event.target !== event.currentTarget || disabled) return;
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
textareaEl?.focus();
|
|
80
|
+
}
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<div class={clsx('relative w-full', className)}>
|
|
84
|
+
<div class={cls.base()}>
|
|
85
|
+
{#if leadingIconProps}
|
|
86
|
+
<Icon class={cls.leadingIcon()} {...leadingIconProps} />
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<div class={cls.inputWrapper()} onpointerdown={focusTextarea} role="presentation">
|
|
90
|
+
<textarea
|
|
91
|
+
bind:this={textareaEl}
|
|
92
|
+
bind:value
|
|
93
|
+
{id}
|
|
94
|
+
class={cls.input()}
|
|
95
|
+
style={sizeStyle}
|
|
96
|
+
rows={1}
|
|
97
|
+
aria-invalid={invalid}
|
|
98
|
+
aria-required={required}
|
|
99
|
+
{required}
|
|
100
|
+
{disabled}
|
|
101
|
+
{placeholder}
|
|
102
|
+
{...restProps}
|
|
103
|
+
aria-describedby={describedBy}
|
|
104
|
+
{@attach autoGrow}></textarea>
|
|
105
|
+
|
|
106
|
+
<label class={cls.label()} for={id}>
|
|
107
|
+
{label}{#if required}<span class={cls.requiredAsterisk()} aria-hidden="true">*</span>{/if}
|
|
108
|
+
</label>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
{#if trailingIcon}
|
|
112
|
+
{@render trailingIcon()}
|
|
113
|
+
{:else if trailingIconProps}
|
|
114
|
+
<ButtonIcon
|
|
115
|
+
variant="standard"
|
|
116
|
+
type="button"
|
|
117
|
+
onclick={trailingOnClick}
|
|
118
|
+
class={cls.trailingIcon()}
|
|
119
|
+
iconProps={trailingIconProps}
|
|
120
|
+
></ButtonIcon>
|
|
121
|
+
{/if}
|
|
122
|
+
|
|
123
|
+
{#if variant === 'outlined'}
|
|
124
|
+
<fieldset class={cls.fieldset()} aria-hidden="true">
|
|
125
|
+
<legend class={cls.legend()}>
|
|
126
|
+
<span class={cls.legendLabel()}>{label}{required ? '*' : ''}</span>
|
|
127
|
+
</legend>
|
|
128
|
+
</fieldset>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{#if supportingText || characterLimit != null}
|
|
133
|
+
<div class={cls.supportingText()}>
|
|
134
|
+
{#if supportingText}
|
|
135
|
+
<p id={supportingId}>{@render supportingText()}</p>
|
|
136
|
+
{/if}
|
|
137
|
+
{#if characterLimit != null}
|
|
138
|
+
<p id={counterId} class={cls.counter()}>{length} / {characterLimit}</p>
|
|
139
|
+
{/if}
|
|
140
|
+
</div>
|
|
141
|
+
{/if}
|
|
142
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TextareaProps } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Multi-line text field.
|
|
4
|
+
*
|
|
5
|
+
* M3 has no textarea spec, only a multi-line text field, so this is the `Textfield` with a
|
|
6
|
+
* `<textarea>` inside: the same filled and outlined containers, floating label and supporting
|
|
7
|
+
* text. It grows with its content from `rows` to `maxRows` lines, then scrolls.
|
|
8
|
+
*
|
|
9
|
+
* @see https://m3.material.io/components/text-fields/guidelines
|
|
10
|
+
*/
|
|
11
|
+
declare const Textarea: import("svelte").Component<TextareaProps, {}, "value">;
|
|
12
|
+
type Textarea = ReturnType<typeof Textarea>;
|
|
13
|
+
export default Textarea;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// THIS FILE IS AUTO-GENERATED BY scripts/generate-components-index.ts - DO NOT EDIT MANUALLY
|
|
2
|
+
// re-export TS/JS modules from this folder
|
|
3
|
+
export * from './theme.js';
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
// default exports for components in this folder
|
|
6
|
+
export { default as Textarea } from './Textarea.svelte';
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { type VariantProps } from '../../../utils/tv.js';
|
|
2
|
+
export type TextareaVariants = VariantProps<typeof textarea>;
|
|
3
|
+
/**
|
|
4
|
+
* M3 defines a multi-line text field but no separate textarea spec. This keeps the text field's
|
|
5
|
+
* containers, label, notch and indicator, and changes only what a growing field has to:
|
|
6
|
+
* the container has a 56dp minimum instead of a fixed height, and the label and icons line up
|
|
7
|
+
* with the first line instead of the vertical center.
|
|
8
|
+
*/
|
|
9
|
+
export declare const textarea: import("tailwind-variants").TVReturnType<{
|
|
10
|
+
variant: {
|
|
11
|
+
filled: {
|
|
12
|
+
base: string;
|
|
13
|
+
inputWrapper: string;
|
|
14
|
+
leadingIcon: string;
|
|
15
|
+
trailingIcon: string;
|
|
16
|
+
label: string;
|
|
17
|
+
};
|
|
18
|
+
outlined: {
|
|
19
|
+
inputWrapper: string;
|
|
20
|
+
label: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
error: {
|
|
24
|
+
true: {
|
|
25
|
+
base: string;
|
|
26
|
+
label: string;
|
|
27
|
+
supportingText: string;
|
|
28
|
+
leadingIcon: string;
|
|
29
|
+
trailingIcon: string;
|
|
30
|
+
fieldset: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
leadingIcon: {
|
|
34
|
+
true: {};
|
|
35
|
+
};
|
|
36
|
+
disabled: {
|
|
37
|
+
true: {
|
|
38
|
+
base: string;
|
|
39
|
+
inputWrapper: string;
|
|
40
|
+
fieldset: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}, {
|
|
44
|
+
base: string;
|
|
45
|
+
inputWrapper: string;
|
|
46
|
+
input: string;
|
|
47
|
+
label: string;
|
|
48
|
+
requiredAsterisk: string;
|
|
49
|
+
leadingIcon: string;
|
|
50
|
+
trailingIcon: string;
|
|
51
|
+
supportingText: string;
|
|
52
|
+
counter: string;
|
|
53
|
+
fieldset: string;
|
|
54
|
+
legend: string;
|
|
55
|
+
legendLabel: string;
|
|
56
|
+
}, undefined, {
|
|
57
|
+
variant: {
|
|
58
|
+
filled: {
|
|
59
|
+
base: string;
|
|
60
|
+
inputWrapper: string;
|
|
61
|
+
leadingIcon: string;
|
|
62
|
+
trailingIcon: string;
|
|
63
|
+
label: string;
|
|
64
|
+
};
|
|
65
|
+
outlined: {
|
|
66
|
+
inputWrapper: string;
|
|
67
|
+
label: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
error: {
|
|
71
|
+
true: {
|
|
72
|
+
base: string;
|
|
73
|
+
label: string;
|
|
74
|
+
supportingText: string;
|
|
75
|
+
leadingIcon: string;
|
|
76
|
+
trailingIcon: string;
|
|
77
|
+
fieldset: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
leadingIcon: {
|
|
81
|
+
true: {};
|
|
82
|
+
};
|
|
83
|
+
disabled: {
|
|
84
|
+
true: {
|
|
85
|
+
base: string;
|
|
86
|
+
inputWrapper: string;
|
|
87
|
+
fieldset: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}, {
|
|
91
|
+
base: string;
|
|
92
|
+
inputWrapper: string;
|
|
93
|
+
input: string;
|
|
94
|
+
label: string;
|
|
95
|
+
requiredAsterisk: string;
|
|
96
|
+
leadingIcon: string;
|
|
97
|
+
trailingIcon: string;
|
|
98
|
+
supportingText: string;
|
|
99
|
+
counter: string;
|
|
100
|
+
fieldset: string;
|
|
101
|
+
legend: string;
|
|
102
|
+
legendLabel: string;
|
|
103
|
+
}, import("tailwind-variants").TVReturnTypeLike<{
|
|
104
|
+
variant: {
|
|
105
|
+
filled: {
|
|
106
|
+
base: string;
|
|
107
|
+
inputWrapper: string;
|
|
108
|
+
leadingIcon: string;
|
|
109
|
+
trailingIcon: string;
|
|
110
|
+
label: string;
|
|
111
|
+
};
|
|
112
|
+
outlined: {
|
|
113
|
+
inputWrapper: string;
|
|
114
|
+
label: string;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
error: {
|
|
118
|
+
true: {
|
|
119
|
+
base: string;
|
|
120
|
+
label: string;
|
|
121
|
+
supportingText: string;
|
|
122
|
+
leadingIcon: string;
|
|
123
|
+
trailingIcon: string;
|
|
124
|
+
fieldset: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
leadingIcon: {
|
|
128
|
+
true: {};
|
|
129
|
+
};
|
|
130
|
+
disabled: {
|
|
131
|
+
true: {
|
|
132
|
+
base: string;
|
|
133
|
+
inputWrapper: string;
|
|
134
|
+
fieldset: string;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
}, {
|
|
138
|
+
base: string;
|
|
139
|
+
inputWrapper: string;
|
|
140
|
+
input: string;
|
|
141
|
+
label: string;
|
|
142
|
+
requiredAsterisk: string;
|
|
143
|
+
leadingIcon: string;
|
|
144
|
+
trailingIcon: string;
|
|
145
|
+
supportingText: string;
|
|
146
|
+
counter: string;
|
|
147
|
+
fieldset: string;
|
|
148
|
+
legend: string;
|
|
149
|
+
legendLabel: string;
|
|
150
|
+
}>>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { tv } from '../../../utils/tv.js';
|
|
2
|
+
/**
|
|
3
|
+
* M3 defines a multi-line text field but no separate textarea spec. This keeps the text field's
|
|
4
|
+
* containers, label, notch and indicator, and changes only what a growing field has to:
|
|
5
|
+
* the container has a 56dp minimum instead of a fixed height, and the label and icons line up
|
|
6
|
+
* with the first line instead of the vertical center.
|
|
7
|
+
*/
|
|
8
|
+
export const textarea = tv({
|
|
9
|
+
slots: {
|
|
10
|
+
base: `
|
|
11
|
+
group relative w-full min-h-spacing-700 flex items-start
|
|
12
|
+
`,
|
|
13
|
+
/* The wrapper holds the vertical padding, not the textarea, so text scrolled past `maxRows`
|
|
14
|
+
is clipped at the textarea's edge instead of sliding under the floated label. */
|
|
15
|
+
inputWrapper: `
|
|
16
|
+
relative flex-1 self-stretch px-spacing-200 cursor-text
|
|
17
|
+
`,
|
|
18
|
+
input: `
|
|
19
|
+
peer block w-full bg-transparent outline-none resize-none field-sizing-content
|
|
20
|
+
md-sys-typescale-body-large text-md-sys-color-on-surface
|
|
21
|
+
disabled:text-md-sys-color-on-surface/38 disabled:cursor-not-allowed
|
|
22
|
+
`,
|
|
23
|
+
label: `
|
|
24
|
+
absolute left-spacing-200 top-spacing-200
|
|
25
|
+
md-sys-typescale-body-large
|
|
26
|
+
text-md-sys-color-on-surface-variant
|
|
27
|
+
pointer-events-none
|
|
28
|
+
transition-[top,left,font-size,line-height,color,transform] md-sys-motion-fast-spatial
|
|
29
|
+
|
|
30
|
+
peer-focus:text-md-sys-color-primary
|
|
31
|
+
`,
|
|
32
|
+
requiredAsterisk: `
|
|
33
|
+
ml-spacing-25
|
|
34
|
+
text-md-sys-color-error
|
|
35
|
+
group-focus-within:text-md-sys-color-primary
|
|
36
|
+
transition-colors md-sys-motion-effects
|
|
37
|
+
`,
|
|
38
|
+
/* On the first line of text: 16dp from the top when outlined, 24dp when filled. */
|
|
39
|
+
leadingIcon: `
|
|
40
|
+
text-md-sys-color-on-surface-variant size-(--text-icon-24)
|
|
41
|
+
group-focus-within:text-md-sys-color-primary
|
|
42
|
+
ml-spacing-150 mt-spacing-200 text-icon-24
|
|
43
|
+
`,
|
|
44
|
+
/* Sized to the 24dp glyph like Textfield's, so it takes the same offset as the leading icon. */
|
|
45
|
+
trailingIcon: `
|
|
46
|
+
mr-spacing-150 mt-spacing-200 text-md-sys-color-on-surface-variant size-(--text-icon-24)
|
|
47
|
+
group-focus-within:text-md-sys-color-primary
|
|
48
|
+
z-20 text-icon-24
|
|
49
|
+
`,
|
|
50
|
+
supportingText: `
|
|
51
|
+
px-spacing-200 pt-spacing-50 flex justify-between gap-spacing-200
|
|
52
|
+
md-sys-typescale-body-small
|
|
53
|
+
text-md-sys-color-on-surface-variant
|
|
54
|
+
`,
|
|
55
|
+
counter: `
|
|
56
|
+
ms-auto shrink-0
|
|
57
|
+
`,
|
|
58
|
+
fieldset: `
|
|
59
|
+
absolute -top-spacing-100 left-spacing-0 right-spacing-0 bottom-spacing-0 m-spacing-0 px-spacing-150
|
|
60
|
+
pointer-events-none rounded-xs border border-md-sys-color-outline
|
|
61
|
+
transition-colors md-sys-motion-fast-effects
|
|
62
|
+
|
|
63
|
+
group-hover:border-md-sys-color-on-surface
|
|
64
|
+
group-focus-within:border-2 group-focus-within:border-md-sys-color-primary
|
|
65
|
+
`,
|
|
66
|
+
legend: `
|
|
67
|
+
invisible float-none block h-auto max-w-[0.01px] overflow-hidden
|
|
68
|
+
whitespace-nowrap md-sys-typescale-body-small
|
|
69
|
+
transition-[max-width] md-sys-motion-fast-spatial
|
|
70
|
+
|
|
71
|
+
group-focus-within:max-w-full
|
|
72
|
+
group-has-[textarea:not(:placeholder-shown)]:max-w-full
|
|
73
|
+
`,
|
|
74
|
+
legendLabel: `
|
|
75
|
+
px-spacing-50
|
|
76
|
+
`
|
|
77
|
+
},
|
|
78
|
+
variants: {
|
|
79
|
+
variant: {
|
|
80
|
+
filled: {
|
|
81
|
+
base: `
|
|
82
|
+
px-spacing-0 bg-md-sys-color-surface-container-highest
|
|
83
|
+
rounded-t-xs state-layer before:rounded-xs hover:before:bg-md-sys-color-on-surface/8
|
|
84
|
+
after:absolute after:bottom-spacing-0 after:left-spacing-0 after:right-spacing-0 after:h-px after:bg-md-sys-color-on-surface-variant
|
|
85
|
+
after:transition-[height,background-color] after:md-sys-motion-fast-spatial
|
|
86
|
+
hover:after:bg-md-sys-color-on-surface
|
|
87
|
+
focus-within:after:bg-md-sys-color-primary
|
|
88
|
+
focus-within:after:h-[2px]
|
|
89
|
+
`,
|
|
90
|
+
/* 24dp above the first line leaves room for the label floated inside the container;
|
|
91
|
+
24 + 24 + 8 = the 56dp single-line field. The resting label and the icons sit on that
|
|
92
|
+
first line too, so the icons stay beside the text once the label floats to 8dp. */
|
|
93
|
+
inputWrapper: 'pt-spacing-300 pb-spacing-100',
|
|
94
|
+
leadingIcon: 'mt-spacing-300',
|
|
95
|
+
trailingIcon: 'mt-spacing-300',
|
|
96
|
+
label: `
|
|
97
|
+
top-spacing-300
|
|
98
|
+
peer-focus:top-spacing-100 peer-focus:md-sys-typescale-body-small
|
|
99
|
+
peer-not-placeholder-shown:top-spacing-100 peer-not-placeholder-shown:md-sys-typescale-body-small
|
|
100
|
+
`
|
|
101
|
+
},
|
|
102
|
+
outlined: {
|
|
103
|
+
inputWrapper: 'py-spacing-200',
|
|
104
|
+
label: `
|
|
105
|
+
peer-focus:top-spacing-0 peer-focus:md-sys-typescale-body-small peer-focus:-translate-y-1/2
|
|
106
|
+
peer-not-placeholder-shown:top-spacing-0 peer-not-placeholder-shown:md-sys-typescale-body-small peer-not-placeholder-shown:-translate-y-1/2
|
|
107
|
+
`
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
error: {
|
|
111
|
+
true: {
|
|
112
|
+
base: 'after:bg-md-sys-color-error focus-within:after:bg-md-sys-color-error',
|
|
113
|
+
label: 'text-md-sys-color-error peer-focus:text-md-sys-color-error',
|
|
114
|
+
supportingText: 'text-md-sys-color-error',
|
|
115
|
+
leadingIcon: 'text-md-sys-color-error',
|
|
116
|
+
trailingIcon: 'text-md-sys-color-error',
|
|
117
|
+
fieldset: `
|
|
118
|
+
border-md-sys-color-error
|
|
119
|
+
group-hover:border-md-sys-color-error
|
|
120
|
+
group-focus-within:border-md-sys-color-error
|
|
121
|
+
`
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
leadingIcon: {
|
|
125
|
+
true: {}
|
|
126
|
+
},
|
|
127
|
+
disabled: {
|
|
128
|
+
true: {
|
|
129
|
+
base: 'cursor-not-allowed opacity-60',
|
|
130
|
+
inputWrapper: 'cursor-not-allowed',
|
|
131
|
+
fieldset: 'border-md-sys-color-on-surface/12 group-hover:border-md-sys-color-on-surface/12'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
compoundVariants: [
|
|
136
|
+
{
|
|
137
|
+
/* Same as Textfield: the outlined label floats back over the leading icon into the notch. */
|
|
138
|
+
variant: 'outlined',
|
|
139
|
+
leadingIcon: true,
|
|
140
|
+
class: {
|
|
141
|
+
label: 'peer-focus:-left-spacing-250 peer-not-placeholder-shown:-left-spacing-250'
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
variant: 'filled',
|
|
146
|
+
disabled: true,
|
|
147
|
+
class: {
|
|
148
|
+
base: 'bg-md-sys-color-on-surface/4 after:bg-md-sys-color-on-surface/12'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
defaultVariants: {
|
|
153
|
+
variant: 'outlined'
|
|
154
|
+
}
|
|
155
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { IconProps } from '../../../utils/index.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
4
|
+
import type { TextareaVariants } from './theme.js';
|
|
5
|
+
/**
|
|
6
|
+
* Props for the Textarea component.
|
|
7
|
+
*/
|
|
8
|
+
export type TextareaProps = TextareaVariants & Omit<HTMLTextareaAttributes, 'children' | 'value' | 'rows'> & {
|
|
9
|
+
/**
|
|
10
|
+
* The current value of the textarea.
|
|
11
|
+
*/
|
|
12
|
+
value?: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* The label text for the textarea.
|
|
15
|
+
*/
|
|
16
|
+
label: string;
|
|
17
|
+
/**
|
|
18
|
+
* The ID for the textarea element. Auto-generated but can be replaced.
|
|
19
|
+
*/
|
|
20
|
+
id?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The placeholder text. Not used in M3: the label takes its place.
|
|
23
|
+
*/
|
|
24
|
+
placeholder?: '';
|
|
25
|
+
/**
|
|
26
|
+
* Minimum height, in lines. The field grows with its content from here. Defaults to 3.
|
|
27
|
+
*/
|
|
28
|
+
rows?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum height, in lines, before the content scrolls. Omit to grow without limit; set it
|
|
31
|
+
* to `rows` for a fixed-height field.
|
|
32
|
+
*/
|
|
33
|
+
maxRows?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Snippet for supporting text (helper or error text) displayed below the field.
|
|
36
|
+
*/
|
|
37
|
+
supportingText?: Snippet;
|
|
38
|
+
/**
|
|
39
|
+
* Props for the leading icon, aligned with the first line.
|
|
40
|
+
*/
|
|
41
|
+
leadingIconProps?: IconProps;
|
|
42
|
+
/**
|
|
43
|
+
* Props for the trailing icon button, aligned with the first line.
|
|
44
|
+
*/
|
|
45
|
+
trailingIconProps?: IconProps;
|
|
46
|
+
/**
|
|
47
|
+
* Shows a character counter below the field. Going over it shows the error state; it does
|
|
48
|
+
* not stop typing (pass `maxlength` for a hard limit).
|
|
49
|
+
*/
|
|
50
|
+
characterLimit?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Callback function when the trailing icon is clicked.
|
|
53
|
+
*/
|
|
54
|
+
trailingOnClick?: () => void;
|
|
55
|
+
/**
|
|
56
|
+
* Optional snippet for trailing icon override.
|
|
57
|
+
*/
|
|
58
|
+
trailingIcon?: Snippet;
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { RadioGroup } from '../forms/radio-group/index.js';
|
|
3
|
-
import { themeState, extractColorFromImage } from '../../utils/
|
|
3
|
+
import { themeState, extractColorFromImage } from '../../utils/themeState.svelte.js';
|
|
4
4
|
import { Title } from '../typography/title/index.js';
|
|
5
5
|
import { Body } from '../typography/body/index.js';
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Container-tier M3 color roles (see `styles/theme/*.css`) used as a generated avatar's
|
|
3
3
|
* background. Deliberately NOT a fixed hex list: every consuming app picks its own source
|
|
4
|
-
* color (`ThemeConfig.sourceColor` in `
|
|
4
|
+
* color (`ThemeConfig.sourceColor` in `themeState.svelte.ts`), which the M3 dynamic color
|
|
5
5
|
* algorithm expands into these tokens as real `--color-md-sys-color-*` custom properties on
|
|
6
6
|
* `:root` — reading them live is what makes the avatar match that app's actual palette
|
|
7
7
|
* instead of this library's own hardcoded pastel.
|
|
@@ -14,12 +14,14 @@ travels around the circle.
|
|
|
14
14
|
import { animate, type AnimationPlaybackControls } from 'motion';
|
|
15
15
|
import { springTokens, springTransition } from '../../animation/spring.js';
|
|
16
16
|
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
17
|
+
import type { ProgressA11yProps } from './types.js';
|
|
17
18
|
|
|
18
19
|
let {
|
|
19
20
|
percent,
|
|
20
21
|
size = 48,
|
|
21
|
-
thickness = 4
|
|
22
|
-
|
|
22
|
+
thickness = 4,
|
|
23
|
+
...a11y
|
|
24
|
+
}: ProgressA11yProps & {
|
|
23
25
|
/** The current progress percentage (0–100). Omit or pass null for indeterminate. */
|
|
24
26
|
percent?: number | null;
|
|
25
27
|
/** Diameter of the indicator in pixels. Defaults to 48. */
|
|
@@ -93,7 +95,7 @@ travels around the circle.
|
|
|
93
95
|
);
|
|
94
96
|
</script>
|
|
95
97
|
|
|
96
|
-
<Progress.Root value={percent ?? null} max={100} data-cy="m3-circular-progress">
|
|
98
|
+
<Progress.Root {...a11y} value={percent ?? null} max={100} data-cy="m3-circular-progress">
|
|
97
99
|
{#snippet child({ props })}
|
|
98
100
|
<svg
|
|
99
101
|
{...props}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
type
|
|
1
|
+
import type { ProgressA11yProps } from './types.js';
|
|
2
|
+
type $$ComponentProps = ProgressA11yProps & {
|
|
2
3
|
/** The current progress percentage (0–100). Omit or pass null for indeterminate. */
|
|
3
4
|
percent?: number | null;
|
|
4
5
|
/** Diameter of the indicator in pixels. Defaults to 48. */
|
|
@@ -16,12 +16,14 @@ The track always keeps a gap either side of the bar.
|
|
|
16
16
|
import { animate, type AnimationPlaybackControls } from 'motion';
|
|
17
17
|
import { springTokens, springTransition } from '../../animation/spring.js';
|
|
18
18
|
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
19
|
+
import type { ProgressA11yProps } from './types.js';
|
|
19
20
|
|
|
20
21
|
let {
|
|
21
22
|
percent,
|
|
22
23
|
height = 4,
|
|
23
|
-
class: className
|
|
24
|
-
|
|
24
|
+
class: className,
|
|
25
|
+
...a11y
|
|
26
|
+
}: ProgressA11yProps & {
|
|
25
27
|
/** The current progress percentage (0–100). Omit or pass null for indeterminate. */
|
|
26
28
|
percent?: number | null;
|
|
27
29
|
/** Height of the bar in pixels. Defaults to 4. */
|
|
@@ -84,6 +86,7 @@ The track always keeps a gap either side of the bar.
|
|
|
84
86
|
</script>
|
|
85
87
|
|
|
86
88
|
<Progress.Root
|
|
89
|
+
{...a11y}
|
|
87
90
|
value={percent ?? null}
|
|
88
91
|
max={100}
|
|
89
92
|
class={container({ class: clsx(className) })}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
type
|
|
1
|
+
import type { ProgressA11yProps } from './types.js';
|
|
2
|
+
type $$ComponentProps = ProgressA11yProps & {
|
|
2
3
|
/** The current progress percentage (0–100). Omit or pass null for indeterminate. */
|
|
3
4
|
percent?: number | null;
|
|
4
5
|
/** Height of the bar in pixels. Defaults to 4. */
|
|
@@ -13,14 +13,16 @@ effects spring (critically damped, so progress never visibly runs backwards).
|
|
|
13
13
|
import { springTokens } from '../../animation/spring.js';
|
|
14
14
|
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
15
15
|
import { linear, trackOpacity, WAVE_PERIOD_MS } from './_wavy.js';
|
|
16
|
+
import type { ProgressA11yProps } from './types.js';
|
|
16
17
|
|
|
17
18
|
let {
|
|
18
19
|
width = 600,
|
|
19
20
|
height = 10,
|
|
20
21
|
thickness = 4,
|
|
21
22
|
percent,
|
|
22
|
-
class: className
|
|
23
|
-
|
|
23
|
+
class: className,
|
|
24
|
+
...a11y
|
|
25
|
+
}: ProgressA11yProps & {
|
|
24
26
|
/** Width of the SVG viewBox. */
|
|
25
27
|
width?: number;
|
|
26
28
|
/** Height of the SVG viewBox. */
|
|
@@ -56,7 +58,7 @@ effects spring (critically damped, so progress never visibly runs backwards).
|
|
|
56
58
|
let wavePath = $derived(linear(height / 2 - thickness / 2, height / 2, left, percentX, time));
|
|
57
59
|
</script>
|
|
58
60
|
|
|
59
|
-
<Progress.Root value={percent ?? null} max={100}>
|
|
61
|
+
<Progress.Root {...a11y} value={percent ?? null} max={100}>
|
|
60
62
|
{#snippet child({ props })}
|
|
61
63
|
<svg {...props} viewBox="0 0 {width} {height}" class={className}>
|
|
62
64
|
<path
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './_wavy.js';
|
|
2
|
+
export * from './types.js';
|
|
2
3
|
export { default as CircularProgress } from './CircularProgress.svelte';
|
|
3
4
|
export { default as LinearProgress } from './LinearProgress.svelte';
|
|
4
5
|
export { default as WavyLinearProgress } from './WavyLinearProgress.svelte';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// THIS FILE IS AUTO-GENERATED BY scripts/generate-components-index.ts - DO NOT EDIT MANUALLY
|
|
2
2
|
// re-export TS/JS modules from this folder
|
|
3
3
|
export * from './_wavy.js';
|
|
4
|
+
export * from './types.js';
|
|
4
5
|
// default exports for components in this folder
|
|
5
6
|
export { default as CircularProgress } from './CircularProgress.svelte';
|
|
6
7
|
export { default as LinearProgress } from './LinearProgress.svelte';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessible-name attributes shared by every progress indicator, forwarded to the element with
|
|
3
|
+
* `role="progressbar"`. Give each indicator a name when more than one is on screen, or when the
|
|
4
|
+
* visible label is not already associated with it.
|
|
5
|
+
*/
|
|
6
|
+
export type ProgressA11yProps = {
|
|
7
|
+
id?: string;
|
|
8
|
+
/** Accessible name, e.g. `"Reading comprehension"`. */
|
|
9
|
+
'aria-label'?: string;
|
|
10
|
+
/** Id of the element whose text names the indicator. */
|
|
11
|
+
'aria-labelledby'?: string;
|
|
12
|
+
/** Id of the element that describes the indicator. */
|
|
13
|
+
'aria-describedby'?: string;
|
|
14
|
+
/** Human-readable value, e.g. `"3 of 5 lessons"`. Defaults to the percentage. */
|
|
15
|
+
'aria-valuetext'?: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils/Theme.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { MediaQuery } from 'svelte/reactivity';
|
|
3
|
-
import { generateThemeCSS, themeState, STORAGE_KEY } from './
|
|
3
|
+
import { generateThemeCSS, themeState, STORAGE_KEY } from './themeState.svelte.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Whether to generate and inject the M3 dynamic-color CSS custom properties
|
|
@@ -9,7 +9,7 @@ Add this to your root +layout.svelte (or any layout that wraps the whole app):
|
|
|
9
9
|
<App>...</App>
|
|
10
10
|
-->
|
|
11
11
|
<script lang="ts">
|
|
12
|
-
import { THEME_INIT_SCRIPT } from './
|
|
12
|
+
import { THEME_INIT_SCRIPT } from './themeState.svelte.js';
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<svelte:head>
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export * from './icon/index.js';
|
|
2
2
|
export * from './shortcut.js';
|
|
3
|
-
export * from './
|
|
3
|
+
export * from './themeState.svelte.js';
|
|
4
4
|
export * from './types.js';
|
|
5
5
|
export * from './ui.js';
|
|
6
|
-
export type { ColorScheme, ContrastMode, ThemeConfig, ThemeVariant } from './
|
|
6
|
+
export type { ColorScheme, ContrastMode, ThemeConfig, ThemeVariant } from './themeState.svelte.js';
|
|
7
7
|
export { default as Layer } from './Layer.svelte';
|
|
8
8
|
export { default as Theme } from './Theme.svelte';
|
|
9
9
|
export { default as ThemeScript } from './ThemeScript.svelte';
|
package/dist/utils/index.js
CHANGED
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|