@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,418 @@
1
+ export const serverFormExampleCode = `// ... see previous examples for FilterPanel setup ...
2
+
3
+ <script lang="ts">
4
+ import FilterPanel from './FilterPanel.svelte';
5
+ import type { Section, FilteredResult } from './types';
6
+ import Accordion from '../Accordion/Accordion.svelte';
7
+ import CodeBlock from '../CodeBlock/CodeBlock.svelte';
8
+ import { serverFormExampleCode } from './codeBlocks.js'; // Assuming this file is aliased
9
+
10
+ export let form: {
11
+ data?: FilteredResult[];
12
+ error?: string;
13
+ message?: string;
14
+ } | null = null;
15
+
16
+ const sectionsData: Section[] = [
17
+ {
18
+ type: "checkboxes" as "checkboxes",
19
+ title: "Select Metrics (Server)",
20
+ name: "metrics",
21
+ options: [
22
+ { label: "Household waste recycling rates", value: "Household waste recycling rates" },
23
+ { label: "Percentage of household waste sent to landfill", value: "Percentage of household waste sent to landfill" },
24
+ { label: "Total fly-tipping incidents", value: "Total fly-tipping incidents" },
25
+ ],
26
+ },
27
+ {
28
+ type: "radio" as "radio",
29
+ title: "Select Year (Server)",
30
+ name: "year",
31
+ options: [
32
+ { label: "2019/20", value: "2019/20" },
33
+ { label: "2020/21", value: "2020/21" },
34
+ { label: "2021/22", value: "2021/22" },
35
+ ],
36
+ },
37
+ {
38
+ type: "select" as "select",
39
+ title: "Select Area Type (Server)",
40
+ name: "areaType",
41
+ options: [
42
+ { label: "District", value: "District" },
43
+ { label: "Unitary Authority", value: "Unitary Authority" },
44
+ { label: "County Council", value: "County Council" },
45
+ { label: "Region", value: "Region" },
46
+ ],
47
+ }
48
+ ];
49
+ </script>
50
+
51
+ <Accordion single>
52
+ <div class="accordion-item py-4" data-title="Example 3: Server Form Submission">
53
+ <p class="text-sm text-surface-500 mb-4">
54
+ This example demonstrates a form that submits data to a server action.
55
+ The filtering logic is handled on the server-side in <code>+page.server.ts</code>.
56
+ The results are then returned and displayed.
57
+ </p>
58
+ <FilterPanel
59
+ formClasses="p-4 border rounded-lg"
60
+ fieldSetClasses="mb-4 p-2 border-t"
61
+ legendClasses="text-lg font-semibold"
62
+ showSubmitButton={true}
63
+ submitButtonText="Filter on Server"
64
+ target="?/serverFilter"
65
+ {sectionsData}
66
+ resultsHeaderText="Server Form Submission Results (Filtered in +page.server.ts)"
67
+ >
68
+ <svelte:fragment slot="results">
69
+ {#if form?.data}
70
+ <div class="mt-4 p-4 border rounded-lg bg-surface-100 dark:bg-surface-800">
71
+ <h3 class="text-xl font-semibold mb-2">Results</h3>
72
+ <p class="text-sm text-surface-600 dark:text-surface-300">
73
+ Displaying {form.data.length} results. Filtered by the server.
74
+ </p>
75
+ <div class="overflow-x-auto mt-2">
76
+ <table class="table table-compact w-full">
77
+ <thead>
78
+ <tr>
79
+ <th>Metric</th>
80
+ <th>Area</th>
81
+ <th>Years (X)</th>
82
+ <th>Values (Y)</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody>
86
+ {#each form.data as result}
87
+ <tr>
88
+ <td>{result.metricName}</td>
89
+ <td>{result.areaName}</td>
90
+ <td>{result.data.map(d => d.x).join(', ')}</td>
91
+ <td>{result.data.map(d => d.y).join(', ')}</td>
92
+ </tr>
93
+ {/each}
94
+ </tbody>
95
+ </table>
96
+ </div>
97
+ </div>
98
+ {:else if form?.error}
99
+ <div class="mt-4 p-4 border rounded-lg bg-error-100 dark:bg-error-800 text-error-700 dark:text-error-200">
100
+ <h3 class="text-xl font-semibold mb-2">Error</h3>
101
+ <p>{form.error}</p>
102
+ </div>
103
+ {:else if form?.message}
104
+ <div class="mt-4 p-4 border rounded-lg bg-warning-100 dark:bg-warning-800 text-warning-700 dark:text-warning-200">
105
+ <h3 class="text-xl font-semibold mb-2">Info</h3>
106
+ <p>{form.message}</p>
107
+ </div>
108
+ {/if}
109
+ </svelte:fragment>
110
+ </FilterPanel>
111
+ <h4 class="text-lg font-semibold mt-6 mb-2">Code:</h4>
112
+ <CodeBlock language="svelte" code={serverFormExampleCode} />
113
+ </div>
114
+ </Accordion>
115
+ `;
116
+
117
+ export const enhancedFormExampleCode = `// ... see previous examples for FilterPanel setup ...
118
+
119
+ <script lang="ts">
120
+ import FilterPanel from './FilterPanel.svelte';
121
+ import type { Section, FilterPanelData, FilteredResult } from './types';
122
+ import Accordion from '../Accordion/Accordion.svelte';
123
+ import CodeBlock from '../CodeBlock/CodeBlock.svelte';
124
+ import { enhancedFormExampleCode } from './codeBlocks.js';
125
+ import { page } from '$app/state';
126
+ import { enhance } from '$app/forms';
127
+
128
+ // Reactive state for results
129
+ let clientFilteredResults: FilteredResult[] = $state([]);
130
+ let formMessage: string | undefined = $state(undefined);
131
+ let formError: string | undefined = $state(undefined);
132
+
133
+ // Data for the line chart from +page.ts load function
134
+ const allDataForChart: FilteredResult[] = $page.data.dataInFormatForLineChart || [];
135
+
136
+ const sectionsData: Section[] = [
137
+ // ... same sectionsData as client-side filtering example ...
138
+ {
139
+ type: "checkboxes" as "checkboxes",
140
+ title: "Select Metrics (Client - Enhanced)",
141
+ name: "metrics",
142
+ options: [
143
+ { label: "Household waste recycling rates", value: "Household waste recycling rates" },
144
+ { label: "Percentage of household waste sent to landfill", value: "Percentage of household waste sent to landfill" },
145
+ { label: "Total fly-tipping incidents", value: "Total fly-tipping incidents" },
146
+ ],
147
+ },
148
+ {
149
+ type: "radio" as "radio",
150
+ title: "Select Year (Client - Enhanced)",
151
+ name: "year",
152
+ options: [
153
+ { label: "2019/20", value: "2019/20" },
154
+ { label: "2020/21", value: "2020/21" },
155
+ { label: "2021/22", value: "2021/22" },
156
+ ],
157
+ },
158
+ {
159
+ type: "select" as "select",
160
+ title: "Select Area Type (Client - Enhanced)",
161
+ name: "areaType",
162
+ options: [
163
+ { label: "District", value: "District" },
164
+ { label: "Unitary Authority", value: "Unitary Authority" },
165
+ { label: "County Council", value: "County Council" },
166
+ { label: "Region", value: "Region" },
167
+ ],
168
+ }
169
+ ];
170
+
171
+ function filterData(allChartData: FilteredResult[], formData: FilterPanelData): FilteredResult[] {
172
+ const selectedMetrics = formData.metrics as string[] | string | undefined;
173
+ const selectedYear = formData.year as string | undefined;
174
+ const selectedAreaType = formData.areaType as string | undefined;
175
+
176
+ let results = allChartData;
177
+
178
+ if (selectedMetrics && selectedMetrics.length > 0) {
179
+ const metricsArray = Array.isArray(selectedMetrics) ? selectedMetrics : [selectedMetrics];
180
+ results = results.filter(item => metricsArray.includes(item.metricName));
181
+ }
182
+
183
+ if (selectedAreaType) {
184
+ results = results.filter(item => item.areaType === selectedAreaType);
185
+ }
186
+
187
+ if (selectedYear) {
188
+ results = results.map(metricEntry => {
189
+ const filteredDataPoints = metricEntry.data.filter(dp => dp.x === selectedYear);
190
+ return { ...metricEntry, data: filteredDataPoints };
191
+ }).filter(metricEntry => metricEntry.data.length > 0);
192
+ }
193
+ return results;
194
+ }
195
+ </script>
196
+
197
+ <Accordion single>
198
+ <div class="accordion-item py-4" data-title="Example 4: Progressive Enhancement (Client-Side Filtering from Server Action)">
199
+ <p class="text-sm text-surface-500 mb-4">
200
+ This example uses <code>use:enhance</code> for progressive enhancement. The form submits to a server action
201
+ (<code>?/enhancedFilter</code>), which currently just echoes back the form data.
202
+ The client-side JavaScript then takes this echoed data and performs filtering using
203
+ the same <code>allDataForChart</code> data source as the pure client-side example.
204
+ This demonstrates handling server responses and updating the UI on the client.
205
+ </p>
206
+ <form
207
+ method="POST"
208
+ action="?/enhancedFilter"
209
+ use:enhance={({ form, data, action, cancel, submitter }) => {
210
+ return async ({ result, update }) => {
211
+ formMessage = undefined;
212
+ formError = undefined;
213
+ clientFilteredResults = [];
214
+
215
+ if (result.type === 'success' && result.data?.formData) {
216
+ const rawFormData = result.data.formData as Record<string, FormDataEntryValue>;
217
+ const processedFormData: FilterPanelData = {};
218
+ for (const key in rawFormData) {
219
+ // Handling array values for checkboxes
220
+ if (data.getAll(key).length > 1) {
221
+ processedFormData[key] = data.getAll(key).map(val => val.toString());
222
+ } else {
223
+ processedFormData[key] = rawFormData[key]?.toString();
224
+ }
225
+ }
226
+ clientFilteredResults = filterData(allDataForChart, processedFormData);
227
+ if (clientFilteredResults.length === 0) {
228
+ formMessage = "No results found for your selection (client-filtered).";
229
+ }
230
+ } else if (result.type === 'failure') {
231
+ formError = result.data?.message || 'Filtering failed on the server.';
232
+ } else if (result.type === 'error') {
233
+ formError = result.error?.message || 'An unexpected error occurred.';
234
+ }
235
+ };
236
+ }}
237
+ >
238
+ <FilterPanel
239
+ formClasses="p-4 border rounded-lg"
240
+ fieldSetClasses="mb-4 p-2 border-t"
241
+ legendClasses="text-lg font-semibold"
242
+ showSubmitButton={true}
243
+ submitButtonText="Filter with Enhancement"
244
+ {sectionsData}
245
+ resultsHeaderText="Client-Filtered Results (from Enhanced Server Action)"
246
+ >
247
+ <svelte:fragment slot="results">
248
+ {#if clientFilteredResults.length > 0}
249
+ <div class="mt-4 p-4 border rounded-lg bg-surface-100 dark:bg-surface-800">
250
+ <h3 class="text-xl font-semibold mb-2">Results</h3>
251
+ <p class="text-sm text-surface-600 dark:text-surface-300">
252
+ Displaying {clientFilteredResults.length} results. Filtered by client after server echo.
253
+ </p>
254
+ <div class="overflow-x-auto mt-2">
255
+ <table class="table table-compact w-full">
256
+ <thead>
257
+ <tr>
258
+ <th>Metric</th>
259
+ <th>Area</th>
260
+ <th>Years (X)</th>
261
+ <th>Values (Y)</th>
262
+ </tr>
263
+ </thead>
264
+ <tbody>
265
+ {#each clientFilteredResults as result}
266
+ <tr>
267
+ <td>{result.metricName}</td>
268
+ <td>{result.areaName}</td>
269
+ <td>{result.data.map(d => d.x).join(', ')}</td>
270
+ <td>{result.data.map(d => d.y).join(', ')}</td>
271
+ </tr>
272
+ {/each}
273
+ </tbody>
274
+ </table>
275
+ </div>
276
+ </div>
277
+ {:else if formError}
278
+ <div class="mt-4 p-4 border rounded-lg bg-error-100 dark:bg-error-800 text-error-700 dark:text-error-200">
279
+ <h3 class="text-xl font-semibold mb-2">Error</h3>
280
+ <p>{formError}</p>
281
+ </div>
282
+ {:else if formMessage}
283
+ <div class="mt-4 p-4 border rounded-lg bg-warning-100 dark:bg-warning-800 text-warning-700 dark:text-warning-200">
284
+ <h3 class="text-xl font-semibold mb-2">Info</h3>
285
+ <p>{formMessage}</p>
286
+ </div>
287
+ {/if}
288
+ </svelte:fragment>
289
+ </FilterPanel>
290
+ </form>
291
+ <h4 class="text-lg font-semibold mt-6 mb-2">Code:</h4>
292
+ <CodeBlock language="svelte" code={enhancedFormExampleCode} />
293
+ </div>
294
+ </Accordion>
295
+ `;
296
+
297
+ export const serverFormWithBasicEnhanceExampleCode = `// Basic progressive enhancement with use:enhance
298
+ // This form submits to the same '/serverFilter' action as Example 3.
299
+ // SvelteKit will automatically handle the form submission via fetch,
300
+ // update the 'form' prop, and invalidate data if the action is successful.
301
+
302
+ <script lang="ts">
303
+ import FilterPanel from './FilterPanel.svelte';
304
+ import type { Section, FilteredResult } from './types';
305
+ import Accordion from '../Accordion/Accordion.svelte';
306
+ import CodeBlock from '../CodeBlock/CodeBlock.svelte';
307
+ import { serverFormWithBasicEnhanceExampleCode } from './codeBlocks.js';
308
+ import { enhance } from '$app/forms'; // Import enhance
309
+
310
+ // 'form' prop will be populated by SvelteKit after action completes
311
+ export let form: {
312
+ data?: FilteredResult[];
313
+ error?: string;
314
+ message?: string;
315
+ } | null = null;
316
+
317
+ const sectionsData: Section[] = [
318
+ {
319
+ type: "checkboxes" as "checkboxes",
320
+ title: "Select Metrics (Server - Basic Enhance)",
321
+ name: "metrics",
322
+ options: [
323
+ { label: "Household waste recycling rates", value: "Household waste recycling rates" },
324
+ { label: "Percentage of household waste sent to landfill", value: "Percentage of household waste sent to landfill" },
325
+ { label: "Total fly-tipping incidents", value: "Total fly-tipping incidents" },
326
+ ],
327
+ },
328
+ {
329
+ type: "radio" as "radio",
330
+ title: "Select Year (Server - Basic Enhance)",
331
+ name: "year",
332
+ options: [
333
+ { label: "2019/20", value: "2019/20" },
334
+ { label: "2020/21", value: "2020/21" },
335
+ { label: "2021/22", value: "2021/22" },
336
+ ],
337
+ },
338
+ {
339
+ type: "select" as "select",
340
+ title: "Select Area Type (Server - Basic Enhance)",
341
+ name: "areaType",
342
+ options: [
343
+ { label: "District", value: "District" },
344
+ { label: "Unitary Authority", value: "Unitary Authority" },
345
+ { label: "County Council", value: "County Council" },
346
+ { label: "Region", value: "Region" },
347
+ ],
348
+ }
349
+ ];
350
+ </script>
351
+
352
+ <Accordion single>
353
+ <div class="accordion-item py-4" data-title="Example 5: Server Form with Basic Progressive Enhancement">
354
+ <p class="text-sm text-surface-500 mb-4">
355
+ This example is identical to Example 3 (Server Form Submission) but adds <code>use:enhance</code>
356
+ to the form tag. SvelteKit handles the form submission client-side by default,
357
+ updating the <code>form</code> prop automatically upon completion of the server action.
358
+ No custom client-side filtering or result handling is needed beyond what SvelteKit provides.
359
+ </p>
360
+ <form method="POST" action="?/serverFilter" use:enhance> {/* Added use:enhance here */}
361
+ <FilterPanel
362
+ formClasses="p-4 border rounded-lg"
363
+ fieldSetClasses="mb-4 p-2 border-t"
364
+ legendClasses="text-lg font-semibold"
365
+ showSubmitButton={true}
366
+ submitButtonText="Filter (Basic Enhance)"
367
+ {sectionsData} /* No target needed as form action is used */
368
+ resultsHeaderText="Server Form Results (via Basic Enhance)"
369
+ >
370
+ <svelte:fragment slot="results">
371
+ {#if form?.data}
372
+ <div class="mt-4 p-4 border rounded-lg bg-surface-100 dark:bg-surface-800">
373
+ <h3 class="text-xl font-semibold mb-2">Results</h3>
374
+ <p class="text-sm text-surface-600 dark:text-surface-300">
375
+ Displaying {form.data.length} results. Filtered by server, handled by <code>use:enhance</code>.
376
+ </p>
377
+ <div class="overflow-x-auto mt-2">
378
+ <table class="table table-compact w-full">
379
+ <thead>
380
+ <tr>
381
+ <th>Metric</th>
382
+ <th>Area</th>
383
+ <th>Years (X)</th>
384
+ <th>Values (Y)</th>
385
+ </tr>
386
+ </thead>
387
+ <tbody>
388
+ {#each form.data as result}
389
+ <tr>
390
+ <td>{result.metricName}</td>
391
+ <td>{result.areaName}</td>
392
+ <td>{result.data.map(d => d.x).join(', ')}</td>
393
+ <td>{result.data.map(d => d.y).join(', ')}</td>
394
+ </tr>
395
+ {/each}
396
+ </tbody>
397
+ </table>
398
+ </div>
399
+ </div>
400
+ {:else if form?.error}
401
+ <div class="mt-4 p-4 border rounded-lg bg-error-100 dark:bg-error-800 text-error-700 dark:text-error-200">
402
+ <h3 class="text-xl font-semibold mb-2">Error</h3>
403
+ <p>{form.error}</p>
404
+ </div>
405
+ {:else if form?.message}
406
+ <div class="mt-4 p-4 border rounded-lg bg-warning-100 dark:bg-warning-800 text-warning-700 dark:text-warning-200">
407
+ <h3 class="text-xl font-semibold mb-2">Info</h3>
408
+ <p>{form.message}</p>
409
+ </div>
410
+ {/if}
411
+ </svelte:fragment>
412
+ </FilterPanel>
413
+ </form>
414
+ <h4 class="text-lg font-semibold mt-6 mb-2">Code:</h4>
415
+ <CodeBlock language="svelte" code={serverFormWithBasicEnhanceExampleCode} />
416
+ </div>
417
+ </Accordion>
418
+ `;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ // Define component props with types and default values
5
+ let {
6
+ content = "It can take up to 8 weeks to register a lasting power of attorney if there are no mistakes in the application.",
7
+ } = $props<{
8
+ content?: string | Snippet;
9
+ }>();
10
+ </script>
11
+
12
+ <div class="govuk-inset-text">
13
+ {#if typeof content === "string"}
14
+ {content}
15
+ {:else if content}
16
+ {@render content()}
17
+ {/if}
18
+ </div>
19
+
20
+ <style>
21
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ content?: string | Snippet;
4
+ };
5
+ declare const InsetText: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type InsetText = ReturnType<typeof InsetText>;
7
+ export default InsetText;
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ // Define component props with types and default values
5
+ let {
6
+ assistiveText = "Warning",
7
+ text = "You can be fined up to £5,000 if you do not register.",
8
+ } = $props<{
9
+ assistiveText?: string;
10
+ text?: string | Snippet;
11
+ }>();
12
+ </script>
13
+
14
+ <div class="govuk-warning-text">
15
+ <span class="govuk-warning-text__icon" aria-hidden="true">!</span>
16
+ <strong class="govuk-warning-text__text">
17
+ <span class="govuk-visually-hidden">{assistiveText}</span>
18
+ {#if typeof text === "string"}
19
+ {text}
20
+ {:else if text}
21
+ {@render text()}
22
+ {/if}
23
+ </strong>
24
+ </div>
25
+
26
+ <style>
27
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ assistiveText?: string;
4
+ text?: string | Snippet;
5
+ };
6
+ declare const WarningText: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type WarningText = ReturnType<typeof WarningText>;
8
+ export default WarningText;
@@ -0,0 +1,51 @@
1
+ <script>
2
+ import { FolderArrowRightSolid } from "flowbite-svelte-icons";
3
+ import Ticks from "./Ticks.svelte";
4
+
5
+ let {
6
+ chartHeight,
7
+ chartWidth,
8
+ numberOfTicks,
9
+ ticksArray = $bindable(),
10
+ axisFunction,
11
+ values,
12
+ orientation,
13
+ prefix,
14
+ suffix,
15
+ floor,
16
+ ceiling,
17
+ yearsInput,
18
+ } = $props();
19
+ </script>
20
+
21
+ <g
22
+ data-role="{orientation.axis}-axis"
23
+ transform="translate({orientation.position != 'right'
24
+ ? 0
25
+ : chartWidth},{orientation.position === 'bottom' ? chartHeight : 0})"
26
+ >
27
+ <line
28
+ x1="0"
29
+ y1="0"
30
+ x2={orientation.axis === "x" ? chartWidth : 0}
31
+ y2={orientation.axis === "y" ? chartHeight : 0}
32
+ stroke="black"
33
+ stroke-width="2px"
34
+ ></line>
35
+ {#key numberOfTicks}
36
+ <Ticks
37
+ bind:ticksArray
38
+ {chartWidth}
39
+ {chartHeight}
40
+ {axisFunction}
41
+ {values}
42
+ {numberOfTicks}
43
+ {orientation}
44
+ {floor}
45
+ {ceiling}
46
+ {yearsInput}
47
+ {suffix}
48
+ {prefix}
49
+ ></Ticks>
50
+ {/key}
51
+ </g>
@@ -0,0 +1,33 @@
1
+ export default Axis;
2
+ type Axis = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Axis: import("svelte").Component<{
7
+ chartHeight: any;
8
+ chartWidth: any;
9
+ numberOfTicks: any;
10
+ ticksArray?: any;
11
+ axisFunction: any;
12
+ values: any;
13
+ orientation: any;
14
+ prefix: any;
15
+ suffix: any;
16
+ floor: any;
17
+ ceiling: any;
18
+ yearsInput: any;
19
+ }, {}, "ticksArray">;
20
+ type $$ComponentProps = {
21
+ chartHeight: any;
22
+ chartWidth: any;
23
+ numberOfTicks: any;
24
+ ticksArray?: any;
25
+ axisFunction: any;
26
+ values: any;
27
+ orientation: any;
28
+ prefix: any;
29
+ suffix: any;
30
+ floor: any;
31
+ ceiling: any;
32
+ yearsInput: any;
33
+ };
@@ -0,0 +1,113 @@
1
+ <script>
2
+ import Decimal from "decimal.js";
3
+
4
+ let {
5
+ ticksArray = $bindable(),
6
+ prefix,
7
+ suffix,
8
+ chartWidth,
9
+ chartHeight,
10
+ axisFunction,
11
+ values,
12
+ numberOfTicks,
13
+ floor,
14
+ ceiling,
15
+ orientation,
16
+ yearsInput,
17
+ } = $props();
18
+
19
+ $inspect(ticksArray);
20
+
21
+ function generateTicks(data, numTicks, floor, ceiling) {
22
+ let minValueFromData = Decimal.min(...data);
23
+
24
+ let minVal = floor
25
+ ? Decimal.max(floor, minValueFromData)
26
+ : minValueFromData;
27
+
28
+ let maxValueFromData = Decimal.max(...data);
29
+
30
+ let maxVal = ceiling
31
+ ? Decimal.min(ceiling, maxValueFromData)
32
+ : maxValueFromData;
33
+
34
+ let rangeVal = maxVal.minus(minVal);
35
+
36
+ let roughStep = rangeVal.div(numTicks - 1);
37
+ let normalizedSteps = [1, 2, 5, 10];
38
+
39
+ let stepPower = Decimal.pow(
40
+ 10,
41
+ -Math.floor(Math.log10(roughStep.toNumber())),
42
+ );
43
+ let normalizedStep = roughStep.mul(stepPower);
44
+ let optimalStep = new Decimal(
45
+ normalizedSteps.find((step) => step >= normalizedStep.toNumber()),
46
+ ).div(stepPower);
47
+
48
+ let scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
49
+ let scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
50
+
51
+ let ticks = [];
52
+ for (let i = scaleMin; i.lte(scaleMax); i = i.plus(optimalStep)) {
53
+ ticks.push(i.toNumber());
54
+ }
55
+ return ticks;
56
+ }
57
+
58
+ function tickCount(chartWidth, chartHeight) {
59
+ let tickNum = orientation.axis === "y" ? chartHeight / 50 : chartWidth / 50;
60
+ return tickNum;
61
+ }
62
+
63
+ function yearsFormat(ticks) {
64
+ return ticks.map((tick) => `FY ${tick % 100}-${(tick % 100) + 1}`);
65
+ }
66
+
67
+ numberOfTicks = tickCount(chartWidth, chartHeight);
68
+
69
+ ticksArray = generateTicks(values, numberOfTicks, floor, ceiling);
70
+ let yearTicks = yearsInput ? yearsFormat(ticksArray) : [];
71
+ </script>
72
+
73
+ {#if axisFunction && ticksArray && orientation.axis && orientation.position}
74
+ {#each ticksArray as tick, index}
75
+ <g
76
+ transform="translate({orientation.axis === 'x'
77
+ ? axisFunction(tick)
78
+ : 0},{orientation.axis === 'y' ? axisFunction(tick) : 0})"
79
+ >
80
+ <path
81
+ d={orientation.axis === "y"
82
+ ? orientation.position === "left"
83
+ ? "M0 0 l-8 0"
84
+ : "M0 0 l8 0"
85
+ : orientation.position === "top"
86
+ ? "M0 0 l0 -8"
87
+ : "M0 0 l0 8"}
88
+ stroke="black"
89
+ stroke-width="2px"
90
+ ></path>
91
+ <text
92
+ transform="translate({orientation.axis === 'x'
93
+ ? 0
94
+ : orientation.position === 'left'
95
+ ? -10
96
+ : 10}, {orientation.axis === 'y'
97
+ ? 5
98
+ : orientation.position === 'top'
99
+ ? -10
100
+ : 23})"
101
+ font-size="19"
102
+ text-anchor={orientation.axis === "x"
103
+ ? "middle"
104
+ : orientation.position === "left"
105
+ ? "end"
106
+ : "start"}
107
+ fill="black"
108
+ >
109
+ {yearsInput ? yearTicks[index] : prefix + tick + suffix}
110
+ </text>
111
+ </g>
112
+ {/each}
113
+ {/if}