@onsvisual/svelte-components 1.0.41 → 1.0.42

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.
Files changed (222) hide show
  1. package/README.md +24 -24
  2. package/dist/css/main.css +513 -513
  3. package/dist/datavis/BarChart/BarChart.stories.svelte +84 -84
  4. package/dist/datavis/BarChart/docs/component.md +19 -19
  5. package/dist/datavis/Chart/Chart.stories.svelte +128 -128
  6. package/dist/datavis/Chart/docs/component.md +31 -31
  7. package/dist/datavis/Chart/docs/example.md +28 -28
  8. package/dist/datavis/ColumnChart/ColumnChart.stories.svelte +84 -84
  9. package/dist/datavis/ColumnChart/docs/component.md +19 -19
  10. package/dist/datavis/DataCard/DataCard.stories.svelte +45 -45
  11. package/dist/datavis/DataCard/DataCard.svelte +70 -70
  12. package/dist/datavis/DataCard/Sparkline.svelte +117 -117
  13. package/dist/datavis/DataCard/docs/component.md +20 -20
  14. package/dist/datavis/DataCard/docs/example.md +25 -25
  15. package/dist/datavis/DotPlotChart/DotPlotChart.stories.svelte +40 -40
  16. package/dist/datavis/DotPlotChart/docs/component.md +19 -19
  17. package/dist/datavis/LineChart/LineChart.stories.svelte +64 -64
  18. package/dist/datavis/LineChart/docs/component.md +31 -31
  19. package/dist/datavis/ScatterChart/ScatterChart.stories.svelte +55 -55
  20. package/dist/datavis/ScatterChart/docs/component.md +53 -53
  21. package/dist/datavis/Table/Table.stories.svelte +48 -48
  22. package/dist/datavis/Table/Table.svelte +161 -161
  23. package/dist/datavis/Table/docs/component.md +20 -20
  24. package/dist/datavis/demo-data/data-scatter.js +40 -40
  25. package/dist/datavis/demo-data/data.js +18 -18
  26. package/dist/datavis/intro.mdx +21 -21
  27. package/dist/decorators/Blockquote/Blockquote.stories.svelte +25 -25
  28. package/dist/decorators/Blockquote/Blockquote.svelte +27 -27
  29. package/dist/decorators/Blockquote/docs/component.md +10 -10
  30. package/dist/decorators/Divider/Divider.stories.svelte +29 -29
  31. package/dist/decorators/Divider/Divider.svelte +52 -52
  32. package/dist/decorators/Divider/docs/component.md +12 -12
  33. package/dist/decorators/Em/Em.stories.svelte +30 -30
  34. package/dist/decorators/Em/Em.svelte +58 -58
  35. package/dist/decorators/Em/docs/component.md +12 -12
  36. package/dist/decorators/Icon/Icon.stories.svelte +27 -27
  37. package/dist/decorators/Icon/Icon.svelte +93 -93
  38. package/dist/decorators/Icon/docs/component.md +10 -10
  39. package/dist/decorators/Indent/Indent.stories.svelte +22 -22
  40. package/dist/decorators/Indent/Indent.svelte +3 -3
  41. package/dist/decorators/Indent/docs/component.md +10 -10
  42. package/dist/index.js +86 -86
  43. package/dist/inputs/Button/Button.stories.svelte +70 -70
  44. package/dist/inputs/Button/Button.svelte +152 -152
  45. package/dist/inputs/Button/docs/component.md +17 -17
  46. package/dist/inputs/ButtonGroup/ButtonGroup.stories.svelte +40 -40
  47. package/dist/inputs/ButtonGroup/ButtonGroup.svelte +57 -57
  48. package/dist/inputs/ButtonGroup/ButtonGroupItem.svelte +101 -101
  49. package/dist/inputs/ButtonGroup/docs/component.md +23 -23
  50. package/dist/inputs/Checkbox/Checkbox.stories.svelte +34 -34
  51. package/dist/inputs/Checkbox/Checkbox.svelte +180 -180
  52. package/dist/inputs/Checkbox/docs/component.md +14 -14
  53. package/dist/inputs/Checkboxes/Checkboxes.stories.svelte +34 -34
  54. package/dist/inputs/Checkboxes/Checkboxes.svelte +62 -62
  55. package/dist/inputs/Checkboxes/docs/component.md +20 -20
  56. package/dist/inputs/Checkboxes/docs/example.md +16 -16
  57. package/dist/inputs/Dropdown/Dropdown.stories.svelte +54 -54
  58. package/dist/inputs/Dropdown/Dropdown.svelte +66 -66
  59. package/dist/inputs/Dropdown/docs/component.md +22 -22
  60. package/dist/inputs/ErrorPanel/ErrorPanel.stories.svelte +25 -25
  61. package/dist/inputs/ErrorPanel/ErrorPanel.svelte +24 -24
  62. package/dist/inputs/ErrorPanel/docs/component.md +14 -14
  63. package/dist/inputs/ErrorSummary/ErrorSummary.stories.svelte +34 -34
  64. package/dist/inputs/ErrorSummary/ErrorSummary.svelte +47 -47
  65. package/dist/inputs/ErrorSummary/docs/component.md +17 -17
  66. package/dist/inputs/ErrorSummary/docs/example.md +12 -12
  67. package/dist/inputs/Input/Input.stories.svelte +73 -73
  68. package/dist/inputs/Input/Input.svelte +151 -151
  69. package/dist/inputs/Input/docs/component.md +16 -16
  70. package/dist/inputs/Radios/Radio.svelte +90 -90
  71. package/dist/inputs/Radios/Radios.stories.svelte +51 -51
  72. package/dist/inputs/Radios/Radios.svelte +62 -62
  73. package/dist/inputs/Radios/docs/component.md +24 -24
  74. package/dist/inputs/Radios/docs/example.md +21 -21
  75. package/dist/inputs/Select/Select.stories.svelte +63 -63
  76. package/dist/inputs/Select/Select.svelte +326 -326
  77. package/dist/inputs/Select/docs/component.md +27 -27
  78. package/dist/inputs/Textarea/Textarea.stories.svelte +40 -40
  79. package/dist/inputs/Textarea/Textarea.svelte +113 -113
  80. package/dist/inputs/Textarea/docs/component.md +16 -16
  81. package/dist/inputs/Toolbar/HelpModal.svelte +234 -234
  82. package/dist/inputs/Toolbar/ToolControl.svelte +23 -23
  83. package/dist/inputs/Toolbar/ToolControls.svelte +9 -9
  84. package/dist/inputs/Toolbar/Toolbar.stories.svelte +148 -148
  85. package/dist/inputs/Toolbar/Toolbar.svelte +70 -70
  86. package/dist/inputs/Toolbar/ToolbarButton.svelte +184 -184
  87. package/dist/inputs/Toolbar/ToolbarDivider.svelte +29 -29
  88. package/dist/inputs/Toolbar/ToolbarIcon.svelte +106 -106
  89. package/dist/inputs/Toolbar/ToolbarsContainer.svelte +69 -69
  90. package/dist/inputs/Toolbar/docs/component.md +101 -101
  91. package/dist/intro.mdx +66 -66
  92. package/dist/js/menuOptions.js +14 -14
  93. package/dist/js/utils.js +133 -133
  94. package/dist/js/withParams.js +43 -43
  95. package/dist/layout/Accordion/Accordion.stories.svelte +30 -30
  96. package/dist/layout/Accordion/Accordion.svelte +55 -55
  97. package/dist/layout/Accordion/AccordionItem.svelte +51 -51
  98. package/dist/layout/Accordion/accordion.js +64 -64
  99. package/dist/layout/Accordion/details.js +83 -83
  100. package/dist/layout/Accordion/docs/component.md +19 -19
  101. package/dist/layout/AnalyticsBanner/AnalyticsBanner.stories.svelte +16 -16
  102. package/dist/layout/AnalyticsBanner/AnalyticsBanner.svelte +314 -314
  103. package/dist/layout/AnalyticsBanner/docs/component.md +44 -44
  104. package/dist/layout/BackLink/BackLink.stories.svelte +16 -16
  105. package/dist/layout/BackLink/BackLink.svelte +30 -30
  106. package/dist/layout/BackLink/docs/component.md +12 -12
  107. package/dist/layout/Breadcrumb/Breadcrumb.stories.svelte +31 -31
  108. package/dist/layout/Breadcrumb/Breadcrumb.svelte +69 -69
  109. package/dist/layout/Breadcrumb/docs/component.md +15 -15
  110. package/dist/layout/Card/Card.stories.svelte +39 -39
  111. package/dist/layout/Card/Card.svelte +127 -127
  112. package/dist/layout/Card/docs/component.md +14 -14
  113. package/dist/layout/Card/docs/eg-images.md +27 -27
  114. package/dist/layout/Card/docs/eg-links.md +12 -12
  115. package/dist/layout/Card/docs/eg-spans.md +12 -12
  116. package/dist/layout/Contents/Contents.stories.svelte +27 -27
  117. package/dist/layout/Contents/Contents.svelte +51 -51
  118. package/dist/layout/Contents/docs/component.md +18 -18
  119. package/dist/layout/DescriptionList/DescriptionList.stories.svelte +22 -22
  120. package/dist/layout/DescriptionList/DescriptionList.svelte +59 -59
  121. package/dist/layout/DescriptionList/docs/component.md +18 -18
  122. package/dist/layout/Details/Details.stories.svelte +32 -32
  123. package/dist/layout/Details/Details.svelte +75 -75
  124. package/dist/layout/Details/docs/component.md +14 -14
  125. package/dist/layout/DocumentList/Document.svelte +103 -103
  126. package/dist/layout/DocumentList/DocumentList.stories.svelte +88 -88
  127. package/dist/layout/DocumentList/DocumentList.svelte +33 -33
  128. package/dist/layout/DocumentList/docs/component.md +28 -28
  129. package/dist/layout/DocumentList/docs/example.md +23 -23
  130. package/dist/layout/ErrorPage/ErrorPage.stories.svelte +18 -18
  131. package/dist/layout/ErrorPage/ErrorPage.svelte +48 -48
  132. package/dist/layout/ErrorPage/docs/component.md +13 -13
  133. package/dist/layout/Footer/Footer.stories.svelte +24 -24
  134. package/dist/layout/Footer/Footer.svelte +366 -366
  135. package/dist/layout/Footer/docs/component.md +10 -10
  136. package/dist/layout/Grid/Grid.stories.svelte +50 -50
  137. package/dist/layout/Grid/Grid.svelte +117 -117
  138. package/dist/layout/Grid/GridCell.svelte +65 -65
  139. package/dist/layout/Grid/docs/component.md +14 -14
  140. package/dist/layout/Header/Header.stories.svelte +26 -26
  141. package/dist/layout/Header/Header.svelte +875 -875
  142. package/dist/layout/Header/docs/component.md +11 -11
  143. package/dist/layout/Hero/Hero.stories.svelte +79 -79
  144. package/dist/layout/Hero/Hero.svelte +364 -364
  145. package/dist/layout/Hero/docs/component.md +14 -14
  146. package/dist/layout/Highlight/Highlight.stories.svelte +29 -29
  147. package/dist/layout/Highlight/Highlight.svelte +77 -77
  148. package/dist/layout/Highlight/docs/component.md +12 -12
  149. package/dist/layout/Image/Image.stories.svelte +23 -23
  150. package/dist/layout/Image/Image.svelte +29 -29
  151. package/dist/layout/Image/docs/component.md +15 -15
  152. package/dist/layout/List/Li.svelte +3 -3
  153. package/dist/layout/List/List.stories.svelte +40 -40
  154. package/dist/layout/List/List.svelte +46 -46
  155. package/dist/layout/List/docs/component.md +14 -14
  156. package/dist/layout/List/docs/example.md +12 -12
  157. package/dist/layout/NavSections/NavSection.svelte +90 -90
  158. package/dist/layout/NavSections/NavSections.stories.svelte +51 -51
  159. package/dist/layout/NavSections/NavSections.svelte +160 -160
  160. package/dist/layout/NavSections/docs/component.md +25 -25
  161. package/dist/layout/Notice/Notice.stories.svelte +61 -61
  162. package/dist/layout/Notice/Notice.svelte +56 -56
  163. package/dist/layout/Notice/docs/component.md +14 -14
  164. package/dist/layout/PhaseBanner/PhaseBanner.stories.svelte +24 -24
  165. package/dist/layout/PhaseBanner/PhaseBanner.svelte +66 -66
  166. package/dist/layout/PhaseBanner/docs/component.md +14 -14
  167. package/dist/layout/RelatedContent/RelatedContent.stories.svelte +36 -36
  168. package/dist/layout/RelatedContent/RelatedContent.svelte +54 -54
  169. package/dist/layout/RelatedContent/docs/component.md +16 -16
  170. package/dist/layout/Scroller/Scroller.stories.svelte +60 -60
  171. package/dist/layout/Scroller/Scroller.svelte +368 -368
  172. package/dist/layout/Scroller/ScrollerSection.svelte +70 -70
  173. package/dist/layout/Scroller/docs/component.md +39 -39
  174. package/dist/layout/Section/Section.stories.svelte +33 -33
  175. package/dist/layout/Section/Section.svelte +60 -60
  176. package/dist/layout/Section/docs/component.md +12 -12
  177. package/dist/layout/ShareButtons/ShareButtons.stories.svelte +20 -20
  178. package/dist/layout/ShareButtons/ShareButtons.svelte +131 -131
  179. package/dist/layout/ShareButtons/docs/component.md +14 -14
  180. package/dist/layout/SkipLink/SkipLink.stories.svelte +16 -16
  181. package/dist/layout/SkipLink/SkipLink.svelte +9 -9
  182. package/dist/layout/SkipLink/docs/component.md +11 -11
  183. package/dist/layout/Summary/Summary.stories.svelte +21 -21
  184. package/dist/layout/Summary/Summary.svelte +60 -60
  185. package/dist/layout/Summary/docs/component.md +17 -17
  186. package/dist/layout/Tabs/Tab.svelte +53 -53
  187. package/dist/layout/Tabs/Tabs.stories.svelte +29 -29
  188. package/dist/layout/Tabs/Tabs.svelte +89 -89
  189. package/dist/layout/Tabs/docs/component.md +16 -16
  190. package/dist/layout/Tabs/tabs.js +302 -302
  191. package/dist/layout/Timeline/Timeline.stories.svelte +44 -44
  192. package/dist/layout/Timeline/Timeline.svelte +17 -17
  193. package/dist/layout/Timeline/TimelineItem.svelte +14 -14
  194. package/dist/layout/Timeline/docs/component.md +27 -27
  195. package/dist/layout/Timeline/docs/example.md +20 -20
  196. package/dist/templates/EmbedArticle/EmbedArticle.stories.svelte +72 -72
  197. package/dist/templates/EmbedArticle/docs/component.md +56 -56
  198. package/dist/templates/FeatureArticle/FeatureArticle.stories.svelte +150 -150
  199. package/dist/templates/FeatureArticle/docs/component.md +125 -125
  200. package/dist/templates/StandardArticle/StandardArticle.stories.svelte +86 -86
  201. package/dist/templates/StandardArticle/docs/component.md +76 -76
  202. package/dist/templates/intro.mdx +18 -18
  203. package/dist/wrappers/Container/Container.stories.svelte +38 -38
  204. package/dist/wrappers/Container/Container.svelte +77 -77
  205. package/dist/wrappers/Container/docs/component.md +12 -12
  206. package/dist/wrappers/Embed/Embed.stories.svelte +24 -24
  207. package/dist/wrappers/Embed/Embed.svelte +44 -44
  208. package/dist/wrappers/Embed/docs/component.md +15 -15
  209. package/dist/wrappers/LazyLoad/LazyLoad.stories.svelte +37 -37
  210. package/dist/wrappers/LazyLoad/LazyLoad.svelte +50 -50
  211. package/dist/wrappers/LazyLoad/docs/component.md +29 -29
  212. package/dist/wrappers/Main/Main.stories.svelte +24 -24
  213. package/dist/wrappers/Main/Main.svelte +11 -11
  214. package/dist/wrappers/Main/docs/component.md +16 -16
  215. package/dist/wrappers/Observe/Observe.stories.svelte +29 -29
  216. package/dist/wrappers/Observe/Observe.svelte +35 -35
  217. package/dist/wrappers/Observe/docs/component.md +22 -22
  218. package/dist/wrappers/Theme/Theme.stories.svelte +70 -70
  219. package/dist/wrappers/Theme/Theme.svelte +76 -76
  220. package/dist/wrappers/Theme/docs/component.md +10 -10
  221. package/dist/wrappers/Theme/themes.js +70 -70
  222. package/package.json +88 -88
@@ -1,326 +1,326 @@
1
- <script>
2
- // @ts-nocheck
3
-
4
- import { onMount, createEventDispatcher } from "svelte";
5
- import Dropdown from "../Dropdown/Dropdown.svelte";
6
- import Input from "../Input/Input.svelte";
7
-
8
- const dispatch = createEventDispatcher();
9
- const sleep = (ms = 1000) => new Promise((resolve) => setTimeout(resolve, ms));
10
- const chevron = (opts) =>
11
- `<svg class="${opts?.className}" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 11.75 7.7" width="18" style="z-index:1"><path fill="currentColor" 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"></path></svg>`;
12
-
13
- let inputElement;
14
- let scriptLoaded;
15
- let accessibleAutocomplete;
16
- let hideMenu = false;
17
-
18
- /**
19
- * Unique id for the HTML element
20
- * @type {string}
21
- */
22
- export let id = "autocomplete";
23
- /**
24
- * Name for the HTML element
25
- * @type {string}
26
- */
27
- export let name = id;
28
- /**
29
- * The mode can be either "default" or "search"
30
- * @type {"default"|"search"}
31
- */
32
- export let mode = "default";
33
- /**
34
- * Defines whether the selection can be cleared
35
- * @type {boolean}
36
- */
37
- export let clearable = mode !== "search";
38
- /**
39
- * Clear value on selection (default for "search" mode)
40
- * @type {boolean}
41
- */
42
- export let autoClear = mode === "search";
43
- /**
44
- * A label to describe the element (expected for accessibility)
45
- * @type {string|null}
46
- */
47
- export let label = mode === "search" ? "Type to select" : "Select an option";
48
- /**
49
- * Visually hide the label
50
- * @type {boolean}
51
- */
52
- export let hideLabel = false;
53
- /**
54
- * An optional placeholder text
55
- * @type {string}
56
- */
57
- export let placeholder = mode === "search" ? "Enter text" : "Select one";
58
- /**
59
- * A prop to bind to for the selected value
60
- * @type {object}
61
- */
62
- export let value = null;
63
- /**
64
- * An array of options, formatted {id, label}
65
- * @type {array}
66
- */
67
- export let options = [];
68
- /**
69
- * The attribute of an option that defines its label/name
70
- * @type {string}
71
- */
72
- export let labelKey = "label";
73
- /**
74
- * The attribute of an option that defines its group (optional)
75
- * @type {string|null}
76
- */
77
- export let groupKey = null;
78
- /**
79
- * When using SSR or pre-rendering, this option will render a text input or dropdown before the page is hydrated (allows for progressive enhancement).
80
- * @type {boolean}
81
- */
82
- export let renderFallback = false;
83
- /**
84
- * Optional: Minimum query length to return results
85
- * @type {number}
86
- */
87
- export let minLength = mode === "search" ? 1 : 0;
88
- /**
89
- * Optional: Override function for loading/filtering options based on the entered text
90
- * @type {function}
91
- */
92
- export let loadOptions = (query, populateResults) => {
93
- const filteredResults =
94
- mode !== "search" && options.map((opt) => opt[labelKey]).includes(query)
95
- ? options
96
- : options.filter((opt) =>
97
- opt[labelKey].match(new RegExp(`\\b${query.replace(/[^\w\s]/gi, "")}`, "i"))
98
- );
99
- populateResults(filteredResults);
100
- };
101
- /**
102
- * Optional: Override the default CDN URL for the accessible-autocomplete script
103
- * @type {string}
104
- */
105
- export let scriptUrl =
106
- "https://cdn.ons.gov.uk/vendor/accessible-autocomplete/3.0.1/accessible-autocomplete.min.js";
107
- /**
108
- * Call this function externally to clear the input
109
- * @type {function}
110
- */
111
- export let clearInput = async () => {
112
- await setInputValue(null);
113
- dispatch("clear", null);
114
- };
115
- /**
116
- * Optional: Set an additional CSS class for the component
117
- * @type {string|null}
118
- */
119
- export let cls = null;
120
-
121
- // This method is a bit of a hack, but no better options available at present
122
- // https://github.com/alphagov/accessible-autocomplete/issues/390
123
- async function setInputValue(textValue) {
124
- hideMenu = true;
125
- inputElement.value = textValue || "";
126
- await sleep(110);
127
- inputElement.focus({ preventScroll: true });
128
- inputElement.blur();
129
- hideMenu = false;
130
- }
131
-
132
- function inputValueTemplate(result) {
133
- return result && result[labelKey];
134
- }
135
-
136
- function highlight(text, query = "") {
137
- return text.replace(
138
- new RegExp(`\\b${query.replace(/[^\w\s]/gi, "")}`, "i"),
139
- (str) => `<b>${str}</b>`
140
- );
141
- }
142
-
143
- function suggestionTemplate(result) {
144
- const query = inputElement?.value || "";
145
- return (
146
- result &&
147
- (groupKey
148
- ? `${highlight(result?.[labelKey] || "", query)} <span class="muted-text">${
149
- result[groupKey]
150
- }</span>`
151
- : highlight(result?.[labelKey] || "", query))
152
- );
153
- }
154
-
155
- async function select(option) {
156
- if (option && value !== option) {
157
- value = option;
158
- dispatch("change", value);
159
- if (value && autoClear) {
160
- await sleep(0);
161
- clearInput();
162
- }
163
- }
164
- }
165
-
166
- function inputChange(e) {
167
- if (!e.target.value) select(null);
168
- }
169
-
170
- function handleScriptLoad() {
171
- if (!scriptLoaded && window?.accessibleAutocomplete) {
172
- accessibleAutocomplete = window.accessibleAutocomplete;
173
- scriptLoaded = true;
174
- }
175
- }
176
-
177
- function initAutocomplete(element) {
178
- accessibleAutocomplete({
179
- element,
180
- id,
181
- name,
182
- source: loadOptions,
183
- autoselect: true,
184
- onConfirm: select,
185
- confirmOnBlur: false,
186
- placeholder,
187
- displayMenu: "overlay",
188
- showAllValues: mode === "default",
189
- dropdownArrow: chevron,
190
- minLength,
191
- templates: {
192
- inputValue: inputValueTemplate,
193
- suggestion: suggestionTemplate
194
- }
195
- });
196
- inputElement = element.querySelector(`#${id}`);
197
- setInputValue(value?.[labelKey] || "");
198
- inputElement.addEventListener("blur", inputChange);
199
- }
200
-
201
- // In case input value is updated from outside component
202
- function bindInputValue(value) {
203
- if (inputElement) {
204
- const textValue = value?.[labelKey];
205
- if (textValue && inputElement.value !== textValue) setInputValue(textValue);
206
- else if (!value && inputElement.value) setInputValue("");
207
- }
208
- }
209
- $: bindInputValue(value);
210
-
211
- onMount(handleScriptLoad);
212
- </script>
213
-
214
- <svelte:head>
215
- <script src={scriptUrl} on:load={handleScriptLoad}></script>
216
- </svelte:head>
217
-
218
- {#if renderFallback && !scriptLoaded}
219
- {#if mode === "search"}
220
- <Input {id} {name} {label} {hideLabel} value={value?.[labelKey]} />
221
- {:else}
222
- <Dropdown {id} {name} {options} {label} {hideLabel} {placeholder} {value} />
223
- {/if}
224
- {:else}
225
- <div class="ons-field {cls}">
226
- {#if label}<label for={id} class="ons-label" class:ons-u-vh={hideLabel}>{label}</label>{/if}
227
- <div class="ons-autocomplete-wrapper">
228
- {#if scriptLoaded}
229
- <div
230
- id="{id}-container"
231
- class="ons-autocomplete"
232
- class:hide-menu={hideMenu}
233
- use:initAutocomplete
234
- ></div>
235
- {#if clearable && !autoClear && value}
236
- <button
237
- type="reset"
238
- title="Clear selection"
239
- aria-label="Clear selection"
240
- on:click={clearInput}
241
- class="ons-autocomplete-clear"
242
- >
243
- <svg
244
- xmlns="http://www.w3.org/2000/svg"
245
- aria-hidden="true"
246
- viewBox="0 0 14 14"
247
- width="18"
248
- >
249
- <path
250
- fill="currentColor"
251
- 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"
252
- ></path>
253
- </svg>
254
- </button>
255
- {/if}
256
- {/if}
257
- </div>
258
- </div>
259
- {/if}
260
-
261
- <style>
262
- .ons-autocomplete-wrapper {
263
- position: relative;
264
- }
265
- .ons-autocomplete-clear {
266
- position: absolute;
267
- display: flex;
268
- align-items: center;
269
- align-content: center;
270
- z-index: 1;
271
- right: 3px;
272
- top: calc(50% - 14px);
273
- height: 28px;
274
- width: 28px;
275
- border: none;
276
- background: var(--ons-color-input-bg, white);
277
- }
278
- .ons-autocomplete-clear:focus {
279
- outline: 3px solid var(--ons-color-focus, #fbc900) !important;
280
- }
281
- .hide-menu :global(.autocomplete__menu) {
282
- display: none;
283
- }
284
- .ons-autocomplete :global(.autocomplete__input) {
285
- border-radius: 3px !important;
286
- border-width: 1px !important;
287
- background: var(--ons-color-input-bg, white);
288
- }
289
- .ons-autocomplete :global(.autocomplete__input--focused) {
290
- box-shadow: inset 0 0 0 1px black !important;
291
- outline-color: var(--ons-color-focus, #fbc900) !important;
292
- }
293
- .ons-autocomplete :global(.autocomplete__dropdown-arrow-down) {
294
- width: 18px !important;
295
- transform: translateY(-2px);
296
- }
297
- .ons-autocomplete :global(.muted-text) {
298
- opacity: 0.8;
299
- font-size: smaller;
300
- }
301
- .ons-autocomplete-wrapper :global(*) {
302
- font-size: 18px;
303
- }
304
- .ons-autocomplete-wrapper :global(.autocomplete__hint),
305
- .ons-autocomplete-wrapper :global(.autocomplete__input) {
306
- height: 40px;
307
- }
308
- .ons-autocomplete-wrapper :global(.autocomplete__option) {
309
- margin: 0;
310
- }
311
- .ons-autocomplete-wrapper :global(.autocomplete__menu) {
312
- transform: translateY(5px);
313
- border: 1px solid currentColor;
314
- border-radius: 3px;
315
- }
316
- .ons-autocomplete-wrapper :global(.autocomplete__option--focused),
317
- .ons-autocomplete-wrapper :global(.autocomplete__option:hover) {
318
- background-color: var(--ons-color-branded-secondary, #003c57);
319
- }
320
- .ons-autocomplete-wrapper :global(.autocomplete__option:focus) {
321
- outline: none !important;
322
- }
323
- .ons-autocomplete-wrapper :global(input) {
324
- padding: 0 35px 0 8px;
325
- }
326
- </style>
1
+ <script>
2
+ // @ts-nocheck
3
+
4
+ import { onMount, createEventDispatcher } from "svelte";
5
+ import Dropdown from "../Dropdown/Dropdown.svelte";
6
+ import Input from "../Input/Input.svelte";
7
+
8
+ const dispatch = createEventDispatcher();
9
+ const sleep = (ms = 1000) => new Promise((resolve) => setTimeout(resolve, ms));
10
+ const chevron = (opts) =>
11
+ `<svg class="${opts?.className}" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 11.75 7.7" width="18" style="z-index:1"><path fill="currentColor" 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"></path></svg>`;
12
+
13
+ let inputElement;
14
+ let scriptLoaded;
15
+ let accessibleAutocomplete;
16
+ let hideMenu = false;
17
+
18
+ /**
19
+ * Unique id for the HTML element
20
+ * @type {string}
21
+ */
22
+ export let id = "autocomplete";
23
+ /**
24
+ * Name for the HTML element
25
+ * @type {string}
26
+ */
27
+ export let name = id;
28
+ /**
29
+ * The mode can be either "default" or "search"
30
+ * @type {"default"|"search"}
31
+ */
32
+ export let mode = "default";
33
+ /**
34
+ * Defines whether the selection can be cleared
35
+ * @type {boolean}
36
+ */
37
+ export let clearable = mode !== "search";
38
+ /**
39
+ * Clear value on selection (default for "search" mode)
40
+ * @type {boolean}
41
+ */
42
+ export let autoClear = mode === "search";
43
+ /**
44
+ * A label to describe the element (expected for accessibility)
45
+ * @type {string|null}
46
+ */
47
+ export let label = mode === "search" ? "Type to select" : "Select an option";
48
+ /**
49
+ * Visually hide the label
50
+ * @type {boolean}
51
+ */
52
+ export let hideLabel = false;
53
+ /**
54
+ * An optional placeholder text
55
+ * @type {string}
56
+ */
57
+ export let placeholder = mode === "search" ? "Enter text" : "Select one";
58
+ /**
59
+ * A prop to bind to for the selected value
60
+ * @type {object}
61
+ */
62
+ export let value = null;
63
+ /**
64
+ * An array of options, formatted {id, label}
65
+ * @type {array}
66
+ */
67
+ export let options = [];
68
+ /**
69
+ * The attribute of an option that defines its label/name
70
+ * @type {string}
71
+ */
72
+ export let labelKey = "label";
73
+ /**
74
+ * The attribute of an option that defines its group (optional)
75
+ * @type {string|null}
76
+ */
77
+ export let groupKey = null;
78
+ /**
79
+ * When using SSR or pre-rendering, this option will render a text input or dropdown before the page is hydrated (allows for progressive enhancement).
80
+ * @type {boolean}
81
+ */
82
+ export let renderFallback = false;
83
+ /**
84
+ * Optional: Minimum query length to return results
85
+ * @type {number}
86
+ */
87
+ export let minLength = mode === "search" ? 1 : 0;
88
+ /**
89
+ * Optional: Override function for loading/filtering options based on the entered text
90
+ * @type {function}
91
+ */
92
+ export let loadOptions = (query, populateResults) => {
93
+ const filteredResults =
94
+ mode !== "search" && options.map((opt) => opt[labelKey]).includes(query)
95
+ ? options
96
+ : options.filter((opt) =>
97
+ opt[labelKey].match(new RegExp(`\\b${query.replace(/[^\w\s]/gi, "")}`, "i"))
98
+ );
99
+ populateResults(filteredResults);
100
+ };
101
+ /**
102
+ * Optional: Override the default CDN URL for the accessible-autocomplete script
103
+ * @type {string}
104
+ */
105
+ export let scriptUrl =
106
+ "https://cdn.ons.gov.uk/vendor/accessible-autocomplete/3.0.1/accessible-autocomplete.min.js";
107
+ /**
108
+ * Call this function externally to clear the input
109
+ * @type {function}
110
+ */
111
+ export let clearInput = async () => {
112
+ await setInputValue(null);
113
+ dispatch("clear", null);
114
+ };
115
+ /**
116
+ * Optional: Set an additional CSS class for the component
117
+ * @type {string|null}
118
+ */
119
+ export let cls = null;
120
+
121
+ // This method is a bit of a hack, but no better options available at present
122
+ // https://github.com/alphagov/accessible-autocomplete/issues/390
123
+ async function setInputValue(textValue) {
124
+ hideMenu = true;
125
+ inputElement.value = textValue || "";
126
+ await sleep(110);
127
+ inputElement.focus({ preventScroll: true });
128
+ inputElement.blur();
129
+ hideMenu = false;
130
+ }
131
+
132
+ function inputValueTemplate(result) {
133
+ return result && result[labelKey];
134
+ }
135
+
136
+ function highlight(text, query = "") {
137
+ return text.replace(
138
+ new RegExp(`\\b${query.replace(/[^\w\s]/gi, "")}`, "i"),
139
+ (str) => `<b>${str}</b>`
140
+ );
141
+ }
142
+
143
+ function suggestionTemplate(result) {
144
+ const query = inputElement?.value || "";
145
+ return (
146
+ result &&
147
+ (groupKey
148
+ ? `${highlight(result?.[labelKey] || "", query)} <span class="muted-text">${
149
+ result[groupKey]
150
+ }</span>`
151
+ : highlight(result?.[labelKey] || "", query))
152
+ );
153
+ }
154
+
155
+ async function select(option) {
156
+ if (option && value !== option) {
157
+ value = option;
158
+ dispatch("change", value);
159
+ if (value && autoClear) {
160
+ await sleep(0);
161
+ clearInput();
162
+ }
163
+ }
164
+ }
165
+
166
+ function inputChange(e) {
167
+ if (!e.target.value) select(null);
168
+ }
169
+
170
+ function handleScriptLoad() {
171
+ if (!scriptLoaded && window?.accessibleAutocomplete) {
172
+ accessibleAutocomplete = window.accessibleAutocomplete;
173
+ scriptLoaded = true;
174
+ }
175
+ }
176
+
177
+ function initAutocomplete(element) {
178
+ accessibleAutocomplete({
179
+ element,
180
+ id,
181
+ name,
182
+ source: loadOptions,
183
+ autoselect: true,
184
+ onConfirm: select,
185
+ confirmOnBlur: false,
186
+ placeholder,
187
+ displayMenu: "overlay",
188
+ showAllValues: mode === "default",
189
+ dropdownArrow: chevron,
190
+ minLength,
191
+ templates: {
192
+ inputValue: inputValueTemplate,
193
+ suggestion: suggestionTemplate
194
+ }
195
+ });
196
+ inputElement = element.querySelector(`#${id}`);
197
+ setInputValue(value?.[labelKey] || "");
198
+ inputElement.addEventListener("blur", inputChange);
199
+ }
200
+
201
+ // In case input value is updated from outside component
202
+ function bindInputValue(value) {
203
+ if (inputElement) {
204
+ const textValue = value?.[labelKey];
205
+ if (textValue && inputElement.value !== textValue) setInputValue(textValue);
206
+ else if (!value && inputElement.value) setInputValue("");
207
+ }
208
+ }
209
+ $: bindInputValue(value);
210
+
211
+ onMount(handleScriptLoad);
212
+ </script>
213
+
214
+ <svelte:head>
215
+ <script src={scriptUrl} on:load={handleScriptLoad}></script>
216
+ </svelte:head>
217
+
218
+ {#if renderFallback && !scriptLoaded}
219
+ {#if mode === "search"}
220
+ <Input {id} {name} {label} {hideLabel} value={value?.[labelKey]} />
221
+ {:else}
222
+ <Dropdown {id} {name} {options} {label} {hideLabel} {placeholder} {value} />
223
+ {/if}
224
+ {:else}
225
+ <div class="ons-field {cls}">
226
+ {#if label}<label for={id} class="ons-label" class:ons-u-vh={hideLabel}>{label}</label>{/if}
227
+ <div class="ons-autocomplete-wrapper">
228
+ {#if scriptLoaded}
229
+ <div
230
+ id="{id}-container"
231
+ class="ons-autocomplete"
232
+ class:hide-menu={hideMenu}
233
+ use:initAutocomplete
234
+ ></div>
235
+ {#if clearable && !autoClear && value}
236
+ <button
237
+ type="reset"
238
+ title="Clear selection"
239
+ aria-label="Clear selection"
240
+ on:click={clearInput}
241
+ class="ons-autocomplete-clear"
242
+ >
243
+ <svg
244
+ xmlns="http://www.w3.org/2000/svg"
245
+ aria-hidden="true"
246
+ viewBox="0 0 14 14"
247
+ width="18"
248
+ >
249
+ <path
250
+ fill="currentColor"
251
+ 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"
252
+ ></path>
253
+ </svg>
254
+ </button>
255
+ {/if}
256
+ {/if}
257
+ </div>
258
+ </div>
259
+ {/if}
260
+
261
+ <style>
262
+ .ons-autocomplete-wrapper {
263
+ position: relative;
264
+ }
265
+ .ons-autocomplete-clear {
266
+ position: absolute;
267
+ display: flex;
268
+ align-items: center;
269
+ align-content: center;
270
+ z-index: 1;
271
+ right: 3px;
272
+ top: calc(50% - 14px);
273
+ height: 28px;
274
+ width: 28px;
275
+ border: none;
276
+ background: var(--ons-color-input-bg, white);
277
+ }
278
+ .ons-autocomplete-clear:focus {
279
+ outline: 3px solid var(--ons-color-focus, #fbc900) !important;
280
+ }
281
+ .hide-menu :global(.autocomplete__menu) {
282
+ display: none;
283
+ }
284
+ .ons-autocomplete :global(.autocomplete__input) {
285
+ border-radius: 3px !important;
286
+ border-width: 1px !important;
287
+ background: var(--ons-color-input-bg, white);
288
+ }
289
+ .ons-autocomplete :global(.autocomplete__input--focused) {
290
+ box-shadow: inset 0 0 0 1px black !important;
291
+ outline-color: var(--ons-color-focus, #fbc900) !important;
292
+ }
293
+ .ons-autocomplete :global(.autocomplete__dropdown-arrow-down) {
294
+ width: 18px !important;
295
+ transform: translateY(-2px);
296
+ }
297
+ .ons-autocomplete :global(.muted-text) {
298
+ opacity: 0.8;
299
+ font-size: smaller;
300
+ }
301
+ .ons-autocomplete-wrapper :global(*) {
302
+ font-size: 18px;
303
+ }
304
+ .ons-autocomplete-wrapper :global(.autocomplete__hint),
305
+ .ons-autocomplete-wrapper :global(.autocomplete__input) {
306
+ height: 40px;
307
+ }
308
+ .ons-autocomplete-wrapper :global(.autocomplete__option) {
309
+ margin: 0;
310
+ }
311
+ .ons-autocomplete-wrapper :global(.autocomplete__menu) {
312
+ transform: translateY(5px);
313
+ border: 1px solid currentColor;
314
+ border-radius: 3px;
315
+ }
316
+ .ons-autocomplete-wrapper :global(.autocomplete__option--focused),
317
+ .ons-autocomplete-wrapper :global(.autocomplete__option:hover) {
318
+ background-color: var(--ons-color-branded-secondary, #003c57);
319
+ }
320
+ .ons-autocomplete-wrapper :global(.autocomplete__option:focus) {
321
+ outline: none !important;
322
+ }
323
+ .ons-autocomplete-wrapper :global(input) {
324
+ padding: 0 35px 0 8px;
325
+ }
326
+ </style>