@functionalcms/svelte-components 3.0.27 → 3.1.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/css/functional.css +1 -1
- package/css/functional.css.map +1 -1
- package/dist/components/agnostic/Card/Card.svelte +144 -127
- package/dist/components/agnostic/Card/Card.svelte.d.ts +1 -10
- package/dist/components/form/Input.svelte +465 -402
- package/dist/components/form/Input.svelte.d.ts +1 -22
- package/dist/components/layouts/DefaultLayout.svelte +2 -4
- package/dist/components/layouts/DefaultLayout.svelte.d.ts +0 -1
- package/package.json +1 -1
|
@@ -1,158 +1,356 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { mergedClasses } from '../CssHelper';
|
|
3
|
+
|
|
4
|
+
// Looks like the way to propogate boilerplate events is to
|
|
5
|
+
// just declare in template like on:blur on:focus and so on
|
|
6
|
+
// https://github.com/sveltejs/svelte/issues/585
|
|
7
|
+
// Looks like this is what smelte is doing:
|
|
8
|
+
// https://github.com/matyunya/smelte/blob/master/src/components/TextField/TextField.svelte
|
|
9
|
+
// export let label = '';
|
|
10
|
+
// export let id = '';
|
|
11
|
+
// export let labelCss = '';
|
|
12
|
+
// export let isLabelHidden = false;
|
|
13
|
+
// export let helpText = '';
|
|
14
|
+
// export let invalidText = '';
|
|
15
|
+
// export let hasLeftAddon = false;
|
|
16
|
+
// export let hasRightAddon = false;
|
|
17
|
+
// export let isInvalid = false;
|
|
18
|
+
// export let isInline = false;
|
|
19
|
+
// export let isRounded = false;
|
|
20
|
+
// export let isDisabled = undefined;
|
|
21
|
+
// export let css = '';
|
|
22
|
+
// export let isSkinned = true;
|
|
23
|
+
// export let isUnderlinedWithBackground = false;
|
|
24
|
+
// export let isUnderlined = false;
|
|
25
|
+
// export let size: 'small' | 'large' | '' = '';
|
|
26
|
+
// export let type:
|
|
27
|
+
// | 'text'
|
|
28
|
+
// | 'textarea'
|
|
29
|
+
// | 'email'
|
|
30
|
+
// | 'search'
|
|
31
|
+
// | 'password'
|
|
32
|
+
// | 'tel'
|
|
33
|
+
// | 'number'
|
|
34
|
+
// | 'url'
|
|
35
|
+
// | 'month'
|
|
36
|
+
// | 'time'
|
|
37
|
+
// | 'week'
|
|
38
|
+
// | 'date'
|
|
39
|
+
// | 'datetime-local'
|
|
40
|
+
// | 'color' = 'text';
|
|
41
|
+
|
|
42
|
+
// export let value = '';
|
|
43
|
+
|
|
44
|
+
let {
|
|
45
|
+
label = '',
|
|
46
|
+
id = '',
|
|
47
|
+
labelCss = '',
|
|
48
|
+
isLabelHidden = false,
|
|
49
|
+
helpText = '',
|
|
50
|
+
invalidText = '',
|
|
51
|
+
hasLeftAddon = false,
|
|
52
|
+
hasRightAddon = false,
|
|
53
|
+
isInvalid = false,
|
|
54
|
+
isInline = false,
|
|
55
|
+
isRounded = false,
|
|
56
|
+
isDisabled = undefined,
|
|
57
|
+
css = '',
|
|
58
|
+
isSkinned = true,
|
|
59
|
+
isUnderlinedWithBackground = false,
|
|
60
|
+
isUnderlined = false,
|
|
61
|
+
size = '',
|
|
62
|
+
type = 'text',
|
|
63
|
+
value = $bindable(''),
|
|
64
|
+
...restProps
|
|
65
|
+
}: {
|
|
66
|
+
label: string,
|
|
67
|
+
id: string,
|
|
68
|
+
labelCss: string,
|
|
69
|
+
isLabelHidden: boolean,
|
|
70
|
+
helpText: string,
|
|
71
|
+
invalidText: string,
|
|
72
|
+
hasLeftAddon: boolean,
|
|
73
|
+
hasRightAddon: boolean,
|
|
74
|
+
isInvalid: boolean,
|
|
75
|
+
isInline: boolean,
|
|
76
|
+
isRounded: boolean,
|
|
77
|
+
isDisabled: boolean | undefined,
|
|
78
|
+
css: string,
|
|
79
|
+
isSkinned: boolean,
|
|
80
|
+
isUnderlinedWithBackground: boolean,
|
|
81
|
+
isUnderlined: boolean,
|
|
82
|
+
size: 'small' | 'large' | '',
|
|
83
|
+
type:
|
|
84
|
+
| 'text'
|
|
85
|
+
| 'textarea'
|
|
86
|
+
| 'email'
|
|
87
|
+
| 'search'
|
|
88
|
+
| 'password'
|
|
89
|
+
| 'tel'
|
|
90
|
+
| 'number'
|
|
91
|
+
| 'url'
|
|
92
|
+
| 'month'
|
|
93
|
+
| 'time'
|
|
94
|
+
| 'week'
|
|
95
|
+
| 'date'
|
|
96
|
+
| 'datetime-local'
|
|
97
|
+
| 'color',
|
|
98
|
+
value: string,
|
|
99
|
+
restProps: any
|
|
100
|
+
} = $props();
|
|
101
|
+
|
|
102
|
+
// $: if (!value) value = '';
|
|
103
|
+
const inputType = $derived(type);
|
|
104
|
+
|
|
105
|
+
const labelClasses = $derived(
|
|
106
|
+
mergedClasses(
|
|
107
|
+
'label',
|
|
108
|
+
isInvalid ? 'label-error' : '',
|
|
109
|
+
isInline ? 'label-inline' : '',
|
|
110
|
+
size ? `label-${size}` : '',
|
|
111
|
+
isLabelHidden ? 'screenreader-only' : '',
|
|
112
|
+
labelCss ? labelCss : ''
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const inputClasses = $derived(
|
|
117
|
+
mergedClasses(
|
|
118
|
+
isSkinned ? 'input' : 'input-base',
|
|
119
|
+
isRounded ? 'input-rounded' : '',
|
|
120
|
+
isUnderlined ? 'input-underlined' : '',
|
|
121
|
+
hasLeftAddon ? 'input-has-left-addon' : '',
|
|
122
|
+
hasRightAddon ? 'input-has-right-addon' : '',
|
|
123
|
+
isDisabled ? 'disabled' : '',
|
|
124
|
+
isInvalid ? 'input-error' : '',
|
|
125
|
+
isInline ? 'input-inline' : '',
|
|
126
|
+
isUnderlinedWithBackground ? 'input-underlined-bg' : '',
|
|
127
|
+
css ? css : '',
|
|
128
|
+
size ? `input-${size}` : ''
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const invalidClasses = $derived(size ? `field-error-${size}` : 'field-error');
|
|
133
|
+
|
|
134
|
+
const helpClasses = $derived( size ? `field-help-${size}` : 'field-help');
|
|
135
|
+
|
|
136
|
+
const addonContainerClasses = 'input-addon-container';
|
|
137
|
+
|
|
138
|
+
const handleInput = (e: Event) => {
|
|
139
|
+
value = (e.target as HTMLInputElement).value;
|
|
140
|
+
};
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<div class="w-100">
|
|
144
|
+
<label class={labelClasses} for={id}>{label}</label>
|
|
145
|
+
{#if type == 'textarea'}
|
|
146
|
+
<textarea
|
|
147
|
+
{id}
|
|
148
|
+
class={inputClasses}
|
|
149
|
+
on:blur
|
|
150
|
+
on:change
|
|
151
|
+
bind:value
|
|
152
|
+
on:click
|
|
153
|
+
on:focus
|
|
154
|
+
{...restProps}
|
|
155
|
+
></textarea>
|
|
156
|
+
{:else if hasLeftAddon || hasRightAddon}
|
|
157
|
+
<div class={addonContainerClasses}>
|
|
158
|
+
<slot name="addonLeft" />
|
|
159
|
+
<input
|
|
160
|
+
{id}
|
|
161
|
+
type={inputType}
|
|
162
|
+
{value}
|
|
163
|
+
class={inputClasses}
|
|
164
|
+
disabled={isDisabled}
|
|
165
|
+
on:blur
|
|
166
|
+
on:change
|
|
167
|
+
on:input={handleInput}
|
|
168
|
+
on:click
|
|
169
|
+
on:focus
|
|
170
|
+
{...restProps}
|
|
171
|
+
/>
|
|
172
|
+
<slot name="addonRight" />
|
|
173
|
+
</div>
|
|
174
|
+
{:else}
|
|
175
|
+
<input
|
|
176
|
+
{id}
|
|
177
|
+
type={inputType}
|
|
178
|
+
{value}
|
|
179
|
+
class={inputClasses}
|
|
180
|
+
disabled={isDisabled}
|
|
181
|
+
on:blur
|
|
182
|
+
on:change
|
|
183
|
+
on:input={handleInput}
|
|
184
|
+
on:click
|
|
185
|
+
on:focus
|
|
186
|
+
{...restProps}
|
|
187
|
+
/>
|
|
188
|
+
{/if}
|
|
189
|
+
{#if isInvalid}
|
|
190
|
+
<span role="status" aria-live="polite" class={invalidClasses}>
|
|
191
|
+
{invalidText}
|
|
192
|
+
</span>
|
|
193
|
+
{:else if helpText}<span class={helpClasses}>{helpText}</span>{/if}
|
|
194
|
+
</div>
|
|
195
|
+
|
|
1
196
|
<style>
|
|
2
|
-
.input-base,
|
|
3
|
-
.input {
|
|
4
|
-
|
|
197
|
+
.input-base,
|
|
198
|
+
.input {
|
|
199
|
+
/* Note this cannot be user-select: none else mobile safari won't accept input:
|
|
5
200
|
https://stackoverflow.com/questions/49889003/cannot-write-into-input-field-on-safari/49901069
|
|
6
201
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.label,
|
|
16
|
-
.label-base {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/* Electing to scope these as opposed to doing :root level definitions */
|
|
24
|
-
.field-help,
|
|
25
|
-
.field-help-large,
|
|
26
|
-
.field-help-small,
|
|
27
|
-
.field-error,
|
|
28
|
-
.field-error-large,
|
|
29
|
-
.field-error-small,
|
|
30
|
-
.label-skin,
|
|
31
|
-
.label,
|
|
32
|
-
.input-addon-container,
|
|
33
|
-
.input-small,
|
|
34
|
-
.input-large,
|
|
35
|
-
.input-skin,
|
|
36
|
-
.input-underlined,
|
|
37
|
-
.input-underlined-bg,
|
|
38
|
-
.input {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.input-skin,
|
|
49
|
-
.input {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
202
|
+
user-select: initial;
|
|
203
|
+
appearance: none;
|
|
204
|
+
box-sizing: border-box;
|
|
205
|
+
|
|
206
|
+
/* Use the same color for the cursor */
|
|
207
|
+
caret-color: currentColor;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.label,
|
|
211
|
+
.label-base {
|
|
212
|
+
padding: 0;
|
|
213
|
+
border: 0;
|
|
214
|
+
box-sizing: border-box;
|
|
215
|
+
font-family: inherit;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Electing to scope these as opposed to doing :root level definitions */
|
|
219
|
+
.field-help,
|
|
220
|
+
.field-help-large,
|
|
221
|
+
.field-help-small,
|
|
222
|
+
.field-error,
|
|
223
|
+
.field-error-large,
|
|
224
|
+
.field-error-small,
|
|
225
|
+
.label-skin,
|
|
226
|
+
.label,
|
|
227
|
+
.input-addon-container,
|
|
228
|
+
.input-small,
|
|
229
|
+
.input-large,
|
|
230
|
+
.input-skin,
|
|
231
|
+
.input-underlined,
|
|
232
|
+
.input-underlined-bg,
|
|
233
|
+
.input {
|
|
234
|
+
color: var(--functional-font-color, var(--functional-dark));
|
|
235
|
+
font-family: var(--functional-font-family-body);
|
|
236
|
+
font-weight: var(--functional-font-weight, 300);
|
|
237
|
+
font-size: var(--functional-font-size, 1rem);
|
|
238
|
+
line-height: var(--functional-line-height, var(--fluid-20, 1.25rem));
|
|
239
|
+
width: 100%;
|
|
240
|
+
max-width: 100%;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.input-skin,
|
|
244
|
+
.input {
|
|
245
|
+
/* seems like a reasonable default as chrome picks `outset` which results in a weird 3d effect */
|
|
246
|
+
border-style: solid;
|
|
247
|
+
|
|
248
|
+
/* this can be overriden, but it might mess with the balance of the button heights across variants */
|
|
249
|
+
border-width: var(--functional-input-border-size, 1px);
|
|
250
|
+
border-color: var(--functional-input-border-color, var(--functional-gray-light));
|
|
251
|
+
|
|
252
|
+
/* these can be overriden, but it might mess with the balance of the inputheights across variants */
|
|
253
|
+
padding-block-start: var(--functional-input-vertical-pad, 0.5rem);
|
|
254
|
+
padding-block-end: var(--functional-input-vertical-pad, 0.5rem);
|
|
255
|
+
padding-inline-start: var(--functional-input-side-padding, 0.75rem);
|
|
256
|
+
padding-inline-end: var(--functional-input-side-padding, 0.75rem);
|
|
257
|
+
|
|
258
|
+
/* Note we only want to set properties that we actually want
|
|
64
259
|
to transition in. For example, if we transition "all", the
|
|
65
260
|
inputs will "grow in" on page load—not a happy effect :) */
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
261
|
+
transition-property: box-shadow;
|
|
262
|
+
transition-duration: var(--functional-input-timing, var(--functional-timing-medium));
|
|
263
|
+
}
|
|
69
264
|
|
|
70
|
-
.label {
|
|
71
|
-
|
|
265
|
+
.label {
|
|
266
|
+
display: inline-block;
|
|
72
267
|
|
|
73
|
-
|
|
268
|
+
/* Provided --functional-input-vertical-pad isn't overriden we'll get 20px
|
|
74
269
|
label w/a 6px margin then a 38px input = 64 which is on the 8pt grid */
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Reset field errors and help text to be 2px less then input font size */
|
|
83
|
-
.field-help,
|
|
84
|
-
.field-error {
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.label-inline,
|
|
89
|
-
.input-inline {
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/* When inlined, the margin-block-end will throw the label off-center with adjacent input */
|
|
94
|
-
.label-inline {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
270
|
+
margin-block-start: 0;
|
|
271
|
+
margin-inline-start: 0;
|
|
272
|
+
margin-inline-end: 0;
|
|
273
|
+
margin-block-end: var(--functional-input-label-pad, 0.375rem);
|
|
274
|
+
vertical-align: initial;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Reset field errors and help text to be 2px less then input font size */
|
|
278
|
+
.field-help,
|
|
279
|
+
.field-error {
|
|
280
|
+
font-size: calc(var(--functional-font-size, 1rem) - 2px);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.label-inline,
|
|
284
|
+
.input-inline {
|
|
285
|
+
width: auto;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* When inlined, the margin-block-end will throw the label off-center with adjacent input */
|
|
289
|
+
.label-inline {
|
|
290
|
+
margin-block-start: 0;
|
|
291
|
+
margin-block-end: 0;
|
|
292
|
+
margin-inline-start: 0;
|
|
293
|
+
margin-inline-end: var(--functional-input-side-padding, 0.75rem);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
102
297
|
* Placeholder
|
|
103
298
|
*/
|
|
104
299
|
|
|
105
|
-
/* stylelint-disable-next-line */
|
|
106
|
-
.input::-webkit-input-placeholder {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* stylelint-disable-next-line */
|
|
113
|
-
.input::placeholder {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* stylelint-disable-next-line */
|
|
120
|
-
.input::-ms-placeholder {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/* stylelint-disable-next-line */
|
|
127
|
-
.input:-ms-placeholder {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
300
|
+
/* stylelint-disable-next-line */
|
|
301
|
+
.input::-webkit-input-placeholder {
|
|
302
|
+
color: currentColor;
|
|
303
|
+
opacity: 50%;
|
|
304
|
+
transition: opacity var(--functional-timing-fast) ease-out;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* stylelint-disable-next-line */
|
|
308
|
+
.input::placeholder {
|
|
309
|
+
color: currentColor;
|
|
310
|
+
opacity: 50%;
|
|
311
|
+
transition: opacity var(--functional-timing-fast) ease-out;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* stylelint-disable-next-line */
|
|
315
|
+
.input::-ms-placeholder {
|
|
316
|
+
color: currentColor;
|
|
317
|
+
opacity: 50%;
|
|
318
|
+
transition: opacity var(--functional-timing-fast) ease-out;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* stylelint-disable-next-line */
|
|
322
|
+
.input:-ms-placeholder {
|
|
323
|
+
color: currentColor;
|
|
324
|
+
opacity: 50%;
|
|
325
|
+
transition: opacity var(--functional-timing-fast) ease-out;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
134
329
|
* Underlined inputs
|
|
135
330
|
*/
|
|
136
|
-
.input-underlined {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.input-underlined-bg {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
331
|
+
.input-underlined {
|
|
332
|
+
border-top: 0;
|
|
333
|
+
border-left: 0;
|
|
334
|
+
border-right: 0;
|
|
335
|
+
border-color: var(--functional-input-underlined-color, var(--functional-gray-mid-dark));
|
|
336
|
+
background-color: transparent;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.input-underlined-bg {
|
|
340
|
+
background-color: var(
|
|
341
|
+
--functional-input-underlined-bg-color,
|
|
342
|
+
var(--functional-gray-extra-light)
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
149
347
|
* Rounded inputs
|
|
150
348
|
*/
|
|
151
|
-
.input-rounded {
|
|
152
|
-
|
|
153
|
-
}
|
|
349
|
+
.input-rounded {
|
|
350
|
+
border-radius: var(--functional-radius, 0.25rem);
|
|
351
|
+
}
|
|
154
352
|
|
|
155
|
-
/**
|
|
353
|
+
/**
|
|
156
354
|
* Errors
|
|
157
355
|
* We provide a class-based approach to setting errors which means we do
|
|
158
356
|
* not support :invalid, so it requires custom use of html4 validation API
|
|
@@ -163,84 +361,83 @@
|
|
|
163
361
|
* of characters have been type (blur is actually better but I did not
|
|
164
362
|
* implement that in the contrived example).
|
|
165
363
|
*/
|
|
166
|
-
.label-error {
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.input-error {
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.label-error,
|
|
175
|
-
.field-error,
|
|
176
|
-
.field-error-small,
|
|
177
|
-
.field-error-large {
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.field-help,
|
|
182
|
-
.field-help-small,
|
|
183
|
-
.field-help-large {
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.field-help,
|
|
188
|
-
.field-help-small,
|
|
189
|
-
.field-help-large,
|
|
190
|
-
.field-error,
|
|
191
|
-
.field-error-small,
|
|
192
|
-
.field-error-large {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
364
|
+
.label-error {
|
|
365
|
+
color: var(--functional-input-error-color, var(--functional-error));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.input-error {
|
|
369
|
+
border-color: var(--functional-input-error-color, var(--functional-error));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.label-error,
|
|
373
|
+
.field-error,
|
|
374
|
+
.field-error-small,
|
|
375
|
+
.field-error-large {
|
|
376
|
+
color: var(--functional-input-error-color, var(--functional-error));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.field-help,
|
|
380
|
+
.field-help-small,
|
|
381
|
+
.field-help-large {
|
|
382
|
+
color: var(--functional-input-help-color, var(--functional-gray-dark));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.field-help,
|
|
386
|
+
.field-help-small,
|
|
387
|
+
.field-help-large,
|
|
388
|
+
.field-error,
|
|
389
|
+
.field-error-small,
|
|
390
|
+
.field-error-large {
|
|
391
|
+
display: inline-block;
|
|
392
|
+
width: 100%;
|
|
393
|
+
margin-block-start: calc(var(--functional-input-vertical-pad, 0.5rem) / 2);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
199
397
|
* Sizes
|
|
200
398
|
*/
|
|
201
|
-
.input-large {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.field-help-large,
|
|
207
|
-
.field-error-large {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.label-large {
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.input-small {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.field-help-small,
|
|
222
|
-
.field-error-small,
|
|
223
|
-
.label-small {
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.input:focus {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
/* Set the focus to transparent when there's an error since we use
|
|
399
|
+
.input-large {
|
|
400
|
+
font-size: calc(var(--functional-font-size, 1rem) + 0.25rem);
|
|
401
|
+
line-height: 1.6rem;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.field-help-large,
|
|
405
|
+
.field-error-large {
|
|
406
|
+
/* We initially remove -2px from font-size so setting to font-size essentially adds the 2px back */
|
|
407
|
+
font-size: var(--functional-font-size, 1rem);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.label-large {
|
|
411
|
+
font-size: calc(var(--functional-font-size, 1rem) + 0.25rem);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.input-small {
|
|
415
|
+
font-size: calc(var(--functional-font-size, 1rem) - 0.25rem);
|
|
416
|
+
line-height: 1rem;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.field-help-small,
|
|
420
|
+
.field-error-small,
|
|
421
|
+
.label-small {
|
|
422
|
+
font-size: calc(var(--functional-font-size, 1rem) - 0.25rem);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.input:focus {
|
|
426
|
+
box-shadow: 0 0 0 var(--functional-focus-ring-outline-width) var(--functional-focus-ring-color);
|
|
427
|
+
|
|
428
|
+
/* Needed for High Contrast mode */
|
|
429
|
+
outline: var(--functional-focus-ring-outline-width) var(--functional-focus-ring-outline-style)
|
|
430
|
+
var(--functional-focus-ring-outline-color);
|
|
431
|
+
transition: box-shadow var(--functional-timing-fast) ease-out;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Set the focus to transparent when there's an error since we use
|
|
238
435
|
borders that visually conflict. */
|
|
239
|
-
.input-error:focus {
|
|
240
|
-
|
|
241
|
-
}
|
|
436
|
+
.input-error:focus {
|
|
437
|
+
box-shadow: 0 0 0 3px transparent;
|
|
438
|
+
}
|
|
242
439
|
|
|
243
|
-
/*
|
|
440
|
+
/*
|
|
244
441
|
* Disabled State
|
|
245
442
|
*
|
|
246
443
|
* The disabled state uses the class .disabled,
|
|
@@ -248,197 +445,63 @@ borders that visually conflict. */
|
|
|
248
445
|
* The use of !important is only added because this is a state
|
|
249
446
|
* that must be applied to all inputs when in a disabled state.
|
|
250
447
|
*/
|
|
251
|
-
.input.disabled, /* DEPRECATED -- TODO remove class based disabled */
|
|
448
|
+
.input.disabled, /* DEPRECATED -- TODO remove class based disabled */
|
|
252
449
|
.input:disabled {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
@media screen and (-ms-high-contrast: active) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
450
|
+
background: var(--functional-input-disabled-bg, var(--functional-disabled-bg)) !important;
|
|
451
|
+
color: var(--functional-input-disabled-color, var(--functional-disabled-color)) !important;
|
|
452
|
+
appearance: none !important;
|
|
453
|
+
box-shadow: none !important;
|
|
454
|
+
cursor: not-allowed !important;
|
|
455
|
+
opacity: 80% !important;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
@media screen and (-ms-high-contrast: active) {
|
|
459
|
+
/* High contrast mode outline hacks */
|
|
460
|
+
|
|
461
|
+
/* styleint-disable-next-line no-descending-specificity */
|
|
462
|
+
.input:disabled {
|
|
463
|
+
outline: 2px solid transparent;
|
|
464
|
+
outline-offset: -2px;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
272
469
|
* Input "has addon"
|
|
273
470
|
*/
|
|
274
471
|
|
|
275
|
-
.input-addon-container {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.input-has-left-addon {
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.input-has-right-addon {
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.input-addon-left {
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.input-addon-right {
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
@media (prefers-reduced-motion), (update: slow) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
472
|
+
.input-addon-container {
|
|
473
|
+
display: flex;
|
|
474
|
+
position: relative;
|
|
475
|
+
width: 100%;
|
|
476
|
+
min-height: 100%;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.input-has-left-addon {
|
|
480
|
+
padding-left: calc(var(--functional-side-padding) * 3);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.input-has-right-addon {
|
|
484
|
+
padding-right: calc(var(--functional-side-padding) * 3);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.input-addon-left {
|
|
488
|
+
left: var(--functional-input-side-padding);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.input-addon-right {
|
|
492
|
+
right: var(--functional-input-side-padding);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@media (prefers-reduced-motion), (update: slow) {
|
|
496
|
+
/* stylelint-disable selector-no-vendor-prefix */
|
|
497
|
+
.input-skin,
|
|
498
|
+
.input,
|
|
499
|
+
.input::placeholder,
|
|
500
|
+
.input::-webkit-input-placeholder,
|
|
501
|
+
.input::-ms-placeholder,
|
|
502
|
+
.input:-ms-placeholder,
|
|
503
|
+
.input:focus {
|
|
504
|
+
transition-duration: 0.001ms !important;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
311
507
|
</style>
|
|
312
|
-
<script lang="ts">
|
|
313
|
-
// Looks like the way to propogate boilerplate events is to
|
|
314
|
-
// just declare in template like on:blur on:focus and so on
|
|
315
|
-
// https://github.com/sveltejs/svelte/issues/585
|
|
316
|
-
// Looks like this is what smelte is doing:
|
|
317
|
-
// https://github.com/matyunya/smelte/blob/master/src/components/TextField/TextField.svelte
|
|
318
|
-
export let label = "";
|
|
319
|
-
export let id = "";
|
|
320
|
-
export let labelCss = "";
|
|
321
|
-
export let isLabelHidden = false;
|
|
322
|
-
export let helpText = "";
|
|
323
|
-
export let invalidText = "";
|
|
324
|
-
export let hasLeftAddon = false;
|
|
325
|
-
export let hasRightAddon = false;
|
|
326
|
-
export let isInvalid = false;
|
|
327
|
-
export let isInline = false;
|
|
328
|
-
export let isRounded = false;
|
|
329
|
-
export let isDisabled = undefined;
|
|
330
|
-
export let css = "";
|
|
331
|
-
export let isSkinned = true;
|
|
332
|
-
export let isUnderlinedWithBackground = false;
|
|
333
|
-
export let isUnderlined = false;
|
|
334
|
-
export let size: "small" | "large" | "" = "";
|
|
335
|
-
export let type:
|
|
336
|
-
| "text"
|
|
337
|
-
| "textarea"
|
|
338
|
-
| "email"
|
|
339
|
-
| "search"
|
|
340
|
-
| "password"
|
|
341
|
-
| "tel"
|
|
342
|
-
| "number"
|
|
343
|
-
| "url"
|
|
344
|
-
| "month"
|
|
345
|
-
| "time"
|
|
346
|
-
| "week"
|
|
347
|
-
| "date"
|
|
348
|
-
| "datetime-local"
|
|
349
|
-
| "color" = "text";
|
|
350
|
-
|
|
351
|
-
export let value = "";
|
|
352
|
-
|
|
353
|
-
$: if (!value) value = "";
|
|
354
|
-
$: inputType = type;
|
|
355
|
-
|
|
356
|
-
$: labelClasses = [
|
|
357
|
-
"label",
|
|
358
|
-
isInvalid ? "label-error" : "",
|
|
359
|
-
isInline ? "label-inline" : "",
|
|
360
|
-
size ? `label-${size}` : "",
|
|
361
|
-
isLabelHidden ? "screenreader-only" : "",
|
|
362
|
-
labelCss ? labelCss : "",
|
|
363
|
-
].filter(c => c).join(" ");
|
|
364
|
-
|
|
365
|
-
$: inputClasses = [
|
|
366
|
-
isSkinned ? "input" : "input-base",
|
|
367
|
-
isRounded ? "input-rounded" : "",
|
|
368
|
-
isUnderlined ? "input-underlined" : "",
|
|
369
|
-
hasLeftAddon ? "input-has-left-addon" : "",
|
|
370
|
-
hasRightAddon ? "input-has-right-addon" : "",
|
|
371
|
-
isDisabled ? "disabled" : "",
|
|
372
|
-
isInvalid ? "input-error" : "",
|
|
373
|
-
isInline ? "input-inline" : "",
|
|
374
|
-
isUnderlinedWithBackground ? "input-underlined-bg" : "",
|
|
375
|
-
css ? css : "",
|
|
376
|
-
size ? `input-${size}` : "",
|
|
377
|
-
].filter(c => c).join(" ");
|
|
378
|
-
|
|
379
|
-
$: invalidClasses = () => {
|
|
380
|
-
return size ? `field-error-${size}` : "field-error";
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
$: helpClasses = () => {
|
|
384
|
-
return size ? `field-help-${size}` : "field-help";
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
$: addonContainerClasses = () => "input-addon-container";
|
|
388
|
-
|
|
389
|
-
const handleInput = (e: Event) => {
|
|
390
|
-
value = (e.target as HTMLInputElement).value;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
</script>
|
|
394
|
-
<div class="w-100">
|
|
395
|
-
<label class={labelClasses} for={id}>{label}</label>
|
|
396
|
-
{#if type == "textarea"}
|
|
397
|
-
<textarea
|
|
398
|
-
id={id}
|
|
399
|
-
class={inputClasses}
|
|
400
|
-
on:blur
|
|
401
|
-
on:change
|
|
402
|
-
bind:value
|
|
403
|
-
on:click
|
|
404
|
-
on:focus
|
|
405
|
-
{...$$restProps}></textarea>
|
|
406
|
-
{:else if hasLeftAddon || hasRightAddon}
|
|
407
|
-
<div class={addonContainerClasses()}>
|
|
408
|
-
<slot name="addonLeft" />
|
|
409
|
-
<input
|
|
410
|
-
id={id}
|
|
411
|
-
type={inputType}
|
|
412
|
-
value={value}
|
|
413
|
-
class={inputClasses}
|
|
414
|
-
disabled={isDisabled}
|
|
415
|
-
on:blur
|
|
416
|
-
on:change
|
|
417
|
-
on:input={handleInput}
|
|
418
|
-
on:click
|
|
419
|
-
on:focus
|
|
420
|
-
{...$$restProps}
|
|
421
|
-
/>
|
|
422
|
-
<slot name="addonRight" />
|
|
423
|
-
</div>
|
|
424
|
-
{:else}
|
|
425
|
-
<input
|
|
426
|
-
id={id}
|
|
427
|
-
type={inputType}
|
|
428
|
-
value={value}
|
|
429
|
-
class={inputClasses}
|
|
430
|
-
disabled={isDisabled}
|
|
431
|
-
on:blur
|
|
432
|
-
on:change
|
|
433
|
-
on:input={handleInput}
|
|
434
|
-
on:click
|
|
435
|
-
on:focus
|
|
436
|
-
{...$$restProps}
|
|
437
|
-
/>
|
|
438
|
-
{/if}
|
|
439
|
-
{#if isInvalid}
|
|
440
|
-
<span role="status" aria-live="polite" class={invalidClasses()}>
|
|
441
|
-
{invalidText}
|
|
442
|
-
</span>
|
|
443
|
-
{:else if helpText}<span class={helpClasses()}>{helpText}</span>{/if}
|
|
444
|
-
</div>
|