acinguiux-preact-components 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/package.json +56 -0
  2. package/src/content/themes/theme-acinguiux-amg/theme-acinguiux-amg.css +23 -0
  3. package/src/content/themes/theme-acinguiux-cafe/theme-acinguiux-cafe.css +47 -0
  4. package/src/content/themes/theme-acinguiux-energy/theme-acinguiux-energy.css +45 -0
  5. package/src/content/themes/theme-acinguiux-livewire/theme-acinguiux-livewire.css +22 -0
  6. package/src/content/themes/theme-acinguiux-livewire-italy/theme-acinguiux-livewire-italy.css +22 -0
  7. package/src/content/themes/theme-acinguiux-recharge/theme-acinguiux-recharge.css +49 -0
  8. package/src/content/themes/theme-allon/theme-allon.css +25 -0
  9. package/src/content/themes/theme-atlas/theme-atlas.css +31 -0
  10. package/src/content/themes/theme-aurvana/resources/favicon/apple-touch-icon.png +0 -0
  11. package/src/content/themes/theme-aurvana/resources/favicon/favico.ico +0 -0
  12. package/src/content/themes/theme-aurvana/resources/favicon/favicon-96x96.png +0 -0
  13. package/src/content/themes/theme-aurvana/resources/favicon/favicon.ico +0 -0
  14. package/src/content/themes/theme-aurvana/resources/favicon/favicon.png +0 -0
  15. package/src/content/themes/theme-aurvana/resources/favicon/favicon.svg +13 -0
  16. package/src/content/themes/theme-aurvana/resources/favicon/google-touch-icon.png +0 -0
  17. package/src/content/themes/theme-aurvana/resources/favicon/manifest.json +14 -0
  18. package/src/content/themes/theme-aurvana/resources/favicon/site.webmanifest +21 -0
  19. package/src/content/themes/theme-aurvana/resources/favicon/web-app-manifest-192x192.png +0 -0
  20. package/src/content/themes/theme-aurvana/resources/favicon/web-app-manifest-512x512.png +0 -0
  21. package/src/content/themes/theme-aurvana/theme-aurvana.css +49 -0
  22. package/src/content/themes/theme-base/theme-base.css +49 -0
  23. package/src/content/themes/theme-base2/resources/favicon/android-chrome-192x192.png +0 -0
  24. package/src/content/themes/theme-base2/resources/favicon/android-chrome-512x512.png +0 -0
  25. package/src/content/themes/theme-base2/resources/favicon/apple-touch-icon.png +0 -0
  26. package/src/content/themes/theme-base2/resources/favicon/favico.ico +0 -0
  27. package/src/content/themes/theme-base2/resources/favicon/favicon-16x16.png +0 -0
  28. package/src/content/themes/theme-base2/resources/favicon/favicon-32x32.png +0 -0
  29. package/src/content/themes/theme-base2/resources/favicon/favicon-96x96.png +0 -0
  30. package/src/content/themes/theme-base2/resources/favicon/favicon.ico +0 -0
  31. package/src/content/themes/theme-base2/resources/favicon/favicon.png +0 -0
  32. package/src/content/themes/theme-base2/resources/favicon/favicon.svg +9 -0
  33. package/src/content/themes/theme-base2/resources/favicon/google-touch-icon.png +0 -0
  34. package/src/content/themes/theme-base2/resources/favicon/manifest.json +14 -0
  35. package/src/content/themes/theme-base2/resources/favicon/site.webmanifest +1 -0
  36. package/src/content/themes/theme-base2/resources/favicon/web-app-manifest-192x192.png +0 -0
  37. package/src/content/themes/theme-base2/resources/favicon/web-app-manifest-512x512.png +0 -0
  38. package/src/content/themes/theme-base2/resources/fonts/acinguiux-typeface-la-heavy-221208.woff2 +0 -0
  39. package/src/content/themes/theme-base2/theme-base2.css +47 -0
  40. package/src/content/themes/theme-eco-marathon/theme-eco-marathon.css +22 -0
  41. package/src/content/themes/theme-energy-transition-campus-amsterdam/theme-energy-transition-campus-amsterdam.css +26 -0
  42. package/src/content/themes/theme-evpass/theme-evpass.css +46 -0
  43. package/src/content/themes/theme-nam-2025/resources/favicon/apple-touch-icon.png +0 -0
  44. package/src/content/themes/theme-nam-2025/resources/favicon/favico.ico +0 -0
  45. package/src/content/themes/theme-nam-2025/resources/favicon/favicon-96x96.png +0 -0
  46. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.ico +0 -0
  47. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.png +0 -0
  48. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.svg +9 -0
  49. package/src/content/themes/theme-nam-2025/resources/favicon/google-touch-icon.png +0 -0
  50. package/src/content/themes/theme-nam-2025/resources/favicon/manifest.json +14 -0
  51. package/src/content/themes/theme-nam-2025/resources/favicon/site.webmanifest +21 -0
  52. package/src/content/themes/theme-nam-2025/resources/favicon/web-app-manifest-192x192.png +0 -0
  53. package/src/content/themes/theme-nam-2025/resources/favicon/web-app-manifest-512x512.png +0 -0
  54. package/src/content/themes/theme-nam-2025/theme-nam-2025.css +47 -0
  55. package/src/content/themes/theme-pennzoil/theme-pennzoil.css +36 -0
  56. package/src/content/themes/theme-quaker-state/theme-quaker-state.css +63 -0
  57. package/src/content/themes/theme-tafawoq/theme-tafawoq.css +26 -0
  58. package/src/content/themes/theme-vegetable/resources/favicon/apple-touch-icon.png +0 -0
  59. package/src/content/themes/theme-vegetable/resources/favicon/favico.ico +0 -0
  60. package/src/content/themes/theme-vegetable/resources/favicon/favicon-96x96.png +0 -0
  61. package/src/content/themes/theme-vegetable/resources/favicon/favicon.ico +0 -0
  62. package/src/content/themes/theme-vegetable/resources/favicon/favicon.png +0 -0
  63. package/src/content/themes/theme-vegetable/resources/favicon/favicon.svg +13 -0
  64. package/src/content/themes/theme-vegetable/resources/favicon/google-touch-icon.png +0 -0
  65. package/src/content/themes/theme-vegetable/resources/favicon/manifest.json +14 -0
  66. package/src/content/themes/theme-vegetable/resources/favicon/site.webmanifest +21 -0
  67. package/src/content/themes/theme-vegetable/resources/favicon/web-app-manifest-192x192.png +0 -0
  68. package/src/content/themes/theme-vegetable/resources/favicon/web-app-manifest-512x512.png +0 -0
  69. package/src/content/themes/theme-vegetable/theme-vegetable.css +49 -0
  70. package/src/content/themes/theme-zeolyst/resources/fonts/type-ar-medium.woff2 +0 -0
  71. package/src/content/themes/theme-zeolyst/theme-zeolyst.css +29 -0
  72. package/src/main/atoms/audio.js +16 -0
  73. package/src/main/atoms/box.js +5 -0
  74. package/src/main/atoms/button.js +40 -0
  75. package/src/main/atoms/card.js +22 -0
  76. package/src/main/atoms/form.js +30 -0
  77. package/src/main/atoms/heading.js +17 -0
  78. package/src/main/atoms/icon.js +24 -0
  79. package/src/main/atoms/img.js +131 -0
  80. package/src/main/atoms/input.js +55 -0
  81. package/src/main/atoms/link-text.js +21 -0
  82. package/src/main/atoms/link.js +60 -0
  83. package/src/main/atoms/list.js +12 -0
  84. package/src/main/atoms/logo.js +9 -0
  85. package/src/main/atoms/menu.js +10 -0
  86. package/src/main/atoms/message.js +5 -0
  87. package/src/main/atoms/nav-link.js +49 -0
  88. package/src/main/atoms/popup.js +47 -0
  89. package/src/main/atoms/rich-text.js +128 -0
  90. package/src/main/atoms/scroller.js +224 -0
  91. package/src/main/atoms/svg.js +65 -0
  92. package/src/main/atoms/table.js +32 -0
  93. package/src/main/atoms/textarea.js +10 -0
  94. package/src/main/atoms/time.js +12 -0
  95. package/src/main/atoms/video.js +100 -0
  96. package/src/main/export-main.js +12 -0
  97. package/src/main/export-matter.js +86 -0
  98. package/src/main/export-preact-hooks.js +1 -0
  99. package/src/main/export-preact.js +1 -0
  100. package/src/main/index.js +13 -0
  101. package/src/main/molecules/asset.js +23 -0
  102. package/src/main/molecules/glossary.js +44 -0
  103. package/src/main/molecules/links.js +23 -0
  104. package/src/main/molecules/promo-text.js +27 -0
  105. package/src/main/molecules/tags.js +15 -0
  106. package/src/main/molecules/tree.js +51 -0
  107. package/src/main/organisms/accordion-item.js +106 -0
  108. package/src/main/organisms/author.js +29 -0
  109. package/src/main/organisms/breadcrumb.js +69 -0
  110. package/src/main/organisms/call-to-action.js +24 -0
  111. package/src/main/organisms/carousel.js +178 -0
  112. package/src/main/organisms/cart-item.js +156 -0
  113. package/src/main/organisms/cart.js +162 -0
  114. package/src/main/organisms/contact-form.js +141 -0
  115. package/src/main/organisms/container/ab-test.js +47 -0
  116. package/src/main/organisms/container/default.js +6 -0
  117. package/src/main/organisms/container/filtered-section.js +293 -0
  118. package/src/main/organisms/container/footer.js +12 -0
  119. package/src/main/organisms/container/grid.js +44 -0
  120. package/src/main/organisms/container/header.js +13 -0
  121. package/src/main/organisms/container/list.js +7 -0
  122. package/src/main/organisms/container/main.js +6 -0
  123. package/src/main/organisms/container/raw.js +7 -0
  124. package/src/main/organisms/container/section.js +28 -0
  125. package/src/main/organisms/container.js +29 -0
  126. package/src/main/organisms/content-owner.js +15 -0
  127. package/src/main/organisms/date-entry.js +56 -0
  128. package/src/main/organisms/external-search.js +73 -0
  129. package/src/main/organisms/filtered-item.js +163 -0
  130. package/src/main/organisms/footer-item.js +17 -0
  131. package/src/main/organisms/image-gallery.js +164 -0
  132. package/src/main/organisms/last-modified.js +20 -0
  133. package/src/main/organisms/legal-footer.js +16 -0
  134. package/src/main/organisms/list-item.js +48 -0
  135. package/src/main/organisms/metadata.js +11 -0
  136. package/src/main/organisms/navigation.js +232 -0
  137. package/src/main/organisms/notification.js +87 -0
  138. package/src/main/organisms/order-tracker.js +203 -0
  139. package/src/main/organisms/page-header-banner.js +26 -0
  140. package/src/main/organisms/page-header.js +33 -0
  141. package/src/main/organisms/page-tags.js +14 -0
  142. package/src/main/organisms/page.js +260 -0
  143. package/src/main/organisms/press-release.js +24 -0
  144. package/src/main/organisms/product-admin.js +204 -0
  145. package/src/main/organisms/promo-banner.js +28 -0
  146. package/src/main/organisms/promo-bottom.js +23 -0
  147. package/src/main/organisms/promo-button.js +8 -0
  148. package/src/main/organisms/promo-card-cover.js +35 -0
  149. package/src/main/organisms/promo-card.js +33 -0
  150. package/src/main/organisms/promo-full.js +20 -0
  151. package/src/main/organisms/promo-image.js +22 -0
  152. package/src/main/organisms/promo-lure.js +22 -0
  153. package/src/main/organisms/promo-product-card.js +187 -0
  154. package/src/main/organisms/promo-product-full.js +293 -0
  155. package/src/main/organisms/promo-simple.js +23 -0
  156. package/src/main/organisms/quote.js +21 -0
  157. package/src/main/organisms/search-form.js +42 -0
  158. package/src/main/organisms/search-nav.js +66 -0
  159. package/src/main/organisms/search-result.js +53 -0
  160. package/src/main/organisms/slider.js +26 -0
  161. package/src/main/organisms/standalone-asset.js +22 -0
  162. package/src/main/organisms/tabs.js +277 -0
  163. package/src/main/organisms/topbar.js +83 -0
  164. package/src/main/organisms/web-component.js +53 -0
  165. package/src/main/routing/annotation.js +9 -0
  166. package/src/main/routing/component.js +138 -0
  167. package/src/main/routing/empty.js +5 -0
  168. package/src/main/routing/error-handler.js +64 -0
  169. package/src/main/routing/placeholder-image.svg +5 -0
  170. package/src/main/routing/router.js +219 -0
  171. package/src/main/shared/analytics.js +677 -0
  172. package/src/main/shared/bubble-event.js +11 -0
  173. package/src/main/shared/custom-element.js +21 -0
  174. package/src/main/shared/deep-selector.js +28 -0
  175. package/src/main/shared/disable-transparency.js +10 -0
  176. package/src/main/shared/format-time.js +8 -0
  177. package/src/main/shared/get-id.js +5 -0
  178. package/src/main/shared/get-meta.js +3 -0
  179. package/src/main/shared/get-size-class.js +3 -0
  180. package/src/main/shared/get-size.js +11 -0
  181. package/src/main/shared/h.js +88 -0
  182. package/src/main/shared/hash-jump.js +33 -0
  183. package/src/main/shared/icons/arrow-back.svg +1 -0
  184. package/src/main/shared/icons/arrow-down.svg +1 -0
  185. package/src/main/shared/icons/arrow-next.svg +1 -0
  186. package/src/main/shared/icons/arrow-tail-right.svg +1 -0
  187. package/src/main/shared/icons/arrow-tail-up.svg +1 -0
  188. package/src/main/shared/icons/arrow-up.svg +1 -0
  189. package/src/main/shared/icons/asset-download.svg +1 -0
  190. package/src/main/shared/icons/logo.svg +5 -0
  191. package/src/main/shared/icons/low-carbon-placeholder.svg +9 -0
  192. package/src/main/shared/icons/media-pause.svg +1 -0
  193. package/src/main/shared/icons/media-play.svg +1 -0
  194. package/src/main/shared/icons/navigation-burger.svg +1 -0
  195. package/src/main/shared/icons/navigation-close.svg +1 -0
  196. package/src/main/shared/icons/navigation-link.svg +1 -0
  197. package/src/main/shared/icons/navigation-refresh.svg +1 -0
  198. package/src/main/shared/icons/navigation-search.svg +1 -0
  199. package/src/main/shared/icons/navigation-share.svg +1 -0
  200. package/src/main/shared/icons/toggle-newwindow.svg +1 -0
  201. package/src/main/shared/icons.js +18 -0
  202. package/src/main/shared/id-from-string.js +5 -0
  203. package/src/main/shared/mark-selection.js +19 -0
  204. package/src/main/shared/register.js +26 -0
  205. package/src/main/shared/renderer.js +43 -0
  206. package/src/main/shared/simple-consent-api.js +70 -0
  207. package/src/main/shared/split-links.js +11 -0
  208. package/src/main/shared/t.js +60 -0
  209. package/src/main/shared/twind.js +837 -0
  210. package/src/main/shared/update-head.js +34 -0
  211. package/src/main/shared/update-scrollbar-width.js +30 -0
  212. package/src/main/shared/use-link.js +151 -0
  213. package/src/main/shared/use-persistent-state.js +42 -0
  214. package/src/main/shared/wait-for-dom-ready.js +6 -0
  215. package/src/main/shared/wcm-mode.js +4 -0
  216. package/src/wcs/components/acinguiux-preact-doc/acinguiux-preact-doc.js +207 -0
  217. package/src/wcs/components/admin-dashboard/admin-dashboard.js +487 -0
  218. package/src/wcs/components/admin-login/admin-login.js +91 -0
  219. package/src/wcs/components/bazaar-voice/bazaar-voice.js +56 -0
  220. package/src/wcs/components/chatbot-koreai/chatbot-koreai.js +176 -0
  221. package/src/wcs/components/chatbot-koreai/koreai-transport.js +217 -0
  222. package/src/wcs/components/chatbot-ms/chatbot-ms.js +210 -0
  223. package/src/wcs/components/chatbot-test/chatbot-test.js +44 -0
  224. package/src/wcs/components/comparison-chart/comparison-chart.js +111 -0
  225. package/src/wcs/components/consent-banner/consent-banner.js +248 -0
  226. package/src/wcs/components/consent-banner/icons/ccpa.svg +6 -0
  227. package/src/wcs/components/consent-banner/icons/info.svg +1 -0
  228. package/src/wcs/components/consent-banner/provider-onetrust.js +131 -0
  229. package/src/wcs/components/decision-tree/arrow-back.svg +3 -0
  230. package/src/wcs/components/decision-tree/badges.js +37 -0
  231. package/src/wcs/components/decision-tree/decision-tree.js +162 -0
  232. package/src/wcs/components/dynamic-contact-details/dynamic-contact-details.js +111 -0
  233. package/src/wcs/components/example-accordion/example-accordion.js +10 -0
  234. package/src/wcs/components/example-asset/example-asset.js +12 -0
  235. package/src/wcs/components/example-form/example-form.js +59 -0
  236. package/src/wcs/components/example-nested/example-nested.js +10 -0
  237. package/src/wcs/components/example-routing/example-routing.js +51 -0
  238. package/src/wcs/components/example-rtl/example-rtl.js +28 -0
  239. package/src/wcs/components/example-tabs/example-tabs.js +12 -0
  240. package/src/wcs/components/example-web-component/example-web-component.js +34 -0
  241. package/src/wcs/components/floating-button/floating-button.js +17 -0
  242. package/src/wcs/components/formstack-form/fields/address.js +38 -0
  243. package/src/wcs/components/formstack-form/fields/checkbox.js +42 -0
  244. package/src/wcs/components/formstack-form/fields/date.js +22 -0
  245. package/src/wcs/components/formstack-form/fields/description.js +8 -0
  246. package/src/wcs/components/formstack-form/fields/input.js +8 -0
  247. package/src/wcs/components/formstack-form/fields/name.js +39 -0
  248. package/src/wcs/components/formstack-form/fields/radio.js +24 -0
  249. package/src/wcs/components/formstack-form/fields/rating.js +53 -0
  250. package/src/wcs/components/formstack-form/fields/section.js +8 -0
  251. package/src/wcs/components/formstack-form/fields/select.js +10 -0
  252. package/src/wcs/components/formstack-form/fields/textarea.js +8 -0
  253. package/src/wcs/components/formstack-form/fields/wrapper.js +11 -0
  254. package/src/wcs/components/formstack-form/formstack-form.js +280 -0
  255. package/src/wcs/components/fuel-prices/fuel-prices.js +45 -0
  256. package/src/wcs/components/furniture-overview/furniture-overview.js +115 -0
  257. package/src/wcs/components/gauge-value/gauge-value.js +65 -0
  258. package/src/wcs/components/help-centre/api.js +150 -0
  259. package/src/wcs/components/help-centre/help-centre.js +400 -0
  260. package/src/wcs/components/help-centre/icon-search.svg +1 -0
  261. package/src/wcs/components/image-gen/admin-panel.js +248 -0
  262. package/src/wcs/components/image-gen/image-gen.js +385 -0
  263. package/src/wcs/components/image-gen/labels.js +37 -0
  264. package/src/wcs/components/image-gen/use-api.js +392 -0
  265. package/src/wcs/components/inspired-gallery/inspired-gallery.js +118 -0
  266. package/src/wcs/components/launch-container/launch-container.js +95 -0
  267. package/src/wcs/components/launch-container/ledger.js +140 -0
  268. package/src/wcs/components/lng-map/lng-map.js +44 -0
  269. package/src/wcs/components/mouseflow-analytics/mouseflow-analytics.js +39 -0
  270. package/src/wcs/components/msds-search/msds-search.js +127 -0
  271. package/src/wcs/components/msds-search/navigation-search.svg +3 -0
  272. package/src/wcs/components/product-catalogue/icon-back.svg +3 -0
  273. package/src/wcs/components/product-catalogue/icon-cart.svg +3 -0
  274. package/src/wcs/components/product-catalogue/icon-close.svg +3 -0
  275. package/src/wcs/components/product-catalogue/product-catalogue.js +215 -0
  276. package/src/wcs/components/product-links/icon-cart.svg +3 -0
  277. package/src/wcs/components/product-links/product-links.js +43 -0
  278. package/src/wcs/components/rio-iframe/rio-iframe.js +137 -0
  279. package/src/wcs/components/salsify-products/filter-tools.js +60 -0
  280. package/src/wcs/components/salsify-products/icon-cart.svg +3 -0
  281. package/src/wcs/components/salsify-products/process-products.js +53 -0
  282. package/src/wcs/components/salsify-products/route-tools.js +54 -0
  283. package/src/wcs/components/salsify-products/salsify-products.js +281 -0
  284. package/src/wcs/components/shout-out/shout-out.js +51 -0
  285. package/src/wcs/components/simple-chart/simple-chart.js +53 -0
  286. package/src/wcs/components/single-stat/single-stat.js +85 -0
  287. package/src/wcs/components/site-feedback/site-feedback.js +56 -0
  288. package/src/wcs/components/skds-search/navigation-search.svg +3 -0
  289. package/src/wcs/components/skds-search/skds-search.js +103 -0
  290. package/src/wcs/components/smart-banner/smart-banner.js +104 -0
  291. package/src/wcs/components/standalone-table/arrow-up-down.svg +3 -0
  292. package/src/wcs/components/standalone-table/arrow-up.svg +3 -0
  293. package/src/wcs/components/standalone-table/standalone-table.js +440 -0
  294. package/src/wcs/components/station-locator/station-locator.js +49 -0
  295. package/src/wcs/components/store-badges/badges.js +60 -0
  296. package/src/wcs/components/store-badges/store-badges.js +93 -0
  297. package/src/wcs/components/topbar-button/person.svg +1 -0
  298. package/src/wcs/components/topbar-button/topbar-button.js +22 -0
  299. package/src/wcs/components/universal-gallery/universal-gallery.js +308 -0
  300. package/src/wcs/components/zendesk-chat/zendesk-chat.js +133 -0
  301. package/src/wcs/shared/chat-bot/README.md +61 -0
  302. package/src/wcs/shared/chat-bot/chat-bot.js +216 -0
  303. package/src/wcs/shared/chat-bot/resources/arrow-next.svg +1 -0
  304. package/src/wcs/shared/chat-bot/resources/navigation-close.svg +1 -0
  305. package/src/wcs/shared/chat-bot/resources/person.svg +1 -0
  306. package/src/wcs/shared/chat-bot/resources/upload.svg +1 -0
  307. package/src/wcs/shared/filtered-data/README.md +52 -0
  308. package/src/wcs/shared/filtered-data/fetch-data.js +33 -0
  309. package/src/wcs/shared/filtered-data/filtered-data.js +337 -0
  310. package/src/wcs/shared/promo-with-popup/icon-close.svg +3 -0
  311. package/src/wcs/shared/promo-with-popup/icon-next.svg +3 -0
  312. package/src/wcs/shared/promo-with-popup/icon-prev.svg +3 -0
  313. package/src/wcs/shared/promo-with-popup/promo-with-popup.js +93 -0
@@ -0,0 +1,24 @@
1
+ const { h, matter } = globalThis.ami
2
+
3
+ export function Radio ({ field }) {
4
+ const label = field.required === '1' ? `${field.label} *` : field.label
5
+ let id = 1
6
+
7
+ return h('fieldset', { className: 'block space-y-1' },
8
+ h('legend', { className: 'block font-bold' }, label),
9
+ field.options.map(option =>
10
+ h('label', { className: 'space-x-2 flex items-center cursor-pointer' },
11
+ h(matter.Input, {
12
+ ...field.attrs,
13
+ id: `${field.attrs.id}_${id++}`,
14
+ type: 'radio',
15
+ value: option.value,
16
+ checked: field.attrs.value === option.value ? true : null,
17
+ 'data-label': field.label
18
+ }),
19
+ h('span', option.label)
20
+ )
21
+ ),
22
+ field.description && h('span', { className: 'text-sm' }, field.description)
23
+ )
24
+ }
@@ -0,0 +1,53 @@
1
+ const { h, preactHooks } = globalThis.ami
2
+ const { useState } = preactHooks
3
+
4
+ export function Rating ({ field }) {
5
+ const [clicked, setClicked] = useState(false)
6
+
7
+ const label = field.required === '1' ? `${field.label} *` : field.label
8
+ return h('fieldset', { role: 'radiogroup', className: 'block' },
9
+ h('legend', { id: `legend-${field.id}`, className: 'font-bold' }, label),
10
+ h('div', {
11
+ className: 'flex justify-start',
12
+ children: Array.from({ length: field.count }, (_, ii) => {
13
+ const index = ii + 1
14
+ const id = `field_${field.id}_${index}`
15
+ return [
16
+ h('label', { className: 'sr-only', for: id }, index),
17
+ h('input', {
18
+ ...field.attrs,
19
+ value: index,
20
+ id,
21
+ onClick: () => setClicked(true),
22
+ type: 'radio',
23
+ className: 'relative inline-block appearance-none w-8 h-8',
24
+ 'data-label': field.label,
25
+ _style: styleId => `
26
+ ${styleId}::before {
27
+ cursor:pointer;
28
+ position: absolute;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ color: hsl(var(--color-txa));
33
+ opacity: ${clicked ? '1' : '0.2'};
34
+ left: 0;
35
+ right: 0;
36
+ top: 0;
37
+ bottom: 0;
38
+ content: "★";
39
+ font-size: 28px;
40
+ transition: opacity var(--anim-time);
41
+ text-shadow: 0 0 2px hsl(var(--color-txa));
42
+ }
43
+ input:checked ~ input${styleId}::before {
44
+ opacity: 0.2;
45
+ }
46
+ `
47
+ })
48
+ ]
49
+ })
50
+ }
51
+ )
52
+ )
53
+ }
@@ -0,0 +1,8 @@
1
+ import { Wrapper } from './wrapper'
2
+ const { h, matter } = globalThis.ami
3
+
4
+ export function Section ({ field }) {
5
+ return h(Wrapper, { field },
6
+ h(matter.Heading, { _level: 4 }, field.section_heading)
7
+ )
8
+ }
@@ -0,0 +1,10 @@
1
+ import { Wrapper } from './wrapper'
2
+ const { h, matter } = globalThis.ami
3
+
4
+ export function Select ({ field }) {
5
+ return h(Wrapper, { field },
6
+ h(matter.Input, { ...field.attrs, className: 'w-full', 'data-label': field.label },
7
+ field.options.map(option => h('option', { value: option.value }, option.label))
8
+ )
9
+ )
10
+ }
@@ -0,0 +1,8 @@
1
+ import { Wrapper } from './wrapper'
2
+ const { h, matter } = globalThis.ami
3
+
4
+ export function Textarea ({ field }) {
5
+ return h(Wrapper, { field },
6
+ h(matter.Input, { ...field.attrs, 'data-label': field.label })
7
+ )
8
+ }
@@ -0,0 +1,11 @@
1
+ const { h } = globalThis.ami
2
+
3
+ export function Wrapper ({ field, children }) {
4
+ const labelText = field.hide_label === '1' ? '' : field.label
5
+ const label = field.required === '1' ? `${labelText} *` : labelText
6
+ return h('div',
7
+ h('label', { for: field.attrs.id, className: 'block font-bold' }, label),
8
+ children,
9
+ field.description && h('span', { className: 'text-sm' }, field.description)
10
+ )
11
+ }
@@ -0,0 +1,280 @@
1
+ /* global process, DOMParser, alert, FileReader */
2
+ import { Address } from './fields/address.js'
3
+ import { Checkbox } from './fields/checkbox.js'
4
+ import { DateTime } from './fields/date.js'
5
+ import { Description } from './fields/description.js'
6
+ import { Input } from './fields/input.js'
7
+ import { Name } from './fields/name.js'
8
+ import { Radio } from './fields/radio.js'
9
+ import { Rating } from './fields/rating.js'
10
+ import { Section } from './fields/section.js'
11
+ import { Select } from './fields/select.js'
12
+ import { Textarea } from './fields/textarea.js'
13
+
14
+ const { h, register, preactHooks, matter } = globalThis.ami
15
+ const { useState, useEffect } = preactHooks
16
+ const { Box, Button, Icon, RichText, Form } = matter
17
+
18
+ const ICON_SUCCESS = `
19
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
20
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM6.35355 13.3535L9.64645 16.6464C9.84171 16.8417 10.1583 16.8417 10.3536 16.6464L17.6477 9.35229C17.8425 9.15752 17.843 8.8419 17.6489 8.64644L16.9435 7.93604C16.7485 7.7396 16.4309 7.73904 16.2352 7.93479L10 14.17L7.76355 11.9422C7.56818 11.7476 7.25212 11.7479 7.05713 11.9429L6.35355 12.6464C6.15829 12.8417 6.15829 13.1583 6.35355 13.3535Z" fill="#343434"/>
21
+ </svg>
22
+ `
23
+
24
+ const CAPTCHA_CONTAINER_ID = 'captcha-container'
25
+ const SUCCESS_CODE = 200
26
+ const DEFAULTS = {
27
+ FUNCTION_URL: process.env.FORMSTACK_FUNCTION_URL,
28
+ CAPTCHA_SITE_KEY: process.env.CAPTCHA_SITE_KEY,
29
+ SUCCESS_TEXT: 'Thank you. The form was submitted successfully.',
30
+ ERROR_TEXT: 'There was an error submitting the form. Please try again.',
31
+ CAPTCHA_POLICY_TEXT: `
32
+ This site is protected by reCAPTCHA and the Google
33
+ <a href="https://policies.google.com/privacy">Privacy Policy</a> and
34
+ <a href="https://policies.google.com/terms">Terms of Service</a> apply.
35
+ `
36
+ }
37
+
38
+ // -----------------------------------------------------------------------------
39
+ // Helper functions
40
+ // -----------------------------------------------------------------------------
41
+
42
+ function getStep (decimals) {
43
+ if (decimals === 0) {
44
+ return 'any'
45
+ }
46
+
47
+ if (!decimals) {
48
+ return undefined
49
+ }
50
+
51
+ return `.${Array.from({ length: decimals - 1 }, () => 0).join('')}1`
52
+ }
53
+
54
+ function getFieldType (field) {
55
+ return { phone: 'tel', name: 'text', address: 'text' }[field.type] ?? field.type
56
+ }
57
+
58
+ async function blobToBase64 (blob) {
59
+ return new Promise((resolve, reject) => {
60
+ const reader = new FileReader()
61
+ reader.onload = () => resolve(reader.result.split(',')[1])
62
+ reader.onerror = reject
63
+ reader.readAsDataURL(blob)
64
+ })
65
+ }
66
+
67
+ async function getQueryString (formElement, captchaToken) {
68
+ const formData = new FormData(formElement)
69
+
70
+ // Convert blobs to base64.
71
+ for (const [fieldName, fieldValue] of formData) {
72
+ if (fieldValue instanceof Blob) {
73
+ const contents = await blobToBase64(fieldValue)
74
+ formData.set(fieldName, `${fieldValue.name};${contents}`)
75
+ }
76
+ }
77
+
78
+ // Add recaptcha token to the form data.
79
+ formData.append('g-recaptcha-response', captchaToken)
80
+
81
+ // Avoid encoding of square brackets in field names to match the Formstack API.
82
+ return `${new URLSearchParams(formData)}`
83
+ .split('&')
84
+ .map(par => {
85
+ const [name, value] = par.split('=')
86
+ return `${name.replaceAll('%5B', '[').replaceAll('%5D', ']')}=${value}`
87
+ })
88
+ .join('&')
89
+ }
90
+
91
+ function updateFormData (form) {
92
+ const dp = new DOMParser()
93
+ const doc = dp.parseFromString(form.html, 'text/html')
94
+ const formElement = doc.querySelector('form')
95
+
96
+ for (const field of form.fields) {
97
+ const fieldName = `field_${field.id}`
98
+ field.attrs = {
99
+ id: fieldName,
100
+ name: fieldName,
101
+ type: getFieldType(field),
102
+ size: field.size,
103
+ minlength: field.minlength || null,
104
+ maxlength: field.maxlength || null,
105
+ required: (field.required === '1' && field.hidden !== '1') || null,
106
+ disabled: field.readonly === '1' || null,
107
+ value: formElement.value ?? field.default ?? null,
108
+ placeholder: field.placeholder,
109
+ min: field.min_value,
110
+ max: field.max_value,
111
+ step: getStep(Number(field.decimals))
112
+ }
113
+
114
+ // Get default submit value.
115
+ form.submit_button_title ||= formElement.querySelector('input[type="submit"]').value
116
+
117
+ field.subfields = field?.visible_subfields?.reduce((obj, subfieldName) => {
118
+ // Extract subfield label.
119
+ obj[subfieldName] = {
120
+ label: formElement.querySelector(`label.fsSupporting[for="field${field.id}-${subfieldName}"]`).textContent
121
+ }
122
+
123
+ // Extract subfield options.
124
+ const options = formElement.querySelectorAll(`#field${field.id}-${subfieldName} option`)
125
+ if (options.length > 0) {
126
+ obj[subfieldName].options = [...options].map(option => ({
127
+ name: option.textContent,
128
+ value: option.value
129
+ }))
130
+ }
131
+
132
+ return obj
133
+ }, {})
134
+ }
135
+ }
136
+
137
+ function initCaptcha (sitekey) {
138
+ // Do not run if captcha has already been initialised or is disabled.
139
+ if (!sitekey || document.getElementById(CAPTCHA_CONTAINER_ID)) {
140
+ return
141
+ }
142
+
143
+ // Create a hidden global container for the captcha script and widget.
144
+ // Recaptcha does not work in Shadow DOM.
145
+ const captchaContainer = el('div', { id: CAPTCHA_CONTAINER_ID, style: 'display: none' }, [
146
+ el('script', { src: 'https://www.google.com/recaptcha/api.js?render=explicit&onload=captchaCallback' }),
147
+ el('div')
148
+ ])
149
+
150
+ // Callback to initialise the captcha widget once the API script is loaded.
151
+ globalThis.captchaCallback = () =>
152
+ globalThis.grecaptcha.render(captchaContainer.lastChild, { sitekey, badge: 'inline', size: 'invisible' })
153
+
154
+ document.body.appendChild(captchaContainer)
155
+ }
156
+
157
+ function el (tag, attrs, children) {
158
+ const element = document.createElement(tag)
159
+ Object.assign(element, attrs)
160
+ children && element.append(...children)
161
+ return element
162
+ }
163
+
164
+ // -----------------------------------------------------------------------------
165
+ // Main components
166
+ // -----------------------------------------------------------------------------
167
+
168
+ export function FormstackForm (props) {
169
+ const {
170
+ 'form-id': formId,
171
+ 'success-text': successText = DEFAULTS.SUCCESS_TEXT,
172
+ 'error-text': errorText = DEFAULTS.ERROR_TEXT,
173
+ 'function-url': functionUrl = DEFAULTS.FUNCTION_URL,
174
+ 'captcha-site-key': captchaSiteKey = DEFAULTS.CAPTCHA_SITE_KEY,
175
+ 'captcha-policy-text': captchaPolicyText = DEFAULTS.CAPTCHA_POLICY_TEXT,
176
+ } = props
177
+
178
+ if (!formId) {
179
+ console.error('Missing form ID.')
180
+ return null
181
+ }
182
+
183
+ const getFormUrl = `${functionUrl}/api/get-form/${formId}`
184
+ const submitFormUrl = `${functionUrl}/api/submit-form/${formId}`
185
+
186
+ const [form, setForm] = useState(null)
187
+ const [success, setSuccess] = useState(false)
188
+
189
+ useEffect(() => {
190
+ initCaptcha(captchaSiteKey)
191
+ fetch(getFormUrl).then(res => res.json()).then(formData => {
192
+ updateFormData(formData)
193
+ setForm(formData)
194
+ })
195
+ }, [formId])
196
+
197
+ if (!form) {
198
+ return null
199
+ }
200
+
201
+ if (success) {
202
+ return h('div', { className: 'bg-bga text-txa rounded-2xl overflow-hidden', ariaLive: 'polite' },
203
+ h(Box,
204
+ h('div', { className: 'flex items-center justify-center flex-col space-y-2' },
205
+ h(Icon, ICON_SUCCESS),
206
+ h('span', { className: 'text-lg' }, successText)
207
+ )
208
+ )
209
+ )
210
+ }
211
+
212
+ async function submitForm (formElement, captchaToken) {
213
+ const res = await fetch(submitFormUrl, {
214
+ method: 'POST',
215
+ body: await getQueryString(formElement, captchaToken)
216
+ })
217
+
218
+ if (res.status === SUCCESS_CODE) {
219
+ setSuccess(true)
220
+ } else {
221
+ // Show alert in case of failure. This prevents the form from being wiped out.
222
+ alert(errorText)
223
+ }
224
+ }
225
+
226
+ return h(Form, {
227
+ name: form.name,
228
+ async onSubmit (event) {
229
+ const formElement = event.target
230
+ event.preventDefault()
231
+ // Prevent submission if recaptcha is not loaded.
232
+ if (!globalThis.grecaptcha) {
233
+ alert(errorText)
234
+ return
235
+ }
236
+
237
+ // Get the recaptcha token and submit the form.
238
+ globalThis.grecaptcha.ready(async () => {
239
+ const token = await globalThis.grecaptcha.execute()
240
+ await submitForm(formElement, token)
241
+ })
242
+ },
243
+ children: h(Box, { className: 'text-txa bg-bga rounded-2xl animate-fade' },
244
+ form.fields.map(field => h(Field, { field })),
245
+ h(Button, {
246
+ type: 'submit',
247
+ _model: { name: form.submit_button_title }
248
+ }),
249
+ h('div', { className: 'text-sm' },
250
+ h(RichText, captchaPolicyText)
251
+ )
252
+ )
253
+ })
254
+ }
255
+
256
+ function Field ({ field }) {
257
+ const fieldMap = [
258
+ { types: ['section'], field: Section },
259
+ { types: ['text', 'email', 'phone', 'number', 'file'], field: Input },
260
+ { types: ['textarea'], field: Textarea },
261
+ { types: ['datetime'], field: DateTime },
262
+ { types: ['select'], field: Select },
263
+ { types: ['radio'], field: Radio },
264
+ { types: ['checkbox'], field: Checkbox },
265
+ { types: ['name'], field: Name },
266
+ { types: ['rating'], field: Rating },
267
+ { types: ['address'], field: Address },
268
+ { types: ['richtext'], field: Description }
269
+ ]
270
+
271
+ const ResolvedField = fieldMap.find(entry => entry.types.includes(field.type))?.field
272
+ if (!ResolvedField) {
273
+ return null
274
+ }
275
+
276
+ const resolvedFieldComponent = h(ResolvedField, { field })
277
+ return field.hidden === '1' ? h('div', { className: 'hidden' }, resolvedFieldComponent) : resolvedFieldComponent
278
+ }
279
+
280
+ register(FormstackForm, 'formstack-form')
@@ -0,0 +1,45 @@
1
+ const { h, register, matter, preactHooks } = globalThis.ami
2
+ const { Box, Heading, Table } = matter
3
+ const { useState, useEffect } = preactHooks
4
+
5
+ // Define Preact component.
6
+ function FuelPrices ({ src }) {
7
+ const [data, setData] = useState()
8
+
9
+ if (!src) {
10
+ console.error('Missing data source.')
11
+ return null
12
+ }
13
+
14
+ useEffect(() => {
15
+ (async () => {
16
+ const res = await fetch(src)
17
+ if (!res.ok) {
18
+ console.error(`Error fetching ${src}. Status: ${res.status}`)
19
+ return
20
+ }
21
+ const obj = await res.json()
22
+ setData({ ...obj, trail: [] })
23
+ })().catch(console.error)
24
+ }, [src])
25
+
26
+ if (!data) {
27
+ return null
28
+ }
29
+
30
+ const prices = data?.listPrices?.[0] ?? {}
31
+ const products = prices.products ?? []
32
+
33
+ return h(Box, { className: 'rounded-2xl bg-bga text-txa h-full' },
34
+ h(Heading, { _level: 3 }, data.title),
35
+ h('p', prices.date),
36
+ products.length > 0 && h(Table, products.map(p =>
37
+ h('tr',
38
+ h('th', { className: 'w-1/2' }, p.type),
39
+ h('td', { className: 'text-right' }, `${Number(p.price).toFixed(2)}\xa0${p.currencyCode}`)
40
+ )
41
+ ))
42
+ )
43
+ }
44
+
45
+ register(FuelPrices, 'fuel-prices')
@@ -0,0 +1,115 @@
1
+ import { fetchCsv } from '#acinguiux-preact/wcs/shared/filtered-data/fetch-data.js'
2
+ import FilteredData from '#acinguiux-preact/wcs/shared/filtered-data/filtered-data.js'
3
+ import PromoWithPopup from '#acinguiux-preact/wcs/shared/promo-with-popup/promo-with-popup.js'
4
+
5
+ const { h, register } = globalThis.ami
6
+
7
+ const LABELS = {
8
+ SEARCH_TEXT: 'Search',
9
+ ORDER_BY: 'Sort by',
10
+ LOAD_MORE: 'Load more',
11
+ RESET_FILTERS: 'Reset filters',
12
+ ASCENDING: 'ascending',
13
+ DESCENDING: 'descending',
14
+ BACK_TO_OVERVIEW: 'Back to overview',
15
+ PREVIOUS_ITEM: 'Go to the previous item',
16
+ NEXT_ITEM: 'Go to the next item'
17
+ }
18
+
19
+ const FIELDS = {
20
+ REGION: 'Region',
21
+ PRODUCT_TYPE: 'Product Type',
22
+ MANUFACTURER: 'Manufacturer',
23
+ PRODUCT_NAME: 'Product name',
24
+ NOTES: 'Notes',
25
+ LOCATIONS: 'Locations',
26
+ BIGTALL: 'Big/Tall',
27
+ 247: '24/7',
28
+ IMAGE_PATH: 'Image Path',
29
+ CHARACTERISTICS: 'Characteristics',
30
+ DESCRIPTION: 'Description'
31
+ }
32
+
33
+ // -----------------------------------------------------------------------------
34
+ // Filtered Data hooks & helpers
35
+ // -----------------------------------------------------------------------------
36
+ function valueToArray (item, key) {
37
+ return item[key]?.split(',').map(value => value.trim())
38
+ }
39
+
40
+ async function getData ({ src, imageRoot }) {
41
+ const items = await fetchCsv(src)
42
+ const sanitizedImageRoot = (imageRoot ?? '').replace(/\/$/, '')
43
+
44
+ return items
45
+ .filter(item => item[FIELDS.PRODUCT_NAME])
46
+ .map(item => ({
47
+ ...item,
48
+ [FIELDS.REGION]: valueToArray(item, FIELDS.REGION),
49
+ [FIELDS.LOCATIONS]: valueToArray(item, FIELDS.LOCATIONS),
50
+ [FIELDS.CHARACTERISTICS]: valueToArray(item, FIELDS.CHARACTERISTICS),
51
+ [FIELDS.IMAGE_PATH]: `${sanitizedImageRoot}/${item[FIELDS.IMAGE_PATH]}`
52
+ }))
53
+ }
54
+
55
+ function Items ({ items }) {
56
+ // Convert items to models.
57
+ const models = items.map(item => ({
58
+ image: { src: item[FIELDS.IMAGE_PATH], alt: item[FIELDS.PRODUCT_NAME] },
59
+ title: item[FIELDS.PRODUCT_NAME],
60
+ text: item[FIELDS.REGION],
61
+ links: [
62
+ { name: FIELDS.REGION, value: item[FIELDS.REGION] },
63
+ { name: FIELDS.PRODUCT_TYPE, value: item[FIELDS.PRODUCT_TYPE] },
64
+ { name: FIELDS.MANUFACTURER, value: item[FIELDS.MANUFACTURER] },
65
+ { name: FIELDS.NOTES, value: item[FIELDS.NOTES] },
66
+ { name: FIELDS.LOCATIONS, value: item[FIELDS.LOCATIONS] },
67
+ { name: FIELDS.BIGTALL, value: item[FIELDS.BIGTALL] },
68
+ { name: FIELDS['247'], value: item[FIELDS['247']] },
69
+ { name: FIELDS.CHARACTERISTICS, value: item[FIELDS.CHARACTERISTICS] },
70
+ { name: FIELDS.DESCRIPTION, value: item[FIELDS.DESCRIPTION] }
71
+ ]
72
+ }))
73
+
74
+ return h('div', { className: 'grid gap-5 grid-cols-4 md:grid-cols-2 sm:grid-cols-1', role: 'list' }, models.map((_m, index) =>
75
+ h(PromoWithPopup, { models, currentModelIndex: index })
76
+ ))
77
+ }
78
+
79
+ function FurnitureOverview ({ src, 'image-root': imageRoot }) {
80
+ if (!src) {
81
+ return null
82
+ }
83
+ return h(FilteredData, {
84
+ getData,
85
+ filters: [{
86
+ type: 'text',
87
+ label: LABELS.SEARCH_TEXT,
88
+ keys: [FIELDS.PRODUCT_TYPE, FIELDS.MANUFACTURER, FIELDS.PRODUCT_NAME, FIELDS.NOTES, FIELDS.LOCATIONS, FIELDS.CHARACTERISTICS, FIELDS.DESCRIPTION]
89
+ }, {
90
+ type: 'select',
91
+ keys: ['', FIELDS.REGION]
92
+ }, {
93
+ type: 'select',
94
+ keys: ['', FIELDS.PRODUCT_TYPE]
95
+ }, {
96
+ type: 'select',
97
+ keys: ['', FIELDS.MANUFACTURER]
98
+ }, {
99
+ type: 'select',
100
+ keys: ['', FIELDS.LOCATIONS]
101
+ }, {
102
+ type: 'select',
103
+ keys: ['', FIELDS.CHARACTERISTICS]
104
+ }, {
105
+ type: 'order',
106
+ label: LABELS.ORDER_BY,
107
+ keys: [FIELDS.REGION, `-${FIELDS.REGION}`, FIELDS.PRODUCT_TYPE, `-${FIELDS.PRODUCT_TYPE}`, FIELDS.MANUFACTURER, `-${FIELDS.MANUFACTURER}`]
108
+ }],
109
+ Items,
110
+ src,
111
+ imageRoot
112
+ })
113
+ }
114
+
115
+ register(FurnitureOverview, 'furniture-overview')
@@ -0,0 +1,65 @@
1
+ const { h, register, matter } = globalThis.ami
2
+ const { Box } = matter
3
+
4
+ // Default values for medium and low gauges
5
+ const DEFAULT_MEDIUM = 89
6
+ const DEFAULT_LOW = 50
7
+ // Single percent of the circle is 360 / 100
8
+ const SINGLE_PERCENT = 3.6
9
+ // Default circle radius
10
+ const DEFAULT_RADIUS = 30
11
+ // We need to double Pi to calculate the circumference
12
+ const DEFAULT_DOUBLE_PI = 2
13
+ // Ensure the circle fill starts from the top of the circle
14
+ const DEFAULT_TOPSTART = 4
15
+ // Set the default circle perimeter
16
+ const DEFAULT_CIRCLEPERIMETER = 360
17
+ // Default circumference
18
+ const DEFAULT_CIRCUMFERENCE = DEFAULT_DOUBLE_PI * Math.PI * DEFAULT_RADIUS
19
+ // Default stroke dash offset
20
+ const DEFAULT_STROKE_DASH_OFFSET = (1 / DEFAULT_TOPSTART) * DEFAULT_CIRCUMFERENCE
21
+
22
+ function GaugeValue ({ score, label, medium, low }) {
23
+ // If medium or low are set, use those values
24
+ const mediumScore = medium ?? DEFAULT_MEDIUM
25
+ const lowScore = low ?? DEFAULT_LOW
26
+
27
+ // Set colours based on score, default is green
28
+ let stroke, fill, textFill
29
+
30
+ if (score <= lowScore) {
31
+ stroke = '#DD1D21'; fill = '#FFF2F0'; textFill = '#DD1D21'
32
+ } else if (score <= mediumScore) {
33
+ stroke = '#ED8A00'; fill = '#FFF3E5'; textFill = '#D54410'
34
+ } else {
35
+ stroke = '#008557'; fill = '#EFF7F2'; textFill = '#008557'
36
+ }
37
+
38
+ // Circle calculations to display the right gauge
39
+ const number = (score * SINGLE_PERCENT)
40
+ const angle = Number.parseFloat(number) || 0
41
+ const strokeDashArray = (angle / DEFAULT_CIRCLEPERIMETER) * DEFAULT_CIRCUMFERENCE
42
+
43
+ return h(Box, { className: 'bg-bga text-txa h-full rounded-2xl overflow-hidden' },
44
+ h('div', { className: 'items-center justify-center' },
45
+ h('div', { className: 'flex items-center h-full justify-center' },
46
+ h('svg', {
47
+ height: '64',
48
+ width: '64',
49
+ xmlns: 'http://www.w3.org/2000/svg',
50
+ _style: id => `
51
+ svg${id} { stroke-width: 4px; stroke: ${stroke}; fill: ${fill}; }
52
+ svg${id} text { font-family: var(--font-headers); font-size: 22px; stroke: none; fill: ${textFill}; }
53
+ `
54
+ },
55
+ h('circle', { cx: '32', cy: '32', r: '30', 'stroke-dasharray': [strokeDashArray, DEFAULT_CIRCUMFERENCE - strokeDashArray], 'stroke-dashoffset': DEFAULT_STROKE_DASH_OFFSET }),
56
+ h('text', { x: '50%', y: '52%', 'dominant-baseline': 'middle', 'text-anchor': 'middle' }, score)
57
+ )
58
+ ),
59
+ h('div', { className: 'flex items-center h-full justify-center' },
60
+ h('h4', { className: 'pt-2' }, label)
61
+ )
62
+ )
63
+ )
64
+ }
65
+ register(GaugeValue, 'gauge-value')