@onsvisual/svelte-components 0.0.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/README.md +5 -0
- package/dist/@types/actions/cssVariables/index.d.ts +4 -0
- package/dist/@types/actions/resizeObserver/index.d.ts +4 -0
- package/dist/@types/components/layout/Accordion/Accordion.svelte.d.ts +27 -0
- package/dist/@types/components/layout/Accordion/AccordionItem.svelte.d.ts +31 -0
- package/dist/@types/components/layout/Container/Container.svelte.d.ts +39 -0
- package/dist/@types/components/layout/Footer/Footer.svelte.d.ts +31 -0
- package/dist/@types/components/layout/Footer/ONSLogo.svelte.d.ts +29 -0
- package/dist/@types/components/layout/Header/Header.svelte.d.ts +35 -0
- package/dist/@types/components/layout/Header/ONSLogo.svelte.d.ts +29 -0
- package/dist/@types/components/layout/Theme/Theme.svelte.d.ts +33 -0
- package/dist/@types/components/layout/Theme/themes.d.ts +29 -0
- package/dist/@types/components/layout/Twisty/Twisty.svelte.d.ts +29 -0
- package/dist/@types/components/ui/Button/Button.svelte.d.ts +43 -0
- package/dist/@types/components/ui/Button/Icon.svelte.d.ts +27 -0
- package/dist/@types/components/ui/Dropdown/Dropdown.svelte.d.ts +37 -0
- package/dist/@types/components/ui/Em/Em.svelte.d.ts +29 -0
- package/dist/@types/components/ui/Input/Input.svelte.d.ts +51 -0
- package/dist/@types/components/ui/Select/Select.svelte.d.ts +67 -0
- package/dist/@types/components/ui/Textarea/Textarea.svelte.d.ts +37 -0
- package/dist/@types/index.d.ts +13 -0
- package/dist/@types/js/utils.d.ts +10 -0
- package/dist/actions/cssVariables/index.js +20 -0
- package/dist/actions/resizeObserver/index.js +25 -0
- package/dist/components/layout/Accordion/Accordion.svelte +37 -0
- package/dist/components/layout/Accordion/AccordionItem.svelte +81 -0
- package/dist/components/layout/Container/Container.svelte +99 -0
- package/dist/components/layout/Footer/Footer.svelte +266 -0
- package/dist/components/layout/Footer/ONSLogo.svelte +150 -0
- package/dist/components/layout/Header/Header.svelte +513 -0
- package/dist/components/layout/Header/ONSLogo.svelte +150 -0
- package/dist/components/layout/Theme/Theme.svelte +91 -0
- package/dist/components/layout/Theme/themes.js +24 -0
- package/dist/components/layout/Twisty/Twisty.svelte +48 -0
- package/dist/components/ui/Button/Button.svelte +87 -0
- package/dist/components/ui/Button/Icon.svelte +50 -0
- package/dist/components/ui/Dropdown/Dropdown.svelte +62 -0
- package/dist/components/ui/Em/Em.svelte +44 -0
- package/dist/components/ui/Input/Input.svelte +163 -0
- package/dist/components/ui/Select/Select.svelte +257 -0
- package/dist/components/ui/Textarea/Textarea.svelte +99 -0
- package/dist/css/main.css +55 -0
- package/dist/globals.d.ts +23 -0
- package/dist/index.js +16 -0
- package/dist/js/utils.js +41 -0
- package/package.json +144 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { createEventDispatcher } from "svelte";
|
|
3
|
+
|
|
4
|
+
const dispatch = createEventDispatcher();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ID for <input> element
|
|
8
|
+
* @type {string}
|
|
9
|
+
*/
|
|
10
|
+
export let id = "";
|
|
11
|
+
/**
|
|
12
|
+
* A prop to bind to for the entered value
|
|
13
|
+
* @type {string}
|
|
14
|
+
*/
|
|
15
|
+
export let value = "";
|
|
16
|
+
/**
|
|
17
|
+
* A label to describe the <input> element (expected for accessibility)
|
|
18
|
+
* @type {string}
|
|
19
|
+
*/
|
|
20
|
+
export let label = "Enter some text";
|
|
21
|
+
/**
|
|
22
|
+
* Visually hide the label
|
|
23
|
+
* @type {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export let hideLabel = false;
|
|
26
|
+
/**
|
|
27
|
+
* An optional description to help users know what to enter
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
export let description = "";
|
|
31
|
+
/**
|
|
32
|
+
* The maximum number of characters that can be entered (optional)
|
|
33
|
+
* @type {number}
|
|
34
|
+
*/
|
|
35
|
+
export let charLimit = null;
|
|
36
|
+
/**
|
|
37
|
+
* The width of the <input> in characters
|
|
38
|
+
* @type {number}
|
|
39
|
+
*/
|
|
40
|
+
export let width = null;
|
|
41
|
+
/**
|
|
42
|
+
* An optional prefix (eg. £) to appear on the left of the input
|
|
43
|
+
* @type {string}
|
|
44
|
+
*/
|
|
45
|
+
export let prefix = "";
|
|
46
|
+
/**
|
|
47
|
+
* An optional suffix (eg. %) to appear on the right of the input
|
|
48
|
+
* @type {string}
|
|
49
|
+
*/
|
|
50
|
+
export let suffix = "";
|
|
51
|
+
/**
|
|
52
|
+
* An optional hidden description of the prefix (for accessibility)
|
|
53
|
+
* @type {string}
|
|
54
|
+
*/
|
|
55
|
+
export let unitLabel = prefix || suffix || "";
|
|
56
|
+
/**
|
|
57
|
+
* An optional regex pattern foto limit the input (not currently used)
|
|
58
|
+
* @type {string}
|
|
59
|
+
*/
|
|
60
|
+
export let pattern = "";
|
|
61
|
+
/**
|
|
62
|
+
* Set to `true` if the value should be a number
|
|
63
|
+
* @type {boolean}
|
|
64
|
+
*/
|
|
65
|
+
export let numeric = false;
|
|
66
|
+
/**
|
|
67
|
+
* Set to `true` to highlight border in red
|
|
68
|
+
* @type {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export let error = false;
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<div class="ons-field">
|
|
74
|
+
{#if label}
|
|
75
|
+
<label
|
|
76
|
+
class="ons-label"
|
|
77
|
+
class:ons-label--with-description="{description}"
|
|
78
|
+
class:ons-u-vh="{hideLabel}"
|
|
79
|
+
aria-describedby="{description ? `${id}-description-hint` : null}"
|
|
80
|
+
for="{id}"
|
|
81
|
+
>
|
|
82
|
+
{label}
|
|
83
|
+
</label>
|
|
84
|
+
{/if}
|
|
85
|
+
{#if description}
|
|
86
|
+
<span id="{id}-description-hint" class="ons-label__description ons-input--with-description">
|
|
87
|
+
{description}
|
|
88
|
+
</span>
|
|
89
|
+
{/if}
|
|
90
|
+
{#if prefix || suffix}
|
|
91
|
+
<span class="ons-input-type" class:ons-input-type--prefix="{prefix}">
|
|
92
|
+
<span class="ons-input-type__inner">
|
|
93
|
+
<input
|
|
94
|
+
type="text"
|
|
95
|
+
id="{id}"
|
|
96
|
+
bind:value="{value}"
|
|
97
|
+
maxlength="{charLimit}"
|
|
98
|
+
pattern="{pattern ? pattern : numeric ? '[0-9]*' : null}"
|
|
99
|
+
inputmode="{numeric ? 'numeric' : null}"
|
|
100
|
+
class="ons-input ons-input--text ons-input-type__input {Number.isInteger(width)
|
|
101
|
+
? `ons-input--w-${width}`
|
|
102
|
+
: ''}"
|
|
103
|
+
class:ons-input--error="{error}"
|
|
104
|
+
aria-labelledby="{id} {id}-unit"
|
|
105
|
+
aria-describedby="{description ? `${id}-description-hint` : null}"
|
|
106
|
+
on:change="{(e) => dispatch('change', e)}"
|
|
107
|
+
/>
|
|
108
|
+
<abbr
|
|
109
|
+
id="{id}-unit"
|
|
110
|
+
class="ons-input-type__type ons-js-input-abbr"
|
|
111
|
+
aria-label="{unitLabel}"
|
|
112
|
+
role="figure"
|
|
113
|
+
title="{unitLabel}">{prefix || suffix}</abbr
|
|
114
|
+
>
|
|
115
|
+
</span>
|
|
116
|
+
</span>
|
|
117
|
+
{:else}
|
|
118
|
+
<input
|
|
119
|
+
type="text"
|
|
120
|
+
id="{id}"
|
|
121
|
+
bind:value="{value}"
|
|
122
|
+
pattern="{pattern ? pattern : numeric ? '[0-9]*' : null}"
|
|
123
|
+
inputmode="{numeric ? 'numeric' : null}"
|
|
124
|
+
class="ons-input ons-input--text ons-input-type__input {Number.isInteger(width)
|
|
125
|
+
? `ons-input--w-${width}`
|
|
126
|
+
: ''}"
|
|
127
|
+
class:ons-input--error="{error}"
|
|
128
|
+
aria-describedby="{description ? `${id}-description-hint` : null}"
|
|
129
|
+
on:change="{(e) => dispatch('change', e)}"
|
|
130
|
+
/>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<style>
|
|
135
|
+
.ons-input {
|
|
136
|
+
background: none;
|
|
137
|
+
border-color: currentColor;
|
|
138
|
+
}
|
|
139
|
+
.ons-input--error {
|
|
140
|
+
background: none;
|
|
141
|
+
border-color: rgb(208, 2, 27);
|
|
142
|
+
}
|
|
143
|
+
.ons-input--error:focus {
|
|
144
|
+
border-color: currentColor;
|
|
145
|
+
}
|
|
146
|
+
.ons-js-input-abbr {
|
|
147
|
+
background: var(--hinted, #e2e2e3);
|
|
148
|
+
border-color: currentColor;
|
|
149
|
+
}
|
|
150
|
+
.ons-input-type--prefix .ons-input--error {
|
|
151
|
+
border-left-color: currentColor;
|
|
152
|
+
border-right-color: rgb(208, 2, 27);
|
|
153
|
+
}
|
|
154
|
+
.ons-input--error + .ons-js-input-abbr {
|
|
155
|
+
color: #e2e2e3;
|
|
156
|
+
border-color: #222;
|
|
157
|
+
}
|
|
158
|
+
.ons-input-type__input:focus + .ons-input-type__type::after {
|
|
159
|
+
box-shadow: 0 0 0 1px currentColor, 0 0 0 4px #fbc900;
|
|
160
|
+
}
|
|
161
|
+
.ons-input-type__input.ons-input--error:focus + .ons-input-type__type::after {
|
|
162
|
+
box-shadow: 0 0 0 1px #222, 0 0 0 4px #fbc900;
|
|
163
|
+
}</style>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import SelectInner from "svelte-select";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Unique id for the element
|
|
6
|
+
* @type {string}
|
|
7
|
+
*/
|
|
8
|
+
export let id = "";
|
|
9
|
+
/**
|
|
10
|
+
* The mode can be either "default" or "search"
|
|
11
|
+
* @type {"default"|"search"}
|
|
12
|
+
*/
|
|
13
|
+
export let mode = "default";
|
|
14
|
+
/**
|
|
15
|
+
* Enable multi-select mode
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
*/
|
|
18
|
+
export let multiple = false;
|
|
19
|
+
/**
|
|
20
|
+
* Set a limit on the number of items in multi-select mode
|
|
21
|
+
* @type {number}
|
|
22
|
+
*/
|
|
23
|
+
export let maxSelected = 4;
|
|
24
|
+
/**
|
|
25
|
+
* Defines whether the selection can be cleared
|
|
26
|
+
* @type {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export let clearable = true;
|
|
29
|
+
/**
|
|
30
|
+
* A label to describe the element (expected for accessibility)
|
|
31
|
+
* @type {string}
|
|
32
|
+
*/
|
|
33
|
+
export let label = "";
|
|
34
|
+
/**
|
|
35
|
+
* Visually hide the label
|
|
36
|
+
* @type {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export let hideLabel = false;
|
|
39
|
+
/**
|
|
40
|
+
* An optional placeholder text
|
|
41
|
+
* @type {string}
|
|
42
|
+
*/
|
|
43
|
+
export let placeholder = "Select an option";
|
|
44
|
+
/**
|
|
45
|
+
* A prop to bind to for the selected value (will be an array for multi-select)
|
|
46
|
+
* @type {object}
|
|
47
|
+
*/
|
|
48
|
+
export let value = null;
|
|
49
|
+
/**
|
|
50
|
+
* An array of options, formatted {id, label}
|
|
51
|
+
* @type {array}
|
|
52
|
+
*/
|
|
53
|
+
export let options = [];
|
|
54
|
+
/**
|
|
55
|
+
* The attribute of an option defines its ID
|
|
56
|
+
* @type {string}
|
|
57
|
+
*/
|
|
58
|
+
export let idKey = "id";
|
|
59
|
+
/**
|
|
60
|
+
* The attribute of an option defines its label/name
|
|
61
|
+
* @type {string}
|
|
62
|
+
*/
|
|
63
|
+
export let labelKey = "label";
|
|
64
|
+
/**
|
|
65
|
+
* Defines the width of the input in characters
|
|
66
|
+
* @type {number}
|
|
67
|
+
*/
|
|
68
|
+
export let width = 30;
|
|
69
|
+
/**
|
|
70
|
+
* An array of colours for multi-select
|
|
71
|
+
* @type {array}
|
|
72
|
+
*/
|
|
73
|
+
export let colors = [
|
|
74
|
+
"#206095",
|
|
75
|
+
"#a8bd3a",
|
|
76
|
+
"#871a5b",
|
|
77
|
+
"#27a0cc",
|
|
78
|
+
"rgb(0, 60, 87)",
|
|
79
|
+
"rgb(116, 108, 177)",
|
|
80
|
+
"rgb(246, 96, 104)",
|
|
81
|
+
"rgb(0, 163, 166)",
|
|
82
|
+
];
|
|
83
|
+
/**
|
|
84
|
+
* A function to filter items based on the entered text
|
|
85
|
+
* @type {function}
|
|
86
|
+
*/
|
|
87
|
+
export let itemFilter = (label, filterText, option) =>
|
|
88
|
+
label.match(new RegExp(`\\b${filterText}`, "i")) &&
|
|
89
|
+
!(multiple && value?.length >= maxSelected) &&
|
|
90
|
+
!(mode === "search" && filterText?.length < 3);
|
|
91
|
+
/**
|
|
92
|
+
* An optional function to load options based on the entered text
|
|
93
|
+
* @type {function}
|
|
94
|
+
*/
|
|
95
|
+
export let loadOptions = null;
|
|
96
|
+
/**
|
|
97
|
+
* Optional params for positioning the dropdown
|
|
98
|
+
* @type {object} floatingConfig
|
|
99
|
+
*/
|
|
100
|
+
export let floatingConfig = {};
|
|
101
|
+
|
|
102
|
+
$: style = colors.map((c, i) => `--item${i + 1}:${c}`).join(";");
|
|
103
|
+
$: noOptionsMessage =
|
|
104
|
+
multiple && value?.length >= maxSelected
|
|
105
|
+
? `You can only select up to ${maxSelected} options`
|
|
106
|
+
: mode === "search" && filterText.length < 3
|
|
107
|
+
? "Enter 3 or more characters to see options"
|
|
108
|
+
: filterText
|
|
109
|
+
? `No options match <b>${filterText}</b>`
|
|
110
|
+
: "No options available";
|
|
111
|
+
|
|
112
|
+
let filterText = "";
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<div class="ons-field" style="{style}">
|
|
116
|
+
{#if label}
|
|
117
|
+
<label class="ons-label" for="{id}" class:ons-u-vh="{hideLabel}">{label}</label>
|
|
118
|
+
{/if}
|
|
119
|
+
<div class="ons-themed-select {Number.isInteger(width) ? `ons-input--w-${width}` : ''}">
|
|
120
|
+
<SelectInner
|
|
121
|
+
id="{id}"
|
|
122
|
+
placeholder="{placeholder}"
|
|
123
|
+
loadOptions="{loadOptions}"
|
|
124
|
+
itemFilter="{itemFilter}"
|
|
125
|
+
floatingConfig="{floatingConfig}"
|
|
126
|
+
bind:filterText="{filterText}"
|
|
127
|
+
bind:value="{value}"
|
|
128
|
+
items="{options}"
|
|
129
|
+
itemId="{idKey}"
|
|
130
|
+
label="{labelKey}"
|
|
131
|
+
showChevron="{!value}"
|
|
132
|
+
multiple="{multiple}"
|
|
133
|
+
clearable="{clearable}"
|
|
134
|
+
on:input
|
|
135
|
+
on:change
|
|
136
|
+
on:focus
|
|
137
|
+
on:blur
|
|
138
|
+
on:clear
|
|
139
|
+
>
|
|
140
|
+
<div slot="item" let:item>
|
|
141
|
+
{@html item[labelKey].replace(
|
|
142
|
+
new RegExp(`\\b${filterText}`, "i"),
|
|
143
|
+
(str) => `<b>${str}</b>`
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
<div slot="empty">{@html noOptionsMessage}</div>
|
|
147
|
+
<div slot="chevron-icon" style:transform="{mode === "search" ? "translateY(2px)" : null}">
|
|
148
|
+
{#if mode === "search"}
|
|
149
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 12 12" width="20">
|
|
150
|
+
<path
|
|
151
|
+
fill="currentColor"
|
|
152
|
+
d="M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z"
|
|
153
|
+
></path>
|
|
154
|
+
</svg>
|
|
155
|
+
{:else}
|
|
156
|
+
<svg
|
|
157
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
158
|
+
aria-hidden="true"
|
|
159
|
+
viewBox="0 0 11.75 7.7"
|
|
160
|
+
width="18"
|
|
161
|
+
>
|
|
162
|
+
<path
|
|
163
|
+
fill="currentColor"
|
|
164
|
+
d="m1.37.15 4.5 5.1 4.5-5.1a.37.37 0 0 1 .6 0l.7.7a.45.45 0 0 1 0 .5l-5.5 6.2a.37.37 0 0 1-.6 0l-5.5-6.1a.64.64 0 0 1 0-.6l.7-.7a.64.64 0 0 1 .6 0Z"
|
|
165
|
+
></path>
|
|
166
|
+
</svg>
|
|
167
|
+
{/if}
|
|
168
|
+
</div>
|
|
169
|
+
<div slot="clear-icon" style:transform="translateY(2px)">
|
|
170
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 14 14" width="18">
|
|
171
|
+
<path
|
|
172
|
+
fill="currentColor"
|
|
173
|
+
d="M13.6 1 l -0.71 -0.71 a 0.5 0.5 0 0 0 -0.71 0 l -5.25 5.25 l -5.25 -5.25 a 0.51 0.51 0 0 0 -0.71 0 l -0.71 0.71 a 0.5 0.5 0 0 0 0 0.71 l 5.25 5.25 l -5.25 5.25 a 0.5 0.5 0 0 0 0 0.71 l 0.71 0.71 a 0.5 0.5 0 0 0 0.71 0 l 5.25 -5.25 l 5.25 5.25 a 0.5 0.5 0 0 0 0.71 0 l 0.71 -0.71 a 0.5 0.5 0 0 0 0 -0.71 l -5.25 -5.25 l 5.25 -5.25 a 0.5 0.5 0 0 0 0 -0.71Z"
|
|
174
|
+
></path>
|
|
175
|
+
</svg>
|
|
176
|
+
</div>
|
|
177
|
+
</SelectInner>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<style>
|
|
182
|
+
div[slot="empty"] {
|
|
183
|
+
background-color: rgb(226, 226, 227);
|
|
184
|
+
padding: 6px 10px;
|
|
185
|
+
}
|
|
186
|
+
.ons-themed-select {
|
|
187
|
+
--border: 1px solid currentColor;
|
|
188
|
+
--border-hover: 1px solid currentColor;
|
|
189
|
+
--border-focused: 1px solid currentColor;
|
|
190
|
+
--border-radius: 3px;
|
|
191
|
+
--font-size: 18px;
|
|
192
|
+
--placeholder-color: currentColor;
|
|
193
|
+
--padding: 0 0 0 10px;
|
|
194
|
+
--list-border: 1px solid currentColor;
|
|
195
|
+
--item-hover-bg: #003c57;
|
|
196
|
+
--item-hover-color: white;
|
|
197
|
+
--item-is-active-bg: #003c57;
|
|
198
|
+
--item-padding: 0 0 0 10px;
|
|
199
|
+
--multi-item-outline: none;
|
|
200
|
+
--multi-item-height: 30px;
|
|
201
|
+
--value-container-padding: 3px 0;
|
|
202
|
+
--multi-select-padding: 0 0 0 6px;
|
|
203
|
+
}
|
|
204
|
+
:global(.ons-themed-select > .svelte-select:focus-within) {
|
|
205
|
+
outline: 3px solid #fbc900;
|
|
206
|
+
}
|
|
207
|
+
:global(.ons-themed-select > .svelte-select:focus-within::before) {
|
|
208
|
+
content: "";
|
|
209
|
+
outline: 1px solid rgb(34, 34, 34);
|
|
210
|
+
outline-offset: -1px;
|
|
211
|
+
position: absolute;
|
|
212
|
+
width: 100%;
|
|
213
|
+
height: 100%;
|
|
214
|
+
left: 0;
|
|
215
|
+
pointer-events: none;
|
|
216
|
+
}
|
|
217
|
+
:global(.ons-themed-select .svelte-select-list) {
|
|
218
|
+
z-index: 4 !important;
|
|
219
|
+
color: #222;
|
|
220
|
+
}
|
|
221
|
+
:global(.ons-themed-select .svelte-select-list > div + div) {
|
|
222
|
+
border-top: 1px solid rgb(34, 34, 34);
|
|
223
|
+
}
|
|
224
|
+
:global(.ons-themed-select .multi-item) {
|
|
225
|
+
color: white !important;
|
|
226
|
+
font-weight: bold;
|
|
227
|
+
}
|
|
228
|
+
:global(.ons-themed-select .multi-item svg > path) {
|
|
229
|
+
fill: white !important;
|
|
230
|
+
}
|
|
231
|
+
:global(.ons-themed-select .multi-item:nth-of-type(1)) {
|
|
232
|
+
background-color: var(--item1, #ebedef) !important;
|
|
233
|
+
}
|
|
234
|
+
:global(.ons-themed-select .multi-item:nth-of-type(2)) {
|
|
235
|
+
background-color: var(--item2, #ebedef) !important;
|
|
236
|
+
}
|
|
237
|
+
:global(.ons-themed-select .multi-item:nth-of-type(3)) {
|
|
238
|
+
background-color: var(--item3, #ebedef) !important;
|
|
239
|
+
}
|
|
240
|
+
:global(.ons-themed-select .multi-item:nth-of-type(4)) {
|
|
241
|
+
background-color: var(--item4, #ebedef) !important;
|
|
242
|
+
}
|
|
243
|
+
:global(.ons-themed-select .multi-item:nth-of-type(5)) {
|
|
244
|
+
background-color: var(--item1, #ebedef) !important;
|
|
245
|
+
}
|
|
246
|
+
:global(.ons-themed-select .multi-item:nth-of-type(6)) {
|
|
247
|
+
background-color: var(--item2, #ebedef) !important;
|
|
248
|
+
}
|
|
249
|
+
:global(.ons-themed-select .multi-item:nth-of-type(7)) {
|
|
250
|
+
background-color: var(--item3, #ebedef) !important;
|
|
251
|
+
}
|
|
252
|
+
:global(.ons-themed-select .multi-item:nth-of-type(8)) {
|
|
253
|
+
background-color: var(--item4, #ebedef) !important;
|
|
254
|
+
}
|
|
255
|
+
.ons-field {
|
|
256
|
+
overflow: visible;
|
|
257
|
+
}</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { createEventDispatcher } from "svelte";
|
|
3
|
+
|
|
4
|
+
const dispatch = createEventDispatcher();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ID for <textarea> element
|
|
8
|
+
* @type {string}
|
|
9
|
+
*/
|
|
10
|
+
export let id = "";
|
|
11
|
+
/**
|
|
12
|
+
* A prop to bind to for the entered value
|
|
13
|
+
* @type {string}
|
|
14
|
+
*/
|
|
15
|
+
export let value = "";
|
|
16
|
+
/**
|
|
17
|
+
* A label to describe the <textarea> element (expected for accessibility)
|
|
18
|
+
* @type {string}
|
|
19
|
+
*/
|
|
20
|
+
export let label = "Enter some text";
|
|
21
|
+
/**
|
|
22
|
+
* Visually hide the label
|
|
23
|
+
* @type {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export let hideLabel = false;
|
|
26
|
+
/**
|
|
27
|
+
* An optional description to help users know what to enter
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
export let description = "";
|
|
31
|
+
/**
|
|
32
|
+
* The maximum number of characters that can be entered (optional)
|
|
33
|
+
* @type {number}
|
|
34
|
+
*/
|
|
35
|
+
export let charLimit = null;
|
|
36
|
+
/**
|
|
37
|
+
* The height of the <textarea> element in rows
|
|
38
|
+
* @type {number}
|
|
39
|
+
*/
|
|
40
|
+
export let rows = 8;
|
|
41
|
+
/**
|
|
42
|
+
* The width of the <textarea> element in characters
|
|
43
|
+
* @type {number}
|
|
44
|
+
*/
|
|
45
|
+
export let width = 30;
|
|
46
|
+
|
|
47
|
+
$: remaining = charLimit && value.length ? charLimit - value.length : charLimit || null;
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class="ons-field">
|
|
51
|
+
{#if label}
|
|
52
|
+
<label
|
|
53
|
+
class="ons-label ons-label--with-description"
|
|
54
|
+
class:ons-u-vh="{hideLabel}"
|
|
55
|
+
aria-describedby="{description ? `${id}-description-hint` : null}"
|
|
56
|
+
for="{id}">{label}</label
|
|
57
|
+
>
|
|
58
|
+
{/if}
|
|
59
|
+
{#if description}
|
|
60
|
+
<span id="{id}-description-hint" class="ons-label__description ons-input--with-description"
|
|
61
|
+
>{description}</span
|
|
62
|
+
>
|
|
63
|
+
{/if}
|
|
64
|
+
<textarea
|
|
65
|
+
id="{id}"
|
|
66
|
+
bind:value="{value}"
|
|
67
|
+
class="ons-input ons-input--textarea {Number.isInteger(width) ? `ons-input--w-${width}` : null}"
|
|
68
|
+
class:ons-js-char-limit-input="{charLimit}"
|
|
69
|
+
class:ons-input--limit-reached="{remaining === 0}"
|
|
70
|
+
name="{id}"
|
|
71
|
+
rows="{rows}"
|
|
72
|
+
maxlength="{charLimit}"
|
|
73
|
+
data-char-limit-ref="{id}-lim"
|
|
74
|
+
aria-describedby="textarea-char-limit-lim"></textarea>
|
|
75
|
+
{#if Number.isInteger(remaining)}
|
|
76
|
+
<span
|
|
77
|
+
id="textarea-char-limit-lim"
|
|
78
|
+
class="ons-input__limit ons-u-fs-s--b ons-u-mt-xs"
|
|
79
|
+
class:ons-input__limit--reached="{remaining === 0}"
|
|
80
|
+
data-charcount-singular="You have {remaining} character remaining"
|
|
81
|
+
data-charcount-plural="You have {remaining} characters remaining"
|
|
82
|
+
data-charcount-limit-singular=""
|
|
83
|
+
data-charcount-limit-plural=""
|
|
84
|
+
aria-live="{remaining === 0 ? 'assertive' : 'polite'}"
|
|
85
|
+
>
|
|
86
|
+
You have {remaining}
|
|
87
|
+
{remaining === 1 ? "character" : "characters"} remaining
|
|
88
|
+
</span>
|
|
89
|
+
{/if}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.ons-input--textarea {
|
|
94
|
+
background: var(--background, white);
|
|
95
|
+
border-color: currentColor;
|
|
96
|
+
}
|
|
97
|
+
.ons-input--textarea:focus {
|
|
98
|
+
box-shadow: 0 0 0 1px currentColor, 0 0 0 4px #fbc900;
|
|
99
|
+
}</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
@import url("https://cdn.ons.gov.uk/sdc/design-system/64.0.0/css/main.css");
|
|
2
|
+
@import url("https://cdn.ons.gov.uk/dp-design-system/cc82c54/css/main.css");
|
|
3
|
+
|
|
4
|
+
/* Overrides */
|
|
5
|
+
.ons-page__content {
|
|
6
|
+
padding-bottom: 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Theme settings */
|
|
10
|
+
html,
|
|
11
|
+
body {
|
|
12
|
+
--text: #222;
|
|
13
|
+
--muted: #777;
|
|
14
|
+
--hinted: #e2e2e3;
|
|
15
|
+
--pale: #f0f0f0;
|
|
16
|
+
--background: #fff;
|
|
17
|
+
--link: #206095;
|
|
18
|
+
--link-hover: #003c57;
|
|
19
|
+
}
|
|
20
|
+
.theme-wrapper {
|
|
21
|
+
color: var(--text);
|
|
22
|
+
background: var(--background);
|
|
23
|
+
--ons-color-text: var(--text);
|
|
24
|
+
--ons-color-text-light: var(--muted);
|
|
25
|
+
--ons-color-borders: var(--muted);
|
|
26
|
+
}
|
|
27
|
+
a {
|
|
28
|
+
color: var(--link, --ons-color-text-link);
|
|
29
|
+
}
|
|
30
|
+
a:hover {
|
|
31
|
+
text-decoration: underline solid
|
|
32
|
+
var(--link-hover, --ons-color-text-link-hover) 2px;
|
|
33
|
+
}
|
|
34
|
+
a:hover {
|
|
35
|
+
color: var(--link-hover, --ons-color-text-link-hover);
|
|
36
|
+
}
|
|
37
|
+
.form-label-bold {
|
|
38
|
+
color: var(--text, #222);
|
|
39
|
+
}
|
|
40
|
+
.form-control {
|
|
41
|
+
background: none;
|
|
42
|
+
border-color: var(--text, #222);
|
|
43
|
+
color: var(--text, #222);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Additional global styles */
|
|
47
|
+
.ons-spacer {
|
|
48
|
+
display: block;
|
|
49
|
+
height: 40px;
|
|
50
|
+
margin: 0;
|
|
51
|
+
padding: 0;
|
|
52
|
+
}
|
|
53
|
+
.ons-font-weight__normal {
|
|
54
|
+
font-weight: normal;
|
|
55
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ChartbeatConfig {
|
|
2
|
+
uid?: number;
|
|
3
|
+
domain?: string;
|
|
4
|
+
flickerControl?: boolean;
|
|
5
|
+
useCanonical?: boolean;
|
|
6
|
+
useCanonicalDomain?: boolean;
|
|
7
|
+
sections?: string;
|
|
8
|
+
authors?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
// eslint-disable-next-line no-unused-vars
|
|
13
|
+
interface Window {
|
|
14
|
+
/** Google analytics dataLayer */
|
|
15
|
+
dataLayer: Record<string, any>,
|
|
16
|
+
/** Chartbeat config */
|
|
17
|
+
_sf_async_config: ChartbeatConfig,
|
|
18
|
+
/** Chartbeat method */
|
|
19
|
+
pSUPERFLY: (config: { path: string, title: string }) => void,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Layout
|
|
2
|
+
export { default as Accordion } from "./components/layout/Accordion/Accordion.svelte";
|
|
3
|
+
export { default as Container } from "./components/layout/Container/Container.svelte";
|
|
4
|
+
export { default as Footer } from "./components/layout/Footer/Footer.svelte";
|
|
5
|
+
export { default as Header } from "./components/layout/Header/Header.svelte";
|
|
6
|
+
export { default as Section } from "./components/layout/Section/Section.svelte";
|
|
7
|
+
export { default as Theme } from "./components/layout/Theme/Theme.svelte";
|
|
8
|
+
export { default as Twisty } from "./components/layout/Twisty/Twisty.svelte";
|
|
9
|
+
|
|
10
|
+
// UI
|
|
11
|
+
export { default as Button } from "./components/ui/Button/Button.svelte";
|
|
12
|
+
export { default as Dropdown } from "./components/ui/Dropdown/Dropdown.svelte";
|
|
13
|
+
export { default as Em } from "./components/ui/Em/Em.svelte";
|
|
14
|
+
export { default as Input } from "./components/ui/Input/Input.svelte";
|
|
15
|
+
export { default as Select } from "./components/ui/Select/Select.svelte";
|
|
16
|
+
export { default as Textarea } from "./components/ui/Textarea/Textarea.svelte";
|
package/dist/js/utils.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// const randomString = () => {
|
|
2
|
+
// return Math.random().toString(16).slice(2, 8);
|
|
3
|
+
// };
|
|
4
|
+
|
|
5
|
+
export const slugify = (str, suffix = true) =>
|
|
6
|
+
str
|
|
7
|
+
.toString()
|
|
8
|
+
.normalize("NFD")
|
|
9
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.trim()
|
|
12
|
+
.replace(/\s+/g, "-")
|
|
13
|
+
.replace(/[^\w-]+/g, "")
|
|
14
|
+
.replace(/--+/g, "-");
|
|
15
|
+
// .concat(suffix ? `-${randomString()}` : "");
|
|
16
|
+
|
|
17
|
+
export const validDate = (str) => {
|
|
18
|
+
const date = new Date(str);
|
|
19
|
+
return !isNaN(date);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const formatDate = (
|
|
23
|
+
str,
|
|
24
|
+
locale = "en-GB",
|
|
25
|
+
opts = { year: "numeric", month: "long", day: "2-digit" }
|
|
26
|
+
) => {
|
|
27
|
+
const date = new Date(str);
|
|
28
|
+
return date.toLocaleDateString(locale, opts);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const commas = (num) => {
|
|
32
|
+
const parts = String(num).split(".");
|
|
33
|
+
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
34
|
+
return parts.join(".");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const ascending = (a, b) =>
|
|
38
|
+
a == null || b == null ? NaN : a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
|
|
39
|
+
|
|
40
|
+
export const descending = (a, b) =>
|
|
41
|
+
a == null || b == null ? NaN : b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
|