@jotul/jotul-widgets 0.0.4 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/JotulWidget.css +1 -253
- package/dist/JotulWidget.d.ts +7 -35
- package/dist/JotulWidget.js +219 -149
- package/dist/api.d.ts +7 -0
- package/dist/api.js +118 -0
- package/dist/components/DealerCardSkeleton.d.ts +2 -0
- package/dist/components/DealerCardSkeleton.js +6 -0
- package/dist/components/FinderSearchRowSkeleton.d.ts +2 -0
- package/dist/components/FinderSearchRowSkeleton.js +6 -0
- package/dist/components/InquiryField.d.ts +10 -0
- package/dist/components/InquiryField.js +7 -0
- package/dist/components/ProductPageWidget.d.ts +27 -0
- package/dist/components/ProductPageWidget.js +16 -0
- package/dist/components/product-page/DealerList.d.ts +12 -0
- package/dist/components/product-page/DealerList.js +19 -0
- package/dist/components/product-page/InquiryForm.d.ts +13 -0
- package/dist/components/product-page/InquiryForm.js +18 -0
- package/dist/components/product-page/LocationSearch.d.ts +15 -0
- package/dist/components/product-page/LocationSearch.js +31 -0
- package/dist/components/product-page/StatusBanner.d.ts +6 -0
- package/dist/components/product-page/StatusBanner.js +8 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/i18n/widgetStrings.d.ts +39 -0
- package/dist/i18n/widgetStrings.js +87 -0
- package/dist/icons/ArrowRightIcon.d.ts +5 -0
- package/dist/icons/ArrowRightIcon.js +4 -0
- package/dist/icons/ButtonSpinner.d.ts +5 -0
- package/dist/icons/ButtonSpinner.js +4 -0
- package/dist/icons/PinIcon.d.ts +5 -0
- package/dist/icons/PinIcon.js +4 -0
- package/dist/icons/TelephoneIcon.d.ts +5 -0
- package/dist/icons/TelephoneIcon.js +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +69 -0
- package/dist/types.js +1 -0
- package/dist/utils/widgetPrimaryButtonPresentation.d.ts +14 -0
- package/dist/utils/widgetPrimaryButtonPresentation.js +36 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +75 -0
- package/dist/widgets/ProductPageWidget.d.ts +1 -0
- package/dist/widgets/ProductPageWidget.js +1 -0
- package/package.json +11 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { R10 } from '../../constants';
|
|
3
|
+
import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
4
|
+
import { TelephoneIcon } from '../../icons/TelephoneIcon';
|
|
5
|
+
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
6
|
+
import { asText, formatDistance, getDealerAddressLines, getDealerKey, getDealerName, } from '../../utils';
|
|
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
|
+
});
|
|
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) => {
|
|
12
|
+
const addressLines = getDealerAddressLines(dealer);
|
|
13
|
+
const phone = asText(dealer.phone);
|
|
14
|
+
const distance = formatDistance(dealer);
|
|
15
|
+
const dealerName = getDealerName(dealer, t.unknownDealer);
|
|
16
|
+
const isSelectedDealer = selectedDealerName === dealerName;
|
|
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)));
|
|
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" })] })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
2
|
+
import type { InquiryFormValues, JotulWidgetButtonStyling } from '../../types';
|
|
3
|
+
type InquiryFormProps = {
|
|
4
|
+
t: WidgetStrings;
|
|
5
|
+
buttonStyling?: JotulWidgetButtonStyling;
|
|
6
|
+
inquiryValues: InquiryFormValues;
|
|
7
|
+
inquiryError: string | null;
|
|
8
|
+
onInquiryClose: () => void;
|
|
9
|
+
onInquirySubmit: () => void;
|
|
10
|
+
onInquiryFieldChange: (key: keyof InquiryFormValues, value: string) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }: InquiryFormProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { InquiryField } from '../InquiryField';
|
|
3
|
+
import { R10 } from '../../constants';
|
|
4
|
+
import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
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
|
+
});
|
|
10
|
+
const trimmedProductName = inquiryValues.productName.trim();
|
|
11
|
+
const title = trimmedProductName !== ''
|
|
12
|
+
? t.sendInquiryTitleWithProduct.replace('{product}', trimmedProductName)
|
|
13
|
+
: t.sendInquiryTitle;
|
|
14
|
+
return (_jsxs("form", { onSubmit: (event) => {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
onInquirySubmit();
|
|
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 })] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
2
|
+
import type { LocationSuggestion } from '../../types';
|
|
3
|
+
type LocationSearchProps = {
|
|
4
|
+
t: WidgetStrings;
|
|
5
|
+
isManualSearchEnabled: boolean;
|
|
6
|
+
query: string;
|
|
7
|
+
suggestions: LocationSuggestion[];
|
|
8
|
+
suggestionsOpen: boolean;
|
|
9
|
+
isSuggestionsLoading: boolean;
|
|
10
|
+
onQueryChange: (value: string) => void;
|
|
11
|
+
onSuggestionSelect: (suggestion: LocationSuggestion) => void;
|
|
12
|
+
onDismissSuggestions: () => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function LocationSearch({ t, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, }: LocationSearchProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { R10 } from '../../constants';
|
|
4
|
+
import { PinIcon } from '../../icons/PinIcon';
|
|
5
|
+
export function LocationSearch({ t, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onSuggestionSelect, onDismissSuggestions, }) {
|
|
6
|
+
const hasQuery = query.trim().length > 0;
|
|
7
|
+
const showSuggestions = isManualSearchEnabled && hasQuery && suggestionsOpen;
|
|
8
|
+
const queryLength = query.trim().length;
|
|
9
|
+
const rootRef = useRef(null);
|
|
10
|
+
const inputRef = useRef(null);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
function handleOutsideClick(event) {
|
|
13
|
+
if (!showSuggestions)
|
|
14
|
+
return;
|
|
15
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
16
|
+
onDismissSuggestions();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
document.addEventListener('mousedown', handleOutsideClick);
|
|
20
|
+
return () => {
|
|
21
|
+
document.removeEventListener('mousedown', handleOutsideClick);
|
|
22
|
+
};
|
|
23
|
+
}, [onDismissSuggestions, showSuggestions]);
|
|
24
|
+
return (_jsxs("div", { ref: rootRef, className: "jwi-relative jwi-w-full jwi-flex-shrink-0", children: [_jsx("div", { className: `jwi-w-full jwi-overflow-hidden ${R10} jwi-border jwi-border-[#d8d2c7] jwi-bg-white`, children: _jsx("div", { className: "jwi-flex jwi-w-full jwi-min-w-0 jwi-flex-row jwi-items-stretch", children: _jsxs("div", { className: "jwi-flex jwi-min-w-0 jwi-flex-1 jwi-items-center jwi-gap-3 jwi-bg-[#FCFCFC] jwi-px-5", children: [_jsx("span", { className: "jwi-inline-flex jwi-shrink-0 jwi-text-black", children: _jsx(PinIcon, {}) }), _jsx("input", { ref: inputRef, type: "text", value: query, readOnly: !isManualSearchEnabled, placeholder: isManualSearchEnabled ? t.locationManualHint : t.useLocationHint, "aria-label": t.useLocationHint, onChange: (event) => onQueryChange(event.target.value), onKeyDown: (event) => {
|
|
25
|
+
if (event.key === 'Escape') {
|
|
26
|
+
onDismissSuggestions();
|
|
27
|
+
inputRef.current?.blur();
|
|
28
|
+
}
|
|
29
|
+
}, className: "jwi-min-w-0 jwi-h-[60px] jwi-w-full jwi-flex-1 jwi-border-0 jwi-bg-transparent jwi-text-[14px] jwi-leading-[1.4] jwi-text-[#111111] jwi-outline-none placeholder:jwi-text-[#767676]" })] }) }) }), showSuggestions && (_jsxs("div", { className: `jwi-absolute jwi-z-20 jwi-mt-1 jwi-w-full ${R10} jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-shadow-[0_8px_24px_rgba(17,17,17,0.08)]`, children: [isSuggestionsLoading && (_jsx("div", { className: "jwi-px-4 jwi-py-3 jwi-text-sm jwi-text-[#767676]", children: t.finding })), !isSuggestionsLoading && queryLength < 3 && (_jsx("div", { className: "jwi-px-4 jwi-py-3 jwi-text-sm jwi-text-[#767676]", children: t.locationTypeMore })), !isSuggestionsLoading && queryLength >= 3 && suggestions.length === 0 && (_jsx("div", { className: "jwi-px-4 jwi-py-3 jwi-text-sm jwi-text-[#767676]", children: t.locationNoResults })), !isSuggestionsLoading &&
|
|
30
|
+
suggestions.map((suggestion, index) => (_jsx("button", { type: "button", onClick: () => onSuggestionSelect(suggestion), className: "jwi-flex jwi-w-full jwi-cursor-pointer jwi-items-start jwi-border-0 jwi-bg-white jwi-px-4 jwi-py-3 jwi-text-left jwi-text-sm jwi-leading-[1.35] jwi-text-[#111111] hover:jwi-bg-[#f7f5f0]", children: suggestion.label }, `${suggestion.latitude}-${suggestion.longitude}-${index}`)))] }))] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { R10 } from '../../constants';
|
|
3
|
+
export function StatusBanner({ tone, children }) {
|
|
4
|
+
const toneClass = tone === 'success'
|
|
5
|
+
? 'jwi-border-[#b7e5c2] jwi-bg-[#eefbf2] jwi-text-[#1b5e20]'
|
|
6
|
+
: 'jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-text-[#8f2d21]';
|
|
7
|
+
return (_jsx("div", { className: `jwi-w-full jwi-flex-shrink-0 ${R10} jwi-border jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] ${toneClass}`, children: children }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** 10px radius for widget chrome (matches design spec). */
|
|
2
|
+
export declare const R10 = "jwi-rounded-[10px]";
|
|
3
|
+
export declare const FIND_DEALER_BUTTON_CLASS = "jwi-inline-flex jwi-w-full jwi-min-h-[56px] jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-[10px] jwi-border-0 jwi-bg-[#ef2b18] jwi-px-7 jwi-text-base jwi-font-medium jwi-text-white hover:jwi-bg-[#d92817] disabled:jwi-cursor-wait disabled:hover:jwi-bg-[#ef2b18]";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** 10px radius for widget chrome (matches design spec). */
|
|
2
|
+
export const R10 = 'jwi-rounded-[10px]';
|
|
3
|
+
export const FIND_DEALER_BUTTON_CLASS = `jwi-inline-flex jwi-w-full jwi-min-h-[56px] jwi-cursor-pointer jwi-items-center jwi-justify-center ${R10} jwi-border-0 jwi-bg-[#ef2b18] jwi-px-7 jwi-text-base jwi-font-medium jwi-text-white hover:jwi-bg-[#d92817] disabled:jwi-cursor-wait disabled:hover:jwi-bg-[#ef2b18]`;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type JotulWidgetLocale = 'no' | 'se';
|
|
2
|
+
export type WidgetStrings = {
|
|
3
|
+
genericWidgetError: string;
|
|
4
|
+
invalidPostcodeError: string;
|
|
5
|
+
invalidWidgetTypeError: string;
|
|
6
|
+
locationSearchFailed: string;
|
|
7
|
+
locationUnavailableBrowser: string;
|
|
8
|
+
locationPermissionDenied: string;
|
|
9
|
+
locationPositionUnavailable: string;
|
|
10
|
+
locationTimeout: string;
|
|
11
|
+
locationGenericFailure: string;
|
|
12
|
+
locationTypeMore: string;
|
|
13
|
+
locationNoResults: string;
|
|
14
|
+
locationManualHint: string;
|
|
15
|
+
findDealer: string;
|
|
16
|
+
loading: string;
|
|
17
|
+
useLocationHint: string;
|
|
18
|
+
find: string;
|
|
19
|
+
finding: string;
|
|
20
|
+
inquirySentSuccess: string;
|
|
21
|
+
goBack: string;
|
|
22
|
+
sendInquiryTitle: string;
|
|
23
|
+
sendInquiryTitleWithProduct: string;
|
|
24
|
+
dealerWillContact: string;
|
|
25
|
+
fieldName: string;
|
|
26
|
+
fieldEmail: string;
|
|
27
|
+
fieldPhone: string;
|
|
28
|
+
fieldComment: string;
|
|
29
|
+
formValidationRequired: string;
|
|
30
|
+
formValidationEmail: string;
|
|
31
|
+
dealersNearYou: string;
|
|
32
|
+
unknownDealer: string;
|
|
33
|
+
sendInquiryCta: string;
|
|
34
|
+
sendInquiryEditing: string;
|
|
35
|
+
readyDealerFinder: string;
|
|
36
|
+
readyWarrantyForm: string;
|
|
37
|
+
};
|
|
38
|
+
export declare const WIDGET_STRINGS: Record<JotulWidgetLocale, WidgetStrings>;
|
|
39
|
+
export declare function normalizeWidgetLocale(raw: string | undefined): JotulWidgetLocale;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const WIDGET_STRINGS = {
|
|
2
|
+
no: {
|
|
3
|
+
genericWidgetError: 'Forhandlerkartet er ikke tilgjengelig akkurat nå. Prøv igjen senere.',
|
|
4
|
+
invalidPostcodeError: 'Skriv inn et gyldig postnummer.',
|
|
5
|
+
invalidWidgetTypeError: 'Denne widgeten er ikke tilgjengelig akkurat nå.',
|
|
6
|
+
locationSearchFailed: 'Stedsøk kunne ikke fullføres. Prøv igjen.',
|
|
7
|
+
locationUnavailableBrowser: 'Posisjon er ikke tilgjengelig i denne nettleseren. Prøv en annen nettleser eller enhet.',
|
|
8
|
+
locationPermissionDenied: 'Tilgang til posisjon ble avslått. Tillat posisjon for dette nettstedet for å finne nærmeste forhandler.',
|
|
9
|
+
locationPositionUnavailable: 'Posisjonen din kunne ikke bestemmes. Prøv igjen om et øyeblikk.',
|
|
10
|
+
locationTimeout: 'Det tok for lang tid å finne posisjonen din. Prøv igjen.',
|
|
11
|
+
locationGenericFailure: 'Kunne ikke lese posisjonen din. Prøv igjen.',
|
|
12
|
+
locationTypeMore: 'Skriv minst 3 tegn for å søke etter en adresse.',
|
|
13
|
+
locationNoResults: 'Ingen adresser ble funnet. Prøv et annet søk.',
|
|
14
|
+
locationManualHint: 'Skriv inn adressen din for å finne forhandlere i nærheten',
|
|
15
|
+
findDealer: 'Finn forhandler',
|
|
16
|
+
loading: 'Laster …',
|
|
17
|
+
useLocationHint: 'Bruk posisjonen din for å finne forhandlere i nærheten',
|
|
18
|
+
find: 'Lokaliser',
|
|
19
|
+
finding: 'Lokaliserer …',
|
|
20
|
+
inquirySentSuccess: 'Forespørselen din er sendt.',
|
|
21
|
+
goBack: 'Tilbake',
|
|
22
|
+
sendInquiryTitle: 'Forespørsel',
|
|
23
|
+
sendInquiryTitleWithProduct: 'Forespørsel om {product}',
|
|
24
|
+
dealerWillContact: '{dealer} vil kontakte deg så snart som mulig.',
|
|
25
|
+
fieldName: 'Navn',
|
|
26
|
+
fieldEmail: 'E-post',
|
|
27
|
+
fieldPhone: 'Telefon',
|
|
28
|
+
fieldComment: 'Kommentar',
|
|
29
|
+
formValidationRequired: 'Fyll inn navn, e-post og telefon.',
|
|
30
|
+
formValidationEmail: 'Skriv inn en gyldig e-postadresse.',
|
|
31
|
+
dealersNearYou: '{count} forhandlere i nærheten',
|
|
32
|
+
unknownDealer: 'Ukjent forhandler',
|
|
33
|
+
sendInquiryCta: 'Send forespørsel',
|
|
34
|
+
sendInquiryEditing: 'Redigerer forespørsel',
|
|
35
|
+
readyDealerFinder: 'JotulWidget klar: dealerFinder',
|
|
36
|
+
readyWarrantyForm: 'JotulWidget klar: warrantyForm',
|
|
37
|
+
},
|
|
38
|
+
se: {
|
|
39
|
+
genericWidgetError: 'Aterforsaljarsokaren ar inte tillganglig just nu. Forsok igen senare.',
|
|
40
|
+
invalidPostcodeError: 'Ange ett giltigt postnummer.',
|
|
41
|
+
invalidWidgetTypeError: 'Den har widgeten ar inte tillganglig just nu.',
|
|
42
|
+
locationSearchFailed: 'Platssokningen kunde inte slutföras. Forsok igen.',
|
|
43
|
+
locationUnavailableBrowser: 'Position ar inte tillganglig i den har webblasaren. Prova en annan webblasare eller enhet.',
|
|
44
|
+
locationPermissionDenied: 'Platsatkomst nekades. Tillat plats for den har sidan for att hitta narmsta aterforsaljare.',
|
|
45
|
+
locationPositionUnavailable: 'Din plats kunde inte faststallas. Forsok igen om en stund.',
|
|
46
|
+
locationTimeout: 'Det tog for lang tid att hitta din plats. Forsok igen.',
|
|
47
|
+
locationGenericFailure: 'Det gick inte att lasa din plats. Forsok igen.',
|
|
48
|
+
locationTypeMore: 'Skriv minst 3 tecken for att soka efter en adress.',
|
|
49
|
+
locationNoResults: 'Inga matchande adresser hittades. Prova en annan sokning.',
|
|
50
|
+
locationManualHint: 'Ange din adress for att hitta aterforsaljare i narheten',
|
|
51
|
+
findDealer: 'Hitta aterforsaljare',
|
|
52
|
+
loading: 'Laddar …',
|
|
53
|
+
useLocationHint: 'Anvand din plats for att hitta aterforsaljare i narheten',
|
|
54
|
+
find: 'Lokalisera',
|
|
55
|
+
finding: 'Soker …',
|
|
56
|
+
inquirySentSuccess: 'Din forfragan har skickats.',
|
|
57
|
+
goBack: 'Ga tillbaka',
|
|
58
|
+
sendInquiryTitle: 'Forfragan',
|
|
59
|
+
sendInquiryTitleWithProduct: 'Forfragan om {product}',
|
|
60
|
+
dealerWillContact: '{dealer} kontaktar dig sa snart som mojligt.',
|
|
61
|
+
fieldName: 'Namn',
|
|
62
|
+
fieldEmail: 'E-post',
|
|
63
|
+
fieldPhone: 'Telefon',
|
|
64
|
+
fieldComment: 'Kommentar',
|
|
65
|
+
formValidationRequired: 'Fyll i namn, e-post och telefon.',
|
|
66
|
+
formValidationEmail: 'Ange en giltig e-postadress.',
|
|
67
|
+
dealersNearYou: '{count} aterforsaljare i narheten',
|
|
68
|
+
unknownDealer: 'Okand aterforsaljare',
|
|
69
|
+
sendInquiryCta: 'Skicka forfragan',
|
|
70
|
+
sendInquiryEditing: 'Redigerar forfragan',
|
|
71
|
+
readyDealerFinder: 'JotulWidget klar: dealerFinder',
|
|
72
|
+
readyWarrantyForm: 'JotulWidget klar: warrantyForm',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
export function normalizeWidgetLocale(raw) {
|
|
76
|
+
if (raw == null || raw.trim() === '') {
|
|
77
|
+
return 'no';
|
|
78
|
+
}
|
|
79
|
+
const n = raw.trim().toLowerCase();
|
|
80
|
+
if (n === 'no' || n === 'nb' || n === 'nn') {
|
|
81
|
+
return 'no';
|
|
82
|
+
}
|
|
83
|
+
if (n === 'se' || n === 'sv') {
|
|
84
|
+
return 'se';
|
|
85
|
+
}
|
|
86
|
+
return 'no';
|
|
87
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export function ArrowRightIcon({ className = 'jwi-h-[22px] jwi-w-[22px] jwi-shrink-0', }) {
|
|
3
|
+
return (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", className: className, children: _jsx("path", { d: "M2.75 11L19.25 11M19.25 11L11.4583 3.20836M19.25 11L11.4583 18.7917", stroke: "currentColor", strokeWidth: "2.08333", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function ButtonSpinner({ className = 'jwi-h-5 jwi-w-5 jwi-shrink-0 jwi-animate-spin jwi-text-white', }) {
|
|
3
|
+
return (_jsxs("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("circle", { className: "jwi-opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "jwi-opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function PinIcon({ className = 'jwi-h-4 jwi-w-4 jwi-shrink-0', }) {
|
|
3
|
+
return (_jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", className: className, children: [_jsx("path", { d: "M20 10C20 14.4183 12 22 12 22C12 22 4 14.4183 4 10C4 5.58172 7.58172 2 12 2C16.4183 2 20 5.58172 20 10Z", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M12 11C12.5523 11 13 10.5523 13 10C13 9.44772 12.5523 9 12 9C11.4477 9 11 9.44772 11 10C11 10.5523 11.4477 11 12 11Z", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export function TelephoneIcon({ className = 'jwi-h-4 jwi-w-4 jwi-shrink-0', }) {
|
|
3
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", className: className, children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.70895 0.833313H5.20957C5.41781 0.833313 5.60425 0.962386 5.67756 1.15731L6.64785 3.73723C6.68 3.82263 6.68813 3.91519 6.6714 4.00488L6.1852 6.61285C6.7828 8.01818 7.76987 8.96258 9.40693 9.80971L11.9836 9.31045C12.0751 9.29271 12.1697 9.30091 12.2567 9.33411L14.8446 10.3202C15.0385 10.3941 15.1665 10.58 15.1665 10.7874V13.177C15.1665 14.2606 14.2117 15.14 13.0949 14.897C11.0592 14.454 7.28767 13.3282 4.64633 10.6868C2.11624 8.15678 1.26855 4.66166 0.983187 2.77244C0.820367 1.69446 1.6849 0.833313 2.70895 0.833313Z", fill: "currentColor" }) }));
|
|
4
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { checkWidgetAuthorization, searchDealersByPostalCode, JotulWidget, type CheckWidgetAuthorizationOptions, type DealerSearchResponse, type JotulWidgetProps, type JotulWidgetType, type WidgetAuthClientResponse, } from './JotulWidget';
|
|
1
|
+
export { checkWidgetAuthorization, normalizeWidgetLocale, 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, searchDealersByPostalCode, JotulWidget, } from './JotulWidget';
|
|
2
|
+
export { checkWidgetAuthorization, normalizeWidgetLocale, searchDealersByCoordinates, searchDealersByPostalCode, JotulWidget, } from './JotulWidget';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type JotulWidgetType = 'productPage' | 'dealerFinder' | 'warrantyForm';
|
|
2
|
+
export type WidgetAuthClientResponse = {
|
|
3
|
+
apiVersion?: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
authorized?: boolean;
|
|
6
|
+
permissions?: {
|
|
7
|
+
dealers?: boolean;
|
|
8
|
+
};
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
export type DealerSearchResponse = {
|
|
12
|
+
apiVersion?: string;
|
|
13
|
+
ok: boolean;
|
|
14
|
+
type?: 'postalCode' | 'geolocation';
|
|
15
|
+
origin?: {
|
|
16
|
+
postalCode?: string | null;
|
|
17
|
+
postalCodeNumber?: number | null;
|
|
18
|
+
latitude?: number;
|
|
19
|
+
longitude?: number;
|
|
20
|
+
market?: string;
|
|
21
|
+
};
|
|
22
|
+
total?: number;
|
|
23
|
+
dealers?: Array<Record<string, unknown>>;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
export type LocationSuggestion = {
|
|
27
|
+
label: string;
|
|
28
|
+
latitude: number;
|
|
29
|
+
longitude: number;
|
|
30
|
+
};
|
|
31
|
+
export type LocationAutocompleteResponse = {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
type?: 'autocomplete';
|
|
34
|
+
suggestions?: LocationSuggestion[];
|
|
35
|
+
error?: string;
|
|
36
|
+
};
|
|
37
|
+
export type CheckWidgetAuthorizationOptions = {
|
|
38
|
+
endpoint?: string;
|
|
39
|
+
fetcher?: typeof fetch;
|
|
40
|
+
locale?: string;
|
|
41
|
+
brands?: string[];
|
|
42
|
+
};
|
|
43
|
+
/** CSS values for primary CTA buttons (find dealer, send inquiry, etc.). */
|
|
44
|
+
export type JotulWidgetButtonStyling = {
|
|
45
|
+
borderRadius?: string;
|
|
46
|
+
backgroundColor?: string;
|
|
47
|
+
textColor?: string;
|
|
48
|
+
};
|
|
49
|
+
export type JotulWidgetStyling = {
|
|
50
|
+
button?: JotulWidgetButtonStyling;
|
|
51
|
+
};
|
|
52
|
+
export type JotulWidgetProps = {
|
|
53
|
+
type?: string;
|
|
54
|
+
endpoint?: string;
|
|
55
|
+
className?: string;
|
|
56
|
+
productName?: string;
|
|
57
|
+
locale?: string;
|
|
58
|
+
brands?: string[];
|
|
59
|
+
styling?: JotulWidgetStyling;
|
|
60
|
+
};
|
|
61
|
+
export type DealerRecord = Record<string, unknown>;
|
|
62
|
+
export type InquiryFormValues = {
|
|
63
|
+
productName: string;
|
|
64
|
+
dealerName: string;
|
|
65
|
+
name: string;
|
|
66
|
+
email: string;
|
|
67
|
+
phone: string;
|
|
68
|
+
comment: string;
|
|
69
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DealerRecord, InquiryFormValues, JotulWidgetType } from './types';
|
|
2
|
+
import type { WidgetStrings } from './i18n/widgetStrings';
|
|
3
|
+
export declare const VALID_WIDGET_TYPES: JotulWidgetType[];
|
|
4
|
+
export declare function isWidgetType(value: string | undefined): value is JotulWidgetType;
|
|
5
|
+
export declare function asText(value: unknown): string | null;
|
|
6
|
+
export declare function formatDistance(dealer: DealerRecord): string | null;
|
|
7
|
+
export declare function getDealerKey(dealer: DealerRecord, index: number): string;
|
|
8
|
+
export declare function getDealerName(dealer: DealerRecord, unknownLabel: string): string;
|
|
9
|
+
export declare function getDealerAddressLines(dealer: DealerRecord): string[];
|
|
10
|
+
export declare function createInquiryFormValues(productName: string | undefined, dealerName: string): InquiryFormValues;
|
|
11
|
+
export declare function isValidEmail(value: string): boolean;
|
|
12
|
+
export declare function getSafeWidgetErrorMessage(error: unknown, t: WidgetStrings): string;
|
|
13
|
+
export declare function renderReadyState(type: JotulWidgetType, t: WidgetStrings): string | null;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const VALID_WIDGET_TYPES = [
|
|
2
|
+
'productPage',
|
|
3
|
+
'dealerFinder',
|
|
4
|
+
'warrantyForm',
|
|
5
|
+
];
|
|
6
|
+
export function isWidgetType(value) {
|
|
7
|
+
return value != null && VALID_WIDGET_TYPES.includes(value);
|
|
8
|
+
}
|
|
9
|
+
export function asText(value) {
|
|
10
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
11
|
+
}
|
|
12
|
+
function asNumber(value) {
|
|
13
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
14
|
+
}
|
|
15
|
+
export function formatDistance(dealer) {
|
|
16
|
+
const distanceKm = asNumber(dealer.distanceKm);
|
|
17
|
+
if (distanceKm !== null) {
|
|
18
|
+
return `${Math.round(distanceKm)} km`;
|
|
19
|
+
}
|
|
20
|
+
const postalDistance = asNumber(dealer.postalDistance);
|
|
21
|
+
if (postalDistance !== null) {
|
|
22
|
+
return `${Math.round(postalDistance)} km`;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
export function getDealerKey(dealer, index) {
|
|
27
|
+
return String(dealer.dealerId ?? dealer.name ?? index);
|
|
28
|
+
}
|
|
29
|
+
export function getDealerName(dealer, unknownLabel) {
|
|
30
|
+
return asText(dealer.name) ?? asText(dealer.dealerId) ?? unknownLabel;
|
|
31
|
+
}
|
|
32
|
+
export function getDealerAddressLines(dealer) {
|
|
33
|
+
const address = asText(dealer.address);
|
|
34
|
+
const postalCode = asText(dealer.postalCode);
|
|
35
|
+
return [address, postalCode].filter((value) => Boolean(value));
|
|
36
|
+
}
|
|
37
|
+
export function createInquiryFormValues(productName, dealerName) {
|
|
38
|
+
return {
|
|
39
|
+
productName: productName?.trim() ?? '',
|
|
40
|
+
dealerName,
|
|
41
|
+
name: '',
|
|
42
|
+
email: '',
|
|
43
|
+
phone: '',
|
|
44
|
+
comment: '',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function isValidEmail(value) {
|
|
48
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
49
|
+
}
|
|
50
|
+
export function getSafeWidgetErrorMessage(error, t) {
|
|
51
|
+
if (typeof error !== 'string') {
|
|
52
|
+
return t.genericWidgetError;
|
|
53
|
+
}
|
|
54
|
+
const normalized = error.trim();
|
|
55
|
+
if (!normalized) {
|
|
56
|
+
return t.genericWidgetError;
|
|
57
|
+
}
|
|
58
|
+
if (/postcode|postnummer/i.test(normalized)) {
|
|
59
|
+
return t.invalidPostcodeError;
|
|
60
|
+
}
|
|
61
|
+
if (/location search could not be completed|stedsøk kunne ikke fullføres/i.test(normalized)) {
|
|
62
|
+
return t.locationSearchFailed;
|
|
63
|
+
}
|
|
64
|
+
return t.genericWidgetError;
|
|
65
|
+
}
|
|
66
|
+
export function renderReadyState(type, t) {
|
|
67
|
+
switch (type) {
|
|
68
|
+
case 'productPage':
|
|
69
|
+
return null;
|
|
70
|
+
case 'dealerFinder':
|
|
71
|
+
return t.readyDealerFinder;
|
|
72
|
+
case 'warrantyForm':
|
|
73
|
+
return t.readyWarrantyForm;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProductPageWidget } from '../components/ProductPageWidget';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProductPageWidget } from '../components/ProductPageWidget';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jotul/jotul-widgets",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": [
|
|
@@ -18,9 +18,18 @@
|
|
|
18
18
|
"default": "./dist/index.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"autoprefixer": "^10.4.21",
|
|
23
|
+
"postcss": "^8.5.3",
|
|
24
|
+
"tailwindcss": "^3.4.17"
|
|
25
|
+
},
|
|
21
26
|
"scripts": {
|
|
22
|
-
"build": "
|
|
27
|
+
"build:css": "tailwindcss -i ./src/tw-entry.css -o ./dist/JotulWidget.css --minify",
|
|
28
|
+
"build": "tsc -p tsconfig.build.json && npm run build:css",
|
|
23
29
|
"clean": "rm -rf dist",
|
|
30
|
+
"watch:css": "tailwindcss -i ./src/tw-entry.css -o ./dist/JotulWidget.css --watch",
|
|
31
|
+
"watch:ts": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
|
|
32
|
+
"watch": "sh -c 'npm run watch:ts & npm run watch:css & wait'",
|
|
24
33
|
"prepublishOnly": "npm run clean && npm run build"
|
|
25
34
|
},
|
|
26
35
|
"peerDependencies": {
|