srcdev-nuxt-components 9.6.7 → 9.6.9
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/.claude/rules/consumer/ids-from-useid.md +14 -0
- package/.claude/skills/component-compliance-checklist.md +18 -1
- package/.claude/skills/component-inline-action-button.md +1 -1
- package/.claude/skills/component-local-style-override.md +1 -1
- package/.claude/skills/component-naming.md +13 -13
- package/.claude/skills/components/container-glow.md +2 -2
- package/.claude/skills/components/form-field.md +2 -2
- package/.claude/skills/components/form-wrapper.md +1 -1
- package/.claude/skills/components/glowing-border.md +1 -1
- package/.claude/skills/components/input-checkbox-radio.md +19 -3
- package/.claude/skills/components/input-checkbox.md +14 -3
- package/.claude/skills/components/input-description.md +7 -7
- package/.claude/skills/components/input-error.md +3 -3
- package/.claude/skills/components/input-label.md +7 -8
- package/.claude/skills/components/input-number.md +60 -29
- package/.claude/skills/components/input-radio.md +15 -1
- package/.claude/skills/components/{input-range-core.md → input-range.md} +53 -18
- package/.claude/skills/components/input-select.md +153 -0
- package/.claude/skills/components/input-text.md +220 -0
- package/.claude/skills/components/input-textarea.md +147 -0
- package/.claude/skills/components/select-menu.md +4 -4
- package/.claude/skills/components/toggle-switch.md +143 -0
- package/.claude/skills/composable-zod-validation.md +1 -1
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/icon-sizing.md +1 -1
- package/.claude/skills/index.md +10 -10
- package/.claude/skills/storybook-add-story.md +1 -1
- package/.claude/skills/testing-add-unit-test.md +17 -0
- package/.claude/skills/theming-component-token-pattern.md +8 -8
- package/.claude/skills/theming-form-geometry-tokens.md +1 -1
- package/.vscode/srcdev-component-input-checkbox-radio.code-snippets +0 -1
- package/.vscode/srcdev-component-input-range.code-snippets +38 -18
- package/.vscode/srcdev-component-input-select.code-snippets +20 -14
- package/.vscode/srcdev-component-input-text.code-snippets +46 -40
- package/.vscode/srcdev-component-input-textarea.code-snippets +19 -14
- package/.vscode/srcdev-component-toggle-switch.code-snippets +19 -29
- package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +2 -2
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +2 -2
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +4 -4
- package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +4 -4
- package/app/components/05.forms/form-errors/CONSUMER-STYLING.md +1 -1
- package/app/components/05.forms/form-field/CONSUMER-STYLING.md +2 -2
- package/app/components/05.forms/input-button/stories/InputButton.stories.ts +3 -3
- package/app/components/05.forms/input-checkbox/CONSUMER-STYLING.md +10 -0
- package/app/components/05.forms/input-checkbox/MultipleCheckboxes.vue +18 -4
- package/app/components/05.forms/input-checkbox/SingleCheckbox.vue +14 -2
- package/app/components/05.forms/input-checkbox/stories/MultipleCheckboxes.stories.ts +146 -37
- package/app/components/05.forms/input-checkbox/stories/SingleCheckbox.stories.ts +107 -30
- package/app/components/05.forms/input-checkbox/tests/MultipleCheckboxes.spec.ts +38 -4
- package/app/components/05.forms/input-checkbox/tests/SingleCheckbox.spec.ts +5 -0
- package/app/components/05.forms/input-checkbox-radio/CONSUMER-STYLING.md +10 -3
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadio.vue +1 -0
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +11 -10
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioField.vue +3 -0
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadio.stories.ts +44 -17
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioButton.stories.ts +164 -58
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioField.stories.ts +94 -21
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadio.spec.ts +14 -3
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioButton.spec.ts +38 -3
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioField.spec.ts +14 -3
- package/app/components/05.forms/input-description/CONSUMER-STYLING.md +7 -7
- package/app/components/05.forms/input-label/CONSUMER-STYLING.md +6 -6
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +50 -6
- package/app/components/05.forms/input-number/InputNumber.vue +39 -27
- package/app/components/05.forms/input-number/InputNumberField.vue +29 -16
- package/app/components/05.forms/input-number/stories/InputNumber.stories.ts +76 -25
- package/app/components/05.forms/input-number/stories/InputNumberField.stories.ts +116 -50
- package/app/components/05.forms/input-number/tests/InputNumber.spec.ts +49 -13
- package/app/components/05.forms/input-number/tests/InputNumberField.spec.ts +59 -5
- package/app/components/05.forms/input-radio/CONSUMER-STYLING.md +11 -0
- package/app/components/05.forms/input-radio/MultipleRadiobuttons.vue +14 -3
- package/app/components/05.forms/input-radio/stories/MultipleRadiobuttons.stories.ts +138 -38
- package/app/components/05.forms/input-radio/tests/MultipleRadioButtons.spec.ts +50 -3
- package/app/components/05.forms/input-range/CONSUMER-STYLING.md +68 -26
- package/app/components/05.forms/input-range/InputRange.vue +247 -0
- package/app/components/05.forms/input-range/{InputRangeDefault.vue → InputRangeField.vue} +41 -18
- package/app/components/05.forms/input-range/stories/InputRange.stories.ts +187 -0
- package/app/components/05.forms/input-range/stories/InputRangeField.stories.ts +196 -0
- package/app/components/05.forms/input-range/tests/{InputRangeCore.spec.ts → InputRange.spec.ts} +39 -15
- package/app/components/05.forms/input-range/tests/{InputRangeDefault.spec.ts → InputRangeField.spec.ts} +71 -7
- package/app/components/05.forms/input-select/CONSUMER-STYLING.md +80 -10
- package/app/components/05.forms/input-select/InputSelect.vue +198 -0
- package/app/components/05.forms/input-select/InputSelectField.vue +130 -0
- package/app/components/05.forms/input-select/stories/InputSelect.stories.ts +184 -0
- package/app/components/05.forms/input-select/stories/InputSelectField.stories.ts +187 -0
- package/app/components/05.forms/input-select/tests/{InputSelectCore.spec.ts → InputSelect.spec.ts} +75 -12
- package/app/components/05.forms/input-select/tests/InputSelectField.spec.ts +121 -0
- package/app/components/05.forms/input-text/CONSUMER-STYLING.md +85 -15
- package/app/components/05.forms/input-text/{InputPasswordWithLabel.vue → InputPasswordField.vue} +16 -19
- package/app/components/05.forms/input-text/{InputTextCore.vue → InputText.vue} +42 -78
- package/app/components/05.forms/input-text/{InputTextAsNumberWithLabel.vue → InputTextAsNumberField.vue} +66 -62
- package/app/components/05.forms/input-text/InputTextField.vue +168 -0
- package/app/components/05.forms/input-text/stories/InputPasswordField.stories.ts +163 -0
- package/app/components/05.forms/input-text/stories/InputText.stories.ts +194 -0
- package/app/components/05.forms/input-text/stories/InputTextAsNumberField.stories.ts +182 -0
- package/app/components/05.forms/input-text/stories/InputTextField.stories.ts +206 -0
- package/app/components/05.forms/input-text/tests/{InputPasswordWithLabel.spec.ts → InputPasswordField.spec.ts} +42 -3
- package/app/components/05.forms/input-text/tests/InputText.spec.ts +166 -0
- package/app/components/05.forms/input-text/tests/InputTextAsNumberField.spec.ts +135 -0
- package/app/components/05.forms/input-text/tests/InputTextField.spec.ts +130 -0
- package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +94 -12
- package/app/components/05.forms/input-textarea/InputTextarea.vue +165 -0
- package/app/components/05.forms/input-textarea/InputTextareaField.vue +159 -0
- package/app/components/05.forms/input-textarea/stories/InputTextarea.stories.ts +198 -0
- package/app/components/05.forms/input-textarea/stories/InputTextareaField.stories.ts +161 -0
- package/app/components/05.forms/input-textarea/tests/InputTextarea.spec.ts +149 -0
- package/app/components/05.forms/input-textarea/tests/InputTextareaField.spec.ts +122 -0
- package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +13 -13
- package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +6 -6
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +34 -36
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +71 -50
- package/app/components/05.forms/toggle-switch/{ToggleSwitchCore.vue → ToggleSwitch.vue} +58 -73
- package/app/components/05.forms/toggle-switch/{ToggleSwitchWithLabel.vue → ToggleSwitchField.vue} +65 -19
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitch.stories.ts +148 -0
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchField.stories.ts +176 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitch.spec.ts +145 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchField.spec.ts +155 -0
- package/package.json +1 -1
- package/.claude/skills/components/input-select-core.md +0 -151
- package/.claude/skills/components/input-text-core.md +0 -204
- package/.claude/skills/components/input-textarea-core.md +0 -154
- package/.claude/skills/components/toggle-switch-core.md +0 -169
- package/app/components/05.forms/input-range/InputRangeCore.vue +0 -169
- package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +0 -126
- package/app/components/05.forms/input-range/stories/InputRangeDefault.stories.ts +0 -108
- package/app/components/05.forms/input-select/InputSelectCore.vue +0 -188
- package/app/components/05.forms/input-select/InputSelectWithLabel.vue +0 -118
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +0 -69
- package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +0 -115
- package/app/components/05.forms/input-select/tests/InputSelectWithLabel.spec.ts +0 -87
- package/app/components/05.forms/input-text/InputTextWithLabel.vue +0 -154
- package/app/components/05.forms/input-text/stories/InputPasswordWithLabel.stories.ts +0 -368
- package/app/components/05.forms/input-text/stories/InputTextAsNumberWithLabel.stories.ts +0 -480
- package/app/components/05.forms/input-text/stories/InputTextCore.stories.ts +0 -267
- package/app/components/05.forms/input-text/stories/InputTextWithLabel.stories.ts +0 -319
- package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +0 -75
- package/app/components/05.forms/input-text/tests/InputTextCore.spec.ts +0 -60
- package/app/components/05.forms/input-text/tests/InputTextWithLabel.spec.ts +0 -71
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +0 -159
- package/app/components/05.forms/input-textarea/InputTextareaWithLabel.vue +0 -136
- package/app/components/05.forms/input-textarea/stories/InputTextareaCore.stories.ts +0 -247
- package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +0 -458
- package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +0 -119
- package/app/components/05.forms/input-textarea/tests/InputTextareaWithLabel.spec.ts +0 -93
- package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabelInline.vue +0 -81
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +0 -283
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +0 -101
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabel.spec.ts +0 -133
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabelInline.spec.ts +0 -66
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
1
|
import { computed, ref } from "vue";
|
|
3
|
-
import
|
|
2
|
+
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
|
+
import MultipleRadiobuttons from "../MultipleRadiobuttons.vue";
|
|
4
|
+
import CanvasSwitcher from "../../../01.atoms/canvas-switcher/CanvasSwitcher.vue";
|
|
4
5
|
import type { IFormMultipleOptions, InputUiVariant, OptionsLayout } from "~/types/forms/types.forms.d";
|
|
6
|
+
import type { MediaCanvas } from "~/types/components";
|
|
5
7
|
|
|
6
|
-
interface
|
|
8
|
+
interface StoryArgs {
|
|
7
9
|
name: string;
|
|
8
10
|
legend: string;
|
|
9
11
|
errorMessage: string;
|
|
@@ -16,17 +18,25 @@ interface MultipleRadiobuttonsStoryArgs {
|
|
|
16
18
|
inputVariant: InputUiVariant;
|
|
17
19
|
gap: string;
|
|
18
20
|
descriptionText: string;
|
|
21
|
+
options: IFormMultipleOptions["data"];
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
const enquiryOptions: IFormMultipleOptions["data"] = [
|
|
25
|
+
{ id: "1", name: "enquiryType", value: "booking", label: "Booking" },
|
|
26
|
+
{ id: "2", name: "enquiryType", value: "consultation", label: "Consultation" },
|
|
27
|
+
{ id: "3", name: "enquiryType", value: "product", label: "Product advice" },
|
|
28
|
+
{ id: "4", name: "enquiryType", value: "other", label: "Something else" },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const meta: Meta<StoryArgs> = {
|
|
22
32
|
title: "Components/Forms/Input Radio/MultipleRadiobuttons",
|
|
23
|
-
component:
|
|
33
|
+
component: MultipleRadiobuttons,
|
|
24
34
|
argTypes: {
|
|
25
35
|
name: { control: "text", description: "Shared name of every radio in the group", table: { category: "Basic" } },
|
|
26
36
|
legend: { control: "text", table: { category: "Basic" } },
|
|
27
37
|
errorMessage: { control: "text", table: { category: "Basic" } },
|
|
28
38
|
required: { control: "boolean", table: { category: "Basic" } },
|
|
29
|
-
descriptionText: { control: "text", description: "descriptionText slot", table: { category: "Slots" } },
|
|
39
|
+
descriptionText: { control: "text", description: "Story-only: fills the descriptionText slot", table: { category: "Slots" } },
|
|
30
40
|
fieldHasError: { control: "boolean", table: { category: "States" } },
|
|
31
41
|
isButton: { control: "boolean", table: { category: "Styling" } },
|
|
32
42
|
isPill: { control: "boolean", description: "Only with isButton", table: { category: "Styling" } },
|
|
@@ -49,8 +59,14 @@ export default {
|
|
|
49
59
|
},
|
|
50
60
|
gap: {
|
|
51
61
|
control: "text",
|
|
52
|
-
description:
|
|
53
|
-
|
|
62
|
+
description:
|
|
63
|
+
"**Story control, not a prop.** Sets `--multiple-radiobuttons-gap` on the story wrapper. In your CSS: `.enquiry-form { --multiple-radiobuttons-gap: 0.6rem; }`",
|
|
64
|
+
table: { category: "CSS tokens (story only, set in your CSS)" },
|
|
65
|
+
},
|
|
66
|
+
options: {
|
|
67
|
+
control: "object",
|
|
68
|
+
description: "Story-only: the `data` array of the `fieldData` model",
|
|
69
|
+
table: { category: "Model" },
|
|
54
70
|
},
|
|
55
71
|
},
|
|
56
72
|
args: {
|
|
@@ -66,51 +82,135 @@ export default {
|
|
|
66
82
|
inputVariant: "normal",
|
|
67
83
|
gap: "",
|
|
68
84
|
descriptionText: "Pick the closest match.",
|
|
85
|
+
options: enquiryOptions,
|
|
86
|
+
},
|
|
87
|
+
decorators: [
|
|
88
|
+
(story, context) => ({
|
|
89
|
+
components: { story, CanvasSwitcher },
|
|
90
|
+
setup() {
|
|
91
|
+
const canvasName = ref<MediaCanvas>(context.parameters.initialCanvas ?? "fullWidthCanvas");
|
|
92
|
+
return { canvasName };
|
|
93
|
+
},
|
|
94
|
+
template: `
|
|
95
|
+
<div style="padding: 1.2rem 1.6rem; border-block-end: 1px solid currentColor;">
|
|
96
|
+
<CanvasSwitcher v-model:canvas-name="canvasName" />
|
|
97
|
+
</div>
|
|
98
|
+
<div :class="canvasName" style="margin-inline: auto; padding: 2rem; outline: 1px dashed currentColor;">
|
|
99
|
+
<story />
|
|
100
|
+
</div>
|
|
101
|
+
`,
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
parameters: {
|
|
105
|
+
layout: "fullscreen",
|
|
106
|
+
docs: {
|
|
107
|
+
description: {
|
|
108
|
+
component:
|
|
109
|
+
"A `radiogroup` fieldset of radios built from the `fieldData` model, bound to a single-value `v-model`. `isButton` swaps the labelled radios for button tiles (`isPill` rounds them), `optionsLayout` lays them out as equal-width columns, an inline wrap or a block column, and `errorMessage` with `fieldHasError` shows the error strip. `required` puts native `required` on every radio, which one checked radio satisfies. Slots: `descriptionText`, `descriptionHtml`, `checkedIcon`, `itemIcon`.",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
69
112
|
},
|
|
70
|
-
}
|
|
113
|
+
};
|
|
71
114
|
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
export default meta;
|
|
116
|
+
type Story = StoryObj<StoryArgs>;
|
|
117
|
+
|
|
118
|
+
const render = (args: StoryArgs) => ({
|
|
119
|
+
components: { MultipleRadiobuttons },
|
|
74
120
|
setup() {
|
|
75
|
-
const modelValue = ref<string>("");
|
|
76
|
-
const fieldData = ref<IFormMultipleOptions>({
|
|
77
|
-
data: [
|
|
78
|
-
{ id: "1", name: "enquiryType", value: "booking", label: "Booking" },
|
|
79
|
-
{ id: "2", name: "enquiryType", value: "consultation", label: "Consultation" },
|
|
80
|
-
{ id: "3", name: "enquiryType", value: "product", label: "Product advice" },
|
|
81
|
-
{ id: "4", name: "enquiryType", value: "other", label: "Something else" },
|
|
82
|
-
],
|
|
83
|
-
total: 4,
|
|
84
|
-
skip: 0,
|
|
85
|
-
limit: 10,
|
|
86
|
-
});
|
|
87
121
|
const componentArgs = computed(() => {
|
|
88
|
-
const { gap: _gap, descriptionText:
|
|
122
|
+
const { gap: _gap, descriptionText: _descriptionText, options: _options, ...rest } = args;
|
|
89
123
|
return rest;
|
|
90
124
|
});
|
|
125
|
+
const fieldData = computed<IFormMultipleOptions>(() => ({
|
|
126
|
+
data: args.options,
|
|
127
|
+
total: args.options.length,
|
|
128
|
+
skip: 0,
|
|
129
|
+
limit: args.options.length,
|
|
130
|
+
}));
|
|
91
131
|
const tokenStyles = computed(() => (args.gap ? { "--multiple-radiobuttons-gap": args.gap } : {}));
|
|
92
|
-
|
|
132
|
+
const modelValue = ref<string>("");
|
|
133
|
+
return { args, componentArgs, fieldData, tokenStyles, modelValue };
|
|
93
134
|
},
|
|
94
135
|
template: `
|
|
95
|
-
<div
|
|
96
|
-
<
|
|
136
|
+
<div :style="tokenStyles">
|
|
137
|
+
<MultipleRadiobuttons v-bind="componentArgs" v-model="modelValue" :field-data="fieldData">
|
|
97
138
|
<template v-if="args.descriptionText" #descriptionText>{{ args.descriptionText }}</template>
|
|
98
|
-
</
|
|
99
|
-
<p style="margin-
|
|
139
|
+
</MultipleRadiobuttons>
|
|
140
|
+
<p style="margin-block-start: 2rem; font-family: monospace; overflow-wrap: anywhere;">modelValue: {{ JSON.stringify(modelValue) }}</p>
|
|
100
141
|
</div>
|
|
101
142
|
`,
|
|
102
143
|
});
|
|
103
144
|
|
|
104
|
-
export const Default =
|
|
145
|
+
export const Default: Story = { render };
|
|
105
146
|
|
|
106
|
-
export const Buttons =
|
|
107
|
-
|
|
147
|
+
export const Buttons: Story = {
|
|
148
|
+
args: { isButton: true, optionsLayout: "inline" },
|
|
149
|
+
render,
|
|
150
|
+
parameters: { docs: { description: { story: "Button tiles in an inline wrap, each with the `itemIcon` decorator." } } },
|
|
151
|
+
};
|
|
108
152
|
|
|
109
|
-
export const Pills =
|
|
110
|
-
|
|
153
|
+
export const Pills: Story = {
|
|
154
|
+
args: { isButton: true, isPill: true, optionsLayout: "inline" },
|
|
155
|
+
render,
|
|
156
|
+
parameters: { docs: { description: { story: "Button tiles with `isPill` for fully rounded ends." } } },
|
|
157
|
+
};
|
|
111
158
|
|
|
112
|
-
export const Block =
|
|
113
|
-
|
|
159
|
+
export const Block: Story = {
|
|
160
|
+
args: { optionsLayout: "block" },
|
|
161
|
+
render,
|
|
162
|
+
parameters: { docs: { description: { story: "`optionsLayout=\"block\"` stacks the options in one column." } } },
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export const ErrorState: Story = {
|
|
166
|
+
args: { fieldHasError: true },
|
|
167
|
+
render,
|
|
168
|
+
parameters: {
|
|
169
|
+
docs: { description: { story: "`fieldHasError` shows the error message and links it to every radio with `aria-describedby`." } },
|
|
170
|
+
},
|
|
171
|
+
};
|
|
114
172
|
|
|
115
|
-
export const
|
|
116
|
-
|
|
173
|
+
export const StressTest: Story = {
|
|
174
|
+
name: "Stress Test (Worst-Case Data)",
|
|
175
|
+
args: {
|
|
176
|
+
name: "anfrage art",
|
|
177
|
+
legend:
|
|
178
|
+
"📞 Worumgehtesbeiihreranfragebittewählensiediepassendstekategorieaus https://example.com/a/very/long/unbroken/url/that/should/wrap/somewhere <script>alert(1)</script>",
|
|
179
|
+
descriptionText:
|
|
180
|
+
"نوع الاستفسار Wählen Sie die Kategorie, die am besten zu Ihrer Anfrage passt, damit wir Sie an die richtige Person weiterleiten können. <b>not bold</b>",
|
|
181
|
+
errorMessage: "BittewählenSieeineAnfragekategorieausbevorSiedasFormularabsenden",
|
|
182
|
+
fieldHasError: true,
|
|
183
|
+
options: [
|
|
184
|
+
{ id: "1", name: "s", value: "a", label: "A" },
|
|
185
|
+
{ id: "2", name: "s", value: "", label: "" },
|
|
186
|
+
{ id: "3", name: "s", value: "long", label: "Terminvereinbarungsänderungsanfragebearbeitung" },
|
|
187
|
+
{ id: "4", name: "s", value: "url", label: "https://example.com/a/very/long/unbroken/url/that/should/wrap/somewhere" },
|
|
188
|
+
{ id: "5", name: "s", value: "has spaces <b>", label: "<b>Not bold</b> & <i>not italic</i>" },
|
|
189
|
+
{ id: "6", name: "s", value: "emoji", label: "💬 Something else 🤔" },
|
|
190
|
+
{ id: "7", name: "s", value: "rtl", label: "استشارة" },
|
|
191
|
+
{
|
|
192
|
+
id: "8",
|
|
193
|
+
name: "s",
|
|
194
|
+
value: "sentence",
|
|
195
|
+
label: "A question about an existing booking, including changing the time, the stylist or the treatment",
|
|
196
|
+
},
|
|
197
|
+
...Array.from({ length: 22 }, (_, index) => ({
|
|
198
|
+
id: `extra-${index}`,
|
|
199
|
+
name: "s",
|
|
200
|
+
value: `extra-${index}`,
|
|
201
|
+
label: `Option ${index + 9}`,
|
|
202
|
+
})),
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
render,
|
|
206
|
+
parameters: {
|
|
207
|
+
initialCanvas: "mobileCanvas",
|
|
208
|
+
docs: {
|
|
209
|
+
story: { inline: false, height: "900px" },
|
|
210
|
+
description: {
|
|
211
|
+
story:
|
|
212
|
+
"30 options including an empty label, a single character, long unbroken German and URL labels, emoji, right-to-left text, HTML-like text and a value with spaces. Check at every canvas width: the legend, description, labels and error wrap inside the dashed outline, markup renders as text, the equal-width columns never get wider than the container, and clicking any label (including the odd ones) selects its own radio. Switch `optionsLayout` to `inline` and `block`, and turn on `isButton` and `isPill`: long button labels wrap inside their tile.",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineComponent } from "vue";
|
|
2
|
+
import { afterEach, describe, it, expect, vi } from "vitest";
|
|
3
|
+
import type { VueWrapper } from "@vue/test-utils";
|
|
2
4
|
import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
3
5
|
import type { OptionsLayout, IFormMultipleOptions } from "~/types/forms/types.forms";
|
|
4
6
|
import MultipleRadiobuttons from "../MultipleRadiobuttons.vue";
|
|
@@ -12,15 +14,60 @@ const defaultProps = {
|
|
|
12
14
|
fieldData: tagsData as IFormMultipleOptions,
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
+
const mounted: VueWrapper[] = [];
|
|
18
|
+
|
|
19
|
+
const createWrapper = async (props = {}, slots = {}) => {
|
|
20
|
+
const wrapper = await mountSuspended(MultipleRadiobuttons, {
|
|
17
21
|
attachTo: document.body,
|
|
18
22
|
props: { ...defaultProps, ...props },
|
|
19
23
|
slots,
|
|
20
24
|
});
|
|
25
|
+
mounted.push(wrapper);
|
|
26
|
+
return wrapper;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
mounted.splice(0).forEach((wrapper) => wrapper.unmount());
|
|
31
|
+
vi.restoreAllMocks();
|
|
32
|
+
});
|
|
21
33
|
|
|
22
34
|
describe("MultipleRadiobuttons", () => {
|
|
23
35
|
describe("rendering", () => {
|
|
36
|
+
it("gives button radios unique, space-free ids that don't come from option values", async () => {
|
|
37
|
+
const wrapper = await createWrapper({
|
|
38
|
+
isButton: true,
|
|
39
|
+
fieldData: { ...tagsData, data: [{ id: "x", name: "tags", value: "has spaces", label: "Spaces" }, ...tagsData.data] },
|
|
40
|
+
});
|
|
41
|
+
const ids = wrapper.findAll('input[type="radio"]').map((radio) => radio.attributes("id")!);
|
|
42
|
+
|
|
43
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
44
|
+
ids.forEach((id) => expect(id).not.toMatch(/\s/));
|
|
45
|
+
expect(ids).not.toContain("tags-has spaces");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("keeps button radio ids unique across two groups sharing a name", async () => {
|
|
49
|
+
const Host = defineComponent({
|
|
50
|
+
components: { MultipleRadiobuttons },
|
|
51
|
+
setup: () => ({ props: { ...defaultProps, isButton: true } }),
|
|
52
|
+
template: `<div><MultipleRadiobuttons v-bind="props" /><MultipleRadiobuttons v-bind="props" /></div>`,
|
|
53
|
+
});
|
|
54
|
+
const host = await mountSuspended(Host);
|
|
55
|
+
mounted.push(host);
|
|
56
|
+
const ids = host.findAll('input[type="radio"]').map((radio) => radio.attributes("id"));
|
|
57
|
+
|
|
58
|
+
expect(ids).toHaveLength(tagsData.data.length * 2);
|
|
59
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("updates passthrough classes when the prop changes", async () => {
|
|
63
|
+
const wrapper = await createWrapper({ styleClassPassthrough: ["first"] });
|
|
64
|
+
await wrapper.setProps({ styleClassPassthrough: ["second"] });
|
|
65
|
+
const classes = wrapper.find(".form-fieldset").classes();
|
|
66
|
+
|
|
67
|
+
expect(classes).toContain("second");
|
|
68
|
+
expect(classes).not.toContain("first");
|
|
69
|
+
});
|
|
70
|
+
|
|
24
71
|
it("renders a radiogroup fieldset with the legend", async () => {
|
|
25
72
|
const wrapper = await createWrapper();
|
|
26
73
|
const fieldset = wrapper.find("fieldset");
|
|
@@ -1,36 +1,71 @@
|
|
|
1
|
-
#
|
|
1
|
+
# InputRange / InputRangeField — Consumer Styling Guide
|
|
2
|
+
|
|
3
|
+
> **Renamed 2026-10-16**: `InputRangeCore` → `InputRange`, `InputRangeDefault` → `InputRangeField`.
|
|
4
|
+
> Classes changed with them, see **State hooks**. Token names are unchanged.
|
|
2
5
|
|
|
3
6
|
## Public token API
|
|
4
7
|
|
|
5
8
|
| Token | Default | Controls |
|
|
6
9
|
|---|---|---|
|
|
7
10
|
| `--input-range-accent-colour` | `var(--theme-accent)` | Native `accent-color` of the range input (track/thumb tint) |
|
|
8
|
-
| `--input-range-marker-background-colour` | `
|
|
9
|
-
| `--input-range-marker-padding` | `0.5rem` |
|
|
11
|
+
| `--input-range-marker-background-colour` | `var(--theme-input-surface)` | Background colour of each `markers` slot dot |
|
|
12
|
+
| `--input-range-marker-padding` | `0.5rem` | Space around the icon in each marker dot (dot size = icon size + 2 × this) |
|
|
10
13
|
| `--input-range-marker-border-radius` | `50%` | Corner radius of each marker dot |
|
|
11
14
|
| `--input-range-marker-outline-width` | `0.1rem` | Marker dot outline width |
|
|
12
|
-
| `--input-range-marker-outline-colour` | `var(--
|
|
13
|
-
| `--input-range-marker-icon-size` | `2rem` | Font size of the marker icon (`.marker-icon`) |
|
|
15
|
+
| `--input-range-marker-outline-colour` | `var(--theme-border)` | Marker dot outline colour |
|
|
16
|
+
| `--input-range-marker-icon-size` | `2rem` | Font size of the marker icon (`.input-range-marker-icon`) |
|
|
14
17
|
| `--input-range-marker-icon-colour` | `var(--input-range-accent-colour, var(--theme-accent))` | Marker icon colour |
|
|
18
|
+
| `--input-range-marker-track-colour` | `var(--theme-border)` | The line joining the markers, drawn behind them |
|
|
19
|
+
| `--input-range-marker-track-width` | `0.2rem` | Thickness of that line |
|
|
20
|
+
| `--input-range-marker-selected-colour` | `var(--input-range-accent-colour, var(--theme-accent))` | Ring drawn around the selected marker (the restyled range thumb) |
|
|
21
|
+
| `--input-range-marker-selected-width` | `0.3rem` | Thickness of that ring |
|
|
15
22
|
| `--input-range-datalist-font-size` | `1.4rem` | Font size of the `datalist` slot's option labels |
|
|
16
23
|
| `--input-range-datalist-font-weight` | `500` | Font weight of the `datalist` slot's option labels |
|
|
17
24
|
|
|
18
|
-
Geometry (thumb/track height, border radius) comes from the shared global
|
|
19
|
-
(`--input-range-button-size`, `--form-input-border-radius`), not from this
|
|
20
|
-
tokens
|
|
25
|
+
Geometry (thumb/track height, step-button size, border radius) comes from the shared global
|
|
26
|
+
form-geometry tokens (`--input-range-button-size`, `--form-input-border-radius`), not from this
|
|
27
|
+
component's own tokens: see `theming-form-geometry-tokens.md`.
|
|
28
|
+
|
|
29
|
+
Private (not public API): `--_input-range-marker-size`, the dot size worked out from the icon size
|
|
30
|
+
and padding, shared by the markers, the selected ring and the line inset.
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
> **Changed 2026-10-16**: the marker dot defaults assumed a dark page (`hsl(0, 29%, 3%)`
|
|
33
|
+
> background, `--slate-04` outline). They now default to `--theme-input-surface` and
|
|
34
|
+
> `--theme-border`. Set the two marker tokens if you relied on the dark dots.
|
|
35
|
+
>
|
|
36
|
+
> **Changed 2026-10-16**: markers mode was broken: the range line was drawn across the icons and
|
|
37
|
+
> nothing showed the selected value (the thumb was hidden), and only the thin line was clickable.
|
|
38
|
+
> The line now sits behind the markers, the whole marker row is the slider, and the thumb is a ring
|
|
39
|
+
> the size of a marker that circles the selected one. Markers get an explicit size
|
|
40
|
+
> (`--input-range-marker-icon-size` + 2 × `--input-range-marker-padding`) so the ring lines up.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## State hooks
|
|
45
|
+
|
|
46
|
+
| Attribute | On | When |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `data-theme` | `.input-range-wrapper`, `.input-range-field` | Always, mirrors `theme` |
|
|
49
|
+
| `data-invalid` | `.input-range-wrapper`, `.input-range-field` | `field-has-error` (the input also gets `aria-invalid="true"`) |
|
|
50
|
+
| `data-has-markers` | `.input-range-wrapper` | The `markers` slot is filled: the line moves behind the markers, the input becomes a transparent layer over the whole row, and the thumb becomes a ring around the selected marker |
|
|
28
51
|
|
|
29
|
-
|
|
52
|
+
Inner elements: `.input-range` (the native input), `.input-range-container`, `.input-range-slot`
|
|
53
|
+
(both step-button holders), `.input-range-slot-left`, `.input-range-slot-right`,
|
|
54
|
+
`.input-range-label` (the Field's `InputLabel`). Pre-styled classes for your `markers` slot
|
|
55
|
+
markup: `.input-range-markers` (the row), `.input-range-marker` (each dot),
|
|
56
|
+
`.input-range-marker-icon`; and `.input-range-datalist` for a slotted `<datalist>`.
|
|
30
57
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
58
|
+
> **Changed 2026-10-16**:
|
|
59
|
+
>
|
|
60
|
+
> - Classes renamed: `.input-range-core` → `.input-range`, `.input-range-with-label` →
|
|
61
|
+
> `.input-range-field`, `.slot.left` / `.slot.right` → `.input-range-slot-left` /
|
|
62
|
+
> `.input-range-slot-right`, markers-slot `.marker` / `.marker-icon` → `.input-range-marker` /
|
|
63
|
+
> `.input-range-marker-icon` (update your slot markup).
|
|
64
|
+
> - `.has-markers` on the input → `[data-has-markers]` on the wrapper; the Field's `.error` class →
|
|
65
|
+
> `[data-invalid]`. The Field's label class was the number field's `input-number-label`; it's
|
|
66
|
+
> now `input-range-label`.
|
|
67
|
+
> - Long label, description and error copy wraps (`overflow-wrap: anywhere` on
|
|
68
|
+
> `.input-range-field`), and the slider shrinks between its step buttons instead of overflowing.
|
|
34
69
|
|
|
35
70
|
---
|
|
36
71
|
|
|
@@ -44,15 +79,22 @@ Set the tokens above on an element you own (a page or section class, or a class
|
|
|
44
79
|
|
|
45
80
|
---
|
|
46
81
|
|
|
82
|
+
## Class passthrough
|
|
83
|
+
|
|
84
|
+
`InputRange`'s `:style-class-passthrough` lands on the native `<input class="input-range">`.
|
|
85
|
+
`InputRangeField`'s lands on its root `.input-range-field` only. Both react to prop changes.
|
|
86
|
+
|
|
87
|
+
> **Changed 2026-10-16**: `InputRangeField` also forwarded its passthrough classes to the inner
|
|
88
|
+
> input, so a class meant for the field matched two elements. If you styled the input through the
|
|
89
|
+
> Field's passthrough, select `.your-class .input-range` instead.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
47
93
|
## Notes
|
|
48
94
|
|
|
49
95
|
- `accent-color` was previously read from `--theme-form-range-accent-color`, a custom property
|
|
50
|
-
never declared anywhere in the codebase (no fallback either)
|
|
51
|
-
`accent-color` declaration invalid at computed-value time, so it silently fell back to the
|
|
52
|
-
browser's native default accent colour
|
|
53
|
-
see the component's skill doc for detail.
|
|
96
|
+
never declared anywhere in the codebase (no fallback either). Per the CSS spec that made the
|
|
97
|
+
whole `accent-color` declaration invalid at computed-value time, so it silently fell back to the
|
|
98
|
+
browser's native default accent colour. Fixed 2026-09-21; see the skill doc for detail.
|
|
54
99
|
- The `weight` prop (`FormWeight`) generates an `input-range--{weight}` class on the input, but no
|
|
55
|
-
CSS in this component (or elsewhere in the library)
|
|
56
|
-
today. Left as-is rather than redesigned, since the original intent wasn't clear; flagged here
|
|
57
|
-
so you don't spend time debugging "why doesn't `weight` do anything."
|
|
58
|
-
|
|
100
|
+
CSS in this component (or elsewhere in the library) styles that class: it's a no-op today.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="input-range-wrapper"
|
|
4
|
+
:data-theme="theme"
|
|
5
|
+
:data-invalid="fieldHasError ? '' : undefined"
|
|
6
|
+
:data-has-markers="slots.markers ? '' : undefined"
|
|
7
|
+
>
|
|
8
|
+
<div v-if="slots.left" class="input-range-slot input-range-slot-left">
|
|
9
|
+
<slot name="left"></slot>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="input-range-container">
|
|
13
|
+
<slot v-if="slots.markers" name="markers"></slot>
|
|
14
|
+
|
|
15
|
+
<input
|
|
16
|
+
:id
|
|
17
|
+
v-model="modelValue"
|
|
18
|
+
type="range"
|
|
19
|
+
:name
|
|
20
|
+
:required
|
|
21
|
+
:min
|
|
22
|
+
:max
|
|
23
|
+
:step
|
|
24
|
+
:list="slots.datalist ? datalistId : undefined"
|
|
25
|
+
:class="['input-range', `input-range--${weight}`, elementClasses]"
|
|
26
|
+
:aria-describedby
|
|
27
|
+
:aria-invalid="fieldHasError ? 'true' : undefined"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<slot v-if="slots.datalist" name="datalist" :datalist-id="datalistId"></slot>
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="slots.right" class="input-range-slot input-range-slot-right">
|
|
33
|
+
<slot name="right"></slot>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import type { FormUiTheme, FormWeight } from "~/types/forms/types.forms";
|
|
40
|
+
|
|
41
|
+
interface Props {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
min: number;
|
|
45
|
+
max: number;
|
|
46
|
+
step?: number;
|
|
47
|
+
placeholder?: string;
|
|
48
|
+
required?: boolean;
|
|
49
|
+
theme?: FormUiTheme;
|
|
50
|
+
weight?: FormWeight;
|
|
51
|
+
fieldHasError?: boolean;
|
|
52
|
+
styleClassPassthrough?: string | string[];
|
|
53
|
+
ariaDescribedby?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
57
|
+
step: 1,
|
|
58
|
+
placeholder: "",
|
|
59
|
+
required: false,
|
|
60
|
+
theme: "default",
|
|
61
|
+
weight: "normal",
|
|
62
|
+
fieldHasError: false,
|
|
63
|
+
styleClassPassthrough: () => [],
|
|
64
|
+
ariaDescribedby: "",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const slots = useSlots();
|
|
68
|
+
|
|
69
|
+
const modelValue = defineModel<number>({
|
|
70
|
+
required: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const datalistId = useId();
|
|
74
|
+
|
|
75
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
76
|
+
|
|
77
|
+
watch(
|
|
78
|
+
() => props.styleClassPassthrough,
|
|
79
|
+
() => {
|
|
80
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<style lang="css">
|
|
86
|
+
@layer components {
|
|
87
|
+
.input-range-wrapper {
|
|
88
|
+
--_input-range-marker-size: calc(
|
|
89
|
+
var(--input-range-marker-icon-size, 2rem) + 2 * var(--input-range-marker-padding, 0.5rem)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: space-between;
|
|
95
|
+
gap: 1rem;
|
|
96
|
+
|
|
97
|
+
.input-range-slot {
|
|
98
|
+
align-self: flex-start;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
|
|
101
|
+
.input-button {
|
|
102
|
+
border-radius: var(--form-input-border-radius);
|
|
103
|
+
width: var(--input-range-button-size);
|
|
104
|
+
height: var(--input-range-button-size);
|
|
105
|
+
|
|
106
|
+
.btn-icon {
|
|
107
|
+
margin: initial;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.input-range-container {
|
|
113
|
+
flex-grow: 1;
|
|
114
|
+
min-inline-size: 0;
|
|
115
|
+
|
|
116
|
+
display: grid;
|
|
117
|
+
grid-template-areas: "element-stack";
|
|
118
|
+
|
|
119
|
+
.input-range-markers {
|
|
120
|
+
grid-area: element-stack;
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
justify-content: space-between;
|
|
124
|
+
width: 100%;
|
|
125
|
+
|
|
126
|
+
.input-range-marker {
|
|
127
|
+
box-sizing: border-box;
|
|
128
|
+
display: grid;
|
|
129
|
+
place-items: center;
|
|
130
|
+
flex-shrink: 0;
|
|
131
|
+
inline-size: var(--_input-range-marker-size);
|
|
132
|
+
block-size: var(--_input-range-marker-size);
|
|
133
|
+
background-color: var(--input-range-marker-background-colour, var(--theme-input-surface));
|
|
134
|
+
border-radius: var(--input-range-marker-border-radius, 50%);
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
outline: var(--input-range-marker-outline-width, 0.1rem) solid
|
|
137
|
+
var(--input-range-marker-outline-colour, var(--theme-border));
|
|
138
|
+
|
|
139
|
+
.input-range-marker-icon {
|
|
140
|
+
font-size: var(--input-range-marker-icon-size, 2rem);
|
|
141
|
+
line-height: 1;
|
|
142
|
+
display: block;
|
|
143
|
+
color: var(--input-range-marker-icon-colour, var(--input-range-accent-colour, var(--theme-accent)));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.input-range {
|
|
149
|
+
grid-area: element-stack;
|
|
150
|
+
|
|
151
|
+
accent-color: var(--input-range-accent-colour, var(--theme-accent));
|
|
152
|
+
height: var(--input-range-button-size);
|
|
153
|
+
margin: 0;
|
|
154
|
+
width: 100%;
|
|
155
|
+
|
|
156
|
+
&:focus-visible {
|
|
157
|
+
box-shadow: var(--form-focus-box-shadow);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.input-range-datalist {
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
font-family: var(--font-family);
|
|
165
|
+
font-size: var(--input-range-datalist-font-size, 1.4rem);
|
|
166
|
+
font-weight: var(--input-range-datalist-font-weight, 500);
|
|
167
|
+
justify-content: space-between;
|
|
168
|
+
writing-mode: vertical-lr;
|
|
169
|
+
width: 100%;
|
|
170
|
+
|
|
171
|
+
option {
|
|
172
|
+
padding: 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
&[data-has-markers] {
|
|
178
|
+
.input-range-container::before {
|
|
179
|
+
content: "";
|
|
180
|
+
grid-area: element-stack;
|
|
181
|
+
align-self: center;
|
|
182
|
+
z-index: 1;
|
|
183
|
+
height: var(--input-range-marker-track-width, 0.2rem);
|
|
184
|
+
margin-inline: calc(var(--_input-range-marker-size) / 2);
|
|
185
|
+
background-color: var(--input-range-marker-track-colour, var(--theme-border));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.input-range-markers {
|
|
189
|
+
z-index: 2;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.input-range {
|
|
193
|
+
-webkit-appearance: none;
|
|
194
|
+
appearance: none;
|
|
195
|
+
align-self: center;
|
|
196
|
+
z-index: 3;
|
|
197
|
+
height: var(--_input-range-marker-size);
|
|
198
|
+
background: transparent;
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
|
|
201
|
+
&:focus-visible {
|
|
202
|
+
box-shadow: none;
|
|
203
|
+
|
|
204
|
+
&::-webkit-slider-thumb {
|
|
205
|
+
box-shadow: var(--form-focus-box-shadow);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&::-moz-range-thumb {
|
|
209
|
+
box-shadow: var(--form-focus-box-shadow);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
&::-webkit-slider-runnable-track {
|
|
214
|
+
height: var(--_input-range-marker-size);
|
|
215
|
+
background: transparent;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
&::-moz-range-track {
|
|
219
|
+
background: transparent;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&::-webkit-slider-thumb {
|
|
223
|
+
-webkit-appearance: none;
|
|
224
|
+
appearance: none;
|
|
225
|
+
box-sizing: border-box;
|
|
226
|
+
width: var(--_input-range-marker-size);
|
|
227
|
+
height: var(--_input-range-marker-size);
|
|
228
|
+
border: var(--input-range-marker-selected-width, 0.3rem) solid
|
|
229
|
+
var(--input-range-marker-selected-colour, var(--input-range-accent-colour, var(--theme-accent)));
|
|
230
|
+
border-radius: var(--input-range-marker-border-radius, 50%);
|
|
231
|
+
background: transparent;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
&::-moz-range-thumb {
|
|
235
|
+
box-sizing: border-box;
|
|
236
|
+
width: var(--_input-range-marker-size);
|
|
237
|
+
height: var(--_input-range-marker-size);
|
|
238
|
+
border: var(--input-range-marker-selected-width, 0.3rem) solid
|
|
239
|
+
var(--input-range-marker-selected-colour, var(--input-range-accent-colour, var(--theme-accent)));
|
|
240
|
+
border-radius: var(--input-range-marker-border-radius, 50%);
|
|
241
|
+
background: transparent;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
</style>
|