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,140 @@
1
+ /* global customElements */
2
+ const LENGTH = 128
3
+
4
+ const ledger = []
5
+ const listeners = []
6
+
7
+ function getComponentModel (el) {
8
+ return globalThis.ami.componentModels.get(el?.closest('[data-name]')?.dataset?.key)
9
+ }
10
+
11
+ function addListeners (...args) {
12
+ listeners.push(...args)
13
+ }
14
+
15
+ function getTextContent (el) {
16
+ let contentEl = el
17
+ if (el.querySelector('style')) {
18
+ const clonedEl = el.cloneNode(true)
19
+ for (const s of clonedEl.querySelectorAll('style, [aria-hidden]')) { s.remove() }
20
+ contentEl = clonedEl
21
+ }
22
+ return contentEl.textContent.trim()
23
+ }
24
+
25
+ function record (entry) {
26
+ ledger.push(entry)
27
+ const offset = ledger.length - LENGTH
28
+ if (offset > 0) {
29
+ ledger.splice(0, offset)
30
+ }
31
+
32
+ for (const listener of listeners) {
33
+ listener(entry)
34
+ }
35
+ }
36
+
37
+ let hasLinkListener = false
38
+ function trackLinks () {
39
+ if (hasLinkListener) {
40
+ return
41
+ }
42
+
43
+ // Capture link click to prevent navigation from detaching the element.
44
+ // This ensures tracking happens before the navigation.
45
+ hasLinkListener = true
46
+ document.addEventListener('click', event => {
47
+ // Get all the clickable elements on the path (works inside Shadow DOM).
48
+ const els = event.composedPath().filter(e => e?.classList?.contains?.('clickable'))
49
+
50
+ if (els.length === 0) {
51
+ return
52
+ }
53
+
54
+ const tagName = event.target.tagName.toLowerCase()
55
+ const customElementTagName = customElements.get(tagName) ? tagName : null
56
+
57
+ for (const el of els) {
58
+ trackLink(el, customElementTagName)
59
+ }
60
+ }, { capture: true })
61
+ }
62
+
63
+ function trackLink (el, customElementTagName) {
64
+ // For web components, use a dummy model with the organism set to the element name.
65
+ const componentModel = {
66
+ organism: customElementTagName,
67
+ ...getComponentModel(el)
68
+ }
69
+
70
+ const linkModel = { name: el.title ? el.title : getTextContent(el) }
71
+ const entry = { eventName: 'Button click', componentModel, linkModel }
72
+
73
+ // This event fires before the expand/collapse event.
74
+ // The aria-expanded attribute will not yet be updated.
75
+ if (el.getAttribute('aria-expanded') === 'false' || el.parentElement.matches('details:not([open])')) {
76
+ entry.eventName = 'Toggle'
77
+ entry.value = 'Expanded'
78
+ } else if (el.getAttribute('aria-expanded') === 'true' || el.parentElement.matches('details[open]')) {
79
+ entry.eventName = 'Toggle'
80
+ entry.value = 'Collapsed'
81
+ } else if (el.href) {
82
+ entry.linkModel.value = el.href
83
+ entry.eventName = 'Link click'
84
+ }
85
+
86
+ record(entry)
87
+ }
88
+
89
+ let hasPageViewListener = false
90
+
91
+ function pageViewListener () {
92
+ record({
93
+ eventName: 'Page view',
94
+ componentModel: getComponentModel(document.querySelector('[data-name="Page"]'))
95
+ })
96
+ }
97
+
98
+ function trackPageView () {
99
+ if (hasPageViewListener) {
100
+ return
101
+ }
102
+
103
+ hasPageViewListener = true
104
+ pageViewListener() // Current page view.
105
+ globalThis.addEventListener('route', () => {
106
+ pageViewListener()
107
+ }) // All future page views.
108
+ }
109
+
110
+ export function initLedger () {
111
+ if (globalThis?.ami?.ledger) {
112
+ return
113
+ }
114
+
115
+ // Expose ledger listeners.
116
+ globalThis.ledgerListeners = globalThis.ledgerListeners || []
117
+ globalThis.ledgerListeners.push = function (handler, ...args) {
118
+ if (globalThis.ledgerListeners.includes(handler)) {
119
+ throw new Error('Handler already added.')
120
+ }
121
+
122
+ // Call handler for all existing entries.
123
+ for (const entry of ledger) {
124
+ handler(entry)
125
+ }
126
+
127
+ addListeners(handler, ...args)
128
+ return Array.prototype.push.apply(this, [handler, ...args])
129
+ }
130
+
131
+ // Add existing ledger listeners.
132
+ addListeners(...globalThis.ledgerListeners)
133
+
134
+ globalThis.ami ||= {}
135
+ globalThis.ami.ledger = ledger
136
+ globalThis.ami.ledger.record = record
137
+
138
+ trackPageView()
139
+ trackLinks()
140
+ }
@@ -0,0 +1,44 @@
1
+ const { h, register, preactHooks, getConsent } = globalThis.ami
2
+ const { useRef, useEffect } = preactHooks
3
+
4
+ const POLL_INTERVAL = 1000
5
+ const GAP = '48px'
6
+
7
+ function LNGMap ({ src }) {
8
+ if (!src || !URL.canParse(src)) {
9
+ console.error('Missing data source or invalid URL.')
10
+ return null
11
+ }
12
+
13
+ const ref = useRef(null)
14
+
15
+ useEffect(() => {
16
+ let interval = null
17
+ const iframe = ref.current
18
+ const origin = new URL(src).origin
19
+
20
+ // Post consent to iframe.
21
+ const onLoad = () => {
22
+ interval = setInterval(() => {
23
+ iframe?.contentWindow?.postMessage({ consent: JSON.stringify(getConsent()) }, origin)
24
+ }, POLL_INTERVAL)
25
+ }
26
+
27
+ iframe?.addEventListener('load', onLoad)
28
+
29
+ // Clear interval and load event when destroyed.
30
+ return () => {
31
+ clearInterval(interval)
32
+ iframe?.removeEventListener('load', onLoad)
33
+ }
34
+ }, [src])
35
+
36
+ return h('iframe', {
37
+ ref,
38
+ className: 'block w-full rounded-2xl border border-txa/20 sm:h-svh md:h-svh lg:aspect-video',
39
+ style: `max-height: calc(100svh - ${GAP})`,
40
+ src
41
+ })
42
+ }
43
+
44
+ register(LNGMap, 'lng-map')
@@ -0,0 +1,39 @@
1
+ const { h, preactHooks, register, waitForConsent } = globalThis.ami
2
+ const { useEffect, useState } = preactHooks
3
+
4
+ function MouseflowAnalytics ({ id }) {
5
+ if (!id) {
6
+ return null
7
+ }
8
+
9
+ if (document.documentElement.dataset.mode !== 'publish') {
10
+ return null
11
+ }
12
+
13
+ // Mouseflow is already loaded.
14
+ if (globalThis._mfq) {
15
+ return null
16
+ }
17
+
18
+ const [consent, setConsent] = useState(false)
19
+
20
+ useEffect(() => {
21
+ (async () => {
22
+ // This will stop the execution util the consent is granted.
23
+ await waitForConsent({ statistics: true })
24
+ setConsent(true)
25
+ })()
26
+ }, [])
27
+
28
+ if (!consent) {
29
+ return null
30
+ }
31
+
32
+ globalThis._mfq = []
33
+ return h('script', {
34
+ src: `//cdn.mouseflow.com/projects/${id}.js`,
35
+ defer: true
36
+ })
37
+ }
38
+
39
+ register(MouseflowAnalytics, 'mouseflow-analytics')
@@ -0,0 +1,127 @@
1
+ import FilteredData from '#acinguiux-preact/wcs/shared/filtered-data/filtered-data.js'
2
+
3
+ const { h, register, matter } = globalThis.ami
4
+ const { Box, Table, Link } = matter
5
+
6
+ const LABELS = {
7
+ notAvailable: 'N/A',
8
+ text: 'Enter your search term',
9
+ selectCountry: 'Country/Region',
10
+ selectLanguage: 'Language',
11
+ search: 'Search',
12
+ download: 'Download',
13
+ noResults: 'No results found. Please do not use wild card.'
14
+ }
15
+
16
+ const fields = [
17
+ { key: 'productName', name: 'Name' },
18
+ { key: 'country', name: 'Country/Region' },
19
+ { key: 'language', name: 'Language' },
20
+ { key: 'specId', name: 'Specification' },
21
+ { key: 'cas', name: 'CAS' },
22
+ { key: 'url', name: 'SDS URL', isUrl: true },
23
+ { key: 'versionNumber', name: 'Version' }
24
+ ]
25
+
26
+ // -----------------------------------------------------------------------------
27
+ // Filtered Data hooks & helpers
28
+ // -----------------------------------------------------------------------------
29
+
30
+ async function getData ({ src }) {
31
+ try {
32
+ const res = await fetch(src)
33
+ if (!res.ok) {
34
+ throw new Error(`Failed to load resource: ${src} (Status: ${res.status})`)
35
+ }
36
+ const json = await res.json()
37
+ if (!json?.Items?.length) {
38
+ throw new Error('Invalid JSON structure: missing or empty Items array')
39
+ }
40
+ return json.Items.map(item => {
41
+ const {
42
+ SequenceNumber: msdsId,
43
+ ProductName: productName,
44
+ /* LanguageCode */
45
+ LanguageName: language,
46
+ /* DocDescription */
47
+ URL: url,
48
+ SICC: siccCode,
49
+ /* CountryCode */
50
+ CAS: cas,
51
+ NormedCAS: casNumberFormatted,
52
+ NamSyn: synonyms,
53
+ LanguageCountryDocType: languageCountry,
54
+ SpecIdFull: specId,
55
+ Version: versionNumber
56
+ } = item
57
+
58
+ const country = languageCountry?.split?.(';')?.[1]
59
+
60
+ return {
61
+ msdsId, productName, country, language, siccCode, url, cas, casNumberFormatted, synonyms, specId, versionNumber
62
+ }
63
+ }) || []
64
+ } catch (error) {
65
+ console.error('Error in getData:', error)
66
+ throw error
67
+ }
68
+ }
69
+
70
+ function naFormat (value) {
71
+ return (value === null || value === undefined) ? LABELS.notAvailable : value
72
+ }
73
+
74
+ function Items ({ items }) {
75
+ if (!items?.length) {
76
+ return h('div', { className: 'text-center' }, LABELS.noResults)
77
+ }
78
+
79
+ return h(Box, { className: 'bg-bga text-txa rounded-2xl w-full overflow-x-auto' },
80
+ h(Table,
81
+ h('tr', fields.map(headerItem => h('th', headerItem.name))),
82
+ items.map(
83
+ result => h('tr', fields.map(
84
+ field => {
85
+ const fieldValue = result[field.key]
86
+ return h('td', fieldValue && field.isUrl
87
+ ? h(Link, {
88
+ _variant: 'full',
89
+ _model: {
90
+ name: LABELS.download,
91
+ value: fieldValue
92
+ }
93
+ })
94
+ : naFormat(fieldValue))
95
+ }
96
+ ))
97
+ )
98
+ )
99
+ )
100
+ }
101
+
102
+ // -----------------------------------------------------------------------------
103
+ // Main component, custom element definition
104
+ // -----------------------------------------------------------------------------
105
+
106
+ function MSDSSearch ({ src }) {
107
+ return h(FilteredData, {
108
+ getData,
109
+ filters: [{
110
+ type: 'text',
111
+ label: LABELS.text,
112
+ keys: ['productName', 'siccCode', 'synonyms', 'cas', 'casNumberFormatted', 'specId']
113
+ }, {
114
+ type: 'select',
115
+ label: LABELS.selectCountry,
116
+ keys: ['', 'country']
117
+ }, {
118
+ type: 'select',
119
+ label: LABELS.selectLanguage,
120
+ keys: ['', 'language']
121
+ }],
122
+ Items,
123
+ src
124
+ })
125
+ }
126
+
127
+ register(MSDSSearch, 'msds-search')
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path fill="#595959" d="M10.5,2 C15.1944204,2 19,5.80557963 19,10.5 C19,12.4865245 18.3185332,14.3138839 17.1766205,15.7610573 L21.7071068,20.2928932 L20.2928932,21.7071068 L15.7610573,17.1766205 C14.3138839,18.3185332 12.4865245,19 10.5,19 C5.80557963,19 2,15.1944204 2,10.5 C2,5.80557963 5.80557963,2 10.5,2 Z M10.5,4 C6.91014913,4 4,6.91014913 4,10.5 C4,14.0898509 6.91014913,17 10.5,17 C14.0898509,17 17,14.0898509 17,10.5 C17,6.91014913 14.0898509,4 10.5,4 Z"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <polygon fill="#595959" points="15.543 4.294 7.699 12.922 15.51 20.743 14.095 22.156 4.937 12.987 14.063 2.949"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4.20731 4.42397C4.16978 4.18006 3.95991 4 3.71312 4H2.5C2.22386 4 2 3.77614 2 3.5V2.5C2 2.22386 2.22386 2 2.5 2H4.57104C5.3114 2 5.94102 2.54017 6.0536 3.27191L6.2591 4.60767H20.5105C20.8335 4.60767 21.0717 4.90937 20.9968 5.22359L19.2352 12.6149C19.1815 12.8401 18.9803 12.999 18.7488 12.999H7.55007L7.79269 14.576C7.83022 14.8199 8.04009 15 8.28688 15H18.5C18.7761 15 19 15.2239 19 15.5V16.5C19 16.7761 18.7761 17 18.5 17H7.42896C6.6886 17 6.05898 16.4598 5.9464 15.7281L4.20731 4.42397ZM6.58786 6.60767L7.26279 10.999H17.4458C17.5153 10.999 17.5756 10.9513 17.5917 10.8838L18.5669 6.79244C18.5893 6.69818 18.5179 6.60767 18.421 6.60767H6.58786ZM10 20C10 21.1046 9.10457 22 8 22C6.89543 22 6 21.1046 6 20C6 18.8954 6.89543 18 8 18C9.10457 18 10 18.8954 10 20ZM19 20C19 21.1046 18.1046 22 17 22C15.8954 22 15 21.1046 15 20C15 18.8954 15.8954 18 17 18C18.1046 18 19 18.8954 19 20Z" fill="#292929"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <polygon fill="#595959" points="19.293 3.293 20.707 4.707 13.414 12 20.707 19.293 19.293 20.707 12 13.414 4.707 20.707 3.293 19.293 10.586 12 3.293 4.707 4.707 3.293 12 10.586"/>
3
+ </svg>
@@ -0,0 +1,215 @@
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 ICON_BACK from './icon-back.svg'
4
+ import ICON_CART from './icon-cart.svg'
5
+ import ICON_CLOSE from './icon-close.svg'
6
+
7
+ const { h, matter, preactHooks, register } = globalThis.ami
8
+ const { Box, Button, Card, Heading, Popup, RichText, Scroller } = matter
9
+ const { useEffect, useRef, useState } = preactHooks
10
+
11
+ const LABELS = {
12
+ CATEGORY: 'Nach Kategorie filtern',
13
+ ORDER_BY: 'Sortieren nach',
14
+ LOAD_MORE: 'Mehr laden',
15
+ RESET_FILTERS: 'Filter zurücksetzen',
16
+ ASCENDING: 'aufsteigend',
17
+ DESCENDING: 'absteigend',
18
+ ALL_CATEGORIES: 'Alle Prämienkategorien',
19
+ POINTS: 'Punkte',
20
+ BACK_TO_OVERVIEW: 'Zurück zur übersicht',
21
+ LEARN_MORE: 'Mehr erfahren'
22
+ }
23
+ const IMG_SIZE = { width: 320, height: 320 }
24
+ const FORWARD_URL = 'https://www.acinguiuxsmart.com/smart/reward?site=de-de&code='
25
+
26
+ // -----------------------------------------------------------------------------
27
+ // Data fetch utilities
28
+ // -----------------------------------------------------------------------------
29
+
30
+ async function getData ({ src, imageRoot }) {
31
+ const products = await fetchCsv(src)
32
+ const sanitizedImageRoot = (imageRoot ?? '').replace(/\/$/, '')
33
+ parseProducts(products, sanitizedImageRoot)
34
+
35
+ return products
36
+ }
37
+
38
+ function parseProducts (products, imageRoot) {
39
+ for (const product of products) {
40
+ product.IMAGE = `${imageRoot}/${product.GIFT_CODE}.jpg`
41
+ product[LABELS.POINTS] = Number.parseInt(product.POINTS, 10) || ''
42
+ }
43
+ }
44
+
45
+ // -----------------------------------------------------------------------------
46
+ // Common presentation components (item)
47
+ // -----------------------------------------------------------------------------
48
+
49
+ function Item ({ product, role }) {
50
+ const [isOpen, setIsOpen] = useState(false)
51
+ const cardRef = useRef(null)
52
+
53
+ const closePopup = () => {
54
+ setIsOpen(false)
55
+ cardRef.current?.focus()
56
+ }
57
+
58
+ return h('div', { className: 'animate-fade h-full', role },
59
+ h(Card, {
60
+ key: product.GIFT_CODE,
61
+ tabindex: 0,
62
+ ref: cardRef,
63
+ onClick: event => {
64
+ cardRef.current = event.currentTarget
65
+ setIsOpen(true)
66
+ },
67
+ onKeyDown: event => event.key === 'Enter' && event.currentTarget.click(),
68
+ children: h('div',
69
+ h(ProductAsset, { product }),
70
+ h('div', { className: 'sm:col-span-1' },
71
+ h(Box,
72
+ h('span', product.BRAND),
73
+ h(Heading, { _level: '3' }, product.GIFT_NAME),
74
+ h('p', product.CATEGORY_NAME)
75
+ )
76
+ )
77
+ )
78
+ }),
79
+ h(Popup, {
80
+ onKeyDown: event => event.key === 'Escape' && closePopup(),
81
+ _show: isOpen,
82
+ _layout: 'center',
83
+ children: isOpen && [
84
+ h('div', { className: 'flex gap-2' },
85
+ h('div', { className: 'grow' },
86
+ h(Heading, { _level: '3', className: 'pr-10' }, product.GIFT_NAME)
87
+ ),
88
+ h('div', { className: 'shrink-0' },
89
+ h(Button, {
90
+ _variant: 'plain',
91
+ _textless: true,
92
+ _size: 'xs',
93
+ _model: {
94
+ name: LABELS.BACK_TO_OVERVIEW,
95
+ value: '#',
96
+ icon: ICON_CLOSE
97
+ },
98
+ onClick: event => {
99
+ event.preventDefault()
100
+ closePopup()
101
+ }
102
+ })
103
+ )
104
+ ),
105
+ h(ProductAsset, { product, className: 'rounded-lg overflow-hidden' }),
106
+ h(Heading, { _level: '5' }, product.BRAND),
107
+ h(RichText, product.DESCRIPTION),
108
+ h('div', { className: 'flex gap-5 sm:flex-wrap' },
109
+ h(Button, {
110
+ _size: 'sm',
111
+ _model: {
112
+ name: LABELS.BACK_TO_OVERVIEW,
113
+ value: '#',
114
+ icon: ICON_BACK
115
+ },
116
+ onClick: event => {
117
+ event.preventDefault()
118
+ closePopup()
119
+ }
120
+ }),
121
+ h(Button, {
122
+ target: '_blank',
123
+ rel: 'noopener',
124
+ _size: 'sm',
125
+ _model: {
126
+ name: LABELS.LEARN_MORE,
127
+ value: `${FORWARD_URL}${product.GIFT_CODE}`,
128
+ icon: ICON_CART
129
+ }
130
+ })
131
+ )
132
+ ]
133
+ })
134
+ )
135
+ }
136
+
137
+ function ProductAsset ({ product, className = '' }) {
138
+ return h('div', { className },
139
+ h('img', { src: product.IMAGE, alt: product.GIFT_NAME, className: 'block object-cover w-full h-auto', loading: 'lazy', ...IMG_SIZE }),
140
+ h('div', { className: 'relative' },
141
+ h('div', { className: 'bg-bgb text-txb absolute bottom-3 left-3 pt-2 pb-2 pl-3 pr-3 rounded-lg font-bold' },
142
+ `${product.POINTS} ${LABELS.POINTS}`
143
+ )
144
+ )
145
+ )
146
+ }
147
+
148
+ // -----------------------------------------------------------------------------
149
+ // Default variant (via Filtered data): items
150
+ // -----------------------------------------------------------------------------
151
+
152
+ function Items ({ items }) {
153
+ return h('div', { className: 'grid gap-5 grid-cols-4 md:grid-cols-2 sm:grid-cols-1', role: 'list' }, items.map(product =>
154
+ h(Item, { product, role: 'listitem' })
155
+ ))
156
+ }
157
+
158
+ // -----------------------------------------------------------------------------
159
+ // Slider variant: items
160
+ // -----------------------------------------------------------------------------
161
+
162
+ function Slider ({ src, imageRoot }) {
163
+ const [items, setItems] = useState([])
164
+
165
+ useEffect(() => {
166
+ getData({ src, imageRoot }).then(setItems)
167
+ }, [])
168
+
169
+ return h(Scroller, {
170
+ // Remove gap; shadow compensation is enough.
171
+ _style: id => `${id} [role="list"] { gap: 0 }`,
172
+ // Compensate for shadow.
173
+ children: items.map(product => h('div', { style: 'padding: 10px' },
174
+ h(Item, { product })
175
+ ))
176
+ })
177
+ }
178
+
179
+ // -----------------------------------------------------------------------------
180
+ // Main component, custom element definition
181
+ // -----------------------------------------------------------------------------
182
+
183
+ function ProductCatalogue ({ src, 'image-root': imageRoot, variant }) {
184
+ if (!src) {
185
+ return null
186
+ }
187
+
188
+ if (variant === 'slider') {
189
+ return h(Slider, { src, imageRoot })
190
+ }
191
+
192
+ return h(FilteredData, {
193
+ getData,
194
+ filters: [{
195
+ type: 'select',
196
+ label: LABELS.CATEGORY,
197
+ keys: [LABELS.ALL_CATEGORIES, 'CATEGORY_NAME']
198
+ }, {
199
+ type: 'order',
200
+ label: LABELS.ORDER_BY,
201
+ keys: [LABELS.POINTS, `-${LABELS.POINTS}`]
202
+ }],
203
+ Items,
204
+ i18n: {
205
+ loadMore: LABELS.LOAD_MORE,
206
+ resetFilters: LABELS.RESET_FILTERS,
207
+ ascending: LABELS.ASCENDING,
208
+ descending: LABELS.DESCENDING
209
+ },
210
+ src,
211
+ imageRoot
212
+ })
213
+ }
214
+
215
+ register(ProductCatalogue, 'product-catalogue')
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4.20731 4.42397C4.16978 4.18006 3.95991 4 3.71312 4H2.5C2.22386 4 2 3.77614 2 3.5V2.5C2 2.22386 2.22386 2 2.5 2H4.57104C5.3114 2 5.94102 2.54017 6.0536 3.27191L6.2591 4.60767H20.5105C20.8335 4.60767 21.0717 4.90937 20.9968 5.22359L19.2352 12.6149C19.1815 12.8401 18.9803 12.999 18.7488 12.999H7.55007L7.79269 14.576C7.83022 14.8199 8.04009 15 8.28688 15H18.5C18.7761 15 19 15.2239 19 15.5V16.5C19 16.7761 18.7761 17 18.5 17H7.42896C6.6886 17 6.05898 16.4598 5.9464 15.7281L4.20731 4.42397ZM6.58786 6.60767L7.26279 10.999H17.4458C17.5153 10.999 17.5756 10.9513 17.5917 10.8838L18.5669 6.79244C18.5893 6.69818 18.5179 6.60767 18.421 6.60767H6.58786ZM10 20C10 21.1046 9.10457 22 8 22C6.89543 22 6 21.1046 6 20C6 18.8954 6.89543 18 8 18C9.10457 18 10 18.8954 10 20ZM19 20C19 21.1046 18.1046 22 17 22C15.8954 22 15 21.1046 15 20C15 18.8954 15.8954 18 17 18C18.1046 18 19 18.8954 19 20Z" fill="#292929"/>
3
+ </svg>
@@ -0,0 +1,43 @@
1
+ import ICON_CART from './icon-cart.svg'
2
+
3
+ const { h, preactHooks, matter, register } = globalThis.ami
4
+ const { useMemo, useState } = preactHooks
5
+ const { Box, Button, Heading, Link } = matter
6
+
7
+ const IS_PUBLISH = document.documentElement.dataset.mode === 'publish'
8
+ const VISIBLE_VENDOR_COUNT = 3
9
+
10
+ const getSpaceSeparatedValues = text => {
11
+ const [, name, value] = text?.match(/^(.*)\s(\S+)$/) || []
12
+ return (name && value) ? { name, value } : null
13
+ }
14
+
15
+ function ProductLinks ({ heading = 'Buy now', 'view-more': viewMore = 'View more retailers', ...props }) {
16
+ const [isExpanded, setIsExpanded] = useState(false)
17
+
18
+ const vendors = useMemo(() =>
19
+ Object.entries(props)
20
+ .filter(([propName]) => propName.startsWith('vendor-'))
21
+ .map(([, propValue], i) => {
22
+ const parserVals = getSpaceSeparatedValues(propValue)
23
+ return parserVals ? { ...parserVals, order: i === 0 ? -1 : Math.random() } : null // eslint-disable-line sonarjs/pseudo-random
24
+ })
25
+ .filter(Boolean) // Filter out empty/incorrect values.
26
+ .sort((v1, v2) => IS_PUBLISH ? v1.order - v2.order : 0), // Randomize order for publish mode (except for the first link).
27
+ [])
28
+
29
+ if (!vendors.length) {
30
+ return null
31
+ }
32
+
33
+ return h(Box, { className: 'rounded-2xl bg-bga text-txa space-y-5' },
34
+ h(Heading, { _level: 2 }, heading),
35
+ ...vendors.map((vendor, i) =>
36
+ (isExpanded || i < VISIBLE_VENDOR_COUNT) && h(Button, { _model: { ...vendor, icon: ICON_CART }, _wide: true })
37
+ ),
38
+ !isExpanded && (vendors.length > VISIBLE_VENDOR_COUNT) &&
39
+ h(Link, { _variant: 'full', _model: { name: viewMore }, onClick: () => setIsExpanded(true) })
40
+ )
41
+ }
42
+
43
+ register(ProductLinks, 'product-links')