@onsvisual/svelte-components 1.0.44 → 1.0.45

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 (224) hide show
  1. package/README.md +24 -24
  2. package/dist/css/main.css +513 -2
  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 +101 -93
  38. package/dist/decorators/Icon/Icon.svelte.d.ts +2 -2
  39. package/dist/decorators/Icon/docs/component.md +10 -10
  40. package/dist/decorators/Indent/Indent.stories.svelte +22 -22
  41. package/dist/decorators/Indent/Indent.svelte +3 -3
  42. package/dist/decorators/Indent/docs/component.md +10 -10
  43. package/dist/index.js +86 -86
  44. package/dist/inputs/Button/Button.stories.svelte +70 -70
  45. package/dist/inputs/Button/Button.svelte +152 -152
  46. package/dist/inputs/Button/Button.svelte.d.ts +2 -2
  47. package/dist/inputs/Button/docs/component.md +17 -17
  48. package/dist/inputs/ButtonGroup/ButtonGroup.stories.svelte +40 -40
  49. package/dist/inputs/ButtonGroup/ButtonGroup.svelte +57 -57
  50. package/dist/inputs/ButtonGroup/ButtonGroupItem.svelte +101 -101
  51. package/dist/inputs/ButtonGroup/docs/component.md +23 -23
  52. package/dist/inputs/Checkbox/Checkbox.stories.svelte +34 -34
  53. package/dist/inputs/Checkbox/Checkbox.svelte +180 -180
  54. package/dist/inputs/Checkbox/docs/component.md +14 -14
  55. package/dist/inputs/Checkboxes/Checkboxes.stories.svelte +34 -34
  56. package/dist/inputs/Checkboxes/Checkboxes.svelte +62 -62
  57. package/dist/inputs/Checkboxes/docs/component.md +20 -20
  58. package/dist/inputs/Checkboxes/docs/example.md +16 -16
  59. package/dist/inputs/Dropdown/Dropdown.stories.svelte +54 -54
  60. package/dist/inputs/Dropdown/Dropdown.svelte +66 -66
  61. package/dist/inputs/Dropdown/docs/component.md +22 -22
  62. package/dist/inputs/ErrorPanel/ErrorPanel.stories.svelte +25 -25
  63. package/dist/inputs/ErrorPanel/ErrorPanel.svelte +24 -24
  64. package/dist/inputs/ErrorPanel/docs/component.md +14 -14
  65. package/dist/inputs/ErrorSummary/ErrorSummary.stories.svelte +34 -34
  66. package/dist/inputs/ErrorSummary/ErrorSummary.svelte +47 -47
  67. package/dist/inputs/ErrorSummary/docs/component.md +17 -17
  68. package/dist/inputs/ErrorSummary/docs/example.md +12 -12
  69. package/dist/inputs/Input/Input.stories.svelte +73 -73
  70. package/dist/inputs/Input/Input.svelte +151 -151
  71. package/dist/inputs/Input/docs/component.md +16 -16
  72. package/dist/inputs/Radios/Radio.svelte +90 -90
  73. package/dist/inputs/Radios/Radios.stories.svelte +51 -51
  74. package/dist/inputs/Radios/Radios.svelte +62 -62
  75. package/dist/inputs/Radios/docs/component.md +24 -24
  76. package/dist/inputs/Radios/docs/example.md +21 -21
  77. package/dist/inputs/Select/Select.stories.svelte +63 -63
  78. package/dist/inputs/Select/Select.svelte +326 -326
  79. package/dist/inputs/Select/docs/component.md +27 -27
  80. package/dist/inputs/Textarea/Textarea.stories.svelte +40 -40
  81. package/dist/inputs/Textarea/Textarea.svelte +113 -113
  82. package/dist/inputs/Textarea/docs/component.md +16 -16
  83. package/dist/inputs/Toolbar/HelpModal.svelte +234 -234
  84. package/dist/inputs/Toolbar/ToolControl.svelte +23 -23
  85. package/dist/inputs/Toolbar/ToolControls.svelte +9 -9
  86. package/dist/inputs/Toolbar/Toolbar.stories.svelte +148 -148
  87. package/dist/inputs/Toolbar/Toolbar.svelte +70 -70
  88. package/dist/inputs/Toolbar/ToolbarButton.svelte +184 -184
  89. package/dist/inputs/Toolbar/ToolbarDivider.svelte +29 -29
  90. package/dist/inputs/Toolbar/ToolbarIcon.svelte +106 -106
  91. package/dist/inputs/Toolbar/ToolbarsContainer.svelte +69 -69
  92. package/dist/inputs/Toolbar/docs/component.md +101 -101
  93. package/dist/intro.mdx +66 -66
  94. package/dist/js/menuOptions.js +14 -14
  95. package/dist/js/utils.js +133 -133
  96. package/dist/js/withParams.js +43 -43
  97. package/dist/layout/Accordion/Accordion.stories.svelte +30 -30
  98. package/dist/layout/Accordion/Accordion.svelte +55 -55
  99. package/dist/layout/Accordion/AccordionItem.svelte +51 -51
  100. package/dist/layout/Accordion/accordion.js +64 -64
  101. package/dist/layout/Accordion/details.js +83 -83
  102. package/dist/layout/Accordion/docs/component.md +19 -19
  103. package/dist/layout/AnalyticsBanner/AnalyticsBanner.stories.svelte +16 -16
  104. package/dist/layout/AnalyticsBanner/AnalyticsBanner.svelte +314 -314
  105. package/dist/layout/AnalyticsBanner/docs/component.md +44 -44
  106. package/dist/layout/BackLink/BackLink.stories.svelte +16 -16
  107. package/dist/layout/BackLink/BackLink.svelte +30 -30
  108. package/dist/layout/BackLink/docs/component.md +12 -12
  109. package/dist/layout/Breadcrumb/Breadcrumb.stories.svelte +31 -31
  110. package/dist/layout/Breadcrumb/Breadcrumb.svelte +69 -69
  111. package/dist/layout/Breadcrumb/docs/component.md +15 -15
  112. package/dist/layout/Card/Card.stories.svelte +39 -39
  113. package/dist/layout/Card/Card.svelte +127 -127
  114. package/dist/layout/Card/docs/component.md +14 -14
  115. package/dist/layout/Card/docs/eg-images.md +27 -27
  116. package/dist/layout/Card/docs/eg-links.md +12 -12
  117. package/dist/layout/Card/docs/eg-spans.md +12 -12
  118. package/dist/layout/Contents/Contents.stories.svelte +27 -27
  119. package/dist/layout/Contents/Contents.svelte +51 -51
  120. package/dist/layout/Contents/docs/component.md +18 -18
  121. package/dist/layout/DescriptionList/DescriptionList.stories.svelte +22 -22
  122. package/dist/layout/DescriptionList/DescriptionList.svelte +59 -59
  123. package/dist/layout/DescriptionList/docs/component.md +18 -18
  124. package/dist/layout/Details/Details.stories.svelte +32 -32
  125. package/dist/layout/Details/Details.svelte +75 -75
  126. package/dist/layout/Details/docs/component.md +14 -14
  127. package/dist/layout/DocumentList/Document.svelte +103 -103
  128. package/dist/layout/DocumentList/DocumentList.stories.svelte +88 -88
  129. package/dist/layout/DocumentList/DocumentList.svelte +33 -33
  130. package/dist/layout/DocumentList/docs/component.md +28 -28
  131. package/dist/layout/DocumentList/docs/example.md +23 -23
  132. package/dist/layout/ErrorPage/ErrorPage.stories.svelte +18 -18
  133. package/dist/layout/ErrorPage/ErrorPage.svelte +48 -48
  134. package/dist/layout/ErrorPage/docs/component.md +13 -13
  135. package/dist/layout/Footer/Footer.stories.svelte +24 -24
  136. package/dist/layout/Footer/Footer.svelte +366 -366
  137. package/dist/layout/Footer/docs/component.md +10 -10
  138. package/dist/layout/Grid/Grid.stories.svelte +50 -50
  139. package/dist/layout/Grid/Grid.svelte +117 -117
  140. package/dist/layout/Grid/GridCell.svelte +65 -65
  141. package/dist/layout/Grid/docs/component.md +14 -14
  142. package/dist/layout/Header/Header.stories.svelte +26 -26
  143. package/dist/layout/Header/Header.svelte +875 -875
  144. package/dist/layout/Header/docs/component.md +11 -11
  145. package/dist/layout/Hero/Hero.stories.svelte +79 -79
  146. package/dist/layout/Hero/Hero.svelte +364 -364
  147. package/dist/layout/Hero/docs/component.md +14 -14
  148. package/dist/layout/Highlight/Highlight.stories.svelte +29 -29
  149. package/dist/layout/Highlight/Highlight.svelte +77 -77
  150. package/dist/layout/Highlight/docs/component.md +12 -12
  151. package/dist/layout/Image/Image.stories.svelte +23 -23
  152. package/dist/layout/Image/Image.svelte +29 -29
  153. package/dist/layout/Image/docs/component.md +15 -15
  154. package/dist/layout/List/Li.svelte +3 -3
  155. package/dist/layout/List/List.stories.svelte +40 -40
  156. package/dist/layout/List/List.svelte +46 -46
  157. package/dist/layout/List/docs/component.md +14 -14
  158. package/dist/layout/List/docs/example.md +12 -12
  159. package/dist/layout/NavSections/NavSection.svelte +90 -90
  160. package/dist/layout/NavSections/NavSections.stories.svelte +51 -51
  161. package/dist/layout/NavSections/NavSections.svelte +160 -160
  162. package/dist/layout/NavSections/docs/component.md +25 -25
  163. package/dist/layout/Notice/Notice.stories.svelte +61 -61
  164. package/dist/layout/Notice/Notice.svelte +56 -56
  165. package/dist/layout/Notice/docs/component.md +14 -14
  166. package/dist/layout/PhaseBanner/PhaseBanner.stories.svelte +24 -24
  167. package/dist/layout/PhaseBanner/PhaseBanner.svelte +66 -66
  168. package/dist/layout/PhaseBanner/docs/component.md +14 -14
  169. package/dist/layout/RelatedContent/RelatedContent.stories.svelte +36 -36
  170. package/dist/layout/RelatedContent/RelatedContent.svelte +54 -54
  171. package/dist/layout/RelatedContent/docs/component.md +16 -16
  172. package/dist/layout/Scroller/Scroller.stories.svelte +60 -60
  173. package/dist/layout/Scroller/Scroller.svelte +368 -368
  174. package/dist/layout/Scroller/ScrollerSection.svelte +70 -70
  175. package/dist/layout/Scroller/docs/component.md +39 -39
  176. package/dist/layout/Section/Section.stories.svelte +33 -33
  177. package/dist/layout/Section/Section.svelte +60 -60
  178. package/dist/layout/Section/docs/component.md +12 -12
  179. package/dist/layout/ShareButtons/ShareButtons.stories.svelte +20 -20
  180. package/dist/layout/ShareButtons/ShareButtons.svelte +131 -131
  181. package/dist/layout/ShareButtons/docs/component.md +14 -14
  182. package/dist/layout/SkipLink/SkipLink.stories.svelte +16 -16
  183. package/dist/layout/SkipLink/SkipLink.svelte +9 -9
  184. package/dist/layout/SkipLink/docs/component.md +11 -11
  185. package/dist/layout/Summary/Summary.stories.svelte +21 -21
  186. package/dist/layout/Summary/Summary.svelte +60 -60
  187. package/dist/layout/Summary/docs/component.md +17 -17
  188. package/dist/layout/Tabs/Tab.svelte +53 -53
  189. package/dist/layout/Tabs/Tabs.stories.svelte +29 -29
  190. package/dist/layout/Tabs/Tabs.svelte +89 -89
  191. package/dist/layout/Tabs/docs/component.md +16 -16
  192. package/dist/layout/Tabs/tabs.js +302 -302
  193. package/dist/layout/Timeline/Timeline.stories.svelte +44 -44
  194. package/dist/layout/Timeline/Timeline.svelte +17 -17
  195. package/dist/layout/Timeline/TimelineItem.svelte +14 -14
  196. package/dist/layout/Timeline/docs/component.md +27 -27
  197. package/dist/layout/Timeline/docs/example.md +20 -20
  198. package/dist/templates/EmbedArticle/EmbedArticle.stories.svelte +72 -72
  199. package/dist/templates/EmbedArticle/docs/component.md +56 -56
  200. package/dist/templates/FeatureArticle/FeatureArticle.stories.svelte +150 -150
  201. package/dist/templates/FeatureArticle/docs/component.md +125 -125
  202. package/dist/templates/StandardArticle/StandardArticle.stories.svelte +86 -86
  203. package/dist/templates/StandardArticle/docs/component.md +76 -76
  204. package/dist/templates/intro.mdx +18 -18
  205. package/dist/wrappers/Container/Container.stories.svelte +38 -38
  206. package/dist/wrappers/Container/Container.svelte +77 -77
  207. package/dist/wrappers/Container/docs/component.md +12 -12
  208. package/dist/wrappers/Embed/Embed.stories.svelte +24 -24
  209. package/dist/wrappers/Embed/Embed.svelte +44 -44
  210. package/dist/wrappers/Embed/docs/component.md +15 -15
  211. package/dist/wrappers/LazyLoad/LazyLoad.stories.svelte +37 -37
  212. package/dist/wrappers/LazyLoad/LazyLoad.svelte +50 -50
  213. package/dist/wrappers/LazyLoad/docs/component.md +29 -29
  214. package/dist/wrappers/Main/Main.stories.svelte +24 -24
  215. package/dist/wrappers/Main/Main.svelte +11 -11
  216. package/dist/wrappers/Main/docs/component.md +16 -16
  217. package/dist/wrappers/Observe/Observe.stories.svelte +29 -29
  218. package/dist/wrappers/Observe/Observe.svelte +40 -40
  219. package/dist/wrappers/Observe/docs/component.md +22 -22
  220. package/dist/wrappers/Theme/Theme.stories.svelte +70 -70
  221. package/dist/wrappers/Theme/Theme.svelte +76 -76
  222. package/dist/wrappers/Theme/docs/component.md +10 -10
  223. package/dist/wrappers/Theme/themes.js +70 -70
  224. package/package.json +88 -88
@@ -1,302 +1,302 @@
1
- // Tabs component JS
2
-
3
- // The tab js is based on the GDS tabs component(https://design-system.service.gov.uk/components/tabs/)
4
- // https://github.com/alphagov/govuk-frontend/blob/master/src/components/tabs/tabs.js
5
-
6
- import { matchMedia } from "../../js/utils.js";
7
-
8
- const classTab = "ons-tab";
9
- const classTabTitle = "ons-tabs__title";
10
- const classTabList = "ons-tabs__list";
11
- const classTabListItems = "ons-tab__list-item";
12
- const classTabsPanel = "ons-tabs__panel";
13
- const matchMediaUtil = matchMedia;
14
-
15
- export default class Tabs {
16
- constructor(component) {
17
- this.boundTabClick = this.onTabClick.bind(this);
18
- this.boundTabKeydown = this.onTabKeydown.bind(this);
19
-
20
- this.component = component;
21
- this.tabsTitle = component.querySelector(`.${classTabTitle}`);
22
- this.tabs = [...component.getElementsByClassName(classTab)];
23
- this.tabList = component.getElementsByClassName(classTabList);
24
- this.tabListContainer = this.tabList[0].parentElement;
25
- this.tabListItems = [...component.getElementsByClassName(classTabListItems)];
26
- this.tabPanels = [...component.getElementsByClassName(classTabsPanel)];
27
-
28
- this.jsHiddenClass = "ons-tabs__panel--hidden";
29
- this.jsTabListAsRowClass = "ons-tabs__list--row";
30
- this.jsTabItemAsRowClass = "ons-tab__list-item--row";
31
- this.jsTabAsListClass = "ons-tab--row";
32
-
33
- this.noInitialActiveTab = this.component.getAttribute("data-no-initial-active-tab");
34
- if (matchMediaUtil.hasMatchMedia()) {
35
- this.setupViewportChecks();
36
- } else {
37
- this.makeTabs();
38
- }
39
- }
40
-
41
- // Set up checks for responsive functionality
42
- // The tabs will display as tabs up until this.breakpoint is reached
43
- // Tabs will then display as a table of contents list and show full content for this.breakpoint viewports
44
- // Aria tags are added only in table of contents view
45
- setupViewportChecks() {
46
- const breakpoint = () => {
47
- let finalBreakpoint = 0;
48
- this.tabListItems.forEach((tab) => {
49
- finalBreakpoint += tab.offsetWidth;
50
- });
51
- if (finalBreakpoint < 450) {
52
- return (finalBreakpoint = 450);
53
- } else {
54
- return finalBreakpoint;
55
- }
56
- };
57
- this.viewport = matchMediaUtil(`(min-width: ${breakpoint()}px)`);
58
- this.viewport.addListener(this.checkViewport.bind(this));
59
- this.checkViewport();
60
- }
61
-
62
- checkViewport() {
63
- if (this.viewport.matches) {
64
- this.makeTabs();
65
- } else {
66
- this.makeList();
67
- }
68
- }
69
-
70
- makeTabs() {
71
- this.tabList[0].setAttribute("role", "tablist");
72
- this.tabList[0].classList.add(this.jsTabListAsRowClass);
73
-
74
- this.tabsTitle.classList.add("ons-u-vh");
75
- this.tabListContainer.classList.add("ons-tabs__container");
76
- this.tabPanels.forEach((panel) => {
77
- panel.setAttribute("tabindex", "0");
78
- if (panel.querySelector('[id*="content-title"]')) {
79
- panel.firstElementChild.classList.add("ons-u-vh");
80
- }
81
- });
82
-
83
- this.tabListItems.forEach((item) => {
84
- item.setAttribute("role", "presentation");
85
- item.classList.add(this.jsTabItemAsRowClass);
86
- });
87
-
88
- this.tabs.forEach((tab) => {
89
- this.setAttributes(tab);
90
- tab.classList.add(this.jsTabAsListClass);
91
-
92
- tab.addEventListener("click", this.boundTabClick, true);
93
- tab.addEventListener("keydown", this.boundTabKeydown, true);
94
- this.hideTab(tab);
95
- });
96
-
97
- const hashTab = this.getTab(window.location.hash);
98
- if (!this.noInitialActiveTab || !!hashTab) {
99
- const activeTab = hashTab || this.tabs[0];
100
- this.showTab(activeTab);
101
- }
102
-
103
- this.ensureTabIndexExists();
104
-
105
- this.component.boundOnHashChange = this.onHashChange.bind(this);
106
- window.addEventListener("hashchange", this.component.boundOnHashChange, true);
107
- }
108
-
109
- makeList() {
110
- this.tabList[0].removeAttribute("role");
111
- this.tabList[0].classList.remove(this.jsTabListAsRowClass);
112
- this.tabListContainer.classList.remove("ons-tabs__container");
113
- this.tabsTitle.classList.remove("ons-u-vh");
114
-
115
- this.tabPanels.forEach((panel) => {
116
- panel.removeAttribute("tabindex", "0");
117
- if (panel.firstElementChild.classList.contains("ons-u-vh")) {
118
- panel.firstElementChild.classList.remove("ons-u-vh");
119
- }
120
- });
121
-
122
- this.tabListItems.forEach((item) => {
123
- item.removeAttribute("role", "presentation");
124
- item.classList.remove(this.jsTabItemAsRowClass);
125
- });
126
-
127
- this.tabs.forEach((tab) => {
128
- tab.removeEventListener("click", this.boundTabClick, true);
129
- tab.removeEventListener("keydown", this.boundTabKeydown, true);
130
- tab.classList.remove(this.jsTabAsListClass);
131
- this.unsetAttributes(tab);
132
- });
133
-
134
- window.removeEventListener("hashchange", this.component.boundOnHashChange, true);
135
- }
136
-
137
- // Handle haschange so that the browser can cycle through the tab history
138
- onHashChange() {
139
- const hash = window.location.hash;
140
- const tabWithHash = this.getTab(hash);
141
- if (!tabWithHash) {
142
- return;
143
- }
144
-
145
- if (this.changingHash) {
146
- this.changingHash = false;
147
- return;
148
- }
149
-
150
- const currentTab = this.getCurrentTab();
151
- if (!!currentTab) {
152
- this.hideTab(currentTab);
153
- }
154
- this.showTab(tabWithHash);
155
- tabWithHash.focus();
156
- }
157
-
158
- hideTab(tab) {
159
- this.unhighlightTab(tab);
160
- this.hidePanel(tab);
161
- }
162
-
163
- showTab(tab) {
164
- this.highlightTab(tab);
165
- this.showPanel(tab);
166
- }
167
-
168
- getTab(hash) {
169
- return this.component.querySelector('.ons-tab[href="' + hash + '"]');
170
- }
171
-
172
- // Set aria tags
173
- setAttributes(tab) {
174
- const panelId = this.getHref(tab).slice(1);
175
- tab.setAttribute("id", "tab_" + panelId);
176
- tab.setAttribute("role", "tab");
177
- tab.setAttribute("aria-controls", panelId);
178
- tab.setAttribute("tabindex", "-1");
179
-
180
- const panel = this.getPanel(tab);
181
- panel.setAttribute("role", "tabpanel");
182
- panel.setAttribute("aria-labelledby", tab.id);
183
- panel.classList.add(this.jsHiddenClass);
184
- }
185
-
186
- // Remove aria tags for table of contents view
187
- unsetAttributes(tab) {
188
- tab.removeAttribute("id");
189
- tab.removeAttribute("role");
190
- tab.removeAttribute("aria-controls");
191
- tab.removeAttribute("tabindex");
192
- tab.removeAttribute("aria-selected");
193
-
194
- const panel = this.getPanel(tab);
195
- panel.removeAttribute("role");
196
- panel.removeAttribute("aria-labelledby");
197
- panel.classList.remove(this.jsHiddenClass);
198
- }
199
-
200
- onTabClick(e) {
201
- e.preventDefault();
202
- const newTab = e.target;
203
- const currentTab = this.getCurrentTab();
204
-
205
- if (!!currentTab) {
206
- this.hideTab(currentTab);
207
- }
208
-
209
- if (!this.noInitialActiveTab || newTab !== currentTab) {
210
- this.showTab(newTab);
211
- this.createHash(newTab);
212
- }
213
-
214
- this.ensureTabIndexExists();
215
- }
216
-
217
- ensureTabIndexExists() {
218
- // Ensure that at least the first tab has a tab index when all tabs are hidden.
219
- if (!this.tabs.find((tab) => tab.getAttribute("tabindex") === "0")) {
220
- this.tabs[0].setAttribute("tabindex", "0");
221
- }
222
- }
223
-
224
- createHash(tab) {
225
- const panel = this.getPanel(tab);
226
- const id = panel.id;
227
- panel.id = "";
228
- this.changingHash = true;
229
- window.location.hash = this.getHref(tab).slice(1);
230
- panel.id = id;
231
- }
232
-
233
- onTabKeydown(event) {
234
- if (event.which === 37) {
235
- this.focusPreviousTab();
236
- event.preventDefault();
237
- } else if (event.which === 39) {
238
- this.focusNextTab();
239
- event.preventDefault();
240
- } else if (event.which === 32) {
241
- this.onTabClick(event);
242
- }
243
- }
244
-
245
- focusNextTab() {
246
- const focusedTab = this.getFocusedTab();
247
- const nextTabListItem = focusedTab.nextElementSibling;
248
- if (nextTabListItem) {
249
- nextTabListItem.firstElementChild.focus();
250
- }
251
- }
252
-
253
- focusPreviousTab() {
254
- const focusedTab = this.getFocusedTab();
255
- const previousTabListItem = focusedTab.previousElementSibling;
256
- if (previousTabListItem) {
257
- previousTabListItem.firstElementChild.focus();
258
- }
259
- }
260
-
261
- getPanel(tab) {
262
- const panelSelector = this.getHref(tab).replace(/\./g, "\\.");
263
- const panel = this.component.querySelector(panelSelector);
264
- return panel;
265
- }
266
-
267
- showPanel(tab) {
268
- const panel = this.getPanel(tab);
269
- panel.classList.remove(this.jsHiddenClass);
270
- }
271
-
272
- hidePanel(tab) {
273
- const panel = this.getPanel(tab);
274
- panel.classList.add(this.jsHiddenClass);
275
- }
276
-
277
- unhighlightTab(tab) {
278
- tab.setAttribute("aria-selected", "false");
279
- tab.classList.remove("ons-tab--selected");
280
- tab.setAttribute("tabindex", "-1");
281
- }
282
-
283
- highlightTab(tab) {
284
- tab.setAttribute("aria-selected", "true");
285
- tab.classList.add("ons-tab--selected");
286
- tab.setAttribute("tabindex", "0");
287
- }
288
-
289
- getFocusedTab() {
290
- return document.activeElement.parentNode;
291
- }
292
-
293
- getCurrentTab() {
294
- return this.component.querySelector(".ons-tab--selected");
295
- }
296
-
297
- getHref(tab) {
298
- const href = tab.getAttribute("href");
299
- const hash = href.slice(href.indexOf("#"), href.length);
300
- return hash;
301
- }
302
- }
1
+ // Tabs component JS
2
+
3
+ // The tab js is based on the GDS tabs component(https://design-system.service.gov.uk/components/tabs/)
4
+ // https://github.com/alphagov/govuk-frontend/blob/master/src/components/tabs/tabs.js
5
+
6
+ import { matchMedia } from "../../js/utils.js";
7
+
8
+ const classTab = "ons-tab";
9
+ const classTabTitle = "ons-tabs__title";
10
+ const classTabList = "ons-tabs__list";
11
+ const classTabListItems = "ons-tab__list-item";
12
+ const classTabsPanel = "ons-tabs__panel";
13
+ const matchMediaUtil = matchMedia;
14
+
15
+ export default class Tabs {
16
+ constructor(component) {
17
+ this.boundTabClick = this.onTabClick.bind(this);
18
+ this.boundTabKeydown = this.onTabKeydown.bind(this);
19
+
20
+ this.component = component;
21
+ this.tabsTitle = component.querySelector(`.${classTabTitle}`);
22
+ this.tabs = [...component.getElementsByClassName(classTab)];
23
+ this.tabList = component.getElementsByClassName(classTabList);
24
+ this.tabListContainer = this.tabList[0].parentElement;
25
+ this.tabListItems = [...component.getElementsByClassName(classTabListItems)];
26
+ this.tabPanels = [...component.getElementsByClassName(classTabsPanel)];
27
+
28
+ this.jsHiddenClass = "ons-tabs__panel--hidden";
29
+ this.jsTabListAsRowClass = "ons-tabs__list--row";
30
+ this.jsTabItemAsRowClass = "ons-tab__list-item--row";
31
+ this.jsTabAsListClass = "ons-tab--row";
32
+
33
+ this.noInitialActiveTab = this.component.getAttribute("data-no-initial-active-tab");
34
+ if (matchMediaUtil.hasMatchMedia()) {
35
+ this.setupViewportChecks();
36
+ } else {
37
+ this.makeTabs();
38
+ }
39
+ }
40
+
41
+ // Set up checks for responsive functionality
42
+ // The tabs will display as tabs up until this.breakpoint is reached
43
+ // Tabs will then display as a table of contents list and show full content for this.breakpoint viewports
44
+ // Aria tags are added only in table of contents view
45
+ setupViewportChecks() {
46
+ const breakpoint = () => {
47
+ let finalBreakpoint = 0;
48
+ this.tabListItems.forEach((tab) => {
49
+ finalBreakpoint += tab.offsetWidth;
50
+ });
51
+ if (finalBreakpoint < 450) {
52
+ return (finalBreakpoint = 450);
53
+ } else {
54
+ return finalBreakpoint;
55
+ }
56
+ };
57
+ this.viewport = matchMediaUtil(`(min-width: ${breakpoint()}px)`);
58
+ this.viewport.addListener(this.checkViewport.bind(this));
59
+ this.checkViewport();
60
+ }
61
+
62
+ checkViewport() {
63
+ if (this.viewport.matches) {
64
+ this.makeTabs();
65
+ } else {
66
+ this.makeList();
67
+ }
68
+ }
69
+
70
+ makeTabs() {
71
+ this.tabList[0].setAttribute("role", "tablist");
72
+ this.tabList[0].classList.add(this.jsTabListAsRowClass);
73
+
74
+ this.tabsTitle.classList.add("ons-u-vh");
75
+ this.tabListContainer.classList.add("ons-tabs__container");
76
+ this.tabPanels.forEach((panel) => {
77
+ panel.setAttribute("tabindex", "0");
78
+ if (panel.querySelector('[id*="content-title"]')) {
79
+ panel.firstElementChild.classList.add("ons-u-vh");
80
+ }
81
+ });
82
+
83
+ this.tabListItems.forEach((item) => {
84
+ item.setAttribute("role", "presentation");
85
+ item.classList.add(this.jsTabItemAsRowClass);
86
+ });
87
+
88
+ this.tabs.forEach((tab) => {
89
+ this.setAttributes(tab);
90
+ tab.classList.add(this.jsTabAsListClass);
91
+
92
+ tab.addEventListener("click", this.boundTabClick, true);
93
+ tab.addEventListener("keydown", this.boundTabKeydown, true);
94
+ this.hideTab(tab);
95
+ });
96
+
97
+ const hashTab = this.getTab(window.location.hash);
98
+ if (!this.noInitialActiveTab || !!hashTab) {
99
+ const activeTab = hashTab || this.tabs[0];
100
+ this.showTab(activeTab);
101
+ }
102
+
103
+ this.ensureTabIndexExists();
104
+
105
+ this.component.boundOnHashChange = this.onHashChange.bind(this);
106
+ window.addEventListener("hashchange", this.component.boundOnHashChange, true);
107
+ }
108
+
109
+ makeList() {
110
+ this.tabList[0].removeAttribute("role");
111
+ this.tabList[0].classList.remove(this.jsTabListAsRowClass);
112
+ this.tabListContainer.classList.remove("ons-tabs__container");
113
+ this.tabsTitle.classList.remove("ons-u-vh");
114
+
115
+ this.tabPanels.forEach((panel) => {
116
+ panel.removeAttribute("tabindex", "0");
117
+ if (panel.firstElementChild.classList.contains("ons-u-vh")) {
118
+ panel.firstElementChild.classList.remove("ons-u-vh");
119
+ }
120
+ });
121
+
122
+ this.tabListItems.forEach((item) => {
123
+ item.removeAttribute("role", "presentation");
124
+ item.classList.remove(this.jsTabItemAsRowClass);
125
+ });
126
+
127
+ this.tabs.forEach((tab) => {
128
+ tab.removeEventListener("click", this.boundTabClick, true);
129
+ tab.removeEventListener("keydown", this.boundTabKeydown, true);
130
+ tab.classList.remove(this.jsTabAsListClass);
131
+ this.unsetAttributes(tab);
132
+ });
133
+
134
+ window.removeEventListener("hashchange", this.component.boundOnHashChange, true);
135
+ }
136
+
137
+ // Handle haschange so that the browser can cycle through the tab history
138
+ onHashChange() {
139
+ const hash = window.location.hash;
140
+ const tabWithHash = this.getTab(hash);
141
+ if (!tabWithHash) {
142
+ return;
143
+ }
144
+
145
+ if (this.changingHash) {
146
+ this.changingHash = false;
147
+ return;
148
+ }
149
+
150
+ const currentTab = this.getCurrentTab();
151
+ if (!!currentTab) {
152
+ this.hideTab(currentTab);
153
+ }
154
+ this.showTab(tabWithHash);
155
+ tabWithHash.focus();
156
+ }
157
+
158
+ hideTab(tab) {
159
+ this.unhighlightTab(tab);
160
+ this.hidePanel(tab);
161
+ }
162
+
163
+ showTab(tab) {
164
+ this.highlightTab(tab);
165
+ this.showPanel(tab);
166
+ }
167
+
168
+ getTab(hash) {
169
+ return this.component.querySelector('.ons-tab[href="' + hash + '"]');
170
+ }
171
+
172
+ // Set aria tags
173
+ setAttributes(tab) {
174
+ const panelId = this.getHref(tab).slice(1);
175
+ tab.setAttribute("id", "tab_" + panelId);
176
+ tab.setAttribute("role", "tab");
177
+ tab.setAttribute("aria-controls", panelId);
178
+ tab.setAttribute("tabindex", "-1");
179
+
180
+ const panel = this.getPanel(tab);
181
+ panel.setAttribute("role", "tabpanel");
182
+ panel.setAttribute("aria-labelledby", tab.id);
183
+ panel.classList.add(this.jsHiddenClass);
184
+ }
185
+
186
+ // Remove aria tags for table of contents view
187
+ unsetAttributes(tab) {
188
+ tab.removeAttribute("id");
189
+ tab.removeAttribute("role");
190
+ tab.removeAttribute("aria-controls");
191
+ tab.removeAttribute("tabindex");
192
+ tab.removeAttribute("aria-selected");
193
+
194
+ const panel = this.getPanel(tab);
195
+ panel.removeAttribute("role");
196
+ panel.removeAttribute("aria-labelledby");
197
+ panel.classList.remove(this.jsHiddenClass);
198
+ }
199
+
200
+ onTabClick(e) {
201
+ e.preventDefault();
202
+ const newTab = e.target;
203
+ const currentTab = this.getCurrentTab();
204
+
205
+ if (!!currentTab) {
206
+ this.hideTab(currentTab);
207
+ }
208
+
209
+ if (!this.noInitialActiveTab || newTab !== currentTab) {
210
+ this.showTab(newTab);
211
+ this.createHash(newTab);
212
+ }
213
+
214
+ this.ensureTabIndexExists();
215
+ }
216
+
217
+ ensureTabIndexExists() {
218
+ // Ensure that at least the first tab has a tab index when all tabs are hidden.
219
+ if (!this.tabs.find((tab) => tab.getAttribute("tabindex") === "0")) {
220
+ this.tabs[0].setAttribute("tabindex", "0");
221
+ }
222
+ }
223
+
224
+ createHash(tab) {
225
+ const panel = this.getPanel(tab);
226
+ const id = panel.id;
227
+ panel.id = "";
228
+ this.changingHash = true;
229
+ window.location.hash = this.getHref(tab).slice(1);
230
+ panel.id = id;
231
+ }
232
+
233
+ onTabKeydown(event) {
234
+ if (event.which === 37) {
235
+ this.focusPreviousTab();
236
+ event.preventDefault();
237
+ } else if (event.which === 39) {
238
+ this.focusNextTab();
239
+ event.preventDefault();
240
+ } else if (event.which === 32) {
241
+ this.onTabClick(event);
242
+ }
243
+ }
244
+
245
+ focusNextTab() {
246
+ const focusedTab = this.getFocusedTab();
247
+ const nextTabListItem = focusedTab.nextElementSibling;
248
+ if (nextTabListItem) {
249
+ nextTabListItem.firstElementChild.focus();
250
+ }
251
+ }
252
+
253
+ focusPreviousTab() {
254
+ const focusedTab = this.getFocusedTab();
255
+ const previousTabListItem = focusedTab.previousElementSibling;
256
+ if (previousTabListItem) {
257
+ previousTabListItem.firstElementChild.focus();
258
+ }
259
+ }
260
+
261
+ getPanel(tab) {
262
+ const panelSelector = this.getHref(tab).replace(/\./g, "\\.");
263
+ const panel = this.component.querySelector(panelSelector);
264
+ return panel;
265
+ }
266
+
267
+ showPanel(tab) {
268
+ const panel = this.getPanel(tab);
269
+ panel.classList.remove(this.jsHiddenClass);
270
+ }
271
+
272
+ hidePanel(tab) {
273
+ const panel = this.getPanel(tab);
274
+ panel.classList.add(this.jsHiddenClass);
275
+ }
276
+
277
+ unhighlightTab(tab) {
278
+ tab.setAttribute("aria-selected", "false");
279
+ tab.classList.remove("ons-tab--selected");
280
+ tab.setAttribute("tabindex", "-1");
281
+ }
282
+
283
+ highlightTab(tab) {
284
+ tab.setAttribute("aria-selected", "true");
285
+ tab.classList.add("ons-tab--selected");
286
+ tab.setAttribute("tabindex", "0");
287
+ }
288
+
289
+ getFocusedTab() {
290
+ return document.activeElement.parentNode;
291
+ }
292
+
293
+ getCurrentTab() {
294
+ return this.component.querySelector(".ons-tab--selected");
295
+ }
296
+
297
+ getHref(tab) {
298
+ const href = tab.getAttribute("href");
299
+ const hash = href.slice(href.indexOf("#"), href.length);
300
+ return hash;
301
+ }
302
+ }