@jotul/jotul-widgets 1.0.0 → 1.0.2
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/dist/JotulWidget.d.ts +3 -3
- package/dist/JotulWidget.js +27 -15
- package/dist/api.js +18 -10
- package/dist/components/ProductPageWidget.d.ts +3 -2
- package/dist/components/ProductPageWidget.js +3 -3
- package/dist/components/product-page/DealerList.d.ts +3 -2
- package/dist/components/product-page/DealerList.js +6 -2
- package/dist/components/product-page/InquiryForm.d.ts +3 -2
- package/dist/components/product-page/InquiryForm.js +6 -2
- package/dist/i18n/widgetStrings.d.ts +9 -2
- package/dist/i18n/widgetStrings.js +102 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/utils/widgetPrimaryButtonPresentation.d.ts +14 -0
- package/dist/utils/widgetPrimaryButtonPresentation.js +36 -0
- package/package.json +1 -1
package/dist/JotulWidget.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import './JotulWidget.css';
|
|
2
2
|
import { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode } from './api';
|
|
3
3
|
import type { JotulWidgetProps } from './types';
|
|
4
|
-
export { normalizeWidgetLocale } from './i18n/widgetStrings';
|
|
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, JotulWidgetProps, JotulWidgetType, WidgetAuthClientResponse, } from './types';
|
|
8
|
-
export declare function JotulWidget({ type, endpoint, className, productName, locale: localeProp, brands, }: JotulWidgetProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export type { CheckWidgetAuthorizationOptions, DealerSearchResponse, JotulWidgetButtonStyling, JotulWidgetProps, JotulWidgetStyling, JotulWidgetType, WidgetAuthClientResponse, } from './types';
|
|
8
|
+
export declare function JotulWidget({ type, endpoint, className, productName, locale: localeProp, market: marketProp, brands, styling, }: JotulWidgetProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/JotulWidget.js
CHANGED
|
@@ -4,12 +4,12 @@ import './JotulWidget.css';
|
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { FinderSearchRowSkeleton } from './components/FinderSearchRowSkeleton';
|
|
6
6
|
import { ProductPageWidget } from './widgets/ProductPageWidget';
|
|
7
|
-
import { FIND_DEALER_BUTTON_CLASS } from './constants';
|
|
8
7
|
import { ButtonSpinner } from './icons/ButtonSpinner';
|
|
9
|
-
import {
|
|
8
|
+
import { DEFAULT_WIDGET_LOCALE_TAG, resolveWidgetUiLocale, WIDGET_STRINGS, } from './i18n/widgetStrings';
|
|
10
9
|
import { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode, } from './api';
|
|
11
10
|
import { createInquiryFormValues, getSafeWidgetErrorMessage, isValidEmail, isWidgetType, renderReadyState, } from './utils';
|
|
12
|
-
|
|
11
|
+
import { getWidgetPrimaryButtonPresentation } from './utils/widgetPrimaryButtonPresentation';
|
|
12
|
+
export { DEFAULT_WIDGET_LOCALE_TAG, normalizeWidgetLocale, resolveWidgetUiLocale, } from './i18n/widgetStrings';
|
|
13
13
|
export { checkWidgetAuthorization, searchLocationSuggestions, searchDealersByCoordinates, searchDealersByPostalCode, };
|
|
14
14
|
const GEO_PERMISSION_DENIED = 1;
|
|
15
15
|
const GEO_POSITION_UNAVAILABLE = 2;
|
|
@@ -19,9 +19,20 @@ const GEOLOCATION_OPTIONS = {
|
|
|
19
19
|
timeout: 15000,
|
|
20
20
|
maximumAge: 300000,
|
|
21
21
|
};
|
|
22
|
-
export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, productName, locale: localeProp, brands, }) {
|
|
23
|
-
const
|
|
24
|
-
const t = WIDGET_STRINGS[
|
|
22
|
+
export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, productName, locale: localeProp, market: marketProp, brands, styling, }) {
|
|
23
|
+
const resolvedUiLocale = useMemo(() => resolveWidgetUiLocale(localeProp), [localeProp]);
|
|
24
|
+
const t = WIDGET_STRINGS[resolvedUiLocale];
|
|
25
|
+
const apiLocaleTag = useMemo(() => (localeProp?.trim() ? localeProp.trim() : DEFAULT_WIDGET_LOCALE_TAG), [localeProp]);
|
|
26
|
+
const apiMarket = useMemo(() => {
|
|
27
|
+
const m = marketProp?.trim();
|
|
28
|
+
if (!m)
|
|
29
|
+
return undefined;
|
|
30
|
+
const upper = m.toUpperCase();
|
|
31
|
+
return /^[A-Z]{2}$/.test(upper) ? upper : undefined;
|
|
32
|
+
}, [marketProp]);
|
|
33
|
+
const heroPrimaryButton = useMemo(() => getWidgetPrimaryButtonPresentation(styling?.button, 'hero', {
|
|
34
|
+
disabledWait: true,
|
|
35
|
+
}), [styling?.button]);
|
|
25
36
|
const [auth, setAuth] = useState(null);
|
|
26
37
|
const [isLoading, setIsLoading] = useState(false);
|
|
27
38
|
const [searchResult, setSearchResult] = useState(null);
|
|
@@ -41,9 +52,10 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
41
52
|
const autocompleteCacheRef = useRef(new Map());
|
|
42
53
|
const dealerSearchOptions = useMemo(() => ({
|
|
43
54
|
endpoint,
|
|
44
|
-
locale:
|
|
55
|
+
locale: apiLocaleTag,
|
|
56
|
+
market: apiMarket,
|
|
45
57
|
brands,
|
|
46
|
-
}), [brands, endpoint
|
|
58
|
+
}), [apiLocaleTag, apiMarket, brands, endpoint]);
|
|
47
59
|
const runDealerSearchByCoordinates = useCallback(async (latitude, longitude) => {
|
|
48
60
|
setLocationError(null);
|
|
49
61
|
setSearchResult(null);
|
|
@@ -57,7 +69,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
57
69
|
}
|
|
58
70
|
}, [dealerSearchOptions]);
|
|
59
71
|
const runLocationSearch = useCallback(() => {
|
|
60
|
-
const messages = WIDGET_STRINGS[
|
|
72
|
+
const messages = WIDGET_STRINGS[resolvedUiLocale];
|
|
61
73
|
setShouldAutoLocateAfterAuth(false);
|
|
62
74
|
setLocationError(null);
|
|
63
75
|
setSearchResult(null);
|
|
@@ -89,7 +101,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
89
101
|
setLocationError(messages.locationGenericFailure);
|
|
90
102
|
}
|
|
91
103
|
}, GEOLOCATION_OPTIONS);
|
|
92
|
-
}, [
|
|
104
|
+
}, [resolvedUiLocale, runDealerSearchByCoordinates]);
|
|
93
105
|
useEffect(() => {
|
|
94
106
|
const query = locationQuery.trim();
|
|
95
107
|
if (query.length < 3) {
|
|
@@ -97,7 +109,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
97
109
|
setLocationSuggestions([]);
|
|
98
110
|
return;
|
|
99
111
|
}
|
|
100
|
-
const cacheKey = query.toLowerCase()
|
|
112
|
+
const cacheKey = `${apiLocaleTag}|${apiMarket ?? ''}|${query.toLowerCase()}`;
|
|
101
113
|
const cached = autocompleteCacheRef.current.get(cacheKey);
|
|
102
114
|
if (cached != null) {
|
|
103
115
|
setLocationSuggestions(cached);
|
|
@@ -118,7 +130,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
118
130
|
cancelled = true;
|
|
119
131
|
clearTimeout(timer);
|
|
120
132
|
};
|
|
121
|
-
}, [dealerSearchOptions, locationQuery]);
|
|
133
|
+
}, [apiLocaleTag, apiMarket, dealerSearchOptions, locationQuery]);
|
|
122
134
|
useEffect(() => {
|
|
123
135
|
if (type === 'productPage' && !isOpen)
|
|
124
136
|
return;
|
|
@@ -180,11 +192,11 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
180
192
|
else {
|
|
181
193
|
setShouldAutoLocateAfterAuth(true);
|
|
182
194
|
}
|
|
183
|
-
}, className:
|
|
195
|
+
}, className: heroPrimaryButton.className, style: heroPrimaryButton.style, children: t.findDealer }) }) }));
|
|
184
196
|
}
|
|
185
197
|
const productPageAuthPending = widgetType === 'productPage' && isOpen && (auth === null || isLoading);
|
|
186
198
|
if (productPageAuthPending) {
|
|
187
|
-
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: `${
|
|
199
|
+
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] }) }) }) }));
|
|
188
200
|
}
|
|
189
201
|
const waitingForAuth = auth === null && !(widgetType === 'productPage' && !isOpen);
|
|
190
202
|
if ((isLoading || waitingForAuth) && type !== 'productPage') {
|
|
@@ -194,7 +206,7 @@ export function JotulWidget({ type, endpoint = '/api/jotul/widget', className, p
|
|
|
194
206
|
return _jsx("div", { className: rootClass, children: getSafeWidgetErrorMessage(auth?.error, t) });
|
|
195
207
|
}
|
|
196
208
|
if (widgetType === 'productPage') {
|
|
197
|
-
return (_jsx("div", { className: rootClass, children: _jsx(ProductPageWidget, { t: t, isSearching: isSearching, locationError: locationError, searchResult: searchResult?.ok === false
|
|
209
|
+
return (_jsx("div", { className: rootClass, children: _jsx(ProductPageWidget, { t: t, buttonStyling: styling?.button, isSearching: isSearching, locationError: locationError, searchResult: searchResult?.ok === false
|
|
198
210
|
? { ...searchResult, error: getSafeWidgetErrorMessage(searchResult.error, t) }
|
|
199
211
|
: searchResult, inquiryValues: inquiryValues, inquiryError: inquiryError, isInquirySubmitted: isInquirySubmitted, selectedDealerName: selectedDealerName, isManualSearchEnabled: isManualLocationSearchEnabled, query: locationQuery, suggestions: locationSuggestions, suggestionsOpen: isSuggestionListOpen, isSuggestionsLoading: isSearchingSuggestions, onQueryChange: (value) => {
|
|
200
212
|
setLocationQuery(value);
|
package/dist/api.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
import { DEFAULT_WIDGET_LOCALE_TAG } from './i18n/widgetStrings';
|
|
1
2
|
/** Client-side default when JSON parse fails (English; localized in UI). */
|
|
2
3
|
export const GENERIC_WIDGET_ERROR = 'Dealer finder is currently unavailable. Please try again later.';
|
|
4
|
+
function appendLocaleAndMarket(params, options) {
|
|
5
|
+
const locale = options?.locale != null && options.locale.trim() !== ''
|
|
6
|
+
? options.locale.trim()
|
|
7
|
+
: DEFAULT_WIDGET_LOCALE_TAG;
|
|
8
|
+
params.set('locale', locale);
|
|
9
|
+
const market = options?.market?.trim();
|
|
10
|
+
if (market) {
|
|
11
|
+
const upper = market.toUpperCase();
|
|
12
|
+
if (/^[A-Z]{2}$/.test(upper)) {
|
|
13
|
+
params.set('market', upper);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
3
17
|
export async function checkWidgetAuthorization(options) {
|
|
4
18
|
const endpoint = options?.endpoint ?? '/api/jotul/widget';
|
|
5
19
|
const fetcher = options?.fetcher ?? fetch;
|
|
@@ -25,9 +39,7 @@ export async function searchDealersByPostalCode(postalCode, options) {
|
|
|
25
39
|
const endpoint = options?.endpoint ?? '/api/jotul/widget';
|
|
26
40
|
const fetcher = options?.fetcher ?? fetch;
|
|
27
41
|
const params = new URLSearchParams({ postalCode: postalCode.trim() });
|
|
28
|
-
|
|
29
|
-
params.set('locale', options.locale.trim());
|
|
30
|
-
}
|
|
42
|
+
appendLocaleAndMarket(params, options);
|
|
31
43
|
if (Array.isArray(options?.brands)) {
|
|
32
44
|
for (const brand of options.brands) {
|
|
33
45
|
const value = brand.trim();
|
|
@@ -60,9 +72,7 @@ export async function searchDealersByCoordinates(latitude, longitude, options) {
|
|
|
60
72
|
latitude: String(latitude),
|
|
61
73
|
longitude: String(longitude),
|
|
62
74
|
});
|
|
63
|
-
|
|
64
|
-
params.set('locale', options.locale.trim());
|
|
65
|
-
}
|
|
75
|
+
appendLocaleAndMarket(params, options);
|
|
66
76
|
if (Array.isArray(options?.brands)) {
|
|
67
77
|
for (const brand of options.brands) {
|
|
68
78
|
const value = brand.trim();
|
|
@@ -92,12 +102,10 @@ export async function searchLocationSuggestions(query, options) {
|
|
|
92
102
|
const endpoint = options?.endpoint ?? '/api/jotul/widget';
|
|
93
103
|
const fetcher = options?.fetcher ?? fetch;
|
|
94
104
|
const params = new URLSearchParams({
|
|
95
|
-
|
|
105
|
+
type: 'autocomplete',
|
|
96
106
|
q: query.trim(),
|
|
97
107
|
});
|
|
98
|
-
|
|
99
|
-
params.set('locale', options.locale.trim());
|
|
100
|
-
}
|
|
108
|
+
appendLocaleAndMarket(params, options);
|
|
101
109
|
let response;
|
|
102
110
|
try {
|
|
103
111
|
response = await fetcher(`${endpoint}?${params.toString()}`, {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { WidgetStrings } from '../i18n/widgetStrings';
|
|
2
|
-
import type { DealerSearchResponse, InquiryFormValues, LocationSuggestion } from '../types';
|
|
2
|
+
import type { DealerSearchResponse, InquiryFormValues, JotulWidgetButtonStyling, LocationSuggestion } from '../types';
|
|
3
3
|
type ProductPageWidgetProps = {
|
|
4
4
|
t: WidgetStrings;
|
|
5
|
+
buttonStyling?: JotulWidgetButtonStyling;
|
|
5
6
|
isSearching: boolean;
|
|
6
7
|
locationError: string | null;
|
|
7
8
|
searchResult: DealerSearchResponse | null;
|
|
@@ -22,5 +23,5 @@ type ProductPageWidgetProps = {
|
|
|
22
23
|
onInquiryFieldChange: (key: keyof InquiryFormValues, value: string) => void;
|
|
23
24
|
onStartInquiry: (dealerName: string) => void;
|
|
24
25
|
};
|
|
25
|
-
export declare function ProductPageWidget({ t, isSearching, locationError, searchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, }: ProductPageWidgetProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function ProductPageWidget({ t, buttonStyling, isSearching, locationError, searchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, }: ProductPageWidgetProps): import("react/jsx-runtime").JSX.Element;
|
|
26
27
|
export {};
|
|
@@ -5,12 +5,12 @@ import { InquiryForm } from './product-page/InquiryForm';
|
|
|
5
5
|
import { LocationSearch } from './product-page/LocationSearch';
|
|
6
6
|
import { StatusBanner } from './product-page/StatusBanner';
|
|
7
7
|
import { R10 } from '../constants';
|
|
8
|
-
export function ProductPageWidget({ t, isSearching, locationError, searchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, }) {
|
|
8
|
+
export function ProductPageWidget({ t, buttonStyling, isSearching, locationError, searchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, }) {
|
|
9
9
|
const dealers = (searchResult?.dealers ?? []);
|
|
10
10
|
const total = searchResult?.total ?? dealers.length;
|
|
11
11
|
const inquiryFormOpen = inquiryValues != null;
|
|
12
12
|
if (inquiryFormOpen && searchResult?.ok) {
|
|
13
|
-
return (_jsx("div", { className: "jwi-flex jwi-w-full jwi-flex-col jwi-gap-3", children: _jsx(InquiryForm, { t: t, inquiryValues: inquiryValues, inquiryError: inquiryError, onInquiryClose: onInquiryClose, onInquirySubmit: onInquirySubmit, onInquiryFieldChange: onInquiryFieldChange }) }));
|
|
13
|
+
return (_jsx("div", { className: "jwi-flex jwi-w-full jwi-flex-col jwi-gap-3", children: _jsx(InquiryForm, { t: t, buttonStyling: buttonStyling, inquiryValues: inquiryValues, inquiryError: inquiryError, onInquiryClose: onInquiryClose, onInquirySubmit: onInquirySubmit, onInquiryFieldChange: onInquiryFieldChange }) }));
|
|
14
14
|
}
|
|
15
|
-
return (_jsxs("div", { className: `jwi-flex jwi-w-full jwi-flex-col jwi-gap-3 ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-6`, children: [_jsx(LocationSearch, { t: t, isManualSearchEnabled: isManualSearchEnabled, query: query, suggestions: suggestions, suggestionsOpen: suggestionsOpen, isSuggestionsLoading: isSuggestionsLoading, onQueryChange: onQueryChange, onSuggestionSelect: onSuggestionSelect, onDismissSuggestions: onDismissSuggestions }), locationError != null && !isManualSearchEnabled && (_jsx(StatusBanner, { tone: "error", children: locationError })), searchResult?.ok === false && (_jsx(StatusBanner, { tone: "error", children: searchResult.error ?? '' })), isInquirySubmitted && _jsx(StatusBanner, { tone: "success", children: t.inquirySentSuccess }), isSearching && (_jsxs("div", { className: "jwi-flex jwi-flex-col", children: [_jsx("div", { className: "jwi-w-full jwi-flex-shrink-0 jwi-border-b jwi-border-[#e6e1d7] jwi-pb-3", children: _jsx("div", { className: "jwi-h-5 jwi-w-48 jwi-animate-pulse jwi-rounded-full jwi-bg-[#ece8df]" }) }), _jsxs("div", { className: "jwi-mt-3 jwi-mr-[-12px] jwi-flex jwi-max-h-[min(60vh,480px)] jwi-flex-col jwi-gap-4 jwi-overflow-y-auto jwi-pr-[12px]", children: [_jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {})] })] })), searchResult?.ok && !isSearching && (_jsx(DealerList, { dealers: dealers, total: total, selectedDealerName: selectedDealerName, t: t, onStartInquiry: onStartInquiry }))] }));
|
|
15
|
+
return (_jsxs("div", { className: `jwi-flex jwi-w-full jwi-flex-col jwi-gap-3 ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-6`, children: [_jsx(LocationSearch, { t: t, isManualSearchEnabled: isManualSearchEnabled, query: query, suggestions: suggestions, suggestionsOpen: suggestionsOpen, isSuggestionsLoading: isSuggestionsLoading, onQueryChange: onQueryChange, onSuggestionSelect: onSuggestionSelect, onDismissSuggestions: onDismissSuggestions }), locationError != null && !isManualSearchEnabled && (_jsx(StatusBanner, { tone: "error", children: locationError })), searchResult?.ok === false && (_jsx(StatusBanner, { tone: "error", children: searchResult.error ?? '' })), isInquirySubmitted && _jsx(StatusBanner, { tone: "success", children: t.inquirySentSuccess }), isSearching && (_jsxs("div", { className: "jwi-flex jwi-flex-col", children: [_jsx("div", { className: "jwi-w-full jwi-flex-shrink-0 jwi-border-b jwi-border-[#e6e1d7] jwi-pb-3", children: _jsx("div", { className: "jwi-h-5 jwi-w-48 jwi-animate-pulse jwi-rounded-full jwi-bg-[#ece8df]" }) }), _jsxs("div", { className: "jwi-mt-3 jwi-mr-[-12px] jwi-flex jwi-max-h-[min(60vh,480px)] jwi-flex-col jwi-gap-4 jwi-overflow-y-auto jwi-pr-[12px]", children: [_jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {})] })] })), searchResult?.ok && !isSearching && (_jsx(DealerList, { dealers: dealers, total: total, selectedDealerName: selectedDealerName, t: t, buttonStyling: buttonStyling, onStartInquiry: onStartInquiry }))] }));
|
|
16
16
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
2
|
-
import type { DealerRecord } from '../../types';
|
|
2
|
+
import type { DealerRecord, JotulWidgetButtonStyling } from '../../types';
|
|
3
3
|
type DealerListProps = {
|
|
4
4
|
dealers: DealerRecord[];
|
|
5
5
|
total: number;
|
|
6
6
|
selectedDealerName: string | null;
|
|
7
7
|
t: WidgetStrings;
|
|
8
|
+
buttonStyling?: JotulWidgetButtonStyling;
|
|
8
9
|
onStartInquiry: (dealerName: string) => void;
|
|
9
10
|
};
|
|
10
|
-
export declare function DealerList({ dealers, total, selectedDealerName, t, onStartInquiry, }: DealerListProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function DealerList({ dealers, total, selectedDealerName, t, buttonStyling, onStartInquiry, }: DealerListProps): import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
export {};
|
|
@@ -2,14 +2,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { R10 } from '../../constants';
|
|
3
3
|
import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
4
4
|
import { TelephoneIcon } from '../../icons/TelephoneIcon';
|
|
5
|
+
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
5
6
|
import { asText, formatDistance, getDealerAddressLines, getDealerKey, getDealerName, } from '../../utils';
|
|
6
|
-
export function DealerList({ dealers, total, selectedDealerName, t, onStartInquiry, }) {
|
|
7
|
+
export function DealerList({ dealers, total, selectedDealerName, t, buttonStyling, onStartInquiry, }) {
|
|
8
|
+
const inquiryCta = getWidgetPrimaryButtonPresentation(buttonStyling, 'panel', {
|
|
9
|
+
extraClassName: 'jwi-w-full jwi-max-w-full jwi-shrink-0 jwi-justify-between jwi-gap-2 md:jwi-max-w-[220px]',
|
|
10
|
+
});
|
|
7
11
|
return (_jsxs("div", { className: "jwi-flex jwi-flex-col", children: [_jsx("div", { className: "jwi--mx-6 jwi-w-auto jwi-flex-shrink-0 jwi-border-b jwi-border-[#e6e1d7] jwi-pb-3 jwi-px-6 jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: t.dealersNearYou.replace('{count}', String(total)) }), _jsxs("div", { className: "jwi-relative jwi-mt-4", children: [_jsx("div", { className: "jwi-mr-[-12px] jwi-flex jwi-max-h-[min(60vh,480px)] jwi-flex-col jwi-gap-4 jwi-overflow-y-auto jwi-pr-[12px]", children: dealers.map((dealer, index) => {
|
|
8
12
|
const addressLines = getDealerAddressLines(dealer);
|
|
9
13
|
const phone = asText(dealer.phone);
|
|
10
14
|
const distance = formatDistance(dealer);
|
|
11
15
|
const dealerName = getDealerName(dealer, t.unknownDealer);
|
|
12
16
|
const isSelectedDealer = selectedDealerName === dealerName;
|
|
13
|
-
return (_jsxs("div", { className: `jwi-w-full ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]`, children: [_jsxs("div", { className: "jwi-flex jwi-items-start jwi-justify-between jwi-gap-3", children: [_jsx("div", { className: "jwi-min-w-0 jwi-max-w-[calc(100%-5rem)] jwi-pr-1", children: _jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: dealerName }) }), distance && (_jsx("div", { className: `jwi-shrink-0 jwi-whitespace-nowrap ${R10} jwi-bg-[#fbf3db] jwi-px-2.5 jwi-py-1 jwi-text-xs jwi-font-medium jwi-leading-none jwi-text-[#111111]`, children: distance }))] }), addressLines.length > 0 && (_jsx("div", { className: "jwi-mt-3 jwi-flex jwi-flex-col jwi-gap-0.5 jwi-text-sm jwi-leading-snug jwi-text-[#111111]", children: addressLines.map((line) => (_jsx("div", { children: line }, line))) })), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3 md:jwi-flex-row md:jwi-items-center md:jwi-justify-between", children: [_jsxs("button", { type: "button", onClick: () => onStartInquiry(dealerName), className:
|
|
17
|
+
return (_jsxs("div", { className: `jwi-w-full ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]`, children: [_jsxs("div", { className: "jwi-flex jwi-items-start jwi-justify-between jwi-gap-3", children: [_jsx("div", { className: "jwi-min-w-0 jwi-max-w-[calc(100%-5rem)] jwi-pr-1", children: _jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: dealerName }) }), distance && (_jsx("div", { className: `jwi-shrink-0 jwi-whitespace-nowrap ${R10} jwi-bg-[#fbf3db] jwi-px-2.5 jwi-py-1 jwi-text-xs jwi-font-medium jwi-leading-none jwi-text-[#111111]`, children: distance }))] }), addressLines.length > 0 && (_jsx("div", { className: "jwi-mt-3 jwi-flex jwi-flex-col jwi-gap-0.5 jwi-text-sm jwi-leading-snug jwi-text-[#111111]", children: addressLines.map((line) => (_jsx("div", { children: line }, line))) })), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3 md:jwi-flex-row md:jwi-items-center md:jwi-justify-between", children: [_jsxs("button", { type: "button", onClick: () => onStartInquiry(dealerName), className: inquiryCta.className, style: inquiryCta.style, children: [_jsx("span", { children: isSelectedDealer ? t.sendInquiryEditing : t.sendInquiryCta }), _jsx(ArrowRightIcon, { className: "jwi-h-[18px] jwi-w-[18px] jwi-shrink-0" })] }), phone && (_jsxs("a", { href: `tel:${phone.replace(/\s+/g, '')}`, className: "jwi-inline-flex jwi-min-w-0 jwi-items-center jwi-gap-2 jwi-text-sm jwi-font-normal jwi-tabular-nums jwi-text-[#111111] hover:jwi-underline", children: [_jsx(TelephoneIcon, {}), _jsx("span", { className: "jwi-break-all", children: phone })] }))] })] }, getDealerKey(dealer, index)));
|
|
14
18
|
}) }), _jsx("div", { "aria-hidden": true, className: "jwi-pointer-events-none jwi-absolute jwi-bottom-0 jwi-left-0 jwi-right-0 jwi-h-10 jwi-bg-gradient-to-t jwi-from-white jwi-to-transparent" })] })] }));
|
|
15
19
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
2
|
-
import type { InquiryFormValues } from '../../types';
|
|
2
|
+
import type { InquiryFormValues, JotulWidgetButtonStyling } from '../../types';
|
|
3
3
|
type InquiryFormProps = {
|
|
4
4
|
t: WidgetStrings;
|
|
5
|
+
buttonStyling?: JotulWidgetButtonStyling;
|
|
5
6
|
inquiryValues: InquiryFormValues;
|
|
6
7
|
inquiryError: string | null;
|
|
7
8
|
onInquiryClose: () => void;
|
|
8
9
|
onInquirySubmit: () => void;
|
|
9
10
|
onInquiryFieldChange: (key: keyof InquiryFormValues, value: string) => void;
|
|
10
11
|
};
|
|
11
|
-
export declare function InquiryForm({ t, inquiryValues, inquiryError, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }: InquiryFormProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }: InquiryFormProps): import("react/jsx-runtime").JSX.Element;
|
|
12
13
|
export {};
|
|
@@ -2,7 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { InquiryField } from '../InquiryField';
|
|
3
3
|
import { R10 } from '../../constants';
|
|
4
4
|
import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
5
|
-
|
|
5
|
+
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
6
|
+
export function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }) {
|
|
7
|
+
const submitButton = getWidgetPrimaryButtonPresentation(buttonStyling, 'panel', {
|
|
8
|
+
extraClassName: 'jwi-mt-4 jwi-w-full',
|
|
9
|
+
});
|
|
6
10
|
const trimmedProductName = inquiryValues.productName.trim();
|
|
7
11
|
const title = trimmedProductName !== ''
|
|
8
12
|
? t.sendInquiryTitleWithProduct.replace('{product}', trimmedProductName)
|
|
@@ -10,5 +14,5 @@ export function InquiryForm({ t, inquiryValues, inquiryError, onInquiryClose, on
|
|
|
10
14
|
return (_jsxs("form", { onSubmit: (event) => {
|
|
11
15
|
event.preventDefault();
|
|
12
16
|
onInquirySubmit();
|
|
13
|
-
}, className: `jwi-w-full ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]`, children: [_jsxs("button", { type: "button", onClick: onInquiryClose, className: "jwi-mb-3 jwi-inline-flex jwi-w-fit jwi-cursor-pointer jwi-items-center jwi-gap-1.5 jwi-border-0 jwi-bg-transparent jwi-p-0 jwi-text-left jwi-text-sm jwi-font-medium jwi-text-[#767676] hover:jwi-text-[#444444]", children: [_jsx("span", { className: "jwi-inline-flex jwi-shrink-0", style: { transform: 'scaleX(-1)' }, "aria-hidden": true, children: _jsx(ArrowRightIcon, { className: "jwi-h-[14px] jwi-w-[14px]" }) }), t.goBack] }), _jsx("input", { type: "hidden", name: "product", value: inquiryValues.productName }), _jsx("input", { type: "hidden", name: "dealer", value: inquiryValues.dealerName }), _jsxs("div", { className: "jwi-min-w-0", children: [_jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: title }), _jsx("p", { className: "jwi-m-0 jwi-mt-2 jwi-text-sm jwi-leading-[1.4] jwi-text-[#767676]", children: t.dealerWillContact.replace('{dealer}', inquiryValues.dealerName) })] }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsx(InquiryField, { label: t.fieldName, value: inquiryValues.name, onChange: (value) => onInquiryFieldChange('name', value) }), _jsx(InquiryField, { label: t.fieldEmail, type: "email", value: inquiryValues.email, onChange: (value) => onInquiryFieldChange('email', value) }), _jsx(InquiryField, { label: t.fieldPhone, type: "tel", value: inquiryValues.phone, onChange: (value) => onInquiryFieldChange('phone', value) }), _jsx(InquiryField, { label: t.fieldComment, multiline: true, value: inquiryValues.comment, onChange: (value) => onInquiryFieldChange('comment', value) })] }), inquiryError && (_jsx("div", { className: `jwi-mt-4 ${R10} jwi-border jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] jwi-text-[#8f2d21]`, children: inquiryError })), _jsx("button", { type: "submit", className:
|
|
17
|
+
}, className: `jwi-w-full ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]`, children: [_jsxs("button", { type: "button", onClick: onInquiryClose, className: "jwi-mb-3 jwi-inline-flex jwi-w-fit jwi-cursor-pointer jwi-items-center jwi-gap-1.5 jwi-border-0 jwi-bg-transparent jwi-p-0 jwi-text-left jwi-text-sm jwi-font-medium jwi-text-[#767676] hover:jwi-text-[#444444]", children: [_jsx("span", { className: "jwi-inline-flex jwi-shrink-0", style: { transform: 'scaleX(-1)' }, "aria-hidden": true, children: _jsx(ArrowRightIcon, { className: "jwi-h-[14px] jwi-w-[14px]" }) }), t.goBack] }), _jsx("input", { type: "hidden", name: "product", value: inquiryValues.productName }), _jsx("input", { type: "hidden", name: "dealer", value: inquiryValues.dealerName }), _jsxs("div", { className: "jwi-min-w-0", children: [_jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: title }), _jsx("p", { className: "jwi-m-0 jwi-mt-2 jwi-text-sm jwi-leading-[1.4] jwi-text-[#767676]", children: t.dealerWillContact.replace('{dealer}', inquiryValues.dealerName) })] }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsx(InquiryField, { label: t.fieldName, value: inquiryValues.name, onChange: (value) => onInquiryFieldChange('name', value) }), _jsx(InquiryField, { label: t.fieldEmail, type: "email", value: inquiryValues.email, onChange: (value) => onInquiryFieldChange('email', value) }), _jsx(InquiryField, { label: t.fieldPhone, type: "tel", value: inquiryValues.phone, onChange: (value) => onInquiryFieldChange('phone', value) }), _jsx(InquiryField, { label: t.fieldComment, multiline: true, value: inquiryValues.comment, onChange: (value) => onInquiryFieldChange('comment', value) })] }), inquiryError && (_jsx("div", { className: `jwi-mt-4 ${R10} jwi-border jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] jwi-text-[#8f2d21]`, children: inquiryError })), _jsx("button", { type: "submit", className: submitButton.className, style: submitButton.style, children: t.sendInquiryCta })] }));
|
|
14
18
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export type JotulWidgetLocale = 'no' | 'se';
|
|
1
|
+
export type JotulWidgetLocale = 'no' | 'se' | 'en' | 'fr';
|
|
2
|
+
/** Default BCP 47 tag used for API `locale` when the prop is omitted. */
|
|
3
|
+
export declare const DEFAULT_WIDGET_LOCALE_TAG = "nb-NO";
|
|
2
4
|
export type WidgetStrings = {
|
|
3
5
|
genericWidgetError: string;
|
|
4
6
|
invalidPostcodeError: string;
|
|
@@ -36,4 +38,9 @@ export type WidgetStrings = {
|
|
|
36
38
|
readyWarrantyForm: string;
|
|
37
39
|
};
|
|
38
40
|
export declare const WIDGET_STRINGS: Record<JotulWidgetLocale, WidgetStrings>;
|
|
39
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Maps `locale` prop: BCP 47 (e.g. `nb-NO`, `en-US`, `fr-FR`) or legacy shorthands (`no`, `sv`).
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveWidgetUiLocale(raw: string | undefined): JotulWidgetLocale;
|
|
45
|
+
/** @deprecated Use `resolveWidgetUiLocale`. */
|
|
46
|
+
export declare const normalizeWidgetLocale: typeof resolveWidgetUiLocale;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Default BCP 47 tag used for API `locale` when the prop is omitted. */
|
|
2
|
+
export const DEFAULT_WIDGET_LOCALE_TAG = 'nb-NO';
|
|
1
3
|
export const WIDGET_STRINGS = {
|
|
2
4
|
no: {
|
|
3
5
|
genericWidgetError: 'Forhandlerkartet er ikke tilgjengelig akkurat nå. Prøv igjen senere.',
|
|
@@ -71,17 +73,112 @@ export const WIDGET_STRINGS = {
|
|
|
71
73
|
readyDealerFinder: 'JotulWidget klar: dealerFinder',
|
|
72
74
|
readyWarrantyForm: 'JotulWidget klar: warrantyForm',
|
|
73
75
|
},
|
|
76
|
+
en: {
|
|
77
|
+
genericWidgetError: 'The dealer finder is unavailable right now. Please try again later.',
|
|
78
|
+
invalidPostcodeError: 'Enter a valid postcode.',
|
|
79
|
+
invalidWidgetTypeError: 'This widget is not available right now.',
|
|
80
|
+
locationSearchFailed: 'Location search could not be completed. Try again.',
|
|
81
|
+
locationUnavailableBrowser: 'Location is not available in this browser. Try another browser or device.',
|
|
82
|
+
locationPermissionDenied: 'Location access was denied. Allow location for this site to find nearby dealers.',
|
|
83
|
+
locationPositionUnavailable: 'Your position could not be determined. Try again in a moment.',
|
|
84
|
+
locationTimeout: 'Finding your location took too long. Try again.',
|
|
85
|
+
locationGenericFailure: 'Could not read your location. Try again.',
|
|
86
|
+
locationTypeMore: 'Type at least 3 characters to search for an address.',
|
|
87
|
+
locationNoResults: 'No addresses found. Try a different search.',
|
|
88
|
+
locationManualHint: 'Enter your address to find dealers nearby',
|
|
89
|
+
findDealer: 'Find a dealer',
|
|
90
|
+
loading: 'Loading …',
|
|
91
|
+
useLocationHint: 'Use your location to find dealers nearby',
|
|
92
|
+
find: 'Locate',
|
|
93
|
+
finding: 'Searching …',
|
|
94
|
+
inquirySentSuccess: 'Your request has been sent.',
|
|
95
|
+
goBack: 'Back',
|
|
96
|
+
sendInquiryTitle: 'Request',
|
|
97
|
+
sendInquiryTitleWithProduct: 'Request regarding {product}',
|
|
98
|
+
dealerWillContact: '{dealer} will contact you as soon as possible.',
|
|
99
|
+
fieldName: 'Name',
|
|
100
|
+
fieldEmail: 'Email',
|
|
101
|
+
fieldPhone: 'Phone',
|
|
102
|
+
fieldComment: 'Comment',
|
|
103
|
+
formValidationRequired: 'Enter name, email, and phone.',
|
|
104
|
+
formValidationEmail: 'Enter a valid email address.',
|
|
105
|
+
dealersNearYou: '{count} dealers nearby',
|
|
106
|
+
unknownDealer: 'Unknown dealer',
|
|
107
|
+
sendInquiryCta: 'Send request',
|
|
108
|
+
sendInquiryEditing: 'Editing request',
|
|
109
|
+
readyDealerFinder: 'JotulWidget ready: dealerFinder',
|
|
110
|
+
readyWarrantyForm: 'JotulWidget ready: warrantyForm',
|
|
111
|
+
},
|
|
112
|
+
fr: {
|
|
113
|
+
genericWidgetError: "Le recherche de revendeurs n'est pas disponible pour le moment. Réessayez plus tard.",
|
|
114
|
+
invalidPostcodeError: 'Saisissez un code postal valide.',
|
|
115
|
+
invalidWidgetTypeError: "Ce widget n'est pas disponible pour le moment.",
|
|
116
|
+
locationSearchFailed: 'La recherche de lieu a échoué. Réessayez.',
|
|
117
|
+
locationUnavailableBrowser: "La position n'est pas disponible dans ce navigateur. Essayez un autre navigateur ou appareil.",
|
|
118
|
+
locationPermissionDenied: "L'accès à la position a été refusé. Autorisez la position pour ce site afin de trouver les revendeurs proches.",
|
|
119
|
+
locationPositionUnavailable: "Votre position n'a pas pu être déterminée. Réessayez dans un instant.",
|
|
120
|
+
locationTimeout: 'La recherche de votre position a pris trop de temps. Réessayez.',
|
|
121
|
+
locationGenericFailure: 'Impossible de lire votre position. Réessayez.',
|
|
122
|
+
locationTypeMore: 'Saisissez au moins 3 caractères pour chercher une adresse.',
|
|
123
|
+
locationNoResults: 'Aucune adresse trouvée. Essayez une autre recherche.',
|
|
124
|
+
locationManualHint: 'Saisissez votre adresse pour trouver des revendeurs à proximité',
|
|
125
|
+
findDealer: 'Trouver un revendeur',
|
|
126
|
+
loading: 'Chargement …',
|
|
127
|
+
useLocationHint: 'Utilisez votre position pour trouver des revendeurs à proximité',
|
|
128
|
+
find: 'Localiser',
|
|
129
|
+
finding: 'Recherche …',
|
|
130
|
+
inquirySentSuccess: 'Votre demande a été envoyée.',
|
|
131
|
+
goBack: 'Retour',
|
|
132
|
+
sendInquiryTitle: 'Demande',
|
|
133
|
+
sendInquiryTitleWithProduct: 'Demande concernant {product}',
|
|
134
|
+
dealerWillContact: '{dealer} vous contactera dès que possible.',
|
|
135
|
+
fieldName: 'Nom',
|
|
136
|
+
fieldEmail: 'E-mail',
|
|
137
|
+
fieldPhone: 'Téléphone',
|
|
138
|
+
fieldComment: 'Commentaire',
|
|
139
|
+
formValidationRequired: 'Renseignez le nom, l’e-mail et le téléphone.',
|
|
140
|
+
formValidationEmail: 'Saisissez une adresse e-mail valide.',
|
|
141
|
+
dealersNearYou: '{count} revendeurs à proximité',
|
|
142
|
+
unknownDealer: 'Revendeur inconnu',
|
|
143
|
+
sendInquiryCta: 'Envoyer la demande',
|
|
144
|
+
sendInquiryEditing: 'Modification de la demande',
|
|
145
|
+
readyDealerFinder: 'JotulWidget prêt : dealerFinder',
|
|
146
|
+
readyWarrantyForm: 'JotulWidget prêt : warrantyForm',
|
|
147
|
+
},
|
|
74
148
|
};
|
|
75
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Maps `locale` prop: BCP 47 (e.g. `nb-NO`, `en-US`, `fr-FR`) or legacy shorthands (`no`, `sv`).
|
|
151
|
+
*/
|
|
152
|
+
export function resolveWidgetUiLocale(raw) {
|
|
76
153
|
if (raw == null || raw.trim() === '') {
|
|
77
154
|
return 'no';
|
|
78
155
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
156
|
+
const s = raw.trim();
|
|
157
|
+
const lower = s.toLowerCase();
|
|
158
|
+
if (lower === 'no' || lower === 'nb' || lower === 'nn') {
|
|
81
159
|
return 'no';
|
|
82
160
|
}
|
|
83
|
-
if (
|
|
161
|
+
if (lower === 'se' || lower === 'sv') {
|
|
84
162
|
return 'se';
|
|
85
163
|
}
|
|
86
|
-
|
|
164
|
+
try {
|
|
165
|
+
const loc = new Intl.Locale(s.replace(/_/g, '-'));
|
|
166
|
+
const lang = (loc.language ?? '').toLowerCase();
|
|
167
|
+
if (lang === 'nb' || lang === 'nn' || lang === 'no')
|
|
168
|
+
return 'no';
|
|
169
|
+
if (lang === 'sv')
|
|
170
|
+
return 'se';
|
|
171
|
+
if (lang === 'en')
|
|
172
|
+
return 'en';
|
|
173
|
+
if (lang === 'fr')
|
|
174
|
+
return 'fr';
|
|
175
|
+
if (lang === 'de')
|
|
176
|
+
return 'en';
|
|
177
|
+
return 'no';
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return 'no';
|
|
181
|
+
}
|
|
87
182
|
}
|
|
183
|
+
/** @deprecated Use `resolveWidgetUiLocale`. */
|
|
184
|
+
export const normalizeWidgetLocale = resolveWidgetUiLocale;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { checkWidgetAuthorization, normalizeWidgetLocale, searchDealersByCoordinates, searchDealersByPostalCode, JotulWidget, type CheckWidgetAuthorizationOptions, type DealerSearchResponse, type JotulWidgetLocale, type JotulWidgetProps, type JotulWidgetType, type WidgetAuthClientResponse, } from './JotulWidget';
|
|
1
|
+
export { checkWidgetAuthorization, DEFAULT_WIDGET_LOCALE_TAG, normalizeWidgetLocale, resolveWidgetUiLocale, searchDealersByCoordinates, searchDealersByPostalCode, JotulWidget, type CheckWidgetAuthorizationOptions, type DealerSearchResponse, type JotulWidgetButtonStyling, type JotulWidgetLocale, type JotulWidgetProps, type JotulWidgetStyling, type JotulWidgetType, type WidgetAuthClientResponse, } from './JotulWidget';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
export { checkWidgetAuthorization, normalizeWidgetLocale, searchDealersByCoordinates, searchDealersByPostalCode, JotulWidget, } from './JotulWidget';
|
|
2
|
+
export { checkWidgetAuthorization, DEFAULT_WIDGET_LOCALE_TAG, normalizeWidgetLocale, resolveWidgetUiLocale, searchDealersByCoordinates, searchDealersByPostalCode, JotulWidget, } from './JotulWidget';
|
package/dist/types.d.ts
CHANGED
|
@@ -37,16 +37,32 @@ export type LocationAutocompleteResponse = {
|
|
|
37
37
|
export type CheckWidgetAuthorizationOptions = {
|
|
38
38
|
endpoint?: string;
|
|
39
39
|
fetcher?: typeof fetch;
|
|
40
|
+
/** BCP 47 language tag (e.g. `nb-NO`, `en-US`) sent to the API as `locale`. */
|
|
40
41
|
locale?: string;
|
|
42
|
+
/** ISO 3166-1 alpha-2 market/country (e.g. `NO`, `SE`) sent to the API as `market`. */
|
|
43
|
+
market?: string;
|
|
41
44
|
brands?: string[];
|
|
42
45
|
};
|
|
46
|
+
/** CSS values for primary CTA buttons (find dealer, send inquiry, etc.). */
|
|
47
|
+
export type JotulWidgetButtonStyling = {
|
|
48
|
+
borderRadius?: string;
|
|
49
|
+
backgroundColor?: string;
|
|
50
|
+
textColor?: string;
|
|
51
|
+
};
|
|
52
|
+
export type JotulWidgetStyling = {
|
|
53
|
+
button?: JotulWidgetButtonStyling;
|
|
54
|
+
};
|
|
43
55
|
export type JotulWidgetProps = {
|
|
44
56
|
type?: string;
|
|
45
57
|
endpoint?: string;
|
|
46
58
|
className?: string;
|
|
47
59
|
productName?: string;
|
|
60
|
+
/** BCP 47 tag for widget UI strings and API `locale` (default `nb-NO` when omitted). */
|
|
48
61
|
locale?: string;
|
|
62
|
+
/** ISO 3166-1 alpha-2 market filter for dealers and geocoder country bias (`NO`, `SE`, …). */
|
|
63
|
+
market?: string;
|
|
49
64
|
brands?: string[];
|
|
65
|
+
styling?: JotulWidgetStyling;
|
|
50
66
|
};
|
|
51
67
|
export type DealerRecord = Record<string, unknown>;
|
|
52
68
|
export type InquiryFormValues = {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { JotulWidgetButtonStyling } from '../types';
|
|
3
|
+
export type PrimaryButtonVariant = 'hero' | 'panel';
|
|
4
|
+
/**
|
|
5
|
+
* Primary CTA buttons share the same visual tokens; optional `styling.button`
|
|
6
|
+
* overrides border radius, background, and text color (CSS values).
|
|
7
|
+
*/
|
|
8
|
+
export declare function getWidgetPrimaryButtonPresentation(buttonStyling: JotulWidgetButtonStyling | undefined, variant: PrimaryButtonVariant, options?: {
|
|
9
|
+
disabledWait?: boolean;
|
|
10
|
+
extraClassName?: string;
|
|
11
|
+
}): {
|
|
12
|
+
className: string;
|
|
13
|
+
style: CSSProperties;
|
|
14
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const HERO_LAYOUT = 'jwi-inline-flex jwi-w-full jwi-min-h-[56px] jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-border-0 jwi-px-7 jwi-text-base jwi-font-medium';
|
|
2
|
+
const PANEL_LAYOUT = 'jwi-inline-flex jwi-min-h-[48px] jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-border-0 jwi-px-4 jwi-text-sm jwi-font-medium';
|
|
3
|
+
/**
|
|
4
|
+
* Primary CTA buttons share the same visual tokens; optional `styling.button`
|
|
5
|
+
* overrides border radius, background, and text color (CSS values).
|
|
6
|
+
*/
|
|
7
|
+
export function getWidgetPrimaryButtonPresentation(buttonStyling, variant, options) {
|
|
8
|
+
const b = buttonStyling;
|
|
9
|
+
const style = {};
|
|
10
|
+
if (b?.borderRadius?.trim()) {
|
|
11
|
+
style.borderRadius = b.borderRadius.trim();
|
|
12
|
+
}
|
|
13
|
+
if (b?.backgroundColor?.trim()) {
|
|
14
|
+
style.backgroundColor = b.backgroundColor.trim();
|
|
15
|
+
}
|
|
16
|
+
if (b?.textColor?.trim()) {
|
|
17
|
+
style.color = b.textColor.trim();
|
|
18
|
+
}
|
|
19
|
+
const layout = variant === 'hero' ? HERO_LAYOUT : PANEL_LAYOUT;
|
|
20
|
+
const parts = [layout, options?.extraClassName?.trim()].filter((p) => typeof p === 'string' && p.length > 0);
|
|
21
|
+
if (!b?.borderRadius?.trim()) {
|
|
22
|
+
parts.push('jwi-rounded-[10px]');
|
|
23
|
+
}
|
|
24
|
+
if (!b?.backgroundColor?.trim()) {
|
|
25
|
+
parts.push('jwi-bg-[#ef2b18] hover:jwi-bg-[#d92817]');
|
|
26
|
+
}
|
|
27
|
+
if (!b?.textColor?.trim()) {
|
|
28
|
+
parts.push('jwi-text-white');
|
|
29
|
+
}
|
|
30
|
+
if (options?.disabledWait &&
|
|
31
|
+
variant === 'hero' &&
|
|
32
|
+
!b?.backgroundColor?.trim()) {
|
|
33
|
+
parts.push('disabled:jwi-cursor-wait disabled:hover:jwi-bg-[#ef2b18]');
|
|
34
|
+
}
|
|
35
|
+
return { className: parts.join(' '), style };
|
|
36
|
+
}
|