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,260 @@
1
+ import { useEffect, useState } from 'preact/hooks'
2
+ import { Icon } from '../atoms/icon.js'
3
+ import { track } from '../shared/analytics.js'
4
+ import h from '../shared/h.js'
5
+ import { ARROW_TAIL_UP } from '../shared/icons.js'
6
+ import t from '../shared/t.js'
7
+ import updateHead from '../shared/update-head.js'
8
+ import { tw } from '../shared/twind.js'
9
+
10
+ const SCROLL_THRESHOLD = 200 // In pixels.
11
+ const DEBOUNCE = 100 // In milliseconds.
12
+ const SHARED_FAVICON_THEME = 'theme-base2'
13
+ const UNIQUE_FAVICON_THEMES = new Set(['theme-nam-2025', 'theme-vegetable', 'theme-aurvana'])
14
+
15
+ function getFaviconBasePath (themePath) {
16
+ const themeName = themePath?.split('/').pop()
17
+ if (!themeName) {
18
+ return undefined
19
+ }
20
+
21
+ if (UNIQUE_FAVICON_THEMES.has(themeName)) {
22
+ return `${themePath}/resources/favicon/`
23
+ }
24
+
25
+ return `/themes/${SHARED_FAVICON_THEME}/resources/favicon/`
26
+ }
27
+
28
+ // -----------------------------------------------------------------------------
29
+ // Page component
30
+ // -----------------------------------------------------------------------------
31
+
32
+ export function Page ({ _model, children, ...props }) {
33
+ const { links } = _model
34
+ const getMeta = name => links.find(l => l.name === name)?.value ?? undefined
35
+ const aaUrl = getMeta('adobeAnalyticsUrl')
36
+ updateMetas(_model)
37
+
38
+ // Track after render.
39
+ useEffect(() => track(aaUrl), [aaUrl])
40
+
41
+ // Render the page in a div. This makes scrollbar handling inside overlays much simpler.
42
+ // The relative positioning is necessary, since the window does not scroll. The body does.
43
+ // This makes absolutely positioned elements work correctly.
44
+ return h('div', {
45
+ ...props,
46
+ className: 'font-sans text-base bg-bga min-h-dvh text-txa relative',
47
+ children: [
48
+ h(SkipToMain),
49
+ ...children,
50
+ h(BackToTop)
51
+ ]
52
+ })
53
+ }
54
+
55
+ function SkipToMain () {
56
+ return h('a', {
57
+ href: '#main',
58
+ _variant: 'bold',
59
+ 'data-name': 'SkipToMain',
60
+ // Use z-20 for floating elements.
61
+ className: 'p-4 font-bold transition-all rounded-lg fixed start-6 bg-bgb text-txb -top-14 opacity-0 focus:top-6 focus:opacity-100 z-20 print:hidden',
62
+ children: t('Skip to main content')
63
+ })
64
+ }
65
+
66
+ function shouldBeVisible () {
67
+ return document.body.scrollTop >= SCROLL_THRESHOLD
68
+ }
69
+
70
+ function BackToTop () {
71
+ const [isVisible, setVisibility] = useState(shouldBeVisible())
72
+
73
+ useEffect(() => {
74
+ let resizeTimeout
75
+ document.addEventListener('scroll', () => {
76
+ clearTimeout(resizeTimeout)
77
+ resizeTimeout = setTimeout(() => {
78
+ setVisibility(shouldBeVisible())
79
+ }, DEBOUNCE)
80
+ }, true)
81
+ })
82
+
83
+ const name = t('Back to top')
84
+ return isVisible && h('button', {
85
+ // Use z-20 for floating elements.
86
+ className: 'clickable fixed bottom-6 end-6 shadow bg-bga text-txa animate-expand rounded-lg w-10 h-10 hover:text-bga hover:bg-txa transition-all flex justify-center items-center z-20 print:hidden',
87
+ title: name,
88
+ 'aria-label': name,
89
+ 'data-name': 'BackToTop',
90
+ onClick () {
91
+ document.body.scrollTop = 0
92
+ location.hash = '#'
93
+ },
94
+ children: h(Icon, ARROW_TAIL_UP)
95
+ })
96
+ }
97
+
98
+ // -----------------------------------------------------------------------------
99
+ // Page metadata management
100
+ // -----------------------------------------------------------------------------
101
+
102
+ // SEO complains for relative URL's
103
+ function getAbsoluteURL (value) {
104
+ return new URL(value, location.href)?.href
105
+ }
106
+
107
+ // Metadata handlers.
108
+ const handlers = {
109
+ alternate ({ children }) {
110
+ // Remove all alternate links.
111
+ for (const el of document.querySelectorAll('link[rel="alternate"]')) { el.remove() }
112
+
113
+ for (const { name, value } of (children || [])) {
114
+ updateHead('link', { rel: 'alternate', hreflang: name }, { href: getAbsoluteURL(value), hreflang: name })
115
+ }
116
+ },
117
+
118
+ canonical ({ value }) {
119
+ const canonicalUrl = value && getAbsoluteURL(value)
120
+ updateHead('link', { rel: 'canonical' }, { href: canonicalUrl })
121
+ updateHead('meta', { property: 'og:url' }, { content: canonicalUrl })
122
+ },
123
+
124
+ externalDisclaimerText ({ value }) {
125
+ updateHead('meta', { name: 'external-disclaimer-text' }, { content: value })
126
+ },
127
+
128
+ dateModified ({ value }) {
129
+ updateHead('meta', { itemprop: 'dateModified' }, { content: value })
130
+ },
131
+
132
+ errorUrl ({ value }) {
133
+ updateHead('meta', { name: 'error-url' }, { content: value })
134
+ },
135
+
136
+ keywords ({ value }) {
137
+ updateHead('meta', { name: 'keywords' }, { content: value })
138
+ },
139
+
140
+ 'og:type' ({ value }) {
141
+ updateHead('meta', { property: 'og:type' }, { content: value })
142
+ },
143
+
144
+ 'twitter:card' ({ value }) {
145
+ updateHead('meta', { name: 'twitter:card' }, { content: value })
146
+ },
147
+
148
+ schema ({ value }) {
149
+ updateHeadJsonLd('local-business', value)
150
+ },
151
+
152
+ homeTitle ({ value }) {
153
+ updateHead('meta', { name: 'application-name' }, { content: value })
154
+ updateHead('meta', { name: 'apple-mobile-web-app-title' }, { content: value })
155
+ },
156
+
157
+ homeUrl ({ value }) {
158
+ updateHead('meta', { name: 'home-url' }, { content: value })
159
+ },
160
+
161
+ locale ({ value }) {
162
+ updateHead('meta', { property: 'og:locale' }, { content: value })
163
+ // The locale meta uses Java locale format for the sake of og:locale.
164
+ // The lang uses IETF BCP 47 and needs to be converted.
165
+ document.documentElement.lang = value?.replace('_', '-')
166
+ },
167
+
168
+ textDirection ({ value }) {
169
+ document.documentElement.dir = value
170
+ },
171
+
172
+ title ({ value }) {
173
+ document.title = value
174
+ },
175
+
176
+ themeCss ({ value }) {
177
+ updateHead('link', { rel: 'stylesheet', 'data-id': 'theme' }, { href: value })
178
+ },
179
+
180
+ themeMode ({ value }) {
181
+ if (value === 'bw') {
182
+ tw('grayscale')
183
+ document.documentElement.classList.add('grayscale')
184
+ } else {
185
+ document.documentElement.classList.remove('grayscale')
186
+ }
187
+ },
188
+
189
+ themePath ({ value }) {
190
+ let appleTouchIcon, manifest, faviconIco, faviconSvg
191
+
192
+ if (value) {
193
+ const path = getFaviconBasePath(value)
194
+ appleTouchIcon = `${path}apple-touch-icon.png`
195
+ manifest = `${path}manifest.json`
196
+ faviconIco = `${path}favicon.ico`
197
+ faviconSvg = `${path}favicon.svg`
198
+ }
199
+
200
+ updateHead('link', { rel: 'apple-touch-icon' }, { href: appleTouchIcon })
201
+ updateHead('link', { rel: 'manifest' }, { href: manifest })
202
+
203
+ // Remove all icons without data format.
204
+ for (const el of document.querySelectorAll('link[rel=icon]:not([data-format])')) { el.remove() }
205
+
206
+ // Create new icons with data format. From that point onwards only the href will be updated.
207
+ updateHead('link', { rel: 'icon', 'data-format': 'ico' }, { href: faviconIco })
208
+ updateHead('link', { rel: 'icon', 'data-format': 'svg' }, { href: faviconSvg })
209
+ }
210
+ }
211
+
212
+ function updateHeadJsonLd (id, value) {
213
+ const selector = `script[type="application/ld+json"][data-schema="${id}"]`
214
+ const existing = document.head.querySelector(selector)
215
+
216
+ if (!value) {
217
+ existing?.remove()
218
+ return
219
+ }
220
+
221
+ const el = existing ?? document.createElement('script')
222
+ el.type = 'application/ld+json'
223
+ el.setAttribute('data-schema', id)
224
+ el.textContent = value
225
+
226
+ if (!existing) {
227
+ document.head.appendChild(el)
228
+ }
229
+ }
230
+
231
+ function updateMetas (model) {
232
+ // Set the title.
233
+ if (model.title) {
234
+ handlers.title(model.title)
235
+ }
236
+
237
+ // Create a copy of handler references to track which ones were used.
238
+ const unusedHandlers = { ...handlers }
239
+ for (const link of model.links) {
240
+ // Inject new dynamic elements.
241
+ if (handlers[link.name]) {
242
+ delete unusedHandlers[link.name]
243
+ handlers[link.name](link, model)
244
+ }
245
+ }
246
+
247
+ // Remove elements that are not present in the model.
248
+ for (const handler of Object.values(unusedHandlers)) {
249
+ handler({})
250
+ }
251
+
252
+ updateHead('meta', { property: 'og:title' }, { content: document.title })
253
+ updateHead('meta', { itemprop: 'name' }, { content: document.title })
254
+ updateHead('meta', { name: 'description' }, { content: model.text })
255
+ updateHead('meta', { property: 'og:description' }, { content: model.text })
256
+ updateHead('meta', { itemprop: 'description' }, { content: model.text })
257
+ updateHead('meta', { property: 'og:image' }, { content: model.image.src })
258
+ updateHead('meta', { property: 'og:image:alt' }, { content: model.image.alt })
259
+ updateHead('meta', { name: 'twitter:image:alt' }, { content: model.image.alt })
260
+ }
@@ -0,0 +1,24 @@
1
+ import { Box } from '../atoms/box.js'
2
+ import { Heading } from '../atoms/heading.js'
3
+ import { Link } from '../atoms/link.js'
4
+ import { RichText } from '../atoms/rich-text.js'
5
+ import { Time } from '../atoms/time.js'
6
+ import { ValidationError } from '../routing/error-handler.js'
7
+ import h from '../shared/h.js'
8
+
9
+ export function PressRelease ({ _model, _variant, ...props }) {
10
+ const { links, date, title, text } = _model
11
+ if (!title || links.length === 0) {
12
+ throw new ValidationError()
13
+ }
14
+
15
+ const firstLink = links[0]
16
+
17
+ return h(Box, { ...props, className: 'bg-bga text-txa rounded-2xl h-full' },
18
+ h(Heading, { _level: 3 },
19
+ h(Link, { _model: { value: firstLink.value, name: title, icon: null }, _variant: 'full' })
20
+ ),
21
+ date && h(Time, { _time: date }),
22
+ _variant !== 'short' && h(RichText, text)
23
+ )
24
+ }
@@ -0,0 +1,204 @@
1
+ import { useState } from 'preact/hooks'
2
+ import { Img } from '../atoms/img.js'
3
+ import h from '../shared/h.js'
4
+
5
+ export function ProductAdmin ({ _model, ...props }) {
6
+ const { products = [], currency = '\u20b9', categories = [] } = _model || {}
7
+
8
+ const [rows, setRows] = useState(() =>
9
+ products.map((p, idx) => ({
10
+ id: p.id || idx,
11
+ image: p.image?.src || '',
12
+ title: p.title || '',
13
+ brand: p.brand || '',
14
+ category: p.category || '',
15
+ price: p.price != null ? String(p.price) : '',
16
+ oldPrice: p.oldPrice != null ? String(p.oldPrice) : '',
17
+ unitLabel: p.unitLabel || '',
18
+ description: p.description || '',
19
+ changed: false
20
+ }))
21
+ )
22
+
23
+ const updateRow = (idx, field, value) => {
24
+ setRows(prev => prev.map((row, i) =>
25
+ i === idx ? { ...row, [field]: value, changed: true } : row
26
+ ))
27
+ }
28
+
29
+ const changedCount = rows.filter(r => r.changed).length
30
+
31
+ const inputClass = 'w-full rounded border border-txa/20 bg-bga text-txa px-1.5 py-1 text-xs lg:text-sm focus:border-bgb focus:outline-none'
32
+ const selectClass = 'w-full rounded border border-txa/20 bg-bga text-txa px-1 py-1 text-xs lg:text-sm'
33
+ const thClass = 'px-2 py-2 text-[10px] lg:text-xs font-bold text-txa/70 uppercase text-left whitespace-nowrap'
34
+ const tdClass = 'px-1.5 py-1.5 align-middle'
35
+
36
+ return h('div', {
37
+ ...props,
38
+ className: 'bg-bga text-txa border border-txa/10 rounded-xl w-full overflow-hidden'
39
+ },
40
+
41
+ // Header
42
+ h('div', { className: 'flex items-center justify-between p-3 lg:p-4 border-b border-txa/10' },
43
+ h('h2', { className: 'text-sm lg:text-base font-bold' }, 'Product Admin'),
44
+ h('span', { className: 'text-xs text-txa/60' }, `${rows.length} products`)
45
+ ),
46
+
47
+ // Table
48
+ h('div', { className: 'overflow-x-auto' },
49
+ h('table', { className: 'w-full text-left border-collapse min-w-[800px]' },
50
+ h('thead', null,
51
+ h('tr', { className: 'bg-txa/5 border-b border-txa/10' },
52
+ h('th', { className: `${thClass} w-12` }, ''),
53
+ h('th', { className: thClass }, 'Name'),
54
+ h('th', { className: thClass }, 'Brand'),
55
+ h('th', { className: `${thClass} w-28` }, 'Category'),
56
+ h('th', { className: `${thClass} w-20` }, `Price (${currency})`),
57
+ h('th', { className: `${thClass} w-20` }, 'Old Price'),
58
+ h('th', { className: `${thClass} w-24` }, 'Unit'),
59
+ h('th', { className: thClass }, 'Description'),
60
+ h('th', { className: `${thClass} w-10` }, 'Image')
61
+ )
62
+ ),
63
+ h('tbody', null,
64
+ rows.map((row, idx) =>
65
+ h('tr', {
66
+ key: row.id,
67
+ className: `border-b border-txa/5 ${row.changed ? 'bg-bgb/5' : ''}`
68
+ },
69
+ // Thumbnail
70
+ h('td', { className: tdClass },
71
+ row.image
72
+ ? h('div', { className: 'w-9 h-9 lg:w-10 lg:h-10 rounded border border-txa/10 overflow-hidden' },
73
+ h(Img, {
74
+ _model: { src: row.image, alt: row.title, width: 40, height: 40 },
75
+ _fit: 'contain',
76
+ className: 'w-full h-full object-contain'
77
+ })
78
+ )
79
+ : h('div', { className: 'w-9 h-9 lg:w-10 lg:h-10 rounded bg-txa/5' })
80
+ ),
81
+ // Name
82
+ h('td', { className: tdClass },
83
+ h('input', {
84
+ type: 'text',
85
+ value: row.title,
86
+ onInput: e => updateRow(idx, 'title', e.currentTarget.value),
87
+ className: inputClass,
88
+ placeholder: 'Product name'
89
+ })
90
+ ),
91
+ // Brand
92
+ h('td', { className: tdClass },
93
+ h('input', {
94
+ type: 'text',
95
+ value: row.brand,
96
+ onInput: e => updateRow(idx, 'brand', e.currentTarget.value),
97
+ className: inputClass,
98
+ placeholder: 'Brand'
99
+ })
100
+ ),
101
+ // Category (dropdown)
102
+ h('td', { className: tdClass },
103
+ categories.length > 0
104
+ ? h('select', {
105
+ value: row.category,
106
+ onChange: e => updateRow(idx, 'category', e.currentTarget.value),
107
+ className: selectClass
108
+ },
109
+ h('option', { value: '' }, '\u2014'),
110
+ categories.map(cat => h('option', { key: cat, value: cat }, cat))
111
+ )
112
+ : h('input', {
113
+ type: 'text',
114
+ value: row.category,
115
+ onInput: e => updateRow(idx, 'category', e.currentTarget.value),
116
+ className: inputClass,
117
+ placeholder: 'Category'
118
+ })
119
+ ),
120
+ // Price
121
+ h('td', { className: tdClass },
122
+ h('input', {
123
+ type: 'text',
124
+ value: row.price,
125
+ onInput: e => updateRow(idx, 'price', e.currentTarget.value),
126
+ className: `${inputClass} text-right`,
127
+ placeholder: '0'
128
+ })
129
+ ),
130
+ // Old Price
131
+ h('td', { className: tdClass },
132
+ h('input', {
133
+ type: 'text',
134
+ value: row.oldPrice,
135
+ onInput: e => updateRow(idx, 'oldPrice', e.currentTarget.value),
136
+ className: `${inputClass} text-right`,
137
+ placeholder: '\u2014'
138
+ })
139
+ ),
140
+ // Unit label (dropdown)
141
+ h('td', { className: tdClass },
142
+ h('select', {
143
+ value: row.unitLabel,
144
+ onChange: e => updateRow(idx, 'unitLabel', e.currentTarget.value),
145
+ className: selectClass
146
+ },
147
+ h('option', { value: '' }, '\u2014'),
148
+ h('option', { value: 'per 100 gm' }, 'per 100 gm'),
149
+ h('option', { value: 'per 250 gm' }, 'per 250 gm'),
150
+ h('option', { value: 'per 500 gm' }, 'per 500 gm'),
151
+ h('option', { value: 'per 1 kg' }, 'per 1 kg'),
152
+ h('option', { value: 'per piece' }, 'per piece')
153
+ )
154
+ ),
155
+ // Description
156
+ h('td', { className: tdClass },
157
+ h('input', {
158
+ type: 'text',
159
+ value: row.description,
160
+ onInput: e => updateRow(idx, 'description', e.currentTarget.value),
161
+ className: inputClass,
162
+ placeholder: 'Short description'
163
+ })
164
+ ),
165
+ // Image URL
166
+ h('td', { className: tdClass },
167
+ h('input', {
168
+ type: 'text',
169
+ value: row.image,
170
+ onInput: e => updateRow(idx, 'image', e.currentTarget.value),
171
+ className: `${inputClass} w-16 lg:w-20 truncate`,
172
+ placeholder: 'URL',
173
+ title: row.image
174
+ })
175
+ )
176
+ )
177
+ )
178
+ )
179
+ )
180
+ ),
181
+
182
+ // Footer with bulk save
183
+ h('div', { className: 'flex items-center justify-between p-3 lg:p-4 border-t border-txa/10 bg-txa/3' },
184
+ h('span', { className: 'text-xs text-txa/60' },
185
+ changedCount > 0 ? `${changedCount} product${changedCount > 1 ? 's' : ''} modified` : 'No changes'
186
+ ),
187
+ h('div', { className: 'flex gap-2' },
188
+ h('button', {
189
+ type: 'button',
190
+ className: 'clickable border border-txa/30 px-3 py-1.5 rounded-md text-xs lg:text-sm font-semibold hover:bg-txa hover:text-bga transition',
191
+ 'aria-label': 'Discard changes',
192
+ disabled: changedCount === 0
193
+ }, 'Discard'),
194
+ h('button', {
195
+ type: 'button',
196
+ className: `clickable px-3 lg:px-4 py-1.5 lg:py-2 rounded-md text-xs lg:text-sm font-bold transition ${changedCount > 0 ? 'bg-bgb text-txb hover:bg-txb hover:text-bgb' : 'bg-txa/10 text-txa/40 cursor-not-allowed'}`,
197
+ 'aria-label': 'Save all products',
198
+ disabled: changedCount === 0,
199
+ 'data-products': JSON.stringify(rows.filter(r => r.changed))
200
+ }, `Save All${changedCount > 0 ? ` (${changedCount})` : ''}`)
201
+ )
202
+ )
203
+ )
204
+ }
@@ -0,0 +1,28 @@
1
+ import { Asset } from '../molecules/asset.js'
2
+ import { PromoText } from '../molecules/promo-text.js'
3
+ import { ValidationError } from '../routing/error-handler.js'
4
+ import disableTransparency from '../shared/disable-transparency.js'
5
+ import h from '../shared/h.js'
6
+
7
+ const RIGHT_COL = 8
8
+ const LEFT_COL = 2
9
+
10
+ export function PromoBanner ({ _model, _variant, _color, ...props }) {
11
+ const { text, title, asset } = _model
12
+ if (!title || !text || !asset.src) {
13
+ throw new ValidationError()
14
+ }
15
+
16
+ const isRight = _variant === 'right'
17
+
18
+ return h('div', { ...props, className: `relative h-full rounded-2xl overflow-hidden lg:grid lg:grid-cols-12 gap-x-5 bg-bga text-txa ${disableTransparency(_color)}` },
19
+ h('div', { className: 'row-start-1 col-start-1 col-span-12 lg:h-full' },
20
+ h(Asset, { _model, _tall: true })
21
+ ),
22
+ h('div', { className: `row-start-1 col-start-${isRight ? RIGHT_COL : LEFT_COL} col-span-4 flex items-center` },
23
+ h('div', { className: 'lg:rounded-xl lg:overflow-hidden w-full relative bg-bga text-txa' },
24
+ h(PromoText, { _model })
25
+ )
26
+ )
27
+ )
28
+ }
@@ -0,0 +1,23 @@
1
+ import { Asset } from '../molecules/asset.js'
2
+ import { PromoText } from '../molecules/promo-text.js'
3
+ import { ValidationError } from '../routing/error-handler.js'
4
+ import disableTransparency from '../shared/disable-transparency.js'
5
+ import h from '../shared/h.js'
6
+
7
+ export function PromoBottom ({ _model, ...props }) {
8
+ const { title, text, asset } = _model
9
+ if (!title || !text || !asset.src) {
10
+ throw new ValidationError()
11
+ }
12
+
13
+ return h('div', { ...props, className: `relative h-full rounded-2xl overflow-hidden lg:grid lg:grid-cols-2 bg-bga text-txa ${disableTransparency(props._color)}` },
14
+ h('div', { className: 'row-start-1 col-start-1 col-span-2 lg:h-full' },
15
+ h(Asset, { _model, _tall: true })
16
+ ),
17
+ h('div', { className: 'row-start-1 col-start-1 col-span-1 lg:p-6 flex items-end' },
18
+ h('div', { className: 'lg:rounded-xl lg:overflow-hidden w-full relative bg-bga text-txa' },
19
+ h(PromoText, { _model })
20
+ )
21
+ )
22
+ )
23
+ }
@@ -0,0 +1,8 @@
1
+ import { PromoText } from '../molecules/promo-text.js'
2
+ import h from '../shared/h.js'
3
+
4
+ export function PromoButton ({ _model, ...props }) {
5
+ return h('div', { ...props, className: 'bg-bga text-txa rounded-2xl overflow-hidden' },
6
+ h(PromoText, { _model })
7
+ )
8
+ }
@@ -0,0 +1,35 @@
1
+ import { Box } from '../atoms/box.js'
2
+ import { Card } from '../atoms/card.js'
3
+ import { Heading } from '../atoms/heading.js'
4
+ import { LinkText } from '../atoms/link-text.js'
5
+ import { RichText } from '../atoms/rich-text.js'
6
+ import { Time } from '../atoms/time.js'
7
+ import { Asset } from '../molecules/asset.js'
8
+ import { ValidationError } from '../routing/error-handler.js'
9
+ import h from '../shared/h.js'
10
+ import idFromString from '../shared/id-from-string.js'
11
+
12
+ export function PromoCardCover ({ _model, ...props }) {
13
+ const { image, title, links, text, date } = _model
14
+ if (!title || links.length === 0) {
15
+ throw new ValidationError()
16
+ }
17
+
18
+ return h(Card, {
19
+ ...props,
20
+ _model: { value: links[0].value },
21
+ children: h('div',
22
+ { className: 'flex flex-col h-full' },
23
+ Boolean(image?.src) && h('div', { className: 'h-28 overflow-hidden' },
24
+ h(Asset, { _model, _tall: true, _fit: 'cover' })
25
+ ),
26
+ h(Box,
27
+ h(Heading, { _level: 3, id: idFromString(title) },
28
+ h(LinkText, { _model: { name: title, value: links[0].value } })
29
+ ),
30
+ date && h(Time, { _time: date }),
31
+ text && h(RichText, text)
32
+ )
33
+ )
34
+ })
35
+ }
@@ -0,0 +1,33 @@
1
+ import { Box } from '../atoms/box.js'
2
+ import { Card } from '../atoms/card.js'
3
+ import { Heading } from '../atoms/heading.js'
4
+ import { LinkText } from '../atoms/link-text.js'
5
+ import { RichText } from '../atoms/rich-text.js'
6
+ import { Time } from '../atoms/time.js'
7
+ import { Asset } from '../molecules/asset.js'
8
+ import { ValidationError } from '../routing/error-handler.js'
9
+ import h from '../shared/h.js'
10
+ import idFromString from '../shared/id-from-string.js'
11
+
12
+ export function PromoCard ({ _model, ...props }) {
13
+ const { image, video, title, links, text, date } = _model
14
+ if (!title || !links || links.length === 0) {
15
+ throw new ValidationError()
16
+ }
17
+
18
+ return h(Card, {
19
+ ...props,
20
+ _model: { value: links[0].value },
21
+ children: h('div',
22
+ { className: 'flex flex-col h-full' },
23
+ Boolean(image?.src || video?.src) && h(Asset, { _model }),
24
+ h(Box,
25
+ h(Heading, { _level: 3, id: idFromString(title) },
26
+ h(LinkText, { _model: { name: title, value: links[0].value } })
27
+ ),
28
+ date && h(Time, { _time: date }),
29
+ text && h(RichText, text)
30
+ )
31
+ )
32
+ })
33
+ }
@@ -0,0 +1,20 @@
1
+ import { Asset } from '../molecules/asset.js'
2
+ import { PromoText } from '../molecules/promo-text.js'
3
+ import h from '../shared/h.js'
4
+ import { ValidationError } from '../routing/error-handler.js'
5
+
6
+ export function PromoFull ({ _model, _variant, ...props }) {
7
+ const { title, text, asset } = _model
8
+ if (!title || !text || !asset.src) {
9
+ throw new ValidationError()
10
+ }
11
+
12
+ return h('div', { ...props, className: 'bg-bga text-txa rounded-2xl overflow-hidden lg:grid lg:grid-cols-12' },
13
+ h('div', { className: `col-span-6 row-start-1 ${_variant === 'right' ? '' : 'col-start-7'}` },
14
+ h(Asset, { _model, _tall: true })
15
+ ),
16
+ h('div', { className: 'col-span-6 row-start-1 flex items-center' },
17
+ h(PromoText, { _model })
18
+ )
19
+ )
20
+ }
@@ -0,0 +1,22 @@
1
+ import { Asset } from '../molecules/asset.js'
2
+ import { PromoText } from '../molecules/promo-text.js'
3
+ import { ValidationError } from '../routing/error-handler.js'
4
+ import h from '../shared/h.js'
5
+
6
+ export function PromoImage ({ _model, ...props }) {
7
+ const { title, asset } = _model
8
+ if (!title || !asset.src) {
9
+ throw new ValidationError()
10
+ }
11
+
12
+ return h('div', { ...props, className: 'relative h-full rounded-2xl overflow-hidden grid' },
13
+ h('div', { className: 'row-start-1 col-start-1 h-full' },
14
+ h(Asset, { _model, _tall: true })
15
+ ),
16
+ h('div', { className: 'row-start-1 col-start-1 h-full flex items-end' },
17
+ h('div', { className: 'bg-bga text-txa relative w-full' },
18
+ h(PromoText, { _model })
19
+ )
20
+ )
21
+ )
22
+ }