@jotul/jotul-widgets 1.0.4 → 1.1.0

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.
package/README.md CHANGED
@@ -40,7 +40,20 @@ Custom trigger button:
40
40
  ```tsx
41
41
  <JotulWidget
42
42
  type="productPage"
43
- productPageTrigger={({ onOpen, label, className, style }) => (
43
+ trigger={({ onOpen, label, className, style }) => (
44
+ <button type="button" onClick={onOpen} className={className} style={style}>
45
+ {label}
46
+ </button>
47
+ )}
48
+ />
49
+ ```
50
+
51
+ Find dealer drawer with custom trigger:
52
+
53
+ ```tsx
54
+ <JotulWidget
55
+ type="findDealerDrawer"
56
+ trigger={({ onOpen, label, className, style }) => (
44
57
  <button type="button" onClick={onOpen} className={className} style={style}>
45
58
  {label}
46
59
  </button>
@@ -54,3 +67,10 @@ Current behavior:
54
67
  - missing permission -> `Insufficient permissions`
55
68
  - invalid widget type -> `Invalid widget type`
56
69
  - `productPage` renders zipcode input + search button + dealer list
70
+ - `findDealerDrawer` opens the dealer experience in a right-side drawer
71
+
72
+ List/map data behavior:
73
+
74
+ - List uses closest dealers and paginates 10 + 10 via "Show more".
75
+ - Map uses a broader dealer dataset for marker coverage.
76
+ - Widget route calls dealer search with `scope=list` (list) and `scope=map` (map).
@@ -1 +1 @@
1
- .jwi-pointer-events-none{pointer-events:none}.jwi-fixed{position:fixed}.jwi-absolute{position:absolute}.jwi-relative{position:relative}.jwi-inset-0{inset:0}.jwi-inset-x-0{left:0;right:0}.jwi-bottom-0{bottom:0}.jwi-left-0{left:0}.jwi-right-0{right:0}.jwi-right-3{right:.75rem}.jwi-top-3{top:.75rem}.jwi-z-20{z-index:20}.jwi-z-30{z-index:30}.jwi-z-\[1200\]{z-index:1200}.jwi-z-\[9999\]{z-index:9999}.jwi-m-0{margin:0}.jwi--mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.-jwi-mt-px{margin-top:-1px}.jwi-mb-3{margin-bottom:.75rem}.jwi-mr-\[-12px\]{margin-right:-12px}.jwi-mt-2{margin-top:.5rem}.jwi-mt-3{margin-top:.75rem}.jwi-mt-4{margin-top:1rem}.jwi-box-border{box-sizing:border-box}.jwi-flex{display:flex}.jwi-inline-flex{display:inline-flex}.jwi-h-10{height:2.5rem}.jwi-h-12{height:3rem}.jwi-h-14{height:3.5rem}.jwi-h-3\.5{height:.875rem}.jwi-h-4{height:1rem}.jwi-h-5{height:1.25rem}.jwi-h-6{height:1.5rem}.jwi-h-9{height:2.25rem}.jwi-h-\[14px\]{height:14px}.jwi-h-\[18px\]{height:18px}.jwi-h-\[22px\]{height:22px}.jwi-h-\[45\%\]{height:45%}.jwi-h-\[60px\]{height:60px}.jwi-h-\[78vh\]{height:78vh}.jwi-h-\[calc\(78vh-48px\)\]{height:calc(78vh - 48px)}.jwi-h-\[min\(85vh\,860px\)\]{height:min(85vh,860px)}.jwi-h-auto{height:auto}.jwi-h-full{height:100%}.jwi-max-h-\[min\(60vh\,480px\)\]{max-height:min(60vh,480px)}.jwi-max-h-none{max-height:none}.jwi-min-h-0{min-height:0}.jwi-min-h-\[48px\]{min-height:48px}.jwi-min-h-\[56px\]{min-height:56px}.jwi-w-14{width:3.5rem}.jwi-w-2\/3{width:66.666667%}.jwi-w-24{width:6rem}.jwi-w-28{width:7rem}.jwi-w-3\.5{width:.875rem}.jwi-w-4{width:1rem}.jwi-w-48{width:12rem}.jwi-w-5{width:1.25rem}.jwi-w-9{width:2.25rem}.jwi-w-\[14px\]{width:14px}.jwi-w-\[18px\]{width:18px}.jwi-w-\[22px\]{width:22px}.jwi-w-\[40px\]{width:40px}.jwi-w-\[540px\]{width:540px}.jwi-w-\[min\(96vw\,1200px\)\]{width:min(96vw,1200px)}.jwi-w-auto{width:auto}.jwi-w-fit{width:-moz-fit-content;width:fit-content}.jwi-w-full{width:100%}.jwi-min-w-0{min-width:0}.jwi-max-w-\[220px\]{max-width:220px}.jwi-max-w-\[70\%\]{max-width:70%}.jwi-max-w-\[calc\(100\%-5rem\)\]{max-width:calc(100% - 5rem)}.jwi-max-w-full{max-width:100%}.jwi-flex-1{flex:1 1 0%}.jwi-flex-shrink-0,.jwi-shrink-0{flex-shrink:0}.jwi-scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes jwi-pulse{50%{opacity:.5}}.jwi-animate-pulse{animation:jwi-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes jwi-spin{to{transform:rotate(1turn)}}.jwi-animate-spin{animation:jwi-spin 1s linear infinite}.jwi-cursor-pointer{cursor:pointer}.jwi-resize-y{resize:vertical}.jwi-flex-row{flex-direction:row}.jwi-flex-col{flex-direction:column}.jwi-items-start{align-items:flex-start}.jwi-items-center{align-items:center}.jwi-items-stretch{align-items:stretch}.jwi-justify-end{justify-content:flex-end}.jwi-justify-center{justify-content:center}.jwi-justify-between{justify-content:space-between}.jwi-gap-0\.5{gap:.125rem}.jwi-gap-1{gap:.25rem}.jwi-gap-1\.5{gap:.375rem}.jwi-gap-2{gap:.5rem}.jwi-gap-3{gap:.75rem}.jwi-gap-4{gap:1rem}.jwi-space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.jwi-self-stretch{align-self:stretch}.jwi-overflow-hidden{overflow:hidden}.jwi-overflow-y-auto{overflow-y:auto}.jwi-overscroll-y-contain{overscroll-behavior-y:contain}.jwi-whitespace-nowrap{white-space:nowrap}.jwi-break-all{word-break:break-all}.jwi-rounded-\[10px\]{border-radius:10px}.jwi-rounded-full{border-radius:9999px}.jwi-rounded-b-\[10px\]{border-bottom-right-radius:10px;border-bottom-left-radius:10px}.jwi-rounded-t-\[10px\]{border-top-left-radius:10px;border-top-right-radius:10px}.jwi-rounded-t-\[16px\]{border-top-left-radius:16px;border-top-right-radius:16px}.jwi-border{border-width:1px}.jwi-border-0{border-width:0}.jwi-border-b{border-bottom-width:1px}.jwi-border-t{border-top-width:1px}.jwi-border-t-0{border-top-width:0}.jwi-border-\[\#b7e5c2\]{--tw-border-opacity:1;border-color:rgb(183 229 194/var(--tw-border-opacity,1))}.jwi-border-\[\#d8d2c7\]{--tw-border-opacity:1;border-color:rgb(216 210 199/var(--tw-border-opacity,1))}.jwi-border-\[\#e6e1d7\]{--tw-border-opacity:1;border-color:rgb(230 225 215/var(--tw-border-opacity,1))}.jwi-border-\[\#ef2b18\]{--tw-border-opacity:1;border-color:rgb(239 43 24/var(--tw-border-opacity,1))}.jwi-border-\[\#f0c7c2\]{--tw-border-opacity:1;border-color:rgb(240 199 194/var(--tw-border-opacity,1))}.jwi-bg-\[\#FCFCFC\]{--tw-bg-opacity:1;background-color:rgb(252 252 252/var(--tw-bg-opacity,1))}.jwi-bg-\[\#ece8df\]{--tw-bg-opacity:1;background-color:rgb(236 232 223/var(--tw-bg-opacity,1))}.jwi-bg-\[\#eefbf2\]{--tw-bg-opacity:1;background-color:rgb(238 251 242/var(--tw-bg-opacity,1))}.jwi-bg-\[\#ef2b18\]{--tw-bg-opacity:1;background-color:rgb(239 43 24/var(--tw-bg-opacity,1))}.jwi-bg-\[\#f0f0f0\]{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity,1))}.jwi-bg-\[\#f7f5ef\]{--tw-bg-opacity:1;background-color:rgb(247 245 239/var(--tw-bg-opacity,1))}.jwi-bg-\[\#fbf3db\]{--tw-bg-opacity:1;background-color:rgb(251 243 219/var(--tw-bg-opacity,1))}.jwi-bg-\[\#fff3f1\]{--tw-bg-opacity:1;background-color:rgb(255 243 241/var(--tw-bg-opacity,1))}.jwi-bg-black\/45{background-color:rgba(0,0,0,.45)}.jwi-bg-transparent{background-color:transparent}.jwi-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.jwi-bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.jwi-from-white{--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.jwi-to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.jwi-p-0{padding:0}.jwi-p-1{padding:.25rem}.jwi-p-4{padding:1rem}.jwi-p-6{padding:1.5rem}.jwi-px-2\.5{padding-left:.625rem;padding-right:.625rem}.jwi-px-4{padding-left:1rem;padding-right:1rem}.jwi-px-5{padding-left:1.25rem;padding-right:1.25rem}.jwi-px-6{padding-left:1.5rem;padding-right:1.5rem}.jwi-px-7{padding-left:1.75rem;padding-right:1.75rem}.jwi-py-1{padding-top:.25rem;padding-bottom:.25rem}.jwi-py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.jwi-py-3{padding-top:.75rem;padding-bottom:.75rem}.jwi-py-4{padding-top:1rem;padding-bottom:1rem}.jwi-py-8{padding-top:2rem;padding-bottom:2rem}.jwi-pb-24{padding-bottom:6rem}.jwi-pb-3{padding-bottom:.75rem}.jwi-pl-5{padding-left:1.25rem}.jwi-pr-1{padding-right:.25rem}.jwi-pr-3{padding-right:.75rem}.jwi-pr-\[12px\]{padding-right:12px}.jwi-pt-3{padding-top:.75rem}.jwi-text-left{text-align:left}.jwi-font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.jwi-text-\[13px\]{font-size:13px}.jwi-text-base{font-size:1rem;line-height:1.5rem}.jwi-text-sm{font-size:.875rem;line-height:1.25rem}.jwi-text-xl{font-size:1.25rem;line-height:1.75rem}.jwi-text-xs{font-size:.75rem;line-height:1rem}.jwi-font-medium{font-weight:500}.jwi-font-normal{font-weight:400}.jwi-font-semibold{font-weight:600}.jwi-tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.jwi-leading-\[1\.35\]{line-height:1.35}.jwi-leading-\[1\.4\]{line-height:1.4}.jwi-leading-none{line-height:1}.jwi-leading-snug{line-height:1.375}.jwi-text-\[\#000000\]{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.jwi-text-\[\#111111\]{--tw-text-opacity:1;color:rgb(17 17 17/var(--tw-text-opacity,1))}.jwi-text-\[\#1b5e20\]{--tw-text-opacity:1;color:rgb(27 94 32/var(--tw-text-opacity,1))}.jwi-text-\[\#555555\]{--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity,1))}.jwi-text-\[\#767676\]{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.jwi-text-\[\#8f2d21\]{--tw-text-opacity:1;color:rgb(143 45 33/var(--tw-text-opacity,1))}.jwi-text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.jwi-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.jwi-opacity-25{opacity:.25}.jwi-opacity-75{opacity:.75}.jwi-opacity-95{opacity:.95}.jwi-shadow-\[0_-12px_36px_rgba\(0\,0\,0\,0\.22\)\]{--tw-shadow:0 -12px 36px rgba(0,0,0,.22);--tw-shadow-colored:0 -12px 36px var(--tw-shadow-color)}.jwi-shadow-\[0_-12px_36px_rgba\(0\,0\,0\,0\.22\)\],.jwi-shadow-\[0_-6px_20px_rgba\(0\,0\,0\,0\.12\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_-6px_20px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow:0 -6px 20px rgba(0,0,0,.12);--tw-shadow-colored:0 -6px 20px var(--tw-shadow-color)}.jwi-shadow-\[0_1px_2px_rgba\(17\,17\,17\,0\.03\)\]{--tw-shadow:0 1px 2px hsla(0,0%,7%,.03);--tw-shadow-colored:0 1px 2px var(--tw-shadow-color)}.jwi-shadow-\[0_1px_2px_rgba\(17\,17\,17\,0\.03\)\],.jwi-shadow-\[0_20px_60px_rgba\(0\,0\,0\,0\.25\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_20px_60px_rgba\(0\,0\,0\,0\.25\)\]{--tw-shadow:0 20px 60px rgba(0,0,0,.25);--tw-shadow-colored:0 20px 60px var(--tw-shadow-color)}.jwi-shadow-\[0_2px_8px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow:0 2px 8px rgba(0,0,0,.12);--tw-shadow-colored:0 2px 8px var(--tw-shadow-color)}.jwi-shadow-\[0_2px_8px_rgba\(0\,0\,0\,0\.12\)\],.jwi-shadow-\[0_8px_24px_rgba\(17\,17\,17\,0\.08\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_8px_24px_rgba\(17\,17\,17\,0\.08\)\]{--tw-shadow:0 8px 24px hsla(0,0%,7%,.08);--tw-shadow-colored:0 8px 24px var(--tw-shadow-color)}.jwi-outline-none{outline:2px solid transparent;outline-offset:2px}.jwi-transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.placeholder\:jwi-text-\[13px\]::-moz-placeholder{font-size:13px}.placeholder\:jwi-text-\[13px\]::placeholder{font-size:13px}.placeholder\:jwi-text-\[\#767676\]::-moz-placeholder{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.placeholder\:jwi-text-\[\#767676\]::placeholder{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.hover\:jwi-bg-\[\#d92817\]:hover{--tw-bg-opacity:1;background-color:rgb(217 40 23/var(--tw-bg-opacity,1))}.hover\:jwi-bg-\[\#f7f5f0\]:hover{--tw-bg-opacity:1;background-color:rgb(247 245 240/var(--tw-bg-opacity,1))}.hover\:jwi-text-\[\#444444\]:hover{--tw-text-opacity:1;color:rgb(68 68 68/var(--tw-text-opacity,1))}.hover\:jwi-underline:hover{text-decoration-line:underline}.focus\:jwi-border-\[\#111111\]:focus{--tw-border-opacity:1;border-color:rgb(17 17 17/var(--tw-border-opacity,1))}.disabled\:jwi-cursor-wait:disabled{cursor:wait}.disabled\:hover\:jwi-bg-\[\#ef2b18\]:hover:disabled{--tw-bg-opacity:1;background-color:rgb(239 43 24/var(--tw-bg-opacity,1))}@media (min-width:768px){.md\:jwi-h-full{height:100%}.md\:jwi-w-\[48\%\]{width:48%}.md\:jwi-w-\[52\%\]{width:52%}.md\:jwi-max-w-\[220px\]{max-width:220px}.md\:jwi-flex-row{flex-direction:row}.md\:jwi-items-center{align-items:center}.md\:jwi-justify-between{justify-content:space-between}.md\:jwi-border-r{border-right-width:1px}.md\:jwi-border-\[\#ece8df\]{--tw-border-opacity:1;border-color:rgb(236 232 223/var(--tw-border-opacity,1))}.md\:jwi-text-\[14px\]{font-size:14px}.md\:jwi-text-base{font-size:1rem;line-height:1.5rem}.md\:placeholder\:jwi-text-\[14px\]::-moz-placeholder{font-size:14px}.md\:placeholder\:jwi-text-\[14px\]::placeholder{font-size:14px}}
1
+ .jwi-pointer-events-none{pointer-events:none}.jwi-fixed{position:fixed}.jwi-absolute{position:absolute}.jwi-relative{position:relative}.jwi-inset-0{inset:0}.jwi-inset-x-0{left:0;right:0}.jwi-bottom-0{bottom:0}.jwi-left-0{left:0}.jwi-right-0{right:0}.jwi-right-3{right:.75rem}.jwi-top-0{top:0}.jwi-top-3{top:.75rem}.jwi-z-20{z-index:20}.jwi-z-30{z-index:30}.jwi-z-\[1200\]{z-index:1200}.jwi-z-\[9998\]{z-index:9998}.jwi-z-\[9999\]{z-index:9999}.jwi-m-0{margin:0}.jwi--mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.-jwi-mt-px{margin-top:-1px}.jwi-mb-3{margin-bottom:.75rem}.jwi-mr-\[-12px\]{margin-right:-12px}.jwi-mt-2{margin-top:.5rem}.jwi-mt-3{margin-top:.75rem}.jwi-mt-4{margin-top:1rem}.jwi-box-border{box-sizing:border-box}.jwi-flex{display:flex}.jwi-inline-flex{display:inline-flex}.jwi-h-10{height:2.5rem}.jwi-h-12{height:3rem}.jwi-h-14{height:3.5rem}.jwi-h-3\.5{height:.875rem}.jwi-h-4{height:1rem}.jwi-h-5{height:1.25rem}.jwi-h-6{height:1.5rem}.jwi-h-9{height:2.25rem}.jwi-h-\[14px\]{height:14px}.jwi-h-\[18px\]{height:18px}.jwi-h-\[22px\]{height:22px}.jwi-h-\[45\%\]{height:45%}.jwi-h-\[60px\]{height:60px}.jwi-h-\[78vh\]{height:78vh}.jwi-h-\[calc\(78vh-48px\)\]{height:calc(78vh - 48px)}.jwi-h-\[min\(85vh\,860px\)\]{height:min(85vh,860px)}.jwi-h-auto{height:auto}.jwi-h-full{height:100%}.jwi-max-h-\[min\(60vh\,480px\)\]{max-height:min(60vh,480px)}.jwi-max-h-none{max-height:none}.jwi-min-h-0{min-height:0}.jwi-min-h-\[48px\]{min-height:48px}.jwi-min-h-\[56px\]{min-height:56px}.jwi-w-1\/2{width:50%}.jwi-w-14{width:3.5rem}.jwi-w-2\/3{width:66.666667%}.jwi-w-24{width:6rem}.jwi-w-28{width:7rem}.jwi-w-3\.5{width:.875rem}.jwi-w-4{width:1rem}.jwi-w-48{width:12rem}.jwi-w-5{width:1.25rem}.jwi-w-9{width:2.25rem}.jwi-w-\[14px\]{width:14px}.jwi-w-\[18px\]{width:18px}.jwi-w-\[22px\]{width:22px}.jwi-w-\[40px\]{width:40px}.jwi-w-\[540px\]{width:540px}.jwi-w-\[min\(100vw\,1200px\)\]{width:min(100vw,1200px)}.jwi-w-\[min\(96vw\,1200px\)\]{width:min(96vw,1200px)}.jwi-w-auto{width:auto}.jwi-w-fit{width:-moz-fit-content;width:fit-content}.jwi-w-full{width:100%}.jwi-min-w-0{min-width:0}.jwi-max-w-\[220px\]{max-width:220px}.jwi-max-w-\[70\%\]{max-width:70%}.jwi-max-w-\[calc\(100\%-5rem\)\]{max-width:calc(100% - 5rem)}.jwi-max-w-full{max-width:100%}.jwi-flex-1{flex:1 1 0%}.jwi-flex-shrink-0,.jwi-shrink-0{flex-shrink:0}.jwi-scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes jwi-pulse{50%{opacity:.5}}.jwi-animate-pulse{animation:jwi-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes jwi-spin{to{transform:rotate(1turn)}}.jwi-animate-spin{animation:jwi-spin 1s linear infinite}.jwi-cursor-pointer{cursor:pointer}.jwi-resize-y{resize:vertical}.jwi-flex-row{flex-direction:row}.jwi-flex-col{flex-direction:column}.jwi-items-start{align-items:flex-start}.jwi-items-center{align-items:center}.jwi-items-stretch{align-items:stretch}.jwi-justify-end{justify-content:flex-end}.jwi-justify-center{justify-content:center}.jwi-justify-between{justify-content:space-between}.jwi-gap-0\.5{gap:.125rem}.jwi-gap-1{gap:.25rem}.jwi-gap-1\.5{gap:.375rem}.jwi-gap-2{gap:.5rem}.jwi-gap-3{gap:.75rem}.jwi-gap-4{gap:1rem}.jwi-space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.jwi-self-stretch{align-self:stretch}.jwi-overflow-hidden{overflow:hidden}.jwi-overflow-y-auto{overflow-y:auto}.jwi-overscroll-y-contain{overscroll-behavior-y:contain}.jwi-whitespace-nowrap{white-space:nowrap}.jwi-break-all{word-break:break-all}.jwi-rounded-\[10px\]{border-radius:10px}.jwi-rounded-full{border-radius:9999px}.jwi-rounded-md{border-radius:.375rem}.jwi-rounded-b-\[10px\]{border-bottom-right-radius:10px;border-bottom-left-radius:10px}.jwi-rounded-t-\[10px\]{border-top-left-radius:10px;border-top-right-radius:10px}.jwi-rounded-t-\[16px\]{border-top-left-radius:16px;border-top-right-radius:16px}.jwi-border{border-width:1px}.jwi-border-0{border-width:0}.jwi-border-b{border-bottom-width:1px}.jwi-border-t{border-top-width:1px}.jwi-border-t-0{border-top-width:0}.jwi-border-\[\#b7e5c2\]{--tw-border-opacity:1;border-color:rgb(183 229 194/var(--tw-border-opacity,1))}.jwi-border-\[\#d8d2c7\]{--tw-border-opacity:1;border-color:rgb(216 210 199/var(--tw-border-opacity,1))}.jwi-border-\[\#e6e1d7\]{--tw-border-opacity:1;border-color:rgb(230 225 215/var(--tw-border-opacity,1))}.jwi-border-\[\#ef2b18\]{--tw-border-opacity:1;border-color:rgb(239 43 24/var(--tw-border-opacity,1))}.jwi-border-\[\#f0c7c2\]{--tw-border-opacity:1;border-color:rgb(240 199 194/var(--tw-border-opacity,1))}.jwi-bg-\[\#FCFCFC\]{--tw-bg-opacity:1;background-color:rgb(252 252 252/var(--tw-bg-opacity,1))}.jwi-bg-\[\#e8eef1\]{--tw-bg-opacity:1;background-color:rgb(232 238 241/var(--tw-bg-opacity,1))}.jwi-bg-\[\#ece8df\]{--tw-bg-opacity:1;background-color:rgb(236 232 223/var(--tw-bg-opacity,1))}.jwi-bg-\[\#eefbf2\]{--tw-bg-opacity:1;background-color:rgb(238 251 242/var(--tw-bg-opacity,1))}.jwi-bg-\[\#ef2b18\]{--tw-bg-opacity:1;background-color:rgb(239 43 24/var(--tw-bg-opacity,1))}.jwi-bg-\[\#f0f0f0\]{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity,1))}.jwi-bg-\[\#f7f5ef\]{--tw-bg-opacity:1;background-color:rgb(247 245 239/var(--tw-bg-opacity,1))}.jwi-bg-\[\#fbf3db\]{--tw-bg-opacity:1;background-color:rgb(251 243 219/var(--tw-bg-opacity,1))}.jwi-bg-\[\#fff3f1\]{--tw-bg-opacity:1;background-color:rgb(255 243 241/var(--tw-bg-opacity,1))}.jwi-bg-black\/35{background-color:rgba(0,0,0,.35)}.jwi-bg-black\/45{background-color:rgba(0,0,0,.45)}.jwi-bg-transparent{background-color:transparent}.jwi-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.jwi-bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.jwi-from-white{--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.jwi-to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.jwi-p-0{padding:0}.jwi-p-1{padding:.25rem}.jwi-p-4{padding:1rem}.jwi-p-6{padding:1.5rem}.jwi-px-2\.5{padding-left:.625rem;padding-right:.625rem}.jwi-px-3{padding-left:.75rem;padding-right:.75rem}.jwi-px-4{padding-left:1rem;padding-right:1rem}.jwi-px-5{padding-left:1.25rem;padding-right:1.25rem}.jwi-px-6{padding-left:1.5rem;padding-right:1.5rem}.jwi-px-7{padding-left:1.75rem;padding-right:1.75rem}.jwi-py-1{padding-top:.25rem;padding-bottom:.25rem}.jwi-py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.jwi-py-2{padding-top:.5rem;padding-bottom:.5rem}.jwi-py-3{padding-top:.75rem;padding-bottom:.75rem}.jwi-py-4{padding-top:1rem;padding-bottom:1rem}.jwi-py-8{padding-top:2rem;padding-bottom:2rem}.jwi-pb-24{padding-bottom:6rem}.jwi-pb-3{padding-bottom:.75rem}.jwi-pl-5{padding-left:1.25rem}.jwi-pr-1{padding-right:.25rem}.jwi-pr-3{padding-right:.75rem}.jwi-pr-\[12px\]{padding-right:12px}.jwi-pt-3{padding-top:.75rem}.jwi-text-left{text-align:left}.jwi-font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.jwi-text-\[13px\]{font-size:13px}.jwi-text-base{font-size:1rem;line-height:1.5rem}.jwi-text-sm{font-size:.875rem;line-height:1.25rem}.jwi-text-xl{font-size:1.25rem;line-height:1.75rem}.jwi-text-xs{font-size:.75rem;line-height:1rem}.jwi-font-medium{font-weight:500}.jwi-font-normal{font-weight:400}.jwi-font-semibold{font-weight:600}.jwi-tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.jwi-leading-\[1\.35\]{line-height:1.35}.jwi-leading-\[1\.4\]{line-height:1.4}.jwi-leading-none{line-height:1}.jwi-leading-snug{line-height:1.375}.jwi-text-\[\#000000\]{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.jwi-text-\[\#111111\]{--tw-text-opacity:1;color:rgb(17 17 17/var(--tw-text-opacity,1))}.jwi-text-\[\#1b5e20\]{--tw-text-opacity:1;color:rgb(27 94 32/var(--tw-text-opacity,1))}.jwi-text-\[\#555555\]{--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity,1))}.jwi-text-\[\#767676\]{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.jwi-text-\[\#8f2d21\]{--tw-text-opacity:1;color:rgb(143 45 33/var(--tw-text-opacity,1))}.jwi-text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.jwi-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.jwi-opacity-25{opacity:.25}.jwi-opacity-75{opacity:.75}.jwi-opacity-95{opacity:.95}.jwi-shadow-\[-8px_0_32px_rgba\(0\,0\,0\,0\.2\)\]{--tw-shadow:-8px 0 32px rgba(0,0,0,.2);--tw-shadow-colored:-8px 0 32px var(--tw-shadow-color)}.jwi-shadow-\[-8px_0_32px_rgba\(0\,0\,0\,0\.2\)\],.jwi-shadow-\[0_-12px_36px_rgba\(0\,0\,0\,0\.22\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_-12px_36px_rgba\(0\,0\,0\,0\.22\)\]{--tw-shadow:0 -12px 36px rgba(0,0,0,.22);--tw-shadow-colored:0 -12px 36px var(--tw-shadow-color)}.jwi-shadow-\[0_-6px_20px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow:0 -6px 20px rgba(0,0,0,.12);--tw-shadow-colored:0 -6px 20px var(--tw-shadow-color)}.jwi-shadow-\[0_-6px_20px_rgba\(0\,0\,0\,0\.12\)\],.jwi-shadow-\[0_1px_2px_rgba\(17\,17\,17\,0\.03\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_1px_2px_rgba\(17\,17\,17\,0\.03\)\]{--tw-shadow:0 1px 2px hsla(0,0%,7%,.03);--tw-shadow-colored:0 1px 2px var(--tw-shadow-color)}.jwi-shadow-\[0_20px_60px_rgba\(0\,0\,0\,0\.25\)\]{--tw-shadow:0 20px 60px rgba(0,0,0,.25);--tw-shadow-colored:0 20px 60px var(--tw-shadow-color)}.jwi-shadow-\[0_20px_60px_rgba\(0\,0\,0\,0\.25\)\],.jwi-shadow-\[0_2px_8px_rgba\(0\,0\,0\,0\.12\)\]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-shadow-\[0_2px_8px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow:0 2px 8px rgba(0,0,0,.12);--tw-shadow-colored:0 2px 8px var(--tw-shadow-color)}.jwi-shadow-\[0_8px_24px_rgba\(17\,17\,17\,0\.08\)\]{--tw-shadow:0 8px 24px hsla(0,0%,7%,.08);--tw-shadow-colored:0 8px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.jwi-outline-none{outline:2px solid transparent;outline-offset:2px}.jwi-transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.placeholder\:jwi-text-\[13px\]::-moz-placeholder{font-size:13px}.placeholder\:jwi-text-\[13px\]::placeholder{font-size:13px}.placeholder\:jwi-text-\[\#767676\]::-moz-placeholder{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.placeholder\:jwi-text-\[\#767676\]::placeholder{--tw-text-opacity:1;color:rgb(118 118 118/var(--tw-text-opacity,1))}.hover\:jwi-bg-\[\#d92817\]:hover{--tw-bg-opacity:1;background-color:rgb(217 40 23/var(--tw-bg-opacity,1))}.hover\:jwi-bg-\[\#f7f5f0\]:hover{--tw-bg-opacity:1;background-color:rgb(247 245 240/var(--tw-bg-opacity,1))}.hover\:jwi-text-\[\#444444\]:hover{--tw-text-opacity:1;color:rgb(68 68 68/var(--tw-text-opacity,1))}.hover\:jwi-underline:hover{text-decoration-line:underline}.focus\:jwi-border-\[\#111111\]:focus{--tw-border-opacity:1;border-color:rgb(17 17 17/var(--tw-border-opacity,1))}.disabled\:jwi-cursor-wait:disabled{cursor:wait}.disabled\:hover\:jwi-bg-\[\#ef2b18\]:hover:disabled{--tw-bg-opacity:1;background-color:rgb(239 43 24/var(--tw-bg-opacity,1))}@media (min-width:768px){.md\:jwi-h-full{height:100%}.md\:jwi-w-\[48\%\]{width:48%}.md\:jwi-w-\[52\%\]{width:52%}.md\:jwi-max-w-\[220px\]{max-width:220px}.md\:jwi-flex-row{flex-direction:row}.md\:jwi-items-center{align-items:center}.md\:jwi-justify-between{justify-content:space-between}.md\:jwi-border-r{border-right-width:1px}.md\:jwi-border-\[\#ece8df\]{--tw-border-opacity:1;border-color:rgb(236 232 223/var(--tw-border-opacity,1))}.md\:jwi-text-\[14px\]{font-size:14px}.md\:jwi-text-base{font-size:1rem;line-height:1.5rem}.md\:placeholder\:jwi-text-\[14px\]::-moz-placeholder{font-size:14px}.md\:placeholder\:jwi-text-\[14px\]::placeholder{font-size:14px}}
@@ -4,5 +4,5 @@ import type { JotulWidgetProps } from './types';
4
4
  export { DEFAULT_WIDGET_LOCALE_TAG, normalizeWidgetLocale, resolveWidgetUiLocale, } from './i18n/widgetStrings';
5
5
  export type { JotulWidgetLocale } from './i18n/widgetStrings';
6
6
  export { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode, };
7
- export type { CheckWidgetAuthorizationOptions, DealerSearchResponse, JotulWidgetButtonStyling, JotulWidgetProps, ProductPageTriggerRenderProps, JotulWidgetStyling, JotulWidgetType, WidgetAuthClientResponse, } from './types';
8
- export declare function JotulWidget({ type, endpoint, className, productName, locale: localeProp, market: marketProp, brands, styling, productPageTrigger, }: JotulWidgetProps): import("react/jsx-runtime").JSX.Element;
7
+ export type { CheckWidgetAuthorizationOptions, DealerSearchResponse, JotulWidgetButtonStyling, JotulWidgetProps, WidgetTriggerRenderProps, JotulWidgetStyling, JotulWidgetType, WidgetAuthClientResponse, } from './types';
8
+ export declare function JotulWidget({ type, endpoint, className, productName, locale: localeProp, market: marketProp, brands, styling, trigger, productPageTrigger, findDealerDrawerTrigger, }: JotulWidgetProps): import("react/jsx-runtime").JSX.Element;
@@ -3,11 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import './JotulWidget.css';
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
5
  import { FinderSearchRowSkeleton } from './components/FinderSearchRowSkeleton';
6
+ import { DealerCardSkeleton } from './components/DealerCardSkeleton';
6
7
  import { ProductPageWidget } from './widgets/ProductPageWidget';
8
+ import { FindDealerDrawerWidget } from './components/FindDealerDrawerWidget';
7
9
  import { ButtonSpinner } from './icons/ButtonSpinner';
8
10
  import { DEFAULT_WIDGET_LOCALE_TAG, resolveWidgetUiLocale, WIDGET_STRINGS, } from './i18n/widgetStrings';
9
11
  import { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode, } from './api';
10
- import { createInquiryFormValues, getSafeWidgetErrorMessage, isValidEmail, isWidgetType, renderReadyState, } from './utils';
12
+ import { createInquiryFormValues, getSafeWidgetErrorMessage, isDealerInSearchResult, isValidEmail, isWidgetType, renderReadyState, } from './utils';
11
13
  import { getWidgetPrimaryButtonPresentation } from './utils/widgetPrimaryButtonPresentation';
12
14
  export { DEFAULT_WIDGET_LOCALE_TAG, normalizeWidgetLocale, resolveWidgetUiLocale, } from './i18n/widgetStrings';
13
15
  export { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode, };
@@ -19,7 +21,14 @@ const GEOLOCATION_OPTIONS = {
19
21
  timeout: 15000,
20
22
  maximumAge: 300000,
21
23
  };
22
- export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, productName, locale: localeProp, market: marketProp, brands, styling, productPageTrigger, }) {
24
+ const MARKET_FALLBACK_CENTER = {
25
+ NO: [59.9139, 10.7522],
26
+ SE: [59.3293, 18.0686],
27
+ DK: [55.6761, 12.5683],
28
+ FI: [60.1699, 24.9384],
29
+ DE: [52.52, 13.405],
30
+ };
31
+ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, productName, locale: localeProp, market: marketProp, brands, styling, trigger, productPageTrigger, findDealerDrawerTrigger, }) {
23
32
  const resolvedUiLocale = useMemo(() => resolveWidgetUiLocale(localeProp, marketProp), [localeProp, marketProp]);
24
33
  const t = WIDGET_STRINGS[resolvedUiLocale];
25
34
  const apiLocaleTag = useMemo(() => (localeProp?.trim() ? localeProp.trim() : DEFAULT_WIDGET_LOCALE_TAG), [localeProp]);
@@ -36,6 +45,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
36
45
  const [auth, setAuth] = useState(null);
37
46
  const [isLoading, setIsLoading] = useState(false);
38
47
  const [searchResult, setSearchResult] = useState(null);
48
+ const [mapSearchResult, setMapSearchResult] = useState(null);
39
49
  const [isSearching, setIsSearching] = useState(false);
40
50
  const [isSearchingSuggestions, setIsSearchingSuggestions] = useState(false);
41
51
  const [locationError, setLocationError] = useState(null);
@@ -60,13 +70,20 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
60
70
  setLocationError(null);
61
71
  setIsSearching(true);
62
72
  try {
63
- const result = await searchDealersByCoordinates(latitude, longitude, dealerSearchOptions);
73
+ const result = await searchDealersByCoordinates(latitude, longitude, dealerSearchOptions, 'list');
64
74
  setSearchResult(result);
75
+ const mapResult = await searchDealersByCoordinates(latitude, longitude, dealerSearchOptions, 'map');
76
+ setMapSearchResult(mapResult);
65
77
  }
66
78
  finally {
67
79
  setIsSearching(false);
68
80
  }
69
81
  }, [dealerSearchOptions]);
82
+ const runFallbackDealerSearch = useCallback(() => {
83
+ const fallbackCenter = (apiMarket != null ? MARKET_FALLBACK_CENTER[apiMarket] : undefined) ??
84
+ MARKET_FALLBACK_CENTER.NO;
85
+ return runDealerSearchByCoordinates(fallbackCenter[0], fallbackCenter[1]);
86
+ }, [apiMarket, runDealerSearchByCoordinates]);
70
87
  const runLocationSearch = useCallback(() => {
71
88
  const messages = WIDGET_STRINGS[resolvedUiLocale];
72
89
  setShouldAutoLocateAfterAuth(false);
@@ -77,6 +94,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
77
94
  setIsSuggestionListOpen(false);
78
95
  if (typeof navigator === 'undefined' || !navigator.geolocation) {
79
96
  setLocationError(messages.locationUnavailableBrowser);
97
+ void runFallbackDealerSearch();
80
98
  return;
81
99
  }
82
100
  setIsSearching(true);
@@ -99,8 +117,9 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
99
117
  else {
100
118
  setLocationError(messages.locationGenericFailure);
101
119
  }
120
+ void runFallbackDealerSearch();
102
121
  }, GEOLOCATION_OPTIONS);
103
- }, [resolvedUiLocale, runDealerSearchByCoordinates]);
122
+ }, [resolvedUiLocale, runDealerSearchByCoordinates, runFallbackDealerSearch]);
104
123
  useEffect(() => {
105
124
  const query = locationQuery.trim();
106
125
  if (query.length < 3) {
@@ -131,7 +150,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
131
150
  };
132
151
  }, [apiLocaleTag, apiMarket, dealerSearchOptions, locationQuery]);
133
152
  useEffect(() => {
134
- if (type === 'productPage' && !isOpen)
153
+ if ((type === 'productPage' || type === 'findDealerDrawer') && !isOpen)
135
154
  return;
136
155
  if (auth != null)
137
156
  return;
@@ -152,7 +171,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
152
171
  useEffect(() => {
153
172
  if (!shouldAutoLocateAfterAuth)
154
173
  return;
155
- if (type !== 'productPage' || !isOpen)
174
+ if ((type !== 'productPage' && type !== 'findDealerDrawer') || !isOpen)
156
175
  return;
157
176
  if (auth == null || isLoading)
158
177
  return;
@@ -174,6 +193,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
174
193
  const openProductPageWidget = useCallback(() => {
175
194
  setLocationError(null);
176
195
  setSearchResult(null);
196
+ setMapSearchResult(null);
177
197
  setLocationQuery('');
178
198
  setLocationSuggestions([]);
179
199
  setIsSuggestionListOpen(false);
@@ -186,43 +206,72 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
186
206
  setShouldAutoLocateAfterAuth(true);
187
207
  }
188
208
  }, [auth, isLoading, runLocationSearch]);
209
+ const closeDealerWidget = useCallback(() => {
210
+ setIsOpen(false);
211
+ setLocationSuggestions([]);
212
+ setIsSuggestionListOpen(false);
213
+ setIsSearchingSuggestions(false);
214
+ setMapSearchResult(null);
215
+ }, []);
189
216
  const shellClass = 'jwi-box-border jwi-flex jwi-w-[540px] jwi-max-w-full jwi-flex-col jwi-font-sans jwi-text-[#111111]';
190
217
  const rootClass = className != null && className !== '' ? `${shellClass} ${className}` : shellClass;
191
218
  if (typeState !== 'typeReady') {
192
219
  return _jsx("div", { className: rootClass, children: t.invalidWidgetTypeError });
193
220
  }
221
+ const resolvedTrigger = trigger ?? productPageTrigger ?? findDealerDrawerTrigger;
222
+ const resolvedTriggerNode = typeof resolvedTrigger === 'function'
223
+ ? resolvedTrigger({
224
+ onOpen: openProductPageWidget,
225
+ isLoading,
226
+ label: t.findDealer,
227
+ className: heroPrimaryButton.className,
228
+ style: heroPrimaryButton.style,
229
+ })
230
+ : resolvedTrigger;
231
+ const renderTrigger = (alwaysClickable = false) => {
232
+ if (resolvedTriggerNode == null) {
233
+ return (_jsx("button", { type: "button", onClick: openProductPageWidget, className: heroPrimaryButton.className, style: heroPrimaryButton.style, children: t.findDealer }));
234
+ }
235
+ if (typeof resolvedTrigger === 'function' && !alwaysClickable) {
236
+ return resolvedTriggerNode;
237
+ }
238
+ return (_jsx("div", { role: "button", tabIndex: 0, onClick: openProductPageWidget, onKeyDown: (event) => {
239
+ if (event.key === 'Enter' || event.key === ' ') {
240
+ event.preventDefault();
241
+ openProductPageWidget();
242
+ }
243
+ }, children: resolvedTriggerNode }));
244
+ };
194
245
  if (widgetType === 'productPage' && !isOpen) {
195
- const trigger = typeof productPageTrigger === 'function'
196
- ? productPageTrigger({
197
- onOpen: openProductPageWidget,
198
- isLoading,
199
- label: t.findDealer,
200
- className: heroPrimaryButton.className,
201
- style: heroPrimaryButton.style,
202
- })
203
- : productPageTrigger;
204
- return (_jsx("div", { className: rootClass, children: _jsx("div", { className: "jwi-flex jwi-py-8 jwi-items-center jwi-justify-center", children: trigger != null ? (_jsx("div", { role: "button", tabIndex: 0, onClick: openProductPageWidget, onKeyDown: (event) => {
205
- if (event.key === 'Enter' || event.key === ' ') {
206
- event.preventDefault();
207
- openProductPageWidget();
208
- }
209
- }, children: trigger })) : (_jsx("button", { type: "button", onClick: openProductPageWidget, className: heroPrimaryButton.className, style: heroPrimaryButton.style, children: t.findDealer })) }) }));
246
+ return (_jsx("div", { className: rootClass, children: _jsx("div", { className: "jwi-flex jwi-py-8 jwi-items-center jwi-justify-center", children: renderTrigger() }) }));
210
247
  }
211
- const productPageAuthPending = widgetType === 'productPage' && isOpen && (auth === null || isLoading);
248
+ const productPageAuthPending = widgetType === 'productPage' &&
249
+ isOpen &&
250
+ (auth === null || isLoading);
212
251
  if (productPageAuthPending) {
213
252
  return (_jsx("div", { className: rootClass, children: _jsx("div", { className: "jwi-flex jwi-py-8 jwi-items-center jwi-justify-center", children: _jsx("button", { type: "button", disabled: true, className: `${heroPrimaryButton.className} jwi-opacity-95`, style: heroPrimaryButton.style, children: _jsxs("span", { className: "jwi-inline-flex jwi-items-center jwi-gap-2", children: [_jsx(ButtonSpinner, {}), t.loading] }) }) }) }));
214
253
  }
215
- const waitingForAuth = auth === null && !(widgetType === 'productPage' && !isOpen);
216
- if ((isLoading || waitingForAuth) && type !== 'productPage') {
254
+ const waitingForAuth = auth === null &&
255
+ !((widgetType === 'productPage' || widgetType === 'findDealerDrawer') &&
256
+ !isOpen);
257
+ if ((isLoading || waitingForAuth) &&
258
+ type !== 'productPage' &&
259
+ type !== 'findDealerDrawer') {
217
260
  return (_jsx("div", { className: rootClass, children: _jsx("div", { className: "jwi-flex jwi-w-full jwi-flex-col", children: _jsx(FinderSearchRowSkeleton, {}) }) }));
218
261
  }
219
- if (!auth?.ok || !auth.authorized) {
262
+ const deferAuthErrorUntilDrawerOpens = widgetType === 'findDealerDrawer' && !isOpen && auth === null;
263
+ const deferAuthUntilFindDealerDrawerResolves = widgetType === 'findDealerDrawer' && isOpen && (auth === null || isLoading);
264
+ if (!deferAuthErrorUntilDrawerOpens &&
265
+ !deferAuthUntilFindDealerDrawerResolves &&
266
+ (!auth?.ok || !auth.authorized)) {
220
267
  return _jsx("div", { className: rootClass, children: getSafeWidgetErrorMessage(auth?.error, t) });
221
268
  }
222
269
  if (widgetType === 'productPage') {
223
270
  return (_jsx("div", { className: rootClass, children: _jsx(ProductPageWidget, { t: t, buttonStyling: styling?.button, isSearching: isSearching, locationError: locationError, searchResult: searchResult?.ok === false
224
271
  ? { ...searchResult, error: getSafeWidgetErrorMessage(searchResult.error, t) }
225
- : searchResult, inquiryValues: inquiryValues, inquiryError: inquiryError, isInquirySubmitted: isInquirySubmitted, selectedDealerName: selectedDealerName, isManualSearchEnabled: isManualLocationSearchEnabled, query: locationQuery, suggestions: locationSuggestions, suggestionsOpen: isSuggestionListOpen, isSuggestionsLoading: isSearchingSuggestions, onQueryChange: (value) => {
272
+ : searchResult, mapSearchResult: mapSearchResult?.ok === false
273
+ ? { ...mapSearchResult, error: getSafeWidgetErrorMessage(mapSearchResult.error, t) }
274
+ : mapSearchResult, inquiryValues: inquiryValues, inquiryError: inquiryError, isInquirySubmitted: isInquirySubmitted, selectedDealerName: selectedDealerName, isManualSearchEnabled: isManualLocationSearchEnabled, query: locationQuery, suggestions: locationSuggestions, suggestionsOpen: isSuggestionListOpen, isSuggestionsLoading: isSearchingSuggestions, onQueryChange: (value) => {
226
275
  setLocationQuery(value);
227
276
  const trimmed = value.trim();
228
277
  setIsSuggestionListOpen(trimmed.length > 0);
@@ -283,12 +332,91 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
283
332
  setInquiryValues(createInquiryFormValues(productName, dealerName));
284
333
  setInquiryError(null);
285
334
  setIsInquirySubmitted(false);
286
- }, onClosePopup: () => {
287
- setIsOpen(false);
288
- setLocationSuggestions([]);
289
- setIsSuggestionListOpen(false);
290
- setIsSearchingSuggestions(false);
291
- } }) }));
335
+ }, onMapDealerSelect: (dealer) => {
336
+ if (isDealerInSearchResult(dealer.dealerName, searchResult, t.unknownDealer))
337
+ return;
338
+ void runDealerSearchByCoordinates(dealer.latitude, dealer.longitude);
339
+ }, onClosePopup: closeDealerWidget }) }));
340
+ }
341
+ if (widgetType === 'findDealerDrawer') {
342
+ const drawerLoading = isOpen &&
343
+ (auth === null ||
344
+ isLoading ||
345
+ (isSearching && searchResult == null && mapSearchResult == null));
346
+ return (_jsxs("div", { className: rootClass, children: [_jsx("div", { className: "jwi-flex jwi-items-center jwi-justify-center jwi-py-8", children: renderTrigger() }), isOpen && (_jsx("div", { className: "jwi-fixed jwi-inset-0 jwi-z-[9998] jwi-bg-black/35", onClick: closeDealerWidget })), _jsx("div", { className: "jwi-fixed jwi-right-0 jwi-top-0 jwi-z-[9999] jwi-h-full jwi-w-[min(100vw,1200px)] jwi-bg-white jwi-shadow-[-8px_0_32px_rgba(0,0,0,0.2)]", style: {
347
+ transform: isOpen ? 'translateX(0)' : 'translateX(100%)',
348
+ transition: 'transform 300ms ease-out',
349
+ willChange: 'transform',
350
+ }, "aria-hidden": !isOpen, children: drawerLoading ? (_jsxs("div", { className: "jwi-flex jwi-h-full jwi-w-full jwi-bg-white", children: [_jsx("div", { className: "jwi-flex jwi-h-full jwi-min-h-0 jwi-w-1/2 jwi-flex-col jwi-overflow-hidden", children: _jsxs("div", { className: "jwi-flex jwi-h-full jwi-min-h-0 jwi-w-full jwi-flex-col jwi-gap-3 jwi-overflow-hidden jwi-bg-white jwi-p-6", children: [_jsx("div", { className: "jwi-h-12 jwi-w-full jwi-animate-pulse jwi-rounded-[10px] jwi-bg-[#ece8df]" }), _jsx("div", { className: "jwi-h-5 jwi-w-48 jwi-animate-pulse jwi-rounded-full jwi-bg-[#ece8df]" }), _jsxs("div", { className: "jwi-flex jwi-flex-col jwi-gap-4", children: [_jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {})] })] }) }), _jsx("div", { className: "jwi-h-full jwi-w-1/2 jwi-bg-[#e8eef1]" })] })) : (_jsx(FindDealerDrawerWidget, { t: t, buttonStyling: styling?.button, isSearching: isSearching, locationError: locationError, searchResult: searchResult?.ok === false
351
+ ? { ...searchResult, error: getSafeWidgetErrorMessage(searchResult.error, t) }
352
+ : searchResult, mapSearchResult: mapSearchResult?.ok === false
353
+ ? { ...mapSearchResult, error: getSafeWidgetErrorMessage(mapSearchResult.error, t) }
354
+ : mapSearchResult, inquiryValues: inquiryValues, inquiryError: inquiryError, isInquirySubmitted: isInquirySubmitted, selectedDealerName: selectedDealerName, isManualSearchEnabled: isManualLocationSearchEnabled, query: locationQuery, suggestions: locationSuggestions, suggestionsOpen: isSuggestionListOpen, isSuggestionsLoading: isSearchingSuggestions, onQueryChange: (value) => {
355
+ setLocationQuery(value);
356
+ const trimmed = value.trim();
357
+ setIsSuggestionListOpen(trimmed.length > 0);
358
+ if (trimmed.length < 3) {
359
+ setLocationSuggestions([]);
360
+ }
361
+ }, onQuerySubmit: async (value) => {
362
+ const query = value.trim();
363
+ if (query.length < 3)
364
+ return;
365
+ setIsSearchingSuggestions(true);
366
+ const result = await searchLocationSuggestions(query, dealerSearchOptions);
367
+ const resolvedSuggestions = result.ok && Array.isArray(result.suggestions) ? result.suggestions : [];
368
+ setLocationSuggestions(resolvedSuggestions);
369
+ setIsSearchingSuggestions(false);
370
+ const suggestion = resolvedSuggestions[0];
371
+ if (!suggestion)
372
+ return;
373
+ setLocationQuery(suggestion.label);
374
+ setLocationSuggestions([]);
375
+ setIsSearchingSuggestions(false);
376
+ setIsSuggestionListOpen(false);
377
+ await runDealerSearchByCoordinates(suggestion.latitude, suggestion.longitude);
378
+ }, onSuggestionSelect: (suggestion) => {
379
+ setLocationQuery(suggestion.label);
380
+ setLocationSuggestions([]);
381
+ setIsSearchingSuggestions(false);
382
+ setIsSuggestionListOpen(false);
383
+ void runDealerSearchByCoordinates(suggestion.latitude, suggestion.longitude);
384
+ }, onDismissSuggestions: () => {
385
+ setLocationSuggestions([]);
386
+ setIsSearchingSuggestions(false);
387
+ setIsSuggestionListOpen(false);
388
+ }, onInquiryClose: () => {
389
+ setSelectedDealerName(null);
390
+ setInquiryValues(null);
391
+ setInquiryError(null);
392
+ }, onInquirySubmit: () => {
393
+ if (inquiryValues == null)
394
+ return;
395
+ const trimmedName = inquiryValues.name.trim();
396
+ const trimmedEmail = inquiryValues.email.trim();
397
+ const trimmedPhone = inquiryValues.phone.trim();
398
+ if (!trimmedName || !trimmedEmail || !trimmedPhone) {
399
+ setInquiryError(t.formValidationRequired);
400
+ return;
401
+ }
402
+ if (!isValidEmail(trimmedEmail)) {
403
+ setInquiryError(t.formValidationEmail);
404
+ return;
405
+ }
406
+ setInquiryError(null);
407
+ setIsInquirySubmitted(true);
408
+ setSelectedDealerName(null);
409
+ setInquiryValues(null);
410
+ }, onInquiryFieldChange: (key, value) => setInquiryValues((current) => current == null ? current : { ...current, [key]: value }), onStartInquiry: (dealerName) => {
411
+ setSelectedDealerName(dealerName);
412
+ setInquiryValues(createInquiryFormValues(productName, dealerName));
413
+ setInquiryError(null);
414
+ setIsInquirySubmitted(false);
415
+ }, onMapDealerSelect: (dealer) => {
416
+ if (isDealerInSearchResult(dealer.dealerName, searchResult, t.unknownDealer))
417
+ return;
418
+ void runDealerSearchByCoordinates(dealer.latitude, dealer.longitude);
419
+ }, onClose: closeDealerWidget })) })] }));
292
420
  }
293
421
  return _jsx("div", { className: rootClass, children: renderReadyState(widgetType, t) });
294
422
  }
package/dist/api.d.ts CHANGED
@@ -2,6 +2,6 @@ import type { CheckWidgetAuthorizationOptions, DealerSearchResponse, LocationAut
2
2
  /** Client-side default when JSON parse fails (English; localized in UI). */
3
3
  export declare const GENERIC_WIDGET_ERROR = "Dealer finder is currently unavailable. Please try again later.";
4
4
  export declare function checkWidgetAuthorization(options?: CheckWidgetAuthorizationOptions): Promise<WidgetAuthClientResponse>;
5
- export declare function searchDealersByPostalCode(postalCode: string, options?: CheckWidgetAuthorizationOptions): Promise<DealerSearchResponse>;
6
- export declare function searchDealersByCoordinates(latitude: number, longitude: number, options?: CheckWidgetAuthorizationOptions): Promise<DealerSearchResponse>;
5
+ export declare function searchDealersByPostalCode(postalCode: string, options?: CheckWidgetAuthorizationOptions, scope?: 'list' | 'map'): Promise<DealerSearchResponse>;
6
+ export declare function searchDealersByCoordinates(latitude: number, longitude: number, options?: CheckWidgetAuthorizationOptions, scope?: 'list' | 'map'): Promise<DealerSearchResponse>;
7
7
  export declare function searchLocationSuggestions(query: string, options?: CheckWidgetAuthorizationOptions): Promise<LocationAutocompleteResponse>;
package/dist/api.js CHANGED
@@ -35,10 +35,11 @@ export async function checkWidgetAuthorization(options) {
35
35
  return { ok: false, error: GENERIC_WIDGET_ERROR };
36
36
  }
37
37
  }
38
- export async function searchDealersByPostalCode(postalCode, options) {
38
+ export async function searchDealersByPostalCode(postalCode, options, scope = 'list') {
39
39
  const endpoint = options?.endpoint ?? '/api/jotul/widget';
40
40
  const fetcher = options?.fetcher ?? fetch;
41
41
  const params = new URLSearchParams({ postalCode: postalCode.trim() });
42
+ params.set('scope', scope);
42
43
  appendLocaleAndMarket(params, options);
43
44
  if (Array.isArray(options?.brands)) {
44
45
  for (const brand of options.brands) {
@@ -65,13 +66,14 @@ export async function searchDealersByPostalCode(postalCode, options) {
65
66
  return { ok: false, error: GENERIC_WIDGET_ERROR };
66
67
  }
67
68
  }
68
- export async function searchDealersByCoordinates(latitude, longitude, options) {
69
+ export async function searchDealersByCoordinates(latitude, longitude, options, scope = 'list') {
69
70
  const endpoint = options?.endpoint ?? '/api/jotul/widget';
70
71
  const fetcher = options?.fetcher ?? fetch;
71
72
  const params = new URLSearchParams({
72
73
  latitude: String(latitude),
73
74
  longitude: String(longitude),
74
75
  });
76
+ params.set('scope', scope);
75
77
  appendLocaleAndMarket(params, options);
76
78
  if (Array.isArray(options?.brands)) {
77
79
  for (const brand of options.brands) {
@@ -0,0 +1,37 @@
1
+ import 'leaflet/dist/leaflet.css';
2
+ import 'leaflet.markercluster/dist/MarkerCluster.css';
3
+ import type { WidgetStrings } from '../i18n/widgetStrings';
4
+ import type { DealerSearchResponse, InquiryFormValues, JotulWidgetButtonStyling, LocationSuggestion } from '../types';
5
+ type FindDealerDrawerWidgetProps = {
6
+ t: WidgetStrings;
7
+ buttonStyling?: JotulWidgetButtonStyling;
8
+ isSearching: boolean;
9
+ locationError: string | null;
10
+ searchResult: DealerSearchResponse | null;
11
+ mapSearchResult?: DealerSearchResponse | null;
12
+ inquiryValues: InquiryFormValues | null;
13
+ inquiryError: string | null;
14
+ isInquirySubmitted: boolean;
15
+ selectedDealerName: string | null;
16
+ isManualSearchEnabled: boolean;
17
+ query: string;
18
+ suggestions: LocationSuggestion[];
19
+ suggestionsOpen: boolean;
20
+ isSuggestionsLoading: boolean;
21
+ onQueryChange: (value: string) => void;
22
+ onQuerySubmit: (value: string) => void | Promise<void>;
23
+ onSuggestionSelect: (suggestion: LocationSuggestion) => void;
24
+ onDismissSuggestions: () => void;
25
+ onInquiryClose: () => void;
26
+ onInquirySubmit: () => void;
27
+ onInquiryFieldChange: (key: keyof InquiryFormValues, value: string) => void;
28
+ onStartInquiry: (dealerName: string) => void;
29
+ onMapDealerSelect?: (dealer: {
30
+ dealerName: string;
31
+ latitude: number;
32
+ longitude: number;
33
+ }) => void;
34
+ onClose: () => void;
35
+ };
36
+ export declare function FindDealerDrawerWidget({ t, buttonStyling, isSearching, locationError, searchResult, mapSearchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onQuerySubmit, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, onMapDealerSelect, onClose, }: FindDealerDrawerWidgetProps): import("react/jsx-runtime").JSX.Element;
37
+ export {};