@marwes-ui/svelte 1.3.0 → 1.4.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/CHANGELOG.md +30 -0
- package/README.md +43 -11
- package/dist/components/README.md +31 -0
- package/dist/components/accordion/AccordionField.svelte +20 -6
- package/dist/components/accordion/index.d.ts +5 -2
- package/dist/components/accordion/index.js +7 -1
- package/dist/components/accordion/types.d.ts +3 -0
- package/dist/components/avatar/AvatarGroup.svelte +5 -2
- package/dist/components/avatar/AvatarGroup.svelte.d.ts +1 -0
- package/dist/components/avatar/TeamAvatarGroup.svelte +1 -0
- package/dist/components/avatar/TeamAvatarGroup.svelte.d.ts +1 -0
- package/dist/components/avatar/index.d.ts +7 -1
- package/dist/components/avatar/index.js +6 -0
- package/dist/components/avatar/types.d.ts +28 -0
- package/dist/components/badge/BadgeGroup.svelte +2 -1
- package/dist/components/badge/index.d.ts +5 -1
- package/dist/components/badge/index.js +4 -0
- package/dist/components/badge/types.d.ts +3 -0
- package/dist/components/banner/Banner.svelte +67 -0
- package/dist/components/banner/Banner.svelte.d.ts +4 -0
- package/dist/components/banner/ErrorBanner.svelte +9 -0
- package/dist/components/banner/ErrorBanner.svelte.d.ts +4 -0
- package/dist/components/banner/InfoBanner.svelte +9 -0
- package/dist/components/banner/InfoBanner.svelte.d.ts +4 -0
- package/dist/components/banner/SuccessBanner.svelte +9 -0
- package/dist/components/banner/SuccessBanner.svelte.d.ts +4 -0
- package/dist/components/banner/WarningBanner.svelte +9 -0
- package/dist/components/banner/WarningBanner.svelte.d.ts +4 -0
- package/dist/components/banner/index.d.ts +7 -0
- package/dist/components/banner/index.js +6 -0
- package/dist/components/banner/types.d.ts +14 -0
- package/dist/components/banner/types.js +1 -0
- package/dist/components/breadcrumb/Breadcrumb.svelte +115 -0
- package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +4 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +1 -0
- package/dist/components/breadcrumb/types.d.ts +8 -0
- package/dist/components/breadcrumb/types.js +1 -0
- package/dist/components/button/Button.svelte +8 -4
- package/dist/components/button/DestructiveButton.svelte +1 -1
- package/dist/components/button/IconButton.svelte +9 -0
- package/dist/components/button/IconButton.svelte.d.ts +4 -0
- package/dist/components/button/index.d.ts +24 -1
- package/dist/components/button/index.js +23 -0
- package/dist/components/button/types.d.ts +30 -1
- package/dist/components/card/Card.svelte +2 -1
- package/dist/components/card/index.d.ts +4 -1
- package/dist/components/card/index.js +3 -0
- package/dist/components/card/types.d.ts +4 -0
- package/dist/components/checkbox/CheckboxField.svelte +4 -3
- package/dist/components/checkbox/CheckboxGroupField.svelte +21 -9
- package/dist/components/checkbox/index.d.ts +2 -2
- package/dist/components/checkbox/index.js +4 -1
- package/dist/components/checkbox/types.d.ts +7 -0
- package/dist/components/context-menu/ContextMenu.svelte +56 -0
- package/dist/components/context-menu/ContextMenu.svelte.d.ts +4 -0
- package/dist/components/context-menu/index.d.ts +2 -0
- package/dist/components/context-menu/index.js +1 -0
- package/dist/components/context-menu/types.d.ts +7 -0
- package/dist/components/context-menu/types.js +1 -0
- package/dist/components/date-picker/DatePicker.svelte +14 -2
- package/dist/components/date-picker/DatePickerField.svelte +72 -0
- package/dist/components/date-picker/DatePickerField.svelte.d.ts +4 -0
- package/dist/components/date-picker/index.d.ts +2 -2
- package/dist/components/date-picker/index.js +3 -1
- package/dist/components/date-picker/types.d.ts +15 -0
- package/dist/components/dialog/Dialog.svelte +1 -0
- package/dist/components/dialog/DialogModal.svelte +12 -0
- package/dist/components/dialog/index.d.ts +1 -1
- package/dist/components/dialog/types.d.ts +7 -3
- package/dist/components/drawer/Drawer.svelte +132 -0
- package/dist/components/drawer/Drawer.svelte.d.ts +4 -0
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/drawer/index.js +1 -0
- package/dist/components/drawer/types.d.ts +16 -0
- package/dist/components/drawer/types.js +1 -0
- package/dist/components/heading/H1.svelte +10 -0
- package/dist/components/heading/H2.svelte +6 -0
- package/dist/components/heading/H3.svelte +7 -0
- package/dist/components/input/CurrencyField.svelte +1 -1
- package/dist/components/input/Input.svelte +1 -0
- package/dist/components/input/InputField.svelte +4 -3
- package/dist/components/input/InputOtp.svelte +21 -44
- package/dist/components/input/InputOtpField.svelte +81 -0
- package/dist/components/input/InputOtpField.svelte.d.ts +4 -0
- package/dist/components/input/RichText.svelte +7 -6
- package/dist/components/input/RichTextField.svelte +4 -3
- package/dist/components/input/Select.svelte +7 -31
- package/dist/components/input/SelectField.svelte +6 -3
- package/dist/components/input/Textarea.svelte +1 -0
- package/dist/components/input/TextareaField.svelte +20 -5
- package/dist/components/input/index.d.ts +12 -4
- package/dist/components/input/index.js +19 -3
- package/dist/components/input/types.d.ts +59 -5
- package/dist/components/pagination/Pagination.svelte +399 -0
- package/dist/components/pagination/Pagination.svelte.d.ts +4 -0
- package/dist/components/pagination/PaginationField.svelte +72 -0
- package/dist/components/pagination/PaginationField.svelte.d.ts +4 -0
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/index.js +2 -0
- package/dist/components/pagination/types.d.ts +40 -0
- package/dist/components/pagination/types.js +1 -0
- package/dist/components/progress-bar/ProgressBar.svelte +50 -0
- package/dist/components/progress-bar/ProgressBar.svelte.d.ts +4 -0
- package/dist/components/progress-bar/index.d.ts +2 -0
- package/dist/components/progress-bar/index.js +1 -0
- package/dist/components/progress-bar/types.d.ts +6 -0
- package/dist/components/progress-bar/types.js +1 -0
- package/dist/components/radio/OptionRadioGroup.svelte +39 -5
- package/dist/components/radio/OptionRadioGroup.svelte.d.ts +2 -5
- package/dist/components/radio/Radio.svelte +1 -0
- package/dist/components/radio/RadioGroupField.svelte +4 -3
- package/dist/components/radio/RatingRadioGroup.svelte +18 -3
- package/dist/components/radio/YesNoRadioGroup.svelte +18 -3
- package/dist/components/radio/index.d.ts +4 -2
- package/dist/components/radio/index.js +6 -1
- package/dist/components/radio/types.d.ts +47 -0
- package/dist/components/segmented-control/SegmentedControl.svelte +125 -0
- package/dist/components/segmented-control/SegmentedControl.svelte.d.ts +25 -0
- package/dist/components/segmented-control/SegmentedControlField.svelte +76 -0
- package/dist/components/segmented-control/SegmentedControlField.svelte.d.ts +25 -0
- package/dist/components/segmented-control/index.d.ts +2 -0
- package/dist/components/segmented-control/index.js +2 -0
- package/dist/components/segmented-control/types.d.ts +41 -0
- package/dist/components/segmented-control/types.js +1 -0
- package/dist/components/skip-link/SkipLink.svelte +12 -0
- package/dist/components/skip-link/SkipLink.svelte.d.ts +4 -0
- package/dist/components/skip-link/index.d.ts +2 -0
- package/dist/components/skip-link/index.js +1 -0
- package/dist/components/skip-link/types.d.ts +7 -0
- package/dist/components/skip-link/types.js +1 -0
- package/dist/components/slider/SliderField.svelte +9 -8
- package/dist/components/slider/index.d.ts +5 -2
- package/dist/components/slider/index.js +7 -1
- package/dist/components/slider/types.d.ts +21 -0
- package/dist/components/spinner/EmptyStateSpinner.svelte +2 -3
- package/dist/components/spinner/EmptyStateSpinner.svelte.d.ts +1 -2
- package/dist/components/spinner/Spinner.svelte +2 -0
- package/dist/components/spinner/index.d.ts +2 -1
- package/dist/components/spinner/index.js +1 -0
- package/dist/components/spinner/types.d.ts +1 -0
- package/dist/components/switch/Switch.svelte +1 -1
- package/dist/components/switch/SwitchField.svelte +6 -4
- package/dist/components/switch/index.d.ts +4 -2
- package/dist/components/switch/index.js +6 -1
- package/dist/components/switch/types.d.ts +3 -0
- package/dist/components/tab/TabGroup.svelte +7 -4
- package/dist/components/text/Text.svelte +23 -0
- package/dist/components/text/Text.svelte.d.ts +4 -0
- package/dist/components/text/index.d.ts +4 -0
- package/dist/components/text/index.js +2 -0
- package/dist/components/text/types.d.ts +15 -0
- package/dist/components/text/types.js +1 -0
- package/dist/components/tooltip/TooltipGroup.svelte +10 -2
- package/dist/index.d.ts +47 -29
- package/dist/index.js +51 -13
- package/dist/provider/MarwesProvider.svelte +26 -14
- package/dist/provider/types.d.ts +5 -2
- package/package.json +6 -5
- package/dist/components/accordion/README.md +0 -19
- package/dist/components/avatar/README.md +0 -19
- package/dist/components/badge/README.md +0 -16
- package/dist/components/button/README.md +0 -21
- package/dist/components/card/README.md +0 -15
- package/dist/components/checkbox/README.md +0 -17
- package/dist/components/date-picker/README.md +0 -17
- package/dist/components/dialog/README.md +0 -23
- package/dist/components/divider/README.md +0 -15
- package/dist/components/heading/README.md +0 -13
- package/dist/components/icon/README.md +0 -18
- package/dist/components/input/README.md +0 -24
- package/dist/components/paragraph/README.md +0 -13
- package/dist/components/radio/README.md +0 -16
- package/dist/components/skeleton/README.md +0 -16
- package/dist/components/slider/README.md +0 -20
- package/dist/components/spacing/README.md +0 -15
- package/dist/components/spinner/README.md +0 -19
- package/dist/components/stat-tile/README.md +0 -15
- package/dist/components/switch/README.md +0 -17
- package/dist/components/tab/README.md +0 -26
- package/dist/components/toast/README.md +0 -35
- package/dist/components/tooltip/README.md +0 -22
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
import type { RadioGroupFieldOption } from "./types.js";
|
|
1
2
|
interface OptionRadioGroupProps {
|
|
2
3
|
name: string;
|
|
3
4
|
label: string;
|
|
4
5
|
description?: string;
|
|
5
6
|
error?: string;
|
|
6
7
|
ariaDescribedBy?: string;
|
|
7
|
-
options:
|
|
8
|
-
value: string;
|
|
9
|
-
label: string;
|
|
10
|
-
disabled?: boolean;
|
|
11
|
-
}>;
|
|
8
|
+
options: RadioGroupFieldOption[];
|
|
12
9
|
value?: string;
|
|
13
10
|
defaultValue?: string;
|
|
14
11
|
onchange?: (value: string) => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { buildRadioGroupFieldA11yIds } from "@marwes-ui/core";
|
|
3
3
|
import { mergeClass } from "../../internal/merge-class.js";
|
|
4
|
+
import Text from "../text/Text.svelte";
|
|
4
5
|
import type { RadioGroupFieldProps } from "./types.js";
|
|
5
6
|
|
|
6
7
|
let {
|
|
@@ -47,12 +48,12 @@
|
|
|
47
48
|
|
|
48
49
|
<div class={wrapperClass} {...dataAttributes}>
|
|
49
50
|
<div class="mw-radio-group-field__label" id={a11yIds.labelId}>
|
|
50
|
-
<
|
|
51
|
+
<Text variant="label">{label}</Text>
|
|
51
52
|
</div>
|
|
52
53
|
|
|
53
54
|
{#if hasDescription}
|
|
54
55
|
<div class="mw-radio-group-field__description" id={a11yIds.descriptionId}>
|
|
55
|
-
<
|
|
56
|
+
<Text variant="caption">{description}</Text>
|
|
56
57
|
</div>
|
|
57
58
|
{/if}
|
|
58
59
|
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
|
|
70
71
|
{#if hasError}
|
|
71
72
|
<div class="mw-radio-group-field__error" id={a11yIds.errorId} aria-live="polite">
|
|
72
|
-
<
|
|
73
|
+
<Text variant="caption">{error}</Text>
|
|
73
74
|
</div>
|
|
74
75
|
{/if}
|
|
75
76
|
</div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import Text from "../text/Text.svelte";
|
|
2
3
|
import Radio from "./Radio.svelte";
|
|
3
4
|
import RadioGroupField from "./RadioGroupField.svelte";
|
|
4
5
|
|
|
@@ -19,7 +20,13 @@
|
|
|
19
20
|
|
|
20
21
|
let { name, min = 1, max = 5, labelFn = String, value, defaultValue, onchange, disabled, ...fieldProps }: RatingRadioGroupProps = $props();
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
function getInitialSelected(): string {
|
|
24
|
+
return defaultValue ?? "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const isControlled = $derived(value !== undefined);
|
|
28
|
+
let internalSelected = $state(getInitialSelected());
|
|
29
|
+
const selected = $derived(isControlled ? (value ?? "") : internalSelected);
|
|
23
30
|
|
|
24
31
|
const options = $derived(
|
|
25
32
|
Array.from({ length: max - min + 1 }, (_, i) => {
|
|
@@ -29,22 +36,30 @@
|
|
|
29
36
|
);
|
|
30
37
|
|
|
31
38
|
function handleChange(optValue: string) {
|
|
32
|
-
|
|
39
|
+
if (!isControlled) {
|
|
40
|
+
internalSelected = optValue;
|
|
41
|
+
}
|
|
33
42
|
onchange?.(optValue);
|
|
34
43
|
}
|
|
35
44
|
</script>
|
|
36
45
|
<div data-purpose="rating">
|
|
37
46
|
<RadioGroupField {...fieldProps}>
|
|
38
47
|
{#each options as opt (opt.value)}
|
|
48
|
+
{@const optionLabelId = `${name}-${opt.value}-label`}
|
|
39
49
|
<label class="mw-radio-group-field__option">
|
|
40
50
|
<Radio
|
|
41
51
|
{name}
|
|
42
52
|
value={opt.value}
|
|
53
|
+
ariaLabelledBy={optionLabelId}
|
|
43
54
|
checked={selected === opt.value}
|
|
44
55
|
disabled={disabled || false}
|
|
45
56
|
onchange={() => handleChange(opt.value)}
|
|
46
57
|
/>
|
|
47
|
-
<span class="mw-
|
|
58
|
+
<span class="mw-radio-group-field__option-content">
|
|
59
|
+
<Text id={optionLabelId} variant="label" class="mw-radio-group-field__option-label">
|
|
60
|
+
{opt.label}
|
|
61
|
+
</Text>
|
|
62
|
+
</span>
|
|
48
63
|
</label>
|
|
49
64
|
{/each}
|
|
50
65
|
</RadioGroupField>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import Text from "../text/Text.svelte";
|
|
2
3
|
import Radio from "./Radio.svelte";
|
|
3
4
|
import RadioGroupField from "./RadioGroupField.svelte";
|
|
4
5
|
|
|
@@ -18,7 +19,13 @@
|
|
|
18
19
|
|
|
19
20
|
let { name, yesLabel = "Yes", noLabel = "No", value, defaultValue, onchange, disabled, ...fieldProps }: YesNoRadioGroupProps = $props();
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
function getInitialSelected(): string {
|
|
23
|
+
return defaultValue ?? "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isControlled = $derived(value !== undefined);
|
|
27
|
+
let internalSelected = $state(getInitialSelected());
|
|
28
|
+
const selected = $derived(isControlled ? (value ?? "") : internalSelected);
|
|
22
29
|
|
|
23
30
|
const options = $derived([
|
|
24
31
|
{ value: "yes", label: yesLabel },
|
|
@@ -26,22 +33,30 @@
|
|
|
26
33
|
]);
|
|
27
34
|
|
|
28
35
|
function handleChange(optValue: string) {
|
|
29
|
-
|
|
36
|
+
if (!isControlled) {
|
|
37
|
+
internalSelected = optValue;
|
|
38
|
+
}
|
|
30
39
|
onchange?.(optValue);
|
|
31
40
|
}
|
|
32
41
|
</script>
|
|
33
42
|
<div data-purpose="binary-choice">
|
|
34
43
|
<RadioGroupField {...fieldProps}>
|
|
35
44
|
{#each options as opt (opt.value)}
|
|
45
|
+
{@const optionLabelId = `${name}-${opt.value}-label`}
|
|
36
46
|
<label class="mw-radio-group-field__option">
|
|
37
47
|
<Radio
|
|
38
48
|
{name}
|
|
39
49
|
value={opt.value}
|
|
50
|
+
ariaLabelledBy={optionLabelId}
|
|
40
51
|
checked={selected === opt.value}
|
|
41
52
|
disabled={disabled || false}
|
|
42
53
|
onchange={() => handleChange(opt.value)}
|
|
43
54
|
/>
|
|
44
|
-
<span class="mw-
|
|
55
|
+
<span class="mw-radio-group-field__option-content">
|
|
56
|
+
<Text id={optionLabelId} variant="label" class="mw-radio-group-field__option-label">
|
|
57
|
+
{opt.label}
|
|
58
|
+
</Text>
|
|
59
|
+
</span>
|
|
45
60
|
</label>
|
|
46
61
|
{/each}
|
|
47
62
|
</RadioGroupField>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { default as Radio } from "./Radio.svelte";
|
|
2
1
|
export { default as RadioGroupField } from "./RadioGroupField.svelte";
|
|
3
|
-
export
|
|
2
|
+
export { default as YesNoRadioGroup } from "./YesNoRadioGroup.svelte";
|
|
3
|
+
export { default as RatingRadioGroup } from "./RatingRadioGroup.svelte";
|
|
4
|
+
export { default as OptionRadioGroup } from "./OptionRadioGroup.svelte";
|
|
5
|
+
export type { RadioGroupFieldOption, RadioGroupFieldProps, YesNoRadioGroupProps, RatingRadioGroupProps, OptionRadioGroupProps, } from "./types.js";
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// `Radio` (the atom) is intentionally NOT re-exported here.
|
|
2
|
+
// Use `RadioGroupField` (or a purpose variant) instead. Internal
|
|
3
|
+
// consumers can deep-import "./Radio.svelte" directly.
|
|
2
4
|
export { default as RadioGroupField } from "./RadioGroupField.svelte";
|
|
5
|
+
export { default as YesNoRadioGroup } from "./YesNoRadioGroup.svelte";
|
|
6
|
+
export { default as RatingRadioGroup } from "./RatingRadioGroup.svelte";
|
|
7
|
+
export { default as OptionRadioGroup } from "./OptionRadioGroup.svelte";
|
|
@@ -8,6 +8,12 @@ export interface RadioProps extends RadioOptions {
|
|
|
8
8
|
oncheckedchange?: (checked: boolean) => void;
|
|
9
9
|
class?: string;
|
|
10
10
|
}
|
|
11
|
+
export interface RadioGroupFieldOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
11
17
|
export interface RadioGroupFieldProps {
|
|
12
18
|
id?: string;
|
|
13
19
|
label: string;
|
|
@@ -20,3 +26,44 @@ export interface RadioGroupFieldProps {
|
|
|
20
26
|
children?: Snippet;
|
|
21
27
|
class?: string;
|
|
22
28
|
}
|
|
29
|
+
export interface YesNoRadioGroupProps {
|
|
30
|
+
name: string;
|
|
31
|
+
label: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
yesLabel?: string;
|
|
35
|
+
noLabel?: string;
|
|
36
|
+
value?: string;
|
|
37
|
+
defaultValue?: string;
|
|
38
|
+
onchange?: (value: string) => void;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
class?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface RatingRadioGroupProps {
|
|
43
|
+
name: string;
|
|
44
|
+
label: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
min?: number;
|
|
48
|
+
max?: number;
|
|
49
|
+
labelFn?: (value: number) => string;
|
|
50
|
+
value?: string;
|
|
51
|
+
defaultValue?: string;
|
|
52
|
+
onchange?: (value: string) => void;
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
class?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface OptionRadioGroupProps {
|
|
57
|
+
name: string;
|
|
58
|
+
label: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
error?: string;
|
|
61
|
+
ariaDescribedBy?: string;
|
|
62
|
+
options: RadioGroupFieldOption[];
|
|
63
|
+
value?: string;
|
|
64
|
+
defaultValue?: string;
|
|
65
|
+
onchange?: (value: string) => void;
|
|
66
|
+
disabled?: boolean;
|
|
67
|
+
required?: boolean;
|
|
68
|
+
class?: string;
|
|
69
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends string = string">
|
|
2
|
+
import {
|
|
3
|
+
createSegmentedControlRecipe,
|
|
4
|
+
createSegmentedControlItemRecipe,
|
|
5
|
+
moveSegmentedControlSelection,
|
|
6
|
+
resolveSegmentedControlValue,
|
|
7
|
+
} from "@marwes-ui/core";
|
|
8
|
+
import type { SegmentedControlItemState } from "@marwes-ui/core";
|
|
9
|
+
import { mergeClass } from "../../internal/merge-class.js";
|
|
10
|
+
import type { SegmentedControlProps } from "./types.js";
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
items,
|
|
14
|
+
value: controlledValue,
|
|
15
|
+
defaultValue,
|
|
16
|
+
onvaluechange,
|
|
17
|
+
variant,
|
|
18
|
+
size,
|
|
19
|
+
disabled,
|
|
20
|
+
ariaLabel,
|
|
21
|
+
ariaLabelledBy,
|
|
22
|
+
ariaDescribedBy,
|
|
23
|
+
fullWidth,
|
|
24
|
+
label,
|
|
25
|
+
class: className,
|
|
26
|
+
id,
|
|
27
|
+
style,
|
|
28
|
+
}: SegmentedControlProps<T> = $props();
|
|
29
|
+
|
|
30
|
+
const itemStates: SegmentedControlItemState[] = $derived(
|
|
31
|
+
items.map((item) => ({ value: item.value, ...(item.disabled ? { disabled: true } : {}) }))
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
let internalValue = $state<string | undefined>(undefined);
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (internalValue === undefined) {
|
|
38
|
+
internalValue = resolveSegmentedControlValue(itemStates, defaultValue);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const resolvedValue = $derived(
|
|
43
|
+
controlledValue !== undefined
|
|
44
|
+
? resolveSegmentedControlValue(itemStates, controlledValue)
|
|
45
|
+
: internalValue
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
function selectItem(nextValue: string): void {
|
|
49
|
+
const resolved = resolveSegmentedControlValue(itemStates, nextValue);
|
|
50
|
+
if (!resolved || resolved === resolvedValue) return;
|
|
51
|
+
|
|
52
|
+
if (controlledValue === undefined) {
|
|
53
|
+
internalValue = resolved;
|
|
54
|
+
}
|
|
55
|
+
// `resolved` always traces back to one of `items[i].value: T`, so this cast is safe.
|
|
56
|
+
onvaluechange?.(resolved as T);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleKeydown(e: KeyboardEvent): void {
|
|
60
|
+
let direction: "next" | "previous" | "start" | "end" | undefined;
|
|
61
|
+
if (e.key === "ArrowRight") direction = "next";
|
|
62
|
+
else if (e.key === "ArrowLeft") direction = "previous";
|
|
63
|
+
else if (e.key === "Home") direction = "start";
|
|
64
|
+
else if (e.key === "End") direction = "end";
|
|
65
|
+
|
|
66
|
+
if (!direction) return;
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
|
|
69
|
+
const next = moveSegmentedControlSelection(itemStates, resolvedValue, direction);
|
|
70
|
+
if (next) {
|
|
71
|
+
selectItem(next);
|
|
72
|
+
const container = e.currentTarget as HTMLElement;
|
|
73
|
+
const btn = container.querySelector<HTMLButtonElement>(`[data-value="${next}"]`);
|
|
74
|
+
btn?.focus();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const trackKit = $derived(
|
|
79
|
+
createSegmentedControlRecipe({ value: resolvedValue, variant, size, disabled, ariaLabel, ariaLabelledBy, ariaDescribedBy, fullWidth, label })
|
|
80
|
+
);
|
|
81
|
+
const mergedClass = $derived(mergeClass(trackKit.className, className));
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<div
|
|
85
|
+
{id}
|
|
86
|
+
class={mergedClass}
|
|
87
|
+
role={trackKit.a11y.role}
|
|
88
|
+
aria-label={trackKit.a11y.ariaLabel}
|
|
89
|
+
aria-labelledby={trackKit.a11y.ariaLabelledBy}
|
|
90
|
+
aria-describedby={trackKit.a11y.ariaDescribedBy}
|
|
91
|
+
aria-disabled={trackKit.a11y.ariaDisabled}
|
|
92
|
+
onkeydown={handleKeydown}
|
|
93
|
+
style={style}
|
|
94
|
+
>
|
|
95
|
+
{#each items as item}
|
|
96
|
+
{@const isSelected = item.value === resolvedValue}
|
|
97
|
+
{@const isItemDisabled = disabled || item.disabled}
|
|
98
|
+
{@const itemKit = createSegmentedControlItemRecipe({
|
|
99
|
+
value: item.value,
|
|
100
|
+
selected: isSelected,
|
|
101
|
+
disabled: isItemDisabled,
|
|
102
|
+
ariaLabel: item.ariaLabel,
|
|
103
|
+
iconOnly: item.icon != null && item.label == null,
|
|
104
|
+
})}
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
class={itemKit.className}
|
|
108
|
+
role={itemKit.a11y.role}
|
|
109
|
+
aria-checked={itemKit.a11y.ariaChecked}
|
|
110
|
+
aria-disabled={itemKit.a11y.ariaDisabled}
|
|
111
|
+
aria-label={itemKit.a11y.ariaLabel}
|
|
112
|
+
tabindex={itemKit.a11y.tabIndex}
|
|
113
|
+
disabled={isItemDisabled}
|
|
114
|
+
data-value={item.value}
|
|
115
|
+
onclick={() => !isItemDisabled && selectItem(item.value)}
|
|
116
|
+
>
|
|
117
|
+
{#if item.icon}
|
|
118
|
+
{@render item.icon()}
|
|
119
|
+
{/if}
|
|
120
|
+
{#if item.label}
|
|
121
|
+
{item.label}
|
|
122
|
+
{/if}
|
|
123
|
+
</button>
|
|
124
|
+
{/each}
|
|
125
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SegmentedControlProps } from "./types.js";
|
|
2
|
+
declare function $$render<T extends string = string>(): {
|
|
3
|
+
props: SegmentedControlProps<T>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<T extends string = string> {
|
|
10
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
13
|
+
bindings(): "";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <T extends string = string>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
20
|
+
<T extends string = string>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
declare const SegmentedControl: $$IsomorphicComponent;
|
|
24
|
+
type SegmentedControl<T extends string = string> = InstanceType<typeof SegmentedControl<T>>;
|
|
25
|
+
export default SegmentedControl;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends string = string">
|
|
2
|
+
import { buildInputFieldA11yIds } from "@marwes-ui/core";
|
|
3
|
+
import { mergeClass } from "../../internal/merge-class.js";
|
|
4
|
+
import Text from "../text/Text.svelte";
|
|
5
|
+
import SegmentedControl from "./SegmentedControl.svelte";
|
|
6
|
+
import type { SegmentedControlFieldProps, SegmentedControlProps } from "./types.js";
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
id: userProvidedId,
|
|
10
|
+
label,
|
|
11
|
+
description,
|
|
12
|
+
error,
|
|
13
|
+
segmentedControl,
|
|
14
|
+
ariaDescribedBy,
|
|
15
|
+
class: className,
|
|
16
|
+
}: SegmentedControlFieldProps<T> = $props();
|
|
17
|
+
|
|
18
|
+
const uniqueId = $props.id();
|
|
19
|
+
const fieldId = $derived(userProvidedId ?? `mw-segmented-control-${uniqueId}`);
|
|
20
|
+
const labelId = $derived(`${fieldId}-label`);
|
|
21
|
+
|
|
22
|
+
function hasTextContent(text: string | undefined): boolean {
|
|
23
|
+
return text !== undefined && text.trim().length > 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hasDescription = $derived(hasTextContent(description));
|
|
27
|
+
const hasError = $derived(hasTextContent(error));
|
|
28
|
+
|
|
29
|
+
const a11yIds = $derived(
|
|
30
|
+
buildInputFieldA11yIds({
|
|
31
|
+
id: fieldId,
|
|
32
|
+
hasHelperText: hasDescription,
|
|
33
|
+
hasError,
|
|
34
|
+
externalDescribedBy: ariaDescribedBy,
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const wrapperClass = $derived(
|
|
39
|
+
mergeClass(
|
|
40
|
+
"mw-segmented-control-field",
|
|
41
|
+
hasError && "mw-segmented-control-field--invalid",
|
|
42
|
+
className
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const scProps = $derived.by((): SegmentedControlProps<T> => {
|
|
47
|
+
const sc: SegmentedControlProps<T> = {
|
|
48
|
+
...segmentedControl,
|
|
49
|
+
ariaLabelledBy: labelId,
|
|
50
|
+
} as SegmentedControlProps<T>;
|
|
51
|
+
if (a11yIds.describedBy) {
|
|
52
|
+
sc.ariaDescribedBy = a11yIds.describedBy;
|
|
53
|
+
}
|
|
54
|
+
return sc;
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div class={wrapperClass}>
|
|
59
|
+
<span class="mw-segmented-control-field__label" id={labelId}>
|
|
60
|
+
<Text variant="label">{label}</Text>
|
|
61
|
+
</span>
|
|
62
|
+
|
|
63
|
+
{#if hasDescription && !hasError}
|
|
64
|
+
<div class="mw-segmented-control-field__description" id={a11yIds.helperTextId}>
|
|
65
|
+
<Text variant="caption">{description}</Text>
|
|
66
|
+
</div>
|
|
67
|
+
{/if}
|
|
68
|
+
|
|
69
|
+
<SegmentedControl {...scProps} />
|
|
70
|
+
|
|
71
|
+
{#if hasError}
|
|
72
|
+
<div class="mw-segmented-control-field__error" id={a11yIds.errorId} aria-live="polite">
|
|
73
|
+
<Text variant="caption">{error}</Text>
|
|
74
|
+
</div>
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SegmentedControlFieldProps } from "./types.js";
|
|
2
|
+
declare function $$render<T extends string = string>(): {
|
|
3
|
+
props: SegmentedControlFieldProps<T>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<T extends string = string> {
|
|
10
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
13
|
+
bindings(): "";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <T extends string = string>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
20
|
+
<T extends string = string>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
declare const SegmentedControlField: $$IsomorphicComponent;
|
|
24
|
+
type SegmentedControlField<T extends string = string> = InstanceType<typeof SegmentedControlField<T>>;
|
|
25
|
+
export default SegmentedControlField;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SegmentedControlSize, SegmentedControlVariant } from "@marwes-ui/core";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
export interface SegmentedControlItem<T extends string = string> {
|
|
4
|
+
value: T;
|
|
5
|
+
label?: string;
|
|
6
|
+
icon?: Snippet;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
ariaLabel?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SegmentedControlProps<T extends string = string> {
|
|
11
|
+
items: SegmentedControlItem<T>[];
|
|
12
|
+
value?: T;
|
|
13
|
+
defaultValue?: T;
|
|
14
|
+
onvaluechange?: (value: T) => void;
|
|
15
|
+
variant?: SegmentedControlVariant;
|
|
16
|
+
size?: SegmentedControlSize;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
ariaLabel?: string;
|
|
19
|
+
ariaLabelledBy?: string;
|
|
20
|
+
ariaDescribedBy?: string;
|
|
21
|
+
fullWidth?: boolean;
|
|
22
|
+
label?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
id?: string;
|
|
25
|
+
style?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface SegmentedControlFieldProps<T extends string = string> {
|
|
28
|
+
/** Optional: if omitted, a stable id is generated. */
|
|
29
|
+
id?: string;
|
|
30
|
+
/** Field label (required for accessibility). */
|
|
31
|
+
label: string;
|
|
32
|
+
/** Optional description text. */
|
|
33
|
+
description?: string;
|
|
34
|
+
/** Optional error message. */
|
|
35
|
+
error?: string;
|
|
36
|
+
/** Props forwarded to the SegmentedControl atom. */
|
|
37
|
+
segmentedControl: Omit<SegmentedControlProps<T>, "ariaLabel" | "ariaLabelledBy" | "ariaDescribedBy">;
|
|
38
|
+
/** Additional aria-describedby IDs to merge with internal description/error IDs. */
|
|
39
|
+
ariaDescribedBy?: string;
|
|
40
|
+
class?: string;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createSkipLinkRecipe } from "@marwes-ui/core";
|
|
3
|
+
import type { SkipLinkProps } from "./types.js";
|
|
4
|
+
|
|
5
|
+
let { href, children, class: className }: SkipLinkProps = $props();
|
|
6
|
+
|
|
7
|
+
const kit = $derived(createSkipLinkRecipe({ href, className }));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<a {href} class={kit.className}>
|
|
11
|
+
{@render children?.()}
|
|
12
|
+
</a>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SkipLink } from "./SkipLink.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { buildSliderFieldA11yIds } from "@marwes-ui/core";
|
|
3
3
|
import { mergeClass } from "../../internal/merge-class.js";
|
|
4
|
+
import Text from "../text/Text.svelte";
|
|
4
5
|
import Slider from "./Slider.svelte";
|
|
5
6
|
import type { SliderFieldProps, SliderProps } from "./types.js";
|
|
6
7
|
|
|
@@ -54,23 +55,23 @@
|
|
|
54
55
|
<div class={wrapperClass} data-label-position={labelPosition} {...dataAttributes}>
|
|
55
56
|
<div class="mw-slider-field__header">
|
|
56
57
|
<div class="mw-slider-field__label" id={a11yIds.labelId}>
|
|
57
|
-
<
|
|
58
|
+
<Text variant="label">{label}</Text>
|
|
58
59
|
</div>
|
|
59
60
|
</div>
|
|
60
61
|
|
|
61
62
|
{#if hasDescription && !hasError}
|
|
62
63
|
<div class="mw-slider-field__description" id={a11yIds.descriptionId}>
|
|
63
|
-
<
|
|
64
|
+
<Text variant="caption">{descriptionText}</Text>
|
|
64
65
|
</div>
|
|
65
66
|
{/if}
|
|
66
67
|
|
|
67
68
|
{#if labelPosition === "top" && showEdgeValues}
|
|
68
69
|
<div class="mw-slider-field__values-row">
|
|
69
70
|
<span class="mw-slider-field__edge-value mw-slider-field__edge-value--min">
|
|
70
|
-
<
|
|
71
|
+
<Text variant="caption">{minEdgeValue}</Text>
|
|
71
72
|
</span>
|
|
72
73
|
<span class="mw-slider-field__edge-value mw-slider-field__edge-value--max">
|
|
73
|
-
<
|
|
74
|
+
<Text variant="caption">{maxEdgeValue}</Text>
|
|
74
75
|
</span>
|
|
75
76
|
</div>
|
|
76
77
|
{/if}
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
<div class="mw-slider-field__control-row">
|
|
79
80
|
{#if labelPosition === "inline" && showEdgeValues}
|
|
80
81
|
<span class="mw-slider-field__edge-value mw-slider-field__edge-value--min">
|
|
81
|
-
<
|
|
82
|
+
<Text variant="caption">{minEdgeValue}</Text>
|
|
82
83
|
</span>
|
|
83
84
|
{/if}
|
|
84
85
|
|
|
@@ -96,20 +97,20 @@
|
|
|
96
97
|
|
|
97
98
|
{#if labelPosition === "inline" && showEdgeValues}
|
|
98
99
|
<span class="mw-slider-field__edge-value mw-slider-field__edge-value--max">
|
|
99
|
-
<
|
|
100
|
+
<Text variant="caption">{maxEdgeValue}</Text>
|
|
100
101
|
</span>
|
|
101
102
|
{/if}
|
|
102
103
|
</div>
|
|
103
104
|
|
|
104
105
|
{#if hasDescription && hasError}
|
|
105
106
|
<div class="mw-slider-field__description" id={a11yIds.descriptionId}>
|
|
106
|
-
<
|
|
107
|
+
<Text variant="caption">{descriptionText}</Text>
|
|
107
108
|
</div>
|
|
108
109
|
{/if}
|
|
109
110
|
|
|
110
111
|
{#if hasError}
|
|
111
112
|
<div class="mw-slider-field__error" id={a11yIds.errorId} aria-live="polite">
|
|
112
|
-
<
|
|
113
|
+
<Text variant="caption">{error}</Text>
|
|
113
114
|
</div>
|
|
114
115
|
{/if}
|
|
115
116
|
</div>
|