@functionalcms/svelte-components 4.0.3 → 4.1.1
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/form/Button.svelte +113 -113
- package/dist/components/form/ChoiceInput.svelte +375 -0
- package/dist/components/form/ChoiceInput.svelte.d.ts +17 -0
- package/dist/components/form/Input.svelte +1 -1
- package/dist/components/form/Input.svelte.d.ts +1 -1
- package/dist/components/form/utils.d.ts +14 -0
- package/dist/components/utils.d.ts +3 -0
- package/dist/components/utils.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/dist/components/form/Input.d.ts +0 -2
- /package/dist/components/form/{Input.js → utils.js} +0 -0
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { cn } from '../../utils.js';
|
|
3
|
-
import type { Snippet } from 'svelte';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
children: Snippet;
|
|
7
|
-
css: string;
|
|
8
|
-
style: string;
|
|
9
|
-
type?: 'submit' | 'reset' | 'button' | 'link';
|
|
10
|
-
href: string;
|
|
11
|
-
mode?: string;
|
|
12
|
-
size?: string;
|
|
13
|
-
isPrimary: boolean;
|
|
14
|
-
isBordered?: boolean;
|
|
15
|
-
isCapsule?: boolean;
|
|
16
|
-
isGrouped?: boolean;
|
|
17
|
-
isBlock?: boolean;
|
|
18
|
-
isLink?: boolean;
|
|
19
|
-
isBlank?: boolean;
|
|
20
|
-
isDisabled?: boolean;
|
|
21
|
-
role?: string;
|
|
22
|
-
isCircle?: boolean;
|
|
23
|
-
isRounded?: boolean;
|
|
24
|
-
isSkinned?: boolean;
|
|
25
|
-
ariaSelected?: boolean;
|
|
26
|
-
ariaControls?: string;
|
|
27
|
-
tabIndex?: number;
|
|
28
|
-
onclick?: () => void;
|
|
29
|
-
onkeydown?: (e: KeyboardEvent) => void;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let {
|
|
33
|
-
children,
|
|
34
|
-
css = '',
|
|
35
|
-
style = '',
|
|
36
|
-
href = '',
|
|
37
|
-
mode = undefined,
|
|
38
|
-
size = undefined,
|
|
39
|
-
type = 'button',
|
|
40
|
-
isPrimary = false,
|
|
41
|
-
isBordered = false,
|
|
42
|
-
isCapsule = false,
|
|
43
|
-
isGrouped = false,
|
|
44
|
-
isBlock = false,
|
|
45
|
-
isLink = false,
|
|
46
|
-
isBlank = false,
|
|
47
|
-
isDisabled = false,
|
|
48
|
-
role = undefined,
|
|
49
|
-
isCircle = false,
|
|
50
|
-
isRounded = false,
|
|
51
|
-
isSkinned = true,
|
|
52
|
-
ariaSelected = undefined,
|
|
53
|
-
ariaControls = undefined,
|
|
54
|
-
tabIndex = 0,
|
|
55
|
-
onclick = undefined,
|
|
56
|
-
onkeydown = undefined,
|
|
57
|
-
...restProps
|
|
58
|
-
}: Partial<Props> = $props();
|
|
59
|
-
|
|
60
|
-
let klasses = $derived(
|
|
61
|
-
cn(
|
|
62
|
-
isSkinned ? 'btn' : 'btn-base',
|
|
63
|
-
mode ? `btn-${mode}` : '',
|
|
64
|
-
size ? `btn-${size}` : '',
|
|
65
|
-
isBordered ? 'btn-bordered' : '',
|
|
66
|
-
isCapsule ? 'btn-capsule ' : '',
|
|
67
|
-
isGrouped ? 'btn-grouped' : '',
|
|
68
|
-
isBlock ? 'btn-block' : '',
|
|
69
|
-
isCircle ? 'btn-circle' : '',
|
|
70
|
-
isRounded ? 'btn-rounded' : '',
|
|
71
|
-
isDisabled ? 'disabled' : '',
|
|
72
|
-
isBlank ? 'btn-blank' : '',
|
|
73
|
-
isLink ? 'btn-link' : '',
|
|
74
|
-
css ? `${css}` : ''
|
|
75
|
-
)
|
|
76
|
-
);
|
|
77
|
-
</script>
|
|
78
|
-
|
|
79
|
-
{#if type == 'link'}
|
|
80
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
81
|
-
<a
|
|
82
|
-
class={klasses}
|
|
83
|
-
{href}
|
|
84
|
-
{style}
|
|
85
|
-
{role}
|
|
86
|
-
aria-selected={ariaSelected}
|
|
87
|
-
aria-controls={ariaControls}
|
|
88
|
-
tabindex={tabIndex}
|
|
89
|
-
{onclick}
|
|
90
|
-
{onkeydown}
|
|
91
|
-
{...restProps}
|
|
92
|
-
>
|
|
93
|
-
{@render children?.()}
|
|
94
|
-
</a>
|
|
95
|
-
{:else}
|
|
96
|
-
<button
|
|
97
|
-
{type}
|
|
98
|
-
class={klasses}
|
|
99
|
-
{style}
|
|
100
|
-
{role}
|
|
101
|
-
aria-selected={ariaSelected}
|
|
102
|
-
aria-controls={ariaControls}
|
|
103
|
-
tabindex={tabIndex}
|
|
104
|
-
disabled={isDisabled}
|
|
105
|
-
{onclick}
|
|
106
|
-
{onkeydown}
|
|
107
|
-
{...restProps}
|
|
108
|
-
>
|
|
109
|
-
{@render children?.()}
|
|
110
|
-
</button>
|
|
111
|
-
{/if}
|
|
112
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
children: Snippet;
|
|
7
|
+
css: string;
|
|
8
|
+
style: string;
|
|
9
|
+
type?: 'submit' | 'reset' | 'button' | 'link';
|
|
10
|
+
href: string;
|
|
11
|
+
mode?: string;
|
|
12
|
+
size?: string;
|
|
13
|
+
isPrimary: boolean;
|
|
14
|
+
isBordered?: boolean;
|
|
15
|
+
isCapsule?: boolean;
|
|
16
|
+
isGrouped?: boolean;
|
|
17
|
+
isBlock?: boolean;
|
|
18
|
+
isLink?: boolean;
|
|
19
|
+
isBlank?: boolean;
|
|
20
|
+
isDisabled?: boolean;
|
|
21
|
+
role?: string;
|
|
22
|
+
isCircle?: boolean;
|
|
23
|
+
isRounded?: boolean;
|
|
24
|
+
isSkinned?: boolean;
|
|
25
|
+
ariaSelected?: boolean;
|
|
26
|
+
ariaControls?: string;
|
|
27
|
+
tabIndex?: number;
|
|
28
|
+
onclick?: () => void;
|
|
29
|
+
onkeydown?: (e: KeyboardEvent) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
children,
|
|
34
|
+
css = '',
|
|
35
|
+
style = '',
|
|
36
|
+
href = '',
|
|
37
|
+
mode = undefined,
|
|
38
|
+
size = undefined,
|
|
39
|
+
type = 'button',
|
|
40
|
+
isPrimary = false,
|
|
41
|
+
isBordered = false,
|
|
42
|
+
isCapsule = false,
|
|
43
|
+
isGrouped = false,
|
|
44
|
+
isBlock = false,
|
|
45
|
+
isLink = false,
|
|
46
|
+
isBlank = false,
|
|
47
|
+
isDisabled = false,
|
|
48
|
+
role = undefined,
|
|
49
|
+
isCircle = false,
|
|
50
|
+
isRounded = false,
|
|
51
|
+
isSkinned = true,
|
|
52
|
+
ariaSelected = undefined,
|
|
53
|
+
ariaControls = undefined,
|
|
54
|
+
tabIndex = 0,
|
|
55
|
+
onclick = undefined,
|
|
56
|
+
onkeydown = undefined,
|
|
57
|
+
...restProps
|
|
58
|
+
}: Partial<Props> = $props();
|
|
59
|
+
|
|
60
|
+
let klasses = $derived(
|
|
61
|
+
cn(
|
|
62
|
+
isSkinned ? 'btn' : 'btn-base',
|
|
63
|
+
mode ? `btn-${mode}` : '',
|
|
64
|
+
size ? `btn-${size}` : '',
|
|
65
|
+
isBordered ? 'btn-bordered' : '',
|
|
66
|
+
isCapsule ? 'btn-capsule ' : '',
|
|
67
|
+
isGrouped ? 'btn-grouped' : '',
|
|
68
|
+
isBlock ? 'btn-block' : '',
|
|
69
|
+
isCircle ? 'btn-circle' : '',
|
|
70
|
+
isRounded ? 'btn-rounded' : '',
|
|
71
|
+
isDisabled ? 'disabled' : '',
|
|
72
|
+
isBlank ? 'btn-blank' : '',
|
|
73
|
+
isLink ? 'btn-link' : '',
|
|
74
|
+
css ? `${css}` : ''
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
{#if type == 'link'}
|
|
80
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
81
|
+
<a
|
|
82
|
+
class={klasses}
|
|
83
|
+
{href}
|
|
84
|
+
{style}
|
|
85
|
+
{role}
|
|
86
|
+
aria-selected={ariaSelected}
|
|
87
|
+
aria-controls={ariaControls}
|
|
88
|
+
tabindex={tabIndex}
|
|
89
|
+
{onclick}
|
|
90
|
+
{onkeydown}
|
|
91
|
+
{...restProps}
|
|
92
|
+
>
|
|
93
|
+
{@render children?.()}
|
|
94
|
+
</a>
|
|
95
|
+
{:else}
|
|
96
|
+
<button
|
|
97
|
+
{type}
|
|
98
|
+
class={klasses}
|
|
99
|
+
{style}
|
|
100
|
+
{role}
|
|
101
|
+
aria-selected={ariaSelected}
|
|
102
|
+
aria-controls={ariaControls}
|
|
103
|
+
tabindex={tabIndex}
|
|
104
|
+
disabled={isDisabled}
|
|
105
|
+
{onclick}
|
|
106
|
+
{onkeydown}
|
|
107
|
+
{...restProps}
|
|
108
|
+
>
|
|
109
|
+
{@render children?.()}
|
|
110
|
+
</button>
|
|
111
|
+
{/if}
|
|
112
|
+
|
|
113
113
|
<style>.btn-base {
|
|
114
114
|
display: inline-flex;
|
|
115
115
|
align-items: center;
|
|
@@ -381,4 +381,4 @@ on the side padding. As such, these have a good bit less then regular buttons. *
|
|
|
381
381
|
|
|
382
382
|
.btn-link:hover {
|
|
383
383
|
cursor: pointer;
|
|
384
|
-
}</style>
|
|
384
|
+
}</style>
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils.js';
|
|
3
|
+
import type { ChoiceInputOption, ChoiceInputSize, ChoiceInputType, HtmlParts } from './utils.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
isSkinned: boolean;
|
|
7
|
+
isFieldset: boolean;
|
|
8
|
+
isInline: boolean;
|
|
9
|
+
isDisabled: boolean;
|
|
10
|
+
isInvalid: boolean;
|
|
11
|
+
options: ChoiceInputOption[];
|
|
12
|
+
disabledOptions: string[];
|
|
13
|
+
checkedOptions: string[];
|
|
14
|
+
type: ChoiceInputType;
|
|
15
|
+
legendLabel: string;
|
|
16
|
+
size: ChoiceInputSize;
|
|
17
|
+
checked: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
id = '',
|
|
22
|
+
name = '',
|
|
23
|
+
isSkinned = true,
|
|
24
|
+
isFieldset = true,
|
|
25
|
+
isInline = false,
|
|
26
|
+
isDisabled = undefined,
|
|
27
|
+
isInvalid = false,
|
|
28
|
+
options = [],
|
|
29
|
+
disabledOptions = [],
|
|
30
|
+
checkedOptions = [],
|
|
31
|
+
// If isFieldset falsy this will be screenreader only. If legendLabel is not passed
|
|
32
|
+
// in, it will fallback to the type prop or string choice input. Some content must be
|
|
33
|
+
// within the <legenc>CONTENT</legend> element to meet accessibility requirements
|
|
34
|
+
type = 'checkbox',
|
|
35
|
+
legendLabel = type || 'choice input',
|
|
36
|
+
size = '',
|
|
37
|
+
// Provides bind:checked capabilities that consumer can use
|
|
38
|
+
checked = [],
|
|
39
|
+
...restProps
|
|
40
|
+
}: Partial<Props & HtmlParts> = $props();
|
|
41
|
+
|
|
42
|
+
let labelClasses = $derived(
|
|
43
|
+
cn(
|
|
44
|
+
type ? `${type}-label-wrap` : '',
|
|
45
|
+
isInline ? `${type}-label-wrap-inline` : '',
|
|
46
|
+
isDisabled ? 'disabled' : ''
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
let labelSpanClasses = $derived(
|
|
51
|
+
cn(
|
|
52
|
+
type ? `${type}-label` : '',
|
|
53
|
+
isInvalid ? 'choice-input-error' : '',
|
|
54
|
+
size ? `${type}-label-${size}` : ''
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// If consumer sets is skinned to false we don't style the legend
|
|
59
|
+
let skin = $derived(isSkinned ? `${type}-legend` : '');
|
|
60
|
+
|
|
61
|
+
let labelCopyClasses = $derived(
|
|
62
|
+
cn(
|
|
63
|
+
// Will also need to work in the small
|
|
64
|
+
// and large sizes here for the text copy
|
|
65
|
+
type ? `${type}-label-copy` : '',
|
|
66
|
+
size ? `${type}-label-copy-${size}` : '',
|
|
67
|
+
isInvalid ? 'choice-input-error' : ''
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
let legendClasses = $derived(
|
|
72
|
+
cn(
|
|
73
|
+
skin,
|
|
74
|
+
// .screenreader-only is expected to be globally available via common.min.css
|
|
75
|
+
isFieldset === false ? 'screenreader-only' : ''
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
let fieldsetClasses = $derived(
|
|
80
|
+
cn(
|
|
81
|
+
isSkinned ? `${type}-group` : '',
|
|
82
|
+
isSkinned && size === 'large' ? `${type}-group-${size}` : '',
|
|
83
|
+
isFieldset === false ? `${type}-group-hidden` : ''
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
let inputClasses = $derived(cn(type ? `${type}` : '', size ? `${type}-${size}` : ''));
|
|
88
|
+
|
|
89
|
+
let onChange = (e: any) => {
|
|
90
|
+
// This allows the consumer to use bind:checked={checkedValues} idiom.
|
|
91
|
+
// We cannot use the bind:group idiom as we're using dynamic type above,
|
|
92
|
+
// So, essentially, we're manually implementing two-way binding here ;-)
|
|
93
|
+
checked = Array.from(document.getElementsByName(e.target.name))
|
|
94
|
+
.filter((el) => (el as HTMLInputElement).checked)
|
|
95
|
+
.map((el) => (el as HTMLInputElement).value);
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<fieldset class={fieldsetClasses}>
|
|
100
|
+
<legend class={legendClasses}>{legendLabel}</legend>
|
|
101
|
+
{#each options as { name, value, label }, index}
|
|
102
|
+
<label
|
|
103
|
+
class={labelClasses}
|
|
104
|
+
class:disabled={isDisabled || disabledOptions.includes(value) || undefined}
|
|
105
|
+
>
|
|
106
|
+
<input
|
|
107
|
+
class={inputClasses}
|
|
108
|
+
id="{id}-{name}-{index}"
|
|
109
|
+
{type}
|
|
110
|
+
{name}
|
|
111
|
+
{value}
|
|
112
|
+
disabled={isDisabled || disabledOptions.includes(value)}
|
|
113
|
+
checked={checkedOptions.includes(value)}
|
|
114
|
+
onchange={onChange}
|
|
115
|
+
{...restProps}
|
|
116
|
+
/>
|
|
117
|
+
<span class={labelSpanClasses} aria-hidden="true"></span>
|
|
118
|
+
<span class={labelCopyClasses}>{label}</span>
|
|
119
|
+
</label>
|
|
120
|
+
{/each}
|
|
121
|
+
</fieldset>
|
|
122
|
+
|
|
123
|
+
<style>
|
|
124
|
+
/**
|
|
125
|
+
* These radio and checkbox customizations are an amalgamation of various resources I've
|
|
126
|
+
* found on the internets; from Heydon Pickering's radio article (and his Inclusive Components
|
|
127
|
+
* book), to Sara Soueidan, Scott O'Hara, MDO, and Adrian Roselli's research on the matter
|
|
128
|
+
* of inclusive hiding and custom radio/checkbox inputs.
|
|
129
|
+
*/
|
|
130
|
+
.checkbox-group,
|
|
131
|
+
.radio-group {
|
|
132
|
+
--width-28: calc(7 * var(--fluid-4)); /* 1.75rem/28px */
|
|
133
|
+
|
|
134
|
+
border: 1px solid var(--agnostic-checkbox-border-color, var(--agnostic-gray-light));
|
|
135
|
+
padding: var(--fluid-24);
|
|
136
|
+
padding-top: var(--fluid-14);
|
|
137
|
+
border-radius: var(--fluid-8);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.checkbox-group-large,
|
|
141
|
+
.radio-group-large {
|
|
142
|
+
padding: var(--width-28);
|
|
143
|
+
padding-top: var(--fluid-16);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.checkbox-legend,
|
|
147
|
+
.radio-legend {
|
|
148
|
+
padding: var(--fluid-2) var(--fluid-14);
|
|
149
|
+
border-radius: var(--fluid-2);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Hiding technique from https://www.sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/
|
|
153
|
+
*/
|
|
154
|
+
.checkbox,
|
|
155
|
+
.radio {
|
|
156
|
+
position: absolute;
|
|
157
|
+
width: var(--fluid-14);
|
|
158
|
+
height: var(--fluid-14);
|
|
159
|
+
opacity: 0%;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.checkbox-small,
|
|
163
|
+
.radio-small {
|
|
164
|
+
width: var(--fluid-12);
|
|
165
|
+
height: var(--fluid-12);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.checkbox-large,
|
|
169
|
+
.radio-large {
|
|
170
|
+
width: var(--fluid-16);
|
|
171
|
+
height: var(--fluid-16);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.checkbox-label-wrap,
|
|
175
|
+
.radio-label-wrap {
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
user-select: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.checkbox-label-wrap-inline,
|
|
183
|
+
.radio-label-wrap-inline {
|
|
184
|
+
display: inline-flex;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.checkbox-label-wrap-inline:not(:last-child),
|
|
188
|
+
.radio-label-wrap-inline:not(:last-child) {
|
|
189
|
+
margin-inline-end: var(--fluid-8);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* These are not actual <label> elements but the <span> label copy elements */
|
|
193
|
+
.checkbox-label-copy,
|
|
194
|
+
.radio-label-copy,
|
|
195
|
+
.checkbox-label,
|
|
196
|
+
.radio-label {
|
|
197
|
+
display: inline-flex;
|
|
198
|
+
position: relative;
|
|
199
|
+
align-items: center;
|
|
200
|
+
flex-wrap: wrap;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.checkbox-label-copy-small,
|
|
204
|
+
.radio-label-copy-small {
|
|
205
|
+
font-size: var(--agnostic-small); /* 0.875rem */
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.checkbox-label-copy-large,
|
|
209
|
+
.radio-label-copy-large {
|
|
210
|
+
font-size: calc(var(--agnostic-body) + 2px); /* 1rem + 2px (~18px) */
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* The checkmark itself */
|
|
214
|
+
.checkbox-label::after {
|
|
215
|
+
content: '';
|
|
216
|
+
position: absolute;
|
|
217
|
+
left: var(--fluid-6);
|
|
218
|
+
top: 1px;
|
|
219
|
+
width: var(--fluid-6);
|
|
220
|
+
height: var(--fluid-12);
|
|
221
|
+
border: solid var(--agnostic-light);
|
|
222
|
+
border-width: 0 var(--fluid-2) var(--fluid-2) 0;
|
|
223
|
+
transform-origin: center center;
|
|
224
|
+
transform: rotate(40deg) scale(0);
|
|
225
|
+
transition-property: border, background-color, transform;
|
|
226
|
+
transition-duration: var(--agnostic-timing-fast);
|
|
227
|
+
transition-timing-function: ease-in-out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.checkbox-label::before,
|
|
231
|
+
.radio-label::before {
|
|
232
|
+
content: '';
|
|
233
|
+
display: inline-block;
|
|
234
|
+
margin-inline-end: var(--agnostic-checkbox-spacing-end, 0.75rem);
|
|
235
|
+
transition: var(--agnostic-timing-fast) ease-out all;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Since we build up the radio size outwardly, it's naturally larger then the checkboxes
|
|
239
|
+
so we add a multiplyer to even those out initially */
|
|
240
|
+
.checkbox-label::before {
|
|
241
|
+
border: 2px solid var(--agnostic-checkbox-border-color, var(--agnostic-gray-light));
|
|
242
|
+
width: var(--fluid-16);
|
|
243
|
+
height: var(--fluid-16);
|
|
244
|
+
transition: box-shadow var(--agnostic-timing-fast) ease-out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.radio-label::before {
|
|
248
|
+
width: var(--fluid-14);
|
|
249
|
+
height: var(--fluid-14);
|
|
250
|
+
vertical-align: calc(-1 * var(--fluid-2));
|
|
251
|
+
border-radius: 50%;
|
|
252
|
+
border: var(--fluid-2) solid var(--agnostic-checkbox-light, var(--agnostic-light));
|
|
253
|
+
box-shadow: 0 0 0 var(--fluid-2)
|
|
254
|
+
var(--agnostic-checkbox-border-color, var(--agnostic-gray-light));
|
|
255
|
+
transition: box-shadow var(--agnostic-timing-fast) ease-out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@media (prefers-reduced-motion), (update: slow) {
|
|
259
|
+
.checkbox-label::after,
|
|
260
|
+
.checkbox-label::before,
|
|
261
|
+
.radio-label::before {
|
|
262
|
+
transition-duration: 0.001ms !important;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.checkbox-label-small::after {
|
|
267
|
+
left: calc(1.25 * var(--fluid-4));
|
|
268
|
+
top: 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.checkbox-label-small::before {
|
|
272
|
+
width: var(--fluid-14);
|
|
273
|
+
height: var(--fluid-14);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.radio-label-small::before {
|
|
277
|
+
width: var(--fluid-12);
|
|
278
|
+
height: var(--fluid-12);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.checkbox-label-large::after {
|
|
282
|
+
left: calc(1.75 * var(--fluid-4));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.checkbox-label-large::before {
|
|
286
|
+
width: var(--fluid-18);
|
|
287
|
+
height: var(--fluid-18);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.radio-label-large::before {
|
|
291
|
+
width: var(--fluid-16);
|
|
292
|
+
height: var(--fluid-16);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* the checked style using the :checked pseudo class */
|
|
296
|
+
.radio:checked + .radio-label::before {
|
|
297
|
+
background: var(--agnostic-checkbox-fill-color, #08a880);
|
|
298
|
+
box-shadow: 0 0 0 var(--fluid-2)
|
|
299
|
+
var(--agnostic-checkbox-border-color, var(--agnostic-gray-light));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.radio:focus + .radio-label::before {
|
|
303
|
+
/* stylelint-disable-next-line max-line-length */
|
|
304
|
+
box-shadow:
|
|
305
|
+
0 0 0 var(--fluid-2) var(--agnostic-checkbox-border-color, var(--agnostic-gray-light)),
|
|
306
|
+
0 0 0 calc(1.5 * var(--fluid-2)) var(--agnostic-light),
|
|
307
|
+
0 0 0 calc(2.25 * var(--fluid-2)) var(--agnostic-focus-ring-color);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.checkbox:focus + .checkbox-label::before {
|
|
311
|
+
box-shadow: 0 0 0 var(--agnostic-focus-ring-outline-width) var(--agnostic-focus-ring-color);
|
|
312
|
+
|
|
313
|
+
/* Needed for High Contrast mode */
|
|
314
|
+
/* stylelint-disable-next-line max-line-length */
|
|
315
|
+
outline: var(--agnostic-focus-ring-outline-width) var(--agnostic-focus-ring-outline-style)
|
|
316
|
+
var(--agnostic-focus-ring-outline-color);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.checkbox:checked + .checkbox-label::after {
|
|
320
|
+
transform: rotate(40deg) scale(1);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.checkbox:checked + .checkbox-label::before {
|
|
324
|
+
background: var(--agnostic-checkbox-fill-color, #08a880);
|
|
325
|
+
border: 2px solid var(--agnostic-checkbox-fill-color, #08a880);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Consumer styles <legend> themselves, and can opt to use the .screenreader-only from
|
|
330
|
+
* utilities.css if they're design requires it.
|
|
331
|
+
*/
|
|
332
|
+
.checkbox-group-hidden,
|
|
333
|
+
.radio-group-hidden {
|
|
334
|
+
border: 0;
|
|
335
|
+
margin-block-start: 0;
|
|
336
|
+
margin-inline-start: 0;
|
|
337
|
+
margin-inline-end: 0;
|
|
338
|
+
margin-block-end: 0;
|
|
339
|
+
padding-block-start: 0;
|
|
340
|
+
padding-inline-start: 0;
|
|
341
|
+
padding-inline-end: 0;
|
|
342
|
+
padding-block-end: 0;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* Targets both the label container and the span label that is used
|
|
346
|
+
to style the custom radio / checkbox. Note it does NOT target the input
|
|
347
|
+
itself. */
|
|
348
|
+
.checkbox[disabled] ~ .checkbox-label-copy,
|
|
349
|
+
.radio[disabled] ~ .radio-label-copy,
|
|
350
|
+
.checkbox-label-wrap[class='disabled'],
|
|
351
|
+
.radio-label-wrap[class='disabled'],
|
|
352
|
+
.checkbox-label-wrap-inline[class='disabled'],
|
|
353
|
+
.radio-label-wrap-inline[class='disabled'] {
|
|
354
|
+
color: var(--agnostic-input-disabled-color, var(--agnostic-disabled-color)) !important;
|
|
355
|
+
appearance: none !important;
|
|
356
|
+
box-shadow: none !important;
|
|
357
|
+
cursor: not-allowed !important;
|
|
358
|
+
opacity: 80% !important;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.choice-input-error {
|
|
362
|
+
color: var(--agnostic-input-error-color, var(--agnostic-error));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@media screen and (-ms-high-contrast: active) {
|
|
366
|
+
/* High contrast mode outline hacks */
|
|
367
|
+
.checkbox-label-wrap[class='disabled'],
|
|
368
|
+
.radio-label-wrap[class='disabled'],
|
|
369
|
+
.checkbox-label-wrap-inline[class='disabled'],
|
|
370
|
+
.radio-label-wrap-inline[class='disabled'] {
|
|
371
|
+
outline: 2px solid transparent;
|
|
372
|
+
outline-offset: -2px;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ChoiceInputOption, ChoiceInputSize, ChoiceInputType, HtmlParts } from './utils.js';
|
|
2
|
+
declare const ChoiceInput: import("svelte").Component<Partial<{
|
|
3
|
+
isSkinned: boolean;
|
|
4
|
+
isFieldset: boolean;
|
|
5
|
+
isInline: boolean;
|
|
6
|
+
isDisabled: boolean;
|
|
7
|
+
isInvalid: boolean;
|
|
8
|
+
options: ChoiceInputOption[];
|
|
9
|
+
disabledOptions: string[];
|
|
10
|
+
checkedOptions: string[];
|
|
11
|
+
type: ChoiceInputType;
|
|
12
|
+
legendLabel: string;
|
|
13
|
+
size: ChoiceInputSize;
|
|
14
|
+
checked: string[];
|
|
15
|
+
} & HtmlParts>, {}, "">;
|
|
16
|
+
type ChoiceInput = ReturnType<typeof ChoiceInput>;
|
|
17
|
+
export default ChoiceInput;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Type = 'text' | 'textarea' | 'email' | 'search' | 'password' | 'tel' | 'number' | 'url' | 'month' | 'time' | 'week' | 'date' | 'datetime-local' | 'color';
|
|
2
|
+
export type LabelSize = 'small' | 'large' | '';
|
|
3
|
+
export interface HtmlParts {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
css: string;
|
|
7
|
+
}
|
|
8
|
+
export type ChoiceInputOption = {
|
|
9
|
+
name: string;
|
|
10
|
+
value: string;
|
|
11
|
+
label: string;
|
|
12
|
+
};
|
|
13
|
+
export type ChoiceInputType = "checkbox" | "radio";
|
|
14
|
+
export type ChoiceInputSize = "small" | "large" | "";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -21,5 +21,7 @@ export { default as HamburgerMenu } from './components/menu/HamburgerMenu.svelte
|
|
|
21
21
|
export { default as Button } from './components/form/Button.svelte';
|
|
22
22
|
export { default as Input } from './components/form/Input.svelte';
|
|
23
23
|
export { default as Switch } from './components/form/Switch.svelte';
|
|
24
|
+
export { default as ChoiceInput } from './components/form/ChoiceInput.svelte';
|
|
25
|
+
export type { ChoiceInputOption } from './components/form/utils.ts';
|
|
24
26
|
export { default as Markdown } from './components/content/Markdown.svelte';
|
|
25
27
|
export { type BlogPost, listAllPosts, importPost } from './components/blog/blog.js';
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ export { default as HamburgerMenu } from './components/menu/HamburgerMenu.svelte
|
|
|
38
38
|
export { default as Button } from './components/form/Button.svelte';
|
|
39
39
|
export { default as Input } from './components/form/Input.svelte';
|
|
40
40
|
export { default as Switch } from './components/form/Switch.svelte';
|
|
41
|
+
export { default as ChoiceInput } from './components/form/ChoiceInput.svelte';
|
|
41
42
|
/*
|
|
42
43
|
* Content
|
|
43
44
|
*/
|
package/package.json
CHANGED
|
File without changes
|