@sonic-equipment/ui 123.0.0 → 124.0.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/dist/buttons/add-to-cart-button/add-to-cart-button.js +2 -2
- package/dist/buttons/add-to-cart-button/connected-add-to-cart-button.js +8 -0
- package/dist/buttons/button/button.d.ts +2 -1
- package/dist/buttons/button/button.js +8 -3
- package/dist/buttons/button/button.module.css.js +1 -1
- package/dist/exports.d.ts +4 -0
- package/dist/forms/number-field/number-field.d.ts +2 -1
- package/dist/forms/number-field/number-field.js +3 -3
- package/dist/forms/select/select.d.ts +4 -1
- package/dist/forms/select/select.js +6 -3
- package/dist/index.js +4 -0
- package/dist/loading/progress-circle.d.ts +3 -1
- package/dist/loading/progress-circle.js +3 -3
- package/dist/loading/progress-circle.module.css.js +1 -1
- package/dist/pages/product-details-page/product-details.js +7 -0
- package/dist/shared/api/bff/model/bff.model.d.ts +1 -0
- package/dist/shared/ga/data-layer.d.ts +2 -0
- package/dist/shared/ga/data-layer.js +13 -0
- package/dist/shared/ga/google-analytics-provider.d.ts +8 -0
- package/dist/shared/ga/google-analytics-provider.js +15 -0
- package/dist/shared/ga/types.d.ts +34 -0
- package/dist/shared/ga/types.js +8 -0
- package/dist/shared/ga/use-data-layer.d.ts +50 -0
- package/dist/shared/ga/use-data-layer.js +89 -0
- package/dist/shared/model/currency.d.ts +1 -0
- package/dist/shared/model/currency.js +1 -0
- package/dist/styles.css +137 -30
- package/package.json +1 -1
|
@@ -72,7 +72,7 @@ function SpinnerState({ isDisabled, onChange, onManualInput, quantity, }) {
|
|
|
72
72
|
useEffect(() => {
|
|
73
73
|
setInternalQuantity(quantity);
|
|
74
74
|
}, [quantity]);
|
|
75
|
-
return (jsx(NumberField, { withButtons: true, autoGrow: true, formatOptions: {
|
|
75
|
+
return (jsx(NumberField, { withButtons: true, autoGrow: true, "data-test-selector": "quantity", formatOptions: {
|
|
76
76
|
maximumFractionDigits: 0,
|
|
77
77
|
style: 'decimal',
|
|
78
78
|
useGrouping: false,
|
|
@@ -95,7 +95,7 @@ function ManualInputState({ isDisabled, onCancel, onConfirm, quantity, }) {
|
|
|
95
95
|
if (e.key === 'Escape')
|
|
96
96
|
onCancel();
|
|
97
97
|
};
|
|
98
|
-
return (jsxs("div", { className: styles['manual-input-container'], children: [jsx("div", { className: styles['left-button-spacer'] }), jsx(NumberField, { autoFocus: true, autoGrow: true, defaultValue: quantity ? ensureNumber(quantity) : undefined, formatOptions: {
|
|
98
|
+
return (jsxs("div", { className: styles['manual-input-container'], children: [jsx("div", { className: styles['left-button-spacer'] }), jsx(NumberField, { autoFocus: true, autoGrow: true, "data-test-selector": "quantity", defaultValue: quantity ? ensureNumber(quantity) : undefined, formatOptions: {
|
|
99
99
|
maximumFractionDigits: 0,
|
|
100
100
|
style: 'decimal',
|
|
101
101
|
useGrouping: false,
|
|
@@ -5,11 +5,13 @@ import { useAddProductToCurrentCart } from '../../shared/api/storefront/hooks/ca
|
|
|
5
5
|
import { useDeleteCartLineById } from '../../shared/api/storefront/hooks/cart/use-delete-cart-line-by-id.js';
|
|
6
6
|
import { useFetchCurrentCartLines } from '../../shared/api/storefront/hooks/cart/use-fetch-current-cart-lines.js';
|
|
7
7
|
import { useUpdateCartLineById } from '../../shared/api/storefront/hooks/cart/use-update-cart-line-by-id.js';
|
|
8
|
+
import { useDataLayer } from '../../shared/ga/use-data-layer.js';
|
|
8
9
|
import { useCartEvents } from '../../shared/providers/cart-provider.js';
|
|
9
10
|
import { useToast } from '../../toast/use-toast.js';
|
|
10
11
|
import { AddToCartButton } from './add-to-cart-button.js';
|
|
11
12
|
|
|
12
13
|
const ConnectedAddToCartButton = ({ onAddToCart, productId }) => {
|
|
14
|
+
const { createEcommerceEvent, dataLayer } = useDataLayer();
|
|
13
15
|
const { isPending: isPendingAddToCart, mutate: addToCart } = useAddProductToCurrentCart();
|
|
14
16
|
const { data: cartLines, isLoading: isLoadingCartLines } = useFetchCurrentCartLines();
|
|
15
17
|
const { isPending: isPendingDeleteCartLine, mutate: deleteCartLine } = useDeleteCartLineById();
|
|
@@ -77,6 +79,12 @@ const ConnectedAddToCartButton = ({ onAddToCart, productId }) => {
|
|
|
77
79
|
onSuccess: cartLine => {
|
|
78
80
|
onAddToCart?.({ cartLine });
|
|
79
81
|
onCartLineAdded?.(cartLine);
|
|
82
|
+
dataLayer.push(createEcommerceEvent({
|
|
83
|
+
cartLine,
|
|
84
|
+
event: {
|
|
85
|
+
event: 'add_to_cart',
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
80
88
|
addToast({
|
|
81
89
|
body: (jsx(FormattedMessage, { id: "The product has been added to your cart." })),
|
|
82
90
|
isUserDismissable: false,
|
|
@@ -9,10 +9,11 @@ export interface ButtonProps {
|
|
|
9
9
|
icon?: React.ReactNode;
|
|
10
10
|
iconPosition?: 'left' | 'right';
|
|
11
11
|
isDisabled?: boolean;
|
|
12
|
+
isLoading?: string | ReactNode | boolean;
|
|
12
13
|
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
13
14
|
size?: 'sm' | 'md' | 'lg';
|
|
14
15
|
type?: 'button' | 'submit' | 'reset';
|
|
15
16
|
variant?: 'solid' | 'outline' | 'ghost';
|
|
16
17
|
withArrow?: boolean;
|
|
17
18
|
}
|
|
18
|
-
export declare function Button({ _pseudo, children, className, color, condensed, 'data-test-selector': dataTestSelector, icon, iconPosition, isDisabled, onClick: _onClick, size, type, variant, withArrow, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function Button({ _pseudo, children, className, color, condensed, 'data-test-selector': dataTestSelector, icon, iconPosition, isDisabled, isLoading, onClick: _onClick, size, type, variant, withArrow, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { isValidElement } from 'react';
|
|
3
4
|
import clsx from 'clsx';
|
|
5
|
+
import { ProgressCircle } from '../../loading/progress-circle.js';
|
|
4
6
|
import { GlyphsArrowBoldCapsRightIcon } from '../../icons/glyph/glyphs-arrow-boldcaps-right-icon.js';
|
|
5
7
|
import buttonStyles from './button.module.css.js';
|
|
6
8
|
|
|
7
|
-
function Button({ _pseudo = 'none', children, className, color = 'primary', condensed, 'data-test-selector': dataTestSelector, icon, iconPosition = 'left', isDisabled, onClick: _onClick, size = 'lg', type = 'button', variant = 'solid', withArrow = false, }) {
|
|
9
|
+
function Button({ _pseudo = 'none', children, className, color = 'primary', condensed, 'data-test-selector': dataTestSelector, icon, iconPosition = 'left', isDisabled, isLoading = false, onClick: _onClick, size = 'lg', type = 'button', variant = 'solid', withArrow = false, }) {
|
|
8
10
|
const showIconOnLeft = icon && iconPosition === 'left';
|
|
9
11
|
const showIconOnRight = icon && iconPosition === 'right';
|
|
10
12
|
const onClick = (e) => {
|
|
@@ -15,7 +17,10 @@ function Button({ _pseudo = 'none', children, className, color = 'primary', cond
|
|
|
15
17
|
return;
|
|
16
18
|
e.preventDefault();
|
|
17
19
|
};
|
|
18
|
-
return (
|
|
20
|
+
return (jsx("button", { className: clsx({ [buttonStyles.condensed]: condensed }, { [buttonStyles.icon]: icon }, { [buttonStyles['loading-uninformative']]: isLoading === true }, {
|
|
21
|
+
[buttonStyles['loading-informative']]: isLoading &&
|
|
22
|
+
(typeof isLoading === 'string' || isValidElement(isLoading)),
|
|
23
|
+
}, buttonStyles.button, buttonStyles[variant], buttonStyles[size], buttonStyles[color], buttonStyles[_pseudo], className), "data-disabled": isDisabled ? true : undefined, "data-test-selector": dataTestSelector, disabled: isDisabled, onClick: onClick, type: type, children: jsxs(Fragment, { children: [showIconOnLeft && jsx("span", { className: buttonStyles.icon, children: icon }), jsx("span", { className: buttonStyles.children, children: isLoading ? (isLoading === true ? children : isLoading) : children }), withArrow && (jsx(GlyphsArrowBoldCapsRightIcon, { className: buttonStyles['right-arrow-icon'] })), showIconOnRight && jsx("span", { className: buttonStyles.icon, children: icon }), isLoading && (jsx(ProgressCircle, { className: buttonStyles.spinner, size: "sm", variant: color === 'primary' ? 'white' : 'gray' }))] }) }));
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export { Button };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var buttonStyles = {"button":"button-module-V4meK","icon":"button-module-XaNWz","
|
|
1
|
+
var buttonStyles = {"button":"button-module-V4meK","icon":"button-module-XaNWz","loading-uninformative":"button-module-LwuW2","loading-informative":"button-module-U5IxM","spinner":"button-module-13ndF","children":"button-module-vqRq-","primary":"button-module-tmyk8","secondary":"button-module--1bCH","sm":"button-module-Pbwz7","md":"button-module-GVTEW","condensed":"button-module-GKHQc","lg":"button-module-nyNY8","outline":"button-module-vq9GI","solid":"button-module-AjvlY","hover":"button-module-YzPAr","focus":"button-module--xzsY","active":"button-module-XMFzj","ghost":"button-module-f4UVe","right-arrow-icon":"button-module-ydQAo"};
|
|
2
2
|
|
|
3
3
|
export { buttonStyles as default };
|
package/dist/exports.d.ts
CHANGED
|
@@ -217,6 +217,10 @@ export * from './shared/api/storefront/services/website-service';
|
|
|
217
217
|
export * from './shared/api/storefront/services/wishlist-service';
|
|
218
218
|
export * from './shared/feature-flags/use-feature-flags';
|
|
219
219
|
export * from './shared/fetch/request';
|
|
220
|
+
export * from './shared/ga/data-layer';
|
|
221
|
+
export * from './shared/ga/google-analytics-provider';
|
|
222
|
+
export * from './shared/ga/types';
|
|
223
|
+
export * from './shared/ga/use-data-layer';
|
|
220
224
|
export * from './shared/hooks/use-breakpoint';
|
|
221
225
|
export * from './shared/hooks/use-cookie';
|
|
222
226
|
export * from './shared/hooks/use-debounced-callback';
|
|
@@ -4,6 +4,7 @@ export type NumberFieldSize = 'md' | 'lg';
|
|
|
4
4
|
interface NumberFieldProps {
|
|
5
5
|
autoFocus?: boolean;
|
|
6
6
|
autoGrow?: boolean;
|
|
7
|
+
'data-test-selector'?: string;
|
|
7
8
|
defaultValue?: number;
|
|
8
9
|
formatOptions?: Intl.NumberFormatOptions;
|
|
9
10
|
isDisabled?: boolean;
|
|
@@ -29,5 +30,5 @@ interface NumberFieldProps {
|
|
|
29
30
|
* This component is used to create a number field.
|
|
30
31
|
* This field can also grow when a user types in text.
|
|
31
32
|
*/
|
|
32
|
-
export declare function NumberField({ autoFocus, autoGrow, defaultValue, formatOptions, isDisabled, isInvalid, isReadOnly, isRequired, label, maxLength, maxValue, minValue, name, onChange, onInput, onKeyUp, placeholder, showLabel, size, validate, value, withButtons, }: NumberFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare function NumberField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, formatOptions, isDisabled, isInvalid, isReadOnly, isRequired, label, maxLength, maxValue, minValue, name, onChange, onInput, onKeyUp, placeholder, showLabel, size, validate, value, withButtons, }: NumberFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
33
34
|
export {};
|
|
@@ -15,12 +15,12 @@ import styles from './number-field.module.css.js';
|
|
|
15
15
|
* This component is used to create a number field.
|
|
16
16
|
* This field can also grow when a user types in text.
|
|
17
17
|
*/
|
|
18
|
-
function NumberField({ autoFocus, autoGrow, defaultValue, formatOptions = { style: 'decimal', useGrouping: false }, isDisabled, isInvalid, isReadOnly, isRequired, label, maxLength, maxValue, minValue, name, onChange, onInput, onKeyUp, placeholder, showLabel = false, size = 'lg', validate, value, withButtons, }) {
|
|
18
|
+
function NumberField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, formatOptions = { style: 'decimal', useGrouping: false }, isDisabled, isInvalid, isReadOnly, isRequired, label, maxLength, maxValue, minValue, name, onChange, onInput, onKeyUp, placeholder, showLabel = false, size = 'lg', validate, value, withButtons, }) {
|
|
19
19
|
const inputRef = useRef(null);
|
|
20
|
-
return (jsxs(NumberField$1, { "aria-label": label, autoFocus: autoFocus, className: clsx(styles.field, styles[size]), defaultValue: defaultValue, formatOptions: formatOptions, isDisabled: isDisabled, isInvalid: isInvalid, isReadOnly: isReadOnly, isRequired: isRequired, maxValue: maxValue, minValue: minValue, name: name, onChange: onChange, onInput: onInput, validate: validate, value: value, children: [showLabel && jsx(Label, { isRequired: isRequired, children: label }), jsxs("div", { className: styles['button-input-container'], children: [withButtons && (jsx(Button, { "data-test-selector": "decrement", isDisabled: isDisabled,
|
|
20
|
+
return (jsxs(NumberField$1, { "aria-label": label, autoFocus: autoFocus, className: clsx(styles.field, styles[size]), "data-test-selector": dataTestSelector, defaultValue: defaultValue, formatOptions: formatOptions, isDisabled: isDisabled, isInvalid: isInvalid, isReadOnly: isReadOnly, isRequired: isRequired, maxValue: maxValue, minValue: minValue, name: name, onChange: onChange, onInput: onInput, validate: validate, value: value, children: [showLabel && jsx(Label, { isRequired: isRequired, children: label }), jsxs("div", { className: styles['button-input-container'], children: [withButtons && (jsx(Button, { "data-test-selector": "decrement", isDisabled: isDisabled,
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
22
22
|
// @ts-expect-error
|
|
23
|
-
onClick: e => e.preventDefault(), onPressStart: e => e.target.focus(), slot: "decrement", children: (value || 0) <= 1 ? jsx(StrokeTrashIcon, {}) : jsx(StrokeCollapseIcon, {}) })), jsx(Input, { ref: inputRef, autoGrow: autoGrow, label: label, maxLength: maxLength, onFocus: e => (e.target.selectionStart = e.target.value.length || 0), onKeyUp: e => onKeyUp?.(e), placeholder: placeholder, size: size }), withButtons && (jsx(Button, { "data-test-selector": "increment", isDisabled: isDisabled,
|
|
23
|
+
onClick: e => e.preventDefault(), onPressStart: e => e.target.focus(), slot: "decrement", children: (value || 0) <= 1 ? jsx(StrokeTrashIcon, {}) : jsx(StrokeCollapseIcon, {}) })), jsx(Input, { ref: inputRef, autoGrow: autoGrow, "data-test-selector": "value", label: label, maxLength: maxLength, onFocus: e => (e.target.selectionStart = e.target.value.length || 0), onKeyUp: e => onKeyUp?.(e), placeholder: placeholder, size: size }), withButtons && (jsx(Button, { "data-test-selector": "increment", isDisabled: isDisabled,
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
25
25
|
// @ts-expect-error
|
|
26
26
|
onClick: e => e.preventDefault(), onPressStart: e => e.target.focus(), slot: "increment", children: jsx(StrokeExpandIcon, {}) }))] }), jsx(FieldError, {})] }));
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export interface SelectProps<T> {
|
|
2
2
|
'data-test-selector'?: string;
|
|
3
|
+
defaultSelectedOption?: keyof T;
|
|
3
4
|
isDisabled?: boolean;
|
|
5
|
+
isRequired?: boolean;
|
|
4
6
|
label: string;
|
|
7
|
+
name?: string;
|
|
5
8
|
onChange: (value: keyof T) => void;
|
|
6
9
|
options: T;
|
|
7
10
|
placeholder?: string;
|
|
@@ -11,4 +14,4 @@ export interface SelectProps<T> {
|
|
|
11
14
|
size?: 'sm' | 'md';
|
|
12
15
|
variant?: 'outline' | 'solid';
|
|
13
16
|
}
|
|
14
|
-
export declare function Select<T extends
|
|
17
|
+
export declare function Select<T extends Record<string, string>>({ 'data-test-selector': dataTestSelector, defaultSelectedOption, isDisabled, isRequired, label, name, onChange, options, placeholder, selectedOption, showLabel, showPlaceholder, size, variant, }: SelectProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,10 +5,11 @@ import { Select as Select$1, Button, SelectValue, Popover, ListBox, Section, Hea
|
|
|
5
5
|
import clsx from 'clsx';
|
|
6
6
|
import { GlyphsChevronsSlimDownIcon } from '../../icons/glyph/glyphs-chevrons-slim-down-icon.js';
|
|
7
7
|
import { StrokeCheckmarkIcon } from '../../icons/stroke/stroke-checkmark-icon.js';
|
|
8
|
+
import { FieldError } from '../field-error/field-error.js';
|
|
8
9
|
import { Label } from '../label/label.js';
|
|
9
10
|
import styles from './select.module.css.js';
|
|
10
11
|
|
|
11
|
-
function Select({ 'data-test-selector': dataTestSelector, isDisabled = false, label, onChange, options, placeholder, selectedOption, showLabel = true, showPlaceholder = true, size = 'md', variant = 'outline', }) {
|
|
12
|
+
function Select({ 'data-test-selector': dataTestSelector, defaultSelectedOption, isDisabled = false, isRequired = false, label, name, onChange, options, placeholder, selectedOption, showLabel = true, showPlaceholder = true, size = 'md', variant = 'outline', }) {
|
|
12
13
|
const selectRef = useRef(null);
|
|
13
14
|
useEffect(() => {
|
|
14
15
|
const updateWidth = () => {
|
|
@@ -23,9 +24,11 @@ function Select({ 'data-test-selector': dataTestSelector, isDisabled = false, la
|
|
|
23
24
|
window.addEventListener('resize', updateWidth);
|
|
24
25
|
return () => window?.removeEventListener('resize', updateWidth);
|
|
25
26
|
}, []);
|
|
26
|
-
return (jsxs(Select$1, { ref: selectRef, "aria-label": label, className: clsx(styles.select, styles[size], styles[variant]), "data-test-selector": dataTestSelector,
|
|
27
|
+
return (jsxs(Select$1, { ref: selectRef, "aria-label": label, className: clsx(styles.select, styles[size], styles[variant]), "data-test-selector": dataTestSelector, defaultSelectedKey: defaultSelectedOption === undefined
|
|
28
|
+
? undefined
|
|
29
|
+
: String(defaultSelectedOption), isDisabled: isDisabled, isRequired: isRequired, name: name, onSelectionChange: selected => onChange(selected), placeholder: placeholder || label, selectedKey: selectedOption === undefined ? undefined : String(selectedOption), children: [showLabel && jsx(Label, { isRequired: isRequired, children: label }), jsxs(Button, { className: styles.button, children: [jsx(SelectValue, { "data-test-selector": "value" }), jsx(GlyphsChevronsSlimDownIcon, { "aria-hidden": "true", className: styles.chevron })] }), jsx(FieldError, {}), jsx(Popover, { ref: ref =>
|
|
27
30
|
// Workaround for react/react-aria #1513
|
|
28
|
-
ref?.addEventListener('touchend', e => e.preventDefault()), className: clsx(styles.popover, styles[variant]), placement: "bottom left",
|
|
31
|
+
ref?.addEventListener('touchend', e => e.preventDefault()), className: clsx(styles.popover, styles[variant]), placement: "bottom left", children: jsx(ListBox, { className: styles.listbox, "data-test-selector": dataTestSelector ? `${dataTestSelector}_options` : undefined, children: jsxs(Section, { children: [showPlaceholder && (jsx(Header, { className: styles.header, children: placeholder || label })), Object.entries(options).map(([key, value]) => (jsxs(ListBoxItem, { "aria-label": value, className: styles.item, id: key, textValue: value, children: [selectedOption === key && (jsx("span", { slot: "description", children: jsx(StrokeCheckmarkIcon, { className: styles.check }) })), jsx("span", { slot: "label", children: value })] }, key)))] }) }) })] }));
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export { Select };
|
package/dist/index.js
CHANGED
|
@@ -219,6 +219,10 @@ export { fetchCountries, fetchCountriesLanguages, fetchCountriesWithLanguages, f
|
|
|
219
219
|
export { WishListNameAlreadyExistsError, addWishListItemToWishList, createWishList, deleteWishList, deleteWishListItemFromWishList, getWishList, getWishListItemsByWishListId, getWishLists } from './shared/api/storefront/services/wishlist-service.js';
|
|
220
220
|
export { useFeatureFlags } from './shared/feature-flags/use-feature-flags.js';
|
|
221
221
|
export { BadRequestError, ForbiddenRequestError, InternalServerErrorRequestError, NotFoundRequestError, RequestError, TimeoutRequestError, UnauthorizedRequestError, UnprocessableContentRequestError, isRequestError, request } from './shared/fetch/request.js';
|
|
222
|
+
export { dataLayer } from './shared/ga/data-layer.js';
|
|
223
|
+
export { GoogleAnalyticsProvider, useGoogleAnalyticsProvider } from './shared/ga/google-analytics-provider.js';
|
|
224
|
+
export { isGAEvent } from './shared/ga/types.js';
|
|
225
|
+
export { useDataLayer } from './shared/ga/use-data-layer.js';
|
|
222
226
|
export { useBreakpoint } from './shared/hooks/use-breakpoint.js';
|
|
223
227
|
export { useCookie } from './shared/hooks/use-cookie.js';
|
|
224
228
|
export { useDebouncedCallback } from './shared/hooks/use-debounced-callback.js';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export interface ProgressCircleProps {
|
|
2
2
|
className?: string;
|
|
3
|
+
size?: 'sm' | 'lg';
|
|
4
|
+
variant?: 'white' | 'gray';
|
|
3
5
|
}
|
|
4
|
-
export declare function ProgressCircle({ className }: ProgressCircleProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function ProgressCircle({ className, size, variant, }: ProgressCircleProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import styles from './progress-circle.module.css.js';
|
|
4
4
|
|
|
5
|
-
function ProgressCircle({ className }) {
|
|
6
|
-
return (
|
|
5
|
+
function ProgressCircle({ className, size = 'lg', variant = 'white', }) {
|
|
6
|
+
return (jsx("span", { className: clsx(styles['progress-circle'], styles[variant], styles[size], className) }));
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export { ProgressCircle };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var styles = {"progress-circle":"progress-circle-module-4nweP","spin":"progress-circle-module-kCf7K"};
|
|
1
|
+
var styles = {"progress-circle":"progress-circle-module-4nweP","spin":"progress-circle-module-kCf7K","white":"progress-circle-module-SHNFy","gray":"progress-circle-module-TRZWO","sm":"progress-circle-module--Gspu","md":"progress-circle-module-4tRZd"};
|
|
2
2
|
|
|
3
3
|
export { styles as default };
|
|
@@ -4,6 +4,7 @@ import { ConnectedProductCard } from '../../cards/product-card/connected-product
|
|
|
4
4
|
import { CardCarousel } from '../../carousel/card-carousel/card-carousel.js';
|
|
5
5
|
import { ProductUSPCarousel } from '../../carousel/usp-carousel/product-usp-carousel.js';
|
|
6
6
|
import { FormattedMessage } from '../../intl/formatted-message.js';
|
|
7
|
+
import { useDataLayer } from '../../shared/ga/use-data-layer.js';
|
|
7
8
|
import { Heading } from '../../typography/heading/heading.js';
|
|
8
9
|
import { ProductDetailsPageLayout } from '../layouts/product-details-page-layout/product-details-page-layout.js';
|
|
9
10
|
import { Page } from '../page/page.js';
|
|
@@ -13,6 +14,12 @@ import { ProductDetailsRecentlyViewed } from './components/product-details-recen
|
|
|
13
14
|
|
|
14
15
|
function ProductDetails({ data, priceComponent, recentlyViewedComponent, }) {
|
|
15
16
|
const { breadCrumb, included, product, recentlyViewed, usps } = data;
|
|
17
|
+
useDataLayer({
|
|
18
|
+
event: {
|
|
19
|
+
event: 'view_item',
|
|
20
|
+
},
|
|
21
|
+
product,
|
|
22
|
+
});
|
|
16
23
|
return (jsx(Page, { breadCrumb: breadCrumb, children: jsx(ProductDetailsPageLayout, { imageGallery: jsx(ProductDetailImages, { images: product.images }), included: included !== undefined &&
|
|
17
24
|
included.length > 0 && (jsxs(Fragment, { children: [jsx(Heading, { size: "s", tag: "h2", children: jsx(FormattedMessage, { id: "Includes" }) }), jsx(CardCarousel, { allowExpandToGrid: true, hasOverflow: true, cards: included.map(product => (jsx(ConnectedProductCard, { href: product.href, id: product.productId, image: {
|
|
18
25
|
fit: 'contain',
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const _dataLayer = [];
|
|
2
|
+
_dataLayer.push = (function (_push) {
|
|
3
|
+
return (...items) => {
|
|
4
|
+
// eslint-disable-next-line no-console
|
|
5
|
+
console.log('dataLayer.push', items.length === 1 ? items[0] : items);
|
|
6
|
+
return _push(...items);
|
|
7
|
+
};
|
|
8
|
+
})(_dataLayer.push.bind(_dataLayer));
|
|
9
|
+
const dataLayer =
|
|
10
|
+
// eslint-disable-next-line ssr-friendly/no-dom-globals-in-module-scope
|
|
11
|
+
typeof window === 'undefined' ? _dataLayer : (window.dataLayer ?? _dataLayer);
|
|
12
|
+
|
|
13
|
+
export { dataLayer };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export declare function useGoogleAnalyticsProvider(): {
|
|
3
|
+
customerNumber: string | undefined;
|
|
4
|
+
isFetching: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function GoogleAnalyticsProvider({ children }?: {
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}): string | number | boolean | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<ReactNode> | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useFetchSession } from '../api/storefront/hooks/authentication/use-fetch-session.js';
|
|
3
|
+
|
|
4
|
+
function useGoogleAnalyticsProvider() {
|
|
5
|
+
const { data: session, isFetching } = useFetchSession();
|
|
6
|
+
return { customerNumber: session?.billTo?.customerNumber, isFetching };
|
|
7
|
+
}
|
|
8
|
+
function GoogleAnalyticsProvider({ children = null } = { children: null }) {
|
|
9
|
+
const { isFetching } = useGoogleAnalyticsProvider();
|
|
10
|
+
if (isFetching)
|
|
11
|
+
return null;
|
|
12
|
+
return children;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { GoogleAnalyticsProvider, useGoogleAnalyticsProvider };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface GABaseEvent {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
event: string;
|
|
4
|
+
eventAction?: string;
|
|
5
|
+
eventCategory?: string;
|
|
6
|
+
eventLabel?: string;
|
|
7
|
+
eventValue?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function isGAEvent(event: any): event is GABaseEvent;
|
|
10
|
+
export interface GAEcommerceEvent extends GABaseEvent {
|
|
11
|
+
ecommerce?: {
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
currencyCode?: string;
|
|
14
|
+
items?: Array<{
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
item_category?: string;
|
|
17
|
+
item_id?: string;
|
|
18
|
+
item_name?: string;
|
|
19
|
+
price?: number;
|
|
20
|
+
quantity?: number;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface GAPageviewEvent extends GABaseEvent {
|
|
25
|
+
page_location?: string;
|
|
26
|
+
page_path?: string;
|
|
27
|
+
page_title?: string;
|
|
28
|
+
}
|
|
29
|
+
export type GADataLayerEvent = GABaseEvent | GAEcommerceEvent | GAPageviewEvent;
|
|
30
|
+
declare global {
|
|
31
|
+
interface Window {
|
|
32
|
+
dataLayer?: GADataLayerEvent[];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CartModel } from '../api/storefront/model/shop.model';
|
|
2
|
+
import { type GADataLayerEvent, type GAEcommerceEvent } from './types';
|
|
3
|
+
interface GAProductModel {
|
|
4
|
+
price?: number;
|
|
5
|
+
productNumber: string;
|
|
6
|
+
productTitle: string;
|
|
7
|
+
unitListPrice: number;
|
|
8
|
+
unitListPriceDisplay: string;
|
|
9
|
+
}
|
|
10
|
+
interface GACartModel {
|
|
11
|
+
cartLines: CartModel['cartLines'];
|
|
12
|
+
currencySymbol: string;
|
|
13
|
+
orderSubTotal: number;
|
|
14
|
+
}
|
|
15
|
+
interface GACartLineModel {
|
|
16
|
+
erpNumber: string;
|
|
17
|
+
pricing: {
|
|
18
|
+
actualPrice: number;
|
|
19
|
+
actualPriceDisplay: string;
|
|
20
|
+
} | null;
|
|
21
|
+
productName: string;
|
|
22
|
+
qtyOrdered: number | null;
|
|
23
|
+
}
|
|
24
|
+
interface CreateEcommerceEventArgsWithCartLine {
|
|
25
|
+
cartLine: GACartLineModel;
|
|
26
|
+
event: GAEcommerceEvent;
|
|
27
|
+
}
|
|
28
|
+
interface CreateEcommerceEventArgsWithCart {
|
|
29
|
+
event: GAEcommerceEvent;
|
|
30
|
+
product: GAProductModel;
|
|
31
|
+
}
|
|
32
|
+
interface CreateEcommerceEventArgsWithProduct {
|
|
33
|
+
cart: GACartModel;
|
|
34
|
+
event: GAEcommerceEvent;
|
|
35
|
+
}
|
|
36
|
+
interface CreateEcommerceEvent {
|
|
37
|
+
(args: CreateEcommerceEventArgsWithCart): GAEcommerceEvent;
|
|
38
|
+
(args: CreateEcommerceEventArgsWithCartLine): GAEcommerceEvent;
|
|
39
|
+
(args: CreateEcommerceEventArgsWithProduct): GAEcommerceEvent;
|
|
40
|
+
}
|
|
41
|
+
interface UseDataLayerReturnValue {
|
|
42
|
+
createEcommerceEvent: CreateEcommerceEvent;
|
|
43
|
+
dataLayer: GADataLayerEvent[];
|
|
44
|
+
}
|
|
45
|
+
export declare function useDataLayer(): UseDataLayerReturnValue;
|
|
46
|
+
export declare function useDataLayer(event: GADataLayerEvent): UseDataLayerReturnValue;
|
|
47
|
+
export declare function useDataLayer(args: CreateEcommerceEventArgsWithProduct): UseDataLayerReturnValue;
|
|
48
|
+
export declare function useDataLayer(args: CreateEcommerceEventArgsWithCartLine): UseDataLayerReturnValue;
|
|
49
|
+
export declare function useDataLayer(args: CreateEcommerceEventArgsWithCart): UseDataLayerReturnValue;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
import { currencySymbolToISO } from '../model/currency.js';
|
|
3
|
+
import { dataLayer } from './data-layer.js';
|
|
4
|
+
import { useGoogleAnalyticsProvider } from './google-analytics-provider.js';
|
|
5
|
+
import { isGAEvent } from './types.js';
|
|
6
|
+
|
|
7
|
+
function useDataLayer(eventOrArgs) {
|
|
8
|
+
const { customerNumber } = useGoogleAnalyticsProvider();
|
|
9
|
+
const createEcommerceEvent = useCallback(({ cart, cartLine, event, product, }) => {
|
|
10
|
+
if (cart) {
|
|
11
|
+
return {
|
|
12
|
+
...event,
|
|
13
|
+
ecommerce: {
|
|
14
|
+
...event.ecommerce,
|
|
15
|
+
currency: event.ecommerce?.currency ?? cart.currencySymbol,
|
|
16
|
+
customer: event.ecommerce?.customer ?? customerNumber,
|
|
17
|
+
items: cart.cartLines?.map(cartLine => ({
|
|
18
|
+
item_id: cartLine.erpNumber,
|
|
19
|
+
item_name: cartLine.shortDescription,
|
|
20
|
+
price: cartLine.pricing?.unitNetPrice,
|
|
21
|
+
quantity: cartLine.qtyOrdered ?? 0,
|
|
22
|
+
})) ?? [],
|
|
23
|
+
},
|
|
24
|
+
value: event.value ?? cart.orderSubTotal ?? 0, // TODO: Why not grandTotal?
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else if (product) {
|
|
28
|
+
return {
|
|
29
|
+
...event,
|
|
30
|
+
ecommerce: {
|
|
31
|
+
...event.ecommerce,
|
|
32
|
+
currency: event.ecommerce?.currency ??
|
|
33
|
+
currencySymbolToISO[product.unitListPriceDisplay[0] || ''],
|
|
34
|
+
customer: event.ecommerce?.customer ?? customerNumber,
|
|
35
|
+
items: [
|
|
36
|
+
{
|
|
37
|
+
item_id: product.productNumber,
|
|
38
|
+
item_name: product.productTitle,
|
|
39
|
+
price: product.price || product.unitListPrice,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
value: 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else if (cartLine) {
|
|
47
|
+
return {
|
|
48
|
+
...event,
|
|
49
|
+
ecommerce: {
|
|
50
|
+
...event.ecommerce,
|
|
51
|
+
currency: event.ecommerce?.currency ??
|
|
52
|
+
currencySymbolToISO[cartLine.pricing?.actualPriceDisplay[0] || ''],
|
|
53
|
+
customer: event.ecommerce?.customer ?? customerNumber,
|
|
54
|
+
items: [
|
|
55
|
+
{
|
|
56
|
+
item_id: cartLine.erpNumber,
|
|
57
|
+
item_name: cartLine.productName,
|
|
58
|
+
price: cartLine.pricing?.actualPrice,
|
|
59
|
+
quantity: cartLine.qtyOrdered ?? undefined,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
value: cartLine.pricing?.actualPrice,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return event;
|
|
68
|
+
}
|
|
69
|
+
}, [customerNumber]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!eventOrArgs)
|
|
72
|
+
return;
|
|
73
|
+
if (isGAEvent(eventOrArgs)) {
|
|
74
|
+
dataLayer.push(eventOrArgs);
|
|
75
|
+
}
|
|
76
|
+
else if ('cart' in eventOrArgs) {
|
|
77
|
+
dataLayer.push(createEcommerceEvent(eventOrArgs));
|
|
78
|
+
}
|
|
79
|
+
else if ('product' in eventOrArgs) {
|
|
80
|
+
dataLayer.push(createEcommerceEvent(eventOrArgs));
|
|
81
|
+
}
|
|
82
|
+
else if ('cartLine' in eventOrArgs) {
|
|
83
|
+
dataLayer.push(createEcommerceEvent(eventOrArgs));
|
|
84
|
+
}
|
|
85
|
+
}, [eventOrArgs, createEcommerceEvent]);
|
|
86
|
+
return { createEcommerceEvent, dataLayer };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { useDataLayer };
|
package/dist/styles.css
CHANGED
|
@@ -629,6 +629,64 @@
|
|
|
629
629
|
margin-right: -16px;
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
+
.progress-circle-module-4nweP {
|
|
633
|
+
--width: var(--space-24);
|
|
634
|
+
|
|
635
|
+
position: relative;
|
|
636
|
+
display: block;
|
|
637
|
+
width: var(--width);
|
|
638
|
+
height: var(--width);
|
|
639
|
+
border: 4px solid transparent;
|
|
640
|
+
border-radius: var(--width);
|
|
641
|
+
animation: progress-circle-module-kCf7K 0.6s infinite linear;
|
|
642
|
+
-webkit-mask:
|
|
643
|
+
linear-gradient(#000 0 0) padding-box,
|
|
644
|
+
linear-gradient(#000 0 0);
|
|
645
|
+
mask:
|
|
646
|
+
linear-gradient(#000 0 0) padding-box,
|
|
647
|
+
linear-gradient(#000 0 0);
|
|
648
|
+
-webkit-mask-composite: xor;
|
|
649
|
+
mask-composite: exclude;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.progress-circle-module-4nweP:where(.progress-circle-module-SHNFy) {
|
|
653
|
+
background: conic-gradient(
|
|
654
|
+
from 0.5turn,
|
|
655
|
+
var(--color-white),
|
|
656
|
+
rgb(0 0 0 / 0%) 0.08turn,
|
|
657
|
+
rgb(0 0 0 / 0%) 0.17turn,
|
|
658
|
+
var(--color-white) 0.5turn,
|
|
659
|
+
var(--color-white)
|
|
660
|
+
)
|
|
661
|
+
border-box;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.progress-circle-module-4nweP:where(.progress-circle-module-TRZWO) {
|
|
665
|
+
background: conic-gradient(
|
|
666
|
+
from 0.5turn,
|
|
667
|
+
var(--color-black),
|
|
668
|
+
var(--color-brand-light-gray) 0.08turn,
|
|
669
|
+
var(--color-brand-light-gray) 0.17turn,
|
|
670
|
+
var(--color-black) 0.5turn,
|
|
671
|
+
var(--color-black)
|
|
672
|
+
)
|
|
673
|
+
border-box;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.progress-circle-module-4nweP:where(.progress-circle-module--Gspu) {
|
|
677
|
+
--width: var(--space-20);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.progress-circle-module-4nweP:where(.progress-circle-module-4tRZd) {
|
|
681
|
+
--width: var(--space-24);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
@keyframes progress-circle-module-kCf7K {
|
|
685
|
+
to {
|
|
686
|
+
transform: rotate(360deg);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
632
690
|
.button-module-V4meK {
|
|
633
691
|
all: unset;
|
|
634
692
|
display: inline-flex;
|
|
@@ -660,6 +718,48 @@
|
|
|
660
718
|
height: 24px;
|
|
661
719
|
}
|
|
662
720
|
|
|
721
|
+
.button-module-V4meK.button-module-LwuW2,
|
|
722
|
+
.button-module-V4meK.button-module-U5IxM {
|
|
723
|
+
cursor: default;
|
|
724
|
+
pointer-events: none;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.button-module-V4meK.button-module-LwuW2 > :not(.button-module-13ndF) {
|
|
728
|
+
visibility: hidden;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.button-module-V4meK.button-module-LwuW2 .button-module-13ndF {
|
|
732
|
+
position: absolute;
|
|
733
|
+
margin: auto;
|
|
734
|
+
inset: 0;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.button-module-V4meK.button-module-U5IxM {
|
|
738
|
+
position: relative;
|
|
739
|
+
display: inline-grid;
|
|
740
|
+
padding-right: var(--space-48);
|
|
741
|
+
gap: var(--space-12);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.button-module-V4meK.button-module-U5IxM .button-module-vqRq- {
|
|
745
|
+
overflow: hidden;
|
|
746
|
+
text-overflow: ellipsis;
|
|
747
|
+
white-space: nowrap;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.button-module-V4meK.button-module-U5IxM .button-module-13ndF {
|
|
751
|
+
position: absolute;
|
|
752
|
+
right: var(--space-8);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.button-module-V4meK.button-module-U5IxM:where(.button-module-tmyk8) {
|
|
756
|
+
color: color-mix(in srgb, var(--color-white) 40%, transparent 60%);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.button-module-V4meK.button-module-U5IxM:where(.button-module--1bCH) {
|
|
760
|
+
color: var(--color-brand-medium-gray);
|
|
761
|
+
}
|
|
762
|
+
|
|
663
763
|
/*********************************************************
|
|
664
764
|
*
|
|
665
765
|
* Sizes
|
|
@@ -726,11 +826,11 @@
|
|
|
726
826
|
transform: translateY(-1px);
|
|
727
827
|
}
|
|
728
828
|
|
|
729
|
-
.button-module-tmyk8:where([data-disabled]) {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
829
|
+
.button-module-tmyk8:where([data-disabled]):where(:not(.button-module-U5IxM, .button-module-LwuW2)) {
|
|
830
|
+
border-color: var(--color-brand-light-gray);
|
|
831
|
+
background-color: var(--color-red-100);
|
|
832
|
+
color: var(--color-red-50);
|
|
833
|
+
}
|
|
734
834
|
|
|
735
835
|
.button-module--1bCH:where(.button-module-vq9GI, .button-module-AjvlY) {
|
|
736
836
|
background-color: var(--color-brand-white);
|
|
@@ -959,7 +1059,7 @@
|
|
|
959
1059
|
position: relative;
|
|
960
1060
|
display: inline-block;
|
|
961
1061
|
overflow: hidden;
|
|
962
|
-
border:
|
|
1062
|
+
border: 1px solid transparent;
|
|
963
1063
|
border-radius: var(--border-radius-8);
|
|
964
1064
|
background-color: var(--color-brand-light-gray);
|
|
965
1065
|
}
|
|
@@ -1005,16 +1105,19 @@
|
|
|
1005
1105
|
box-shadow: var(--shadow-focus-outline);
|
|
1006
1106
|
outline: 0;
|
|
1007
1107
|
}
|
|
1008
|
-
.input-module-2woJR:has([data-invalid])
|
|
1009
|
-
|
|
1010
|
-
right: 0;
|
|
1011
|
-
bottom: 0;
|
|
1012
|
-
left: 0;
|
|
1013
|
-
display: block;
|
|
1014
|
-
height: 2px;
|
|
1015
|
-
background-color: var(--color-brand-red);
|
|
1016
|
-
content: '';
|
|
1108
|
+
.input-module-2woJR:has([data-invalid]) {
|
|
1109
|
+
border-bottom-color: var(--color-brand-red);
|
|
1017
1110
|
}
|
|
1111
|
+
.input-module-2woJR:has([data-invalid])::after {
|
|
1112
|
+
position: absolute;
|
|
1113
|
+
right: 0;
|
|
1114
|
+
bottom: 0;
|
|
1115
|
+
left: 0;
|
|
1116
|
+
display: block;
|
|
1117
|
+
height: 1px;
|
|
1118
|
+
background-color: var(--color-brand-red);
|
|
1119
|
+
content: '';
|
|
1120
|
+
}
|
|
1018
1121
|
.input-module-2woJR .input-module-6HwY4 {
|
|
1019
1122
|
display: inline-grid;
|
|
1020
1123
|
}
|
|
@@ -1168,6 +1271,7 @@
|
|
|
1168
1271
|
all: unset;
|
|
1169
1272
|
position: relative;
|
|
1170
1273
|
display: flex;
|
|
1274
|
+
overflow: hidden;
|
|
1171
1275
|
width: 100%;
|
|
1172
1276
|
height: var(--height);
|
|
1173
1277
|
box-sizing: border-box;
|
|
@@ -1207,7 +1311,24 @@
|
|
|
1207
1311
|
opacity: 0.4;
|
|
1208
1312
|
}
|
|
1209
1313
|
|
|
1210
|
-
.select-module-ui-Wc
|
|
1314
|
+
.select-module-ui-Wc[data-invalid] .select-module-aMQIQ {
|
|
1315
|
+
border-bottom-color: var(--color-brand-red);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.select-module-ui-Wc[data-invalid] .select-module-aMQIQ::after {
|
|
1319
|
+
position: absolute;
|
|
1320
|
+
right: 0;
|
|
1321
|
+
bottom: 0;
|
|
1322
|
+
left: 0;
|
|
1323
|
+
display: block;
|
|
1324
|
+
height: 1px;
|
|
1325
|
+
background-color: var(--color-brand-red);
|
|
1326
|
+
content: '';
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/* stylelint-disable-next-line no-descending-specificity */
|
|
1330
|
+
|
|
1331
|
+
.select-module-ui-Wc:where(.select-module-IRd4F) .select-module-aMQIQ {
|
|
1211
1332
|
border-color: var(--color-brand-light-gray);
|
|
1212
1333
|
background-color: var(--color-brand-light-gray);
|
|
1213
1334
|
}
|
|
@@ -2345,20 +2466,6 @@
|
|
|
2345
2466
|
}
|
|
2346
2467
|
}
|
|
2347
2468
|
|
|
2348
|
-
.progress-circle-module-4nweP {
|
|
2349
|
-
--width: var(--space-24);
|
|
2350
|
-
|
|
2351
|
-
width: var(--width);
|
|
2352
|
-
height: var(--width);
|
|
2353
|
-
animation: progress-circle-module-kCf7K 0.6s infinite linear;
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
@keyframes progress-circle-module-kCf7K {
|
|
2357
|
-
to {
|
|
2358
|
-
transform: rotate(360deg);
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
2469
|
.product-overview-grid-module-bzys- {
|
|
2363
2470
|
--amount-of-columns: 1;
|
|
2364
2471
|
--column-gap: 0;
|