@communitiesuk/svelte-component-library 0.1.17

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 (217) hide show
  1. package/README.md +188 -0
  2. package/dist/assets/css/govuk-frontend.min.css +2 -0
  3. package/dist/assets/css/govuk-frontend.min.css.map +1 -0
  4. package/dist/assets/fonts/bold-affa96571d-v2.woff +0 -0
  5. package/dist/assets/fonts/bold-b542beb274-v2.woff2 +0 -0
  6. package/dist/assets/fonts/light-94a07e06a1-v2.woff2 +0 -0
  7. package/dist/assets/fonts/light-f591b13f7d-v2.woff +0 -0
  8. package/dist/assets/govuk_publishing_components/images/icon-autocomplete-search-suggestion.svg +4 -0
  9. package/dist/assets/govuk_publishing_components/images/icon-close.svg +1 -0
  10. package/dist/assets/images/favicon.ico +0 -0
  11. package/dist/assets/images/favicon.svg +1 -0
  12. package/dist/assets/images/govuk-crest-2x.png +0 -0
  13. package/dist/assets/images/govuk-crest.png +0 -0
  14. package/dist/assets/images/govuk-crest.svg +1 -0
  15. package/dist/assets/images/govuk-icon-180.png +0 -0
  16. package/dist/assets/images/govuk-icon-192.png +0 -0
  17. package/dist/assets/images/govuk-icon-512.png +0 -0
  18. package/dist/assets/images/govuk-icon-mask.svg +1 -0
  19. package/dist/assets/images/govuk-opengraph-image.png +0 -0
  20. package/dist/assets/images/homepage-illustration.svg +1 -0
  21. package/dist/assets/images/homepage.svg +44 -0
  22. package/dist/assets/images/masthead-illustration.svg +123 -0
  23. package/dist/assets/images/oflog_crest_black.png +0 -0
  24. package/dist/assets/images/oflog_crest_white.png +0 -0
  25. package/dist/assets/images/undraw_approved-wireframe_odf4.svg +1 -0
  26. package/dist/assets/images/undraw_collaborators_rgw4.svg +1 -0
  27. package/dist/assets/images/undraw_content-creator_vuqg.svg +1 -0
  28. package/dist/assets/images/undraw_online-media_opxh.svg +1 -0
  29. package/dist/assets/images/undraw_pull-request_zlsu.svg +1 -0
  30. package/dist/assets/images/undraw_reviewed-docs_g0cg.svg +1 -0
  31. package/dist/components/FilterPanel/codeBlocks.d.ts +3 -0
  32. package/dist/components/FilterPanel/codeBlocks.js +418 -0
  33. package/dist/components/content/InsetText.svelte +21 -0
  34. package/dist/components/content/InsetText.svelte.d.ts +7 -0
  35. package/dist/components/content/WarningText.svelte +27 -0
  36. package/dist/components/content/WarningText.svelte.d.ts +8 -0
  37. package/dist/components/data-vis/axis/Axis.svelte +51 -0
  38. package/dist/components/data-vis/axis/Axis.svelte.d.ts +33 -0
  39. package/dist/components/data-vis/axis/Ticks.svelte +113 -0
  40. package/dist/components/data-vis/axis/Ticks.svelte.d.ts +33 -0
  41. package/dist/components/data-vis/line-chart/Line.svelte +150 -0
  42. package/dist/components/data-vis/line-chart/Line.svelte.d.ts +85 -0
  43. package/dist/components/data-vis/line-chart/LineChart.svelte +249 -0
  44. package/dist/components/data-vis/line-chart/LineChart.svelte.d.ts +73 -0
  45. package/dist/components/data-vis/line-chart/Lines.svelte +138 -0
  46. package/dist/components/data-vis/line-chart/Lines.svelte.d.ts +57 -0
  47. package/dist/components/data-vis/line-chart/Marker.svelte +61 -0
  48. package/dist/components/data-vis/line-chart/Marker.svelte.d.ts +37 -0
  49. package/dist/components/data-vis/line-chart/SeriesLabel.svelte +67 -0
  50. package/dist/components/data-vis/line-chart/SeriesLabel.svelte.d.ts +43 -0
  51. package/dist/components/data-vis/line-chart/ValueLabel.svelte +50 -0
  52. package/dist/components/data-vis/line-chart/ValueLabel.svelte.d.ts +25 -0
  53. package/dist/components/data-vis/map/Map.svelte +392 -0
  54. package/dist/components/data-vis/map/Map.svelte.d.ts +47 -0
  55. package/dist/components/data-vis/map/MapLegend.svelte +41 -0
  56. package/dist/components/data-vis/map/MapLegend.svelte.d.ts +15 -0
  57. package/dist/components/data-vis/map/NonStandardControls.svelte +42 -0
  58. package/dist/components/data-vis/map/NonStandardControls.svelte.d.ts +13 -0
  59. package/dist/components/data-vis/map/Tooltip.svelte +41 -0
  60. package/dist/components/data-vis/map/Tooltip.svelte.d.ts +19 -0
  61. package/dist/components/data-vis/map/colorbrewer.d.ts +337 -0
  62. package/dist/components/data-vis/map/colorbrewer.js +1523 -0
  63. package/dist/components/data-vis/map/colors.d.ts +13 -0
  64. package/dist/components/data-vis/map/colors.js +65 -0
  65. package/dist/components/data-vis/map/dataJoin.d.ts +2 -0
  66. package/dist/components/data-vis/map/dataJoin.js +27 -0
  67. package/dist/components/data-vis/map/fullTopo.json +1 -0
  68. package/dist/components/data-vis/map/jenks.d.ts +1 -0
  69. package/dist/components/data-vis/map/jenks.js +51 -0
  70. package/dist/components/data-vis/map/lad2023.json +1 -0
  71. package/dist/components/data-vis/map/mapUtils.d.ts +5 -0
  72. package/dist/components/data-vis/map/mapUtils.js +86 -0
  73. package/dist/components/data-vis/map/topo.json +1 -0
  74. package/dist/components/data-vis/table/Table.svelte +247 -0
  75. package/dist/components/data-vis/table/Table.svelte.d.ts +19 -0
  76. package/dist/components/layout/Breadcrumbs.svelte +191 -0
  77. package/dist/components/layout/Breadcrumbs.svelte.d.ts +24 -0
  78. package/dist/components/layout/Footer.svelte +171 -0
  79. package/dist/components/layout/Footer.svelte.d.ts +30 -0
  80. package/dist/components/layout/Header.svelte +43 -0
  81. package/dist/components/layout/Header.svelte.d.ts +7 -0
  82. package/dist/components/layout/InternalHeader.svelte +628 -0
  83. package/dist/components/layout/InternalHeader.svelte.d.ts +15 -0
  84. package/dist/components/layout/PhaseBanner.svelte +28 -0
  85. package/dist/components/layout/PhaseBanner.svelte.d.ts +9 -0
  86. package/dist/components/layout/ServiceNavigation.svelte +143 -0
  87. package/dist/components/layout/ServiceNavigation.svelte.d.ts +13 -0
  88. package/dist/components/layout/SideNavigation.svelte +345 -0
  89. package/dist/components/layout/SideNavigation.svelte.d.ts +25 -0
  90. package/dist/components/layout/service-navigation-nested-mobile/HeaderNav.svelte +91 -0
  91. package/dist/components/layout/service-navigation-nested-mobile/HeaderNav.svelte.d.ts +15 -0
  92. package/dist/components/layout/service-navigation-nested-mobile/MobileNav.svelte +233 -0
  93. package/dist/components/layout/service-navigation-nested-mobile/MobileNav.svelte.d.ts +27 -0
  94. package/dist/components/layout/service-navigation-nested-mobile/ServiceNavigationNestedMobile.svelte +70 -0
  95. package/dist/components/layout/service-navigation-nested-mobile/ServiceNavigationNestedMobile.svelte.d.ts +11 -0
  96. package/dist/components/layout/service-navigation-nested-mobile/SideNav.svelte +276 -0
  97. package/dist/components/layout/service-navigation-nested-mobile/SideNav.svelte.d.ts +22 -0
  98. package/dist/components/ui/Accordion.svelte +244 -0
  99. package/dist/components/ui/Accordion.svelte.d.ts +23 -0
  100. package/dist/components/ui/Breadcrumbs.svelte +198 -0
  101. package/dist/components/ui/Breadcrumbs.svelte.d.ts +24 -0
  102. package/dist/components/ui/Button.svelte +96 -0
  103. package/dist/components/ui/Button.svelte.d.ts +17 -0
  104. package/dist/components/ui/CheckBox.svelte +198 -0
  105. package/dist/components/ui/CheckBox.svelte.d.ts +27 -0
  106. package/dist/components/ui/ContentsList.svelte +1117 -0
  107. package/dist/components/ui/ContentsList.svelte.d.ts +25 -0
  108. package/dist/components/ui/DateInput.svelte +255 -0
  109. package/dist/components/ui/DateInput.svelte.d.ts +59 -0
  110. package/dist/components/ui/Details.svelte +12 -0
  111. package/dist/components/ui/Details.svelte.d.ts +13 -0
  112. package/dist/components/ui/FilterPanel.svelte +588 -0
  113. package/dist/components/ui/FilterPanel.svelte.d.ts +74 -0
  114. package/dist/components/ui/Footer.svelte +171 -0
  115. package/dist/components/ui/Footer.svelte.d.ts +30 -0
  116. package/dist/components/ui/Header.svelte +43 -0
  117. package/dist/components/ui/Header.svelte.d.ts +7 -0
  118. package/dist/components/ui/Masthead.svelte +267 -0
  119. package/dist/components/ui/Masthead.svelte.d.ts +12 -0
  120. package/dist/components/ui/NavigationExample.svelte +117 -0
  121. package/dist/components/ui/NavigationExample.svelte.d.ts +3 -0
  122. package/dist/components/ui/NotificationBanner.svelte +93 -0
  123. package/dist/components/ui/NotificationBanner.svelte.d.ts +15 -0
  124. package/dist/components/ui/Radios.svelte +176 -0
  125. package/dist/components/ui/Radios.svelte.d.ts +28 -0
  126. package/dist/components/ui/RelatedContent.svelte +596 -0
  127. package/dist/components/ui/RelatedContent.svelte.d.ts +29 -0
  128. package/dist/components/ui/Search.svelte +499 -0
  129. package/dist/components/ui/Search.svelte.d.ts +32 -0
  130. package/dist/components/ui/SearchAutocomplete.svelte +655 -0
  131. package/dist/components/ui/SearchAutocomplete.svelte.d.ts +37 -0
  132. package/dist/components/ui/Select.svelte +116 -0
  133. package/dist/components/ui/Select.svelte.d.ts +22 -0
  134. package/dist/components/ui/ServiceNavigation.svelte +143 -0
  135. package/dist/components/ui/ServiceNavigation.svelte.d.ts +13 -0
  136. package/dist/components/ui/SideNavigation.svelte +346 -0
  137. package/dist/components/ui/SideNavigation.svelte.d.ts +25 -0
  138. package/dist/components/ui/Tabs.svelte +306 -0
  139. package/dist/components/ui/Tabs.svelte.d.ts +18 -0
  140. package/dist/components/ui/WhatsNew.svelte +155 -0
  141. package/dist/components/ui/WhatsNew.svelte.d.ts +29 -0
  142. package/dist/config.d.ts +51 -0
  143. package/dist/config.js +44 -0
  144. package/dist/icons/DoubleChevronButton.svelte +62 -0
  145. package/dist/icons/DoubleChevronButton.svelte.d.ts +13 -0
  146. package/dist/icons/IconSearch.svelte +42 -0
  147. package/dist/icons/IconSearch.svelte.d.ts +6 -0
  148. package/dist/icons/SingleChevronButtonWithLabel.svelte +132 -0
  149. package/dist/icons/SingleChevronButtonWithLabel.svelte.d.ts +19 -0
  150. package/dist/index.d.ts +44 -0
  151. package/dist/index.js +45 -0
  152. package/dist/main.css +1 -0
  153. package/dist/package-wrapping/BaseInformation.svelte +82 -0
  154. package/dist/package-wrapping/BaseInformation.svelte.d.ts +15 -0
  155. package/dist/package-wrapping/BaseNameAndStatus.svelte +108 -0
  156. package/dist/package-wrapping/BaseNameAndStatus.svelte.d.ts +10 -0
  157. package/dist/package-wrapping/CodeBlock.svelte +62 -0
  158. package/dist/package-wrapping/CodeBlock.svelte.d.ts +12 -0
  159. package/dist/package-wrapping/ComponentDemo.svelte +114 -0
  160. package/dist/package-wrapping/ComponentDemo.svelte.d.ts +25 -0
  161. package/dist/package-wrapping/ComponentDemoTEMP.svelte +305 -0
  162. package/dist/package-wrapping/ComponentDemoTEMP.svelte.d.ts +21 -0
  163. package/dist/package-wrapping/ComponentDetails.svelte +123 -0
  164. package/dist/package-wrapping/ComponentDetails.svelte.d.ts +13 -0
  165. package/dist/package-wrapping/DividerLine.svelte +21 -0
  166. package/dist/package-wrapping/DividerLine.svelte.d.ts +17 -0
  167. package/dist/package-wrapping/InputForParameter.svelte +205 -0
  168. package/dist/package-wrapping/InputForParameter.svelte.d.ts +13 -0
  169. package/dist/package-wrapping/InputForParameterUpdated.svelte +222 -0
  170. package/dist/package-wrapping/InputForParameterUpdated.svelte.d.ts +17 -0
  171. package/dist/package-wrapping/InputForParameterUpdatedTEMP.svelte +203 -0
  172. package/dist/package-wrapping/InputForParameterUpdatedTEMP.svelte.d.ts +17 -0
  173. package/dist/package-wrapping/ListOfComponentStatuses.svelte +19 -0
  174. package/dist/package-wrapping/ListOfComponentStatuses.svelte.d.ts +11 -0
  175. package/dist/package-wrapping/OverlayAndComponentContainer.svelte +426 -0
  176. package/dist/package-wrapping/OverlayAndComponentContainer.svelte.d.ts +33 -0
  177. package/dist/package-wrapping/ParametersSection.svelte +235 -0
  178. package/dist/package-wrapping/ParametersSection.svelte.d.ts +19 -0
  179. package/dist/package-wrapping/ParsingErrorToastsContainer.svelte +50 -0
  180. package/dist/package-wrapping/ParsingErrorToastsContainer.svelte.d.ts +15 -0
  181. package/dist/package-wrapping/Pill.svelte +54 -0
  182. package/dist/package-wrapping/Pill.svelte.d.ts +25 -0
  183. package/dist/package-wrapping/PlaygroundDetails.svelte +106 -0
  184. package/dist/package-wrapping/PlaygroundDetails.svelte.d.ts +13 -0
  185. package/dist/package-wrapping/ScreenSizeRadio.svelte +24 -0
  186. package/dist/package-wrapping/ScreenSizeRadio.svelte.d.ts +11 -0
  187. package/dist/package-wrapping/ScreenSizeRadioUpdated.svelte +23 -0
  188. package/dist/package-wrapping/ScreenSizeRadioUpdated.svelte.d.ts +11 -0
  189. package/dist/package-wrapping/SidebarContainer.svelte +103 -0
  190. package/dist/package-wrapping/SidebarContainer.svelte.d.ts +23 -0
  191. package/dist/package-wrapping/WrapperDetailsUpdate.svelte +40 -0
  192. package/dist/package-wrapping/WrapperDetailsUpdate.svelte.d.ts +15 -0
  193. package/dist/package-wrapping/templates/Template.svelte +100 -0
  194. package/dist/package-wrapping/templates/Template.svelte.d.ts +25 -0
  195. package/dist/templates/ComponentPageTemplate.svelte +1 -0
  196. package/dist/templates/ComponentPageTemplate.svelte.d.ts +26 -0
  197. package/dist/utils/data-transformations/convertCSV.d.ts +2 -0
  198. package/dist/utils/data-transformations/convertCSV.js +22 -0
  199. package/dist/utils/data-transformations/getValueFromParametersArray.d.ts +1 -0
  200. package/dist/utils/data-transformations/getValueFromParametersArray.js +9 -0
  201. package/dist/utils/layoutNavHelpers.d.ts +70 -0
  202. package/dist/utils/layoutNavHelpers.js +129 -0
  203. package/dist/utils/package-wrapping-specific/addIndexAndInitialValue.d.ts +1 -0
  204. package/dist/utils/package-wrapping-specific/addIndexAndInitialValue.js +21 -0
  205. package/dist/utils/package-wrapping-specific/createBindableParametersValuesArray.d.ts +1 -0
  206. package/dist/utils/package-wrapping-specific/createBindableParametersValuesArray.js +12 -0
  207. package/dist/utils/package-wrapping-specific/createParametersObject.d.ts +1 -0
  208. package/dist/utils/package-wrapping-specific/createParametersObject.js +29 -0
  209. package/dist/utils/package-wrapping-specific/defineDefaultEventHandler.d.ts +1 -0
  210. package/dist/utils/package-wrapping-specific/defineDefaultEventHandler.js +14 -0
  211. package/dist/utils/package-wrapping-specific/trackVisibleParameters.d.ts +1 -0
  212. package/dist/utils/package-wrapping-specific/trackVisibleParameters.js +29 -0
  213. package/dist/utils/syntax-highlighting/shikiHighlight.d.ts +7 -0
  214. package/dist/utils/syntax-highlighting/shikiHighlight.js +76 -0
  215. package/dist/utils/text-string-conversion/textStringConversion.d.ts +9 -0
  216. package/dist/utils/text-string-conversion/textStringConversion.js +86 -0
  217. package/package.json +113 -0
@@ -0,0 +1,233 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+
4
+ // Types for navigation items
5
+ export type SubNavItem = {
6
+ text: string;
7
+ href: string;
8
+ };
9
+
10
+ export type NavItem = {
11
+ text: string;
12
+ href: string;
13
+ description?: string;
14
+ };
15
+
16
+ export type NavSection = {
17
+ title: string;
18
+ href?: string; // Optional: Href for the main section link itself
19
+ items: (
20
+ | SubNavItem
21
+ | {
22
+ // Represents a group within a section
23
+ title?: string;
24
+ items: SubNavItem[];
25
+ }
26
+ )[];
27
+ };
28
+
29
+ // Component props
30
+ let {
31
+ isOpen = false,
32
+ sections = [] as NavSection[],
33
+ activeSectionHref = $bindable(""), // Href for the active section header, e.g. /components
34
+ activeDetailHref = $bindable(""), // Href for the detailed active item, e.g. /components/x/y#z
35
+ onNavigate = (href: string, event?: MouseEvent) => {},
36
+ } = $props<{
37
+ isOpen?: boolean;
38
+ sections?: NavSection[];
39
+ activeSectionHref?: string;
40
+ activeDetailHref?: string;
41
+ onNavigate?: (href: string, event?: MouseEvent) => void;
42
+ }>();
43
+
44
+ // Track which sections are expanded
45
+ let expandedSections = $state<Record<string, boolean>>({});
46
+
47
+ // Toggle a section's expanded state
48
+ function toggleSection(sectionTitle: string) {
49
+ expandedSections[sectionTitle] = !expandedSections[sectionTitle];
50
+ }
51
+
52
+ /**
53
+ * Determines if a section is expanded:
54
+ * - If the user has toggled a section explicitly (in expandedSections), use that.
55
+ * - Otherwise, expand the section whose href matches activeSectionHref.
56
+ */
57
+ function isSectionExpanded(sectionTitle: string): boolean {
58
+ const explicit = expandedSections[sectionTitle];
59
+ if (explicit !== undefined) return explicit;
60
+ const section = sections.find((s) => s.title === sectionTitle);
61
+ return !!section?.href && section.href === activeSectionHref;
62
+ }
63
+
64
+ // When a navigation happens, call the onNavigate prop
65
+ function handleNavigate(href: string, event: MouseEvent) {
66
+ if (!event.defaultPrevented) {
67
+ onNavigate(href, event);
68
+ }
69
+ }
70
+ </script>
71
+
72
+ <nav
73
+ id="app-mobile-nav"
74
+ class:app-mobile-nav={true}
75
+ class:js-app-mobile-nav={true}
76
+ class:app-mobile-nav--active={isOpen}
77
+ aria-label="mobile navigation"
78
+ aria-hidden={!isOpen}
79
+ >
80
+ <div class="app-mobile-nav__wrapper">
81
+ <ul class="app-mobile-nav__list">
82
+ {#each sections as section (section.title)}
83
+ <li class="app-mobile-nav__section">
84
+ <div class="app-mobile-nav__section-header">
85
+ <a
86
+ href={section.href || "#"}
87
+ class="app-mobile-nav__section-link"
88
+ class:app-mobile-nav__section-link--current={section.href &&
89
+ section.href === activeSectionHref}
90
+ onclick={(event) => {
91
+ event.preventDefault();
92
+ toggleSection(section.title);
93
+ }}
94
+ >
95
+ {section.title}
96
+ </a>
97
+ </div>
98
+
99
+ <ul
100
+ class="app-mobile-nav__list app-mobile-nav__section-items"
101
+ class:app-mobile-nav__section-items--active={isSectionExpanded(
102
+ section.title,
103
+ )}
104
+ aria-label={section.title}
105
+ >
106
+ {#each section.items as item, i (i)}
107
+ {#if "items" in item && Array.isArray(item.items)}
108
+ <!-- Grouped items with title -->
109
+ <li class="app-mobile-nav__group">
110
+ {#if item.title}
111
+ <h2 class="app-mobile-nav__group-title">{item.title}</h2>
112
+ {/if}
113
+ <ul class="app-mobile-nav__list">
114
+ {#each item.items as subItem, k (k)}
115
+ {@const isSubItemCurrent =
116
+ subItem.href === activeDetailHref}
117
+ <li>
118
+ <a
119
+ class="app-mobile-nav__link"
120
+ class:app-mobile-nav__link--current={isSubItemCurrent}
121
+ href={subItem.href}
122
+ onclick={(e) => handleNavigate(subItem.href, e)}
123
+ >
124
+ {@html subItem.text}
125
+ </a>
126
+ </li>
127
+ {/each}
128
+ </ul>
129
+ </li>
130
+ {:else if "href" in item && "text" in item}
131
+ <!-- Single navigation item -->
132
+ {@const navItem = item as SubNavItem}
133
+ {@const isNavItemCurrent = navItem.href === activeDetailHref}
134
+ <li>
135
+ <a
136
+ class="app-mobile-nav__link"
137
+ class:app-mobile-nav__link--current={isNavItemCurrent}
138
+ href={navItem.href}
139
+ onclick={(e) => handleNavigate(navItem.href, e)}
140
+ >
141
+ {@html navItem.text}
142
+ </a>
143
+ </li>
144
+ {/if}
145
+ {/each}
146
+ </ul>
147
+ </li>
148
+ {/each}
149
+ </ul>
150
+ </div>
151
+ </nav>
152
+
153
+ <style>
154
+ .app-mobile-nav {
155
+ display: none;
156
+ border-bottom: 1px solid #b1b4b6;
157
+ background-color: #f8f8f8;
158
+ }
159
+
160
+ .app-mobile-nav--active {
161
+ display: block;
162
+ }
163
+
164
+ @media (min-width: 40.0625em) {
165
+ .app-mobile-nav--active {
166
+ display: none;
167
+ }
168
+ }
169
+
170
+ .app-mobile-nav__wrapper {
171
+ padding: 0;
172
+ }
173
+
174
+ .app-mobile-nav__list {
175
+ margin: 0;
176
+ padding: 0;
177
+ list-style: none;
178
+ }
179
+
180
+ .app-mobile-nav__section {
181
+ border-bottom: 1px solid #f3f2f1;
182
+ }
183
+
184
+ .app-mobile-nav__section-header {
185
+ display: block;
186
+ background-color: #f3f2f1;
187
+ font-weight: 700;
188
+ }
189
+
190
+ .app-mobile-nav__section-link {
191
+ display: block;
192
+ padding: 15px;
193
+ text-decoration: none;
194
+ color: #1d70b8;
195
+ font-weight: 700;
196
+ }
197
+
198
+ .app-mobile-nav__section-link--current {
199
+ /* border-left: 4px solid #1d70b8; */
200
+ padding-left: 11px;
201
+ }
202
+
203
+ .app-mobile-nav__section-items {
204
+ display: none;
205
+ background-color: #fff;
206
+ }
207
+
208
+ .app-mobile-nav__section-items--active {
209
+ display: block;
210
+ }
211
+
212
+ .app-mobile-nav__link {
213
+ display: block;
214
+ padding: 15px;
215
+ text-decoration: none;
216
+ color: #1d70b8;
217
+ }
218
+
219
+ .app-mobile-nav__link--current {
220
+ border-left: 4px solid #1d70b8;
221
+ padding-left: 11px;
222
+ /* background-color: #f3f2f1; */
223
+ }
224
+
225
+ .app-mobile-nav__group-title {
226
+ margin: 0;
227
+ padding: 15px 15px 5px 15px;
228
+ color: #505a5f;
229
+ font-size: 19px;
230
+ font-weight: normal;
231
+ font-family: Helvetica, Arial, sans-serif;
232
+ }
233
+ </style>
@@ -0,0 +1,27 @@
1
+ export type SubNavItem = {
2
+ text: string;
3
+ href: string;
4
+ };
5
+ export type NavItem = {
6
+ text: string;
7
+ href: string;
8
+ description?: string;
9
+ };
10
+ export type NavSection = {
11
+ title: string;
12
+ href?: string;
13
+ items: (SubNavItem | {
14
+ title?: string;
15
+ items: SubNavItem[];
16
+ })[];
17
+ };
18
+ type $$ComponentProps = {
19
+ isOpen?: boolean;
20
+ sections?: NavSection[];
21
+ activeSectionHref?: string;
22
+ activeDetailHref?: string;
23
+ onNavigate?: (href: string, event?: MouseEvent) => void;
24
+ };
25
+ declare const MobileNav: import("svelte").Component<$$ComponentProps, {}, "activeSectionHref" | "activeDetailHref">;
26
+ type MobileNav = ReturnType<typeof MobileNav>;
27
+ export default MobileNav;
@@ -0,0 +1,70 @@
1
+ <!--
2
+ This component acts as a parent container for HeaderNav and MobileNav,
3
+ managing the shared state and interactions for nested mobile navigation.
4
+ -->
5
+ <script lang="ts">
6
+ import HeaderNav from "./HeaderNav.svelte";
7
+ import MobileNav from "./MobileNav.svelte";
8
+ import type { NavigationItem } from "./HeaderNav.svelte";
9
+ import type { NavSection } from "./MobileNav.svelte";
10
+
11
+ // --- Props ---
12
+ let {
13
+ serviceName = "Service Name",
14
+ homeHref,
15
+ mobileNavSections,
16
+ activeSectionHref, // Path of the active section, e.g. "/components"
17
+ activeDetailHref, // Path of the specific active item, e.g. "/components/forms/button#examples"
18
+ }: {
19
+ serviceName?: string;
20
+ homeHref: string;
21
+ mobileNavSections: NavSection[];
22
+ activeSectionHref: string;
23
+ activeDetailHref: string;
24
+ } = $props();
25
+
26
+ // --- State ---
27
+ let isMobileNavOpen = $state(false);
28
+
29
+ // --- Derived headerNavigationItems for HeaderNav ---
30
+ let headerNavigationItems = $derived(
31
+ mobileNavSections.map(({ title, href }) => ({
32
+ text: title,
33
+ href: href ?? "",
34
+ })),
35
+ );
36
+
37
+ // --- Handlers ---
38
+ // Toggle the mobile nav open/closed state
39
+ function handleToggleMobileNav() {
40
+ isMobileNavOpen = !isMobileNavOpen;
41
+ }
42
+
43
+ // Handle navigation request from mobile menu
44
+ function handleMobileNavigation() {
45
+ isMobileNavOpen = false;
46
+ }
47
+ </script>
48
+
49
+ <div>
50
+ {#if headerNavigationItems && headerNavigationItems.length > 0}
51
+ <HeaderNav
52
+ {serviceName}
53
+ {homeHref}
54
+ navigationItems={headerNavigationItems}
55
+ bind:isMobileNavOpen
56
+ activeItemHref={activeSectionHref}
57
+ onToggle={handleToggleMobileNav}
58
+ />
59
+ {/if}
60
+ </div>
61
+
62
+ {#if isMobileNavOpen}
63
+ <MobileNav
64
+ sections={mobileNavSections}
65
+ {activeSectionHref}
66
+ {activeDetailHref}
67
+ onNavigate={handleMobileNavigation}
68
+ isOpen={isMobileNavOpen}
69
+ />
70
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { NavSection } from "./MobileNav.svelte";
2
+ type $$ComponentProps = {
3
+ serviceName?: string;
4
+ homeHref: string;
5
+ mobileNavSections: NavSection[];
6
+ activeSectionHref: string;
7
+ activeDetailHref: string;
8
+ };
9
+ declare const ServiceNavigationNestedMobile: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type ServiceNavigationNestedMobile = ReturnType<typeof ServiceNavigationNestedMobile>;
11
+ export default ServiceNavigationNestedMobile;
@@ -0,0 +1,276 @@
1
+ <script lang="ts">
2
+ export type SideNavItem = {
3
+ text: string;
4
+ href: string;
5
+ subItems?: { text: string; href: string }[];
6
+ };
7
+
8
+ export type SideNavGroup = {
9
+ title?: string;
10
+ items: SideNavItem[];
11
+ };
12
+
13
+ let {
14
+ title = "Pages in this section",
15
+ items = $bindable([] as SideNavItem[]),
16
+ groups = $bindable([] as SideNavGroup[]),
17
+ currentItem = $bindable(""), // This prop is the SOLE driver for active state
18
+ activeItemBackgroundColor = $bindable("transparent"),
19
+ } = $props<{
20
+ title?: string;
21
+ items?: SideNavItem[];
22
+ groups?: SideNavGroup[];
23
+ currentItem?: string; // Value from parent
24
+ activeItemBackgroundColor?: string;
25
+ }>();
26
+
27
+ function calculateIsActive(
28
+ item: { href: string; subItems?: { href: string }[] },
29
+ activePropValue: string | undefined,
30
+ ): boolean {
31
+ if (!activePropValue) {
32
+ return false; // If currentItem prop is not set, nothing is active by it
33
+ }
34
+
35
+ // Check 1: Direct match with item's href
36
+ if (item.href === activePropValue) {
37
+ return true;
38
+ }
39
+
40
+ // Check 2: If activePropValue is a sub-item of this item
41
+ if (item.subItems) {
42
+ for (const subItem of item.subItems) {
43
+ if (subItem.href === activePropValue) {
44
+ return true; // Parent item is active because its sub-item is the active one
45
+ }
46
+ }
47
+ }
48
+
49
+ // Check 3: If item.href is the base path of activePropValue (when activePropValue has a hash)
50
+ // e.g., activePropValue="/page#section1", item.href="/page"
51
+ const activePropBasePath = activePropValue.split("#")[0];
52
+ if (item.href === activePropBasePath && activePropValue.includes("#")) {
53
+ return true;
54
+ }
55
+
56
+ return false;
57
+ }
58
+ </script>
59
+
60
+ {#snippet navItem(item: SideNavItem, activeGlobalItem: string)}
61
+ {@const isActive = calculateIsActive(item, activeGlobalItem)}
62
+ <li
63
+ class="app-subnav__section-item {isActive
64
+ ? 'app-subnav__section-item--current app-subnav__section-item--bold app-subnav__section-item--top'
65
+ : ''}"
66
+ style={isActive ? `background-color: ${activeItemBackgroundColor};` : ""}
67
+ >
68
+ <a
69
+ class="govuk-link govuk-link--no-visited-state govuk-link--no-underline app-subnav__link"
70
+ href={item.href}
71
+ aria-current={isActive ? "location" : undefined}
72
+ >
73
+ {@html item.text}
74
+ </a>
75
+
76
+ {#if isActive && item.subItems && item.subItems.length > 0}
77
+ <!-- Nested list for sub-items of the current active item -->
78
+ <ul class="app-subnav__section app-subnav__section--nested">
79
+ {#each item.subItems as subItem (subItem.href)}
80
+ {@const isSubActive = subItem.href === activeGlobalItem}
81
+ <li class="app-subnav__section-item">
82
+ <a
83
+ class="govuk-link govuk-link--no-visited-state govuk-link--no-underline app-subnav__link"
84
+ class:app-subnav__link--bold={isSubActive}
85
+ href={subItem.href}
86
+ >
87
+ {@html subItem.text}
88
+ </a>
89
+ </li>
90
+ {/each}
91
+ </ul>
92
+ {/if}
93
+ </li>
94
+ {/snippet}
95
+
96
+ <div class="app-pane__subnav">
97
+ <!-- Main container for the side navigation -->
98
+ <nav class="app-subnav" aria-labelledby="app-subnav-heading">
99
+ <h2 class="govuk-visually-hidden" id="app-subnav-heading">
100
+ {title}
101
+ </h2>
102
+
103
+ {#if items.length > 0}
104
+ <!-- Renders the flat list of items, if provided -->
105
+ <ul class="app-subnav__section">
106
+ {#each items as item (item.href)}
107
+ {@render navItem(item, currentItem)}
108
+ {/each}
109
+ </ul>
110
+ {/if}
111
+
112
+ {#each groups as group (group.title || group.items[0]?.href)}
113
+ <!-- Renders grouped sections of items -->
114
+ {#if group.title}
115
+ <h3 class="app-subnav__theme">{group.title}</h3>
116
+ {/if}
117
+ <ul class="app-subnav__section">
118
+ {#each group.items as item (item.href)}
119
+ {@render navItem(item, currentItem)}
120
+ {/each}
121
+ </ul>
122
+ {/each}
123
+ </nav>
124
+ </div>
125
+
126
+ <style>
127
+ .app-subnav {
128
+ margin-bottom: 100px;
129
+ padding: 30px 15px 0 0;
130
+ font-family: Helvetica, Arial, sans-serif;
131
+ -webkit-font-smoothing: antialiased;
132
+ -moz-osx-font-smoothing: grayscale;
133
+ font-weight: 400;
134
+ font-size: 0.875rem;
135
+ line-height: 1.14286;
136
+ }
137
+
138
+ @media print {
139
+ .app-subnav {
140
+ font-family: sans-serif;
141
+ }
142
+ }
143
+
144
+ @media (min-width: 40.0625em) {
145
+ .app-subnav {
146
+ font-size: 1rem;
147
+ line-height: 1.25;
148
+ }
149
+ }
150
+
151
+ @media print {
152
+ .app-subnav {
153
+ font-size: 14pt;
154
+ line-height: 1.2;
155
+ }
156
+ }
157
+
158
+ .app-subnav__section {
159
+ margin: 0 0 20px;
160
+ padding: 0;
161
+ list-style-type: none;
162
+ }
163
+
164
+ .app-subnav__link {
165
+ padding: 2px 0;
166
+ font-family: Helvetica, Arial, sans-serif;
167
+ -webkit-font-smoothing: antialiased;
168
+ -moz-osx-font-smoothing: grayscale;
169
+ font-weight: 400;
170
+ font-size: 0.875rem;
171
+ line-height: 1.14286;
172
+
173
+ overflow-wrap: break-word;
174
+ }
175
+
176
+ @media print {
177
+ .app-subnav__link {
178
+ font-family: sans-serif;
179
+ }
180
+ }
181
+
182
+ @media (min-width: 40.0625em) {
183
+ .app-subnav__link {
184
+ font-size: 1rem;
185
+ line-height: 1.25;
186
+ }
187
+ }
188
+
189
+ @media print {
190
+ .app-subnav__link {
191
+ font-size: 14pt;
192
+ line-height: 1.2;
193
+ }
194
+ }
195
+
196
+ .app-subnav__link:not(:focus):hover {
197
+ color: #1a65a6;
198
+ }
199
+
200
+ .app-subnav__section-item {
201
+ position: relative;
202
+ margin-bottom: 5px;
203
+ padding-top: 5px;
204
+ padding-bottom: 5px;
205
+ }
206
+
207
+ .app-subnav__section-item--current {
208
+ margin-left: -19px;
209
+ padding-left: 15px;
210
+ border-left: 4px solid #1a65a6;
211
+ background-color: #f8f8f8;
212
+ }
213
+
214
+ .app-subnav__section-item--bold .app-subnav__link {
215
+ font-weight: bold;
216
+ }
217
+
218
+ .app-subnav__section-item--top .app-subnav__link {
219
+ position: relative;
220
+ z-index: 2;
221
+ }
222
+
223
+ .app-subnav__section--nested {
224
+ margin-top: 10px;
225
+ margin-bottom: 0;
226
+ padding-left: 20px;
227
+ }
228
+
229
+ .app-subnav__section--nested .app-subnav__section-item::before {
230
+ margin-left: -20px;
231
+ color: #505a5f;
232
+ content: "—";
233
+ }
234
+
235
+ .app-subnav__section--nested .app-subnav__link {
236
+ padding-left: 0;
237
+ font-weight: normal;
238
+ }
239
+
240
+ .app-subnav__section--nested .app-subnav__link--bold {
241
+ padding-left: 0;
242
+ font-weight: bold;
243
+ }
244
+
245
+ .app-subnav__theme {
246
+ margin: 0;
247
+ padding: 10px 15px 10px 0;
248
+ color: #505a5f;
249
+ font-family: Helvetica, Arial, sans-serif;
250
+ -webkit-font-smoothing: antialiased;
251
+ -moz-osx-font-smoothing: grayscale;
252
+ font-weight: 400;
253
+ font-size: 1rem;
254
+ line-height: 1.25;
255
+ }
256
+
257
+ @media print {
258
+ .app-subnav__theme {
259
+ font-family: sans-serif;
260
+ }
261
+ }
262
+
263
+ @media (min-width: 40.0625em) {
264
+ .app-subnav__theme {
265
+ font-size: 1.1875rem;
266
+ line-height: 1.31579;
267
+ }
268
+ }
269
+
270
+ @media print {
271
+ .app-subnav__theme {
272
+ font-size: 14pt;
273
+ line-height: 1.15;
274
+ }
275
+ }
276
+ </style>
@@ -0,0 +1,22 @@
1
+ export type SideNavItem = {
2
+ text: string;
3
+ href: string;
4
+ subItems?: {
5
+ text: string;
6
+ href: string;
7
+ }[];
8
+ };
9
+ export type SideNavGroup = {
10
+ title?: string;
11
+ items: SideNavItem[];
12
+ };
13
+ type $$ComponentProps = {
14
+ title?: string;
15
+ items?: SideNavItem[];
16
+ groups?: SideNavGroup[];
17
+ currentItem?: string;
18
+ activeItemBackgroundColor?: string;
19
+ };
20
+ declare const SideNav: import("svelte").Component<$$ComponentProps, {}, "items" | "groups" | "currentItem" | "activeItemBackgroundColor">;
21
+ type SideNav = ReturnType<typeof SideNav>;
22
+ export default SideNav;