@akinon/projectzero 2.0.0-beta.19 → 2.0.0-beta.20
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/CHANGELOG.md +9 -7
- package/app-template/CHANGELOG.md +251 -204
- package/app-template/akinon.json +1 -1
- package/app-template/package.json +28 -28
- package/app-template/public/amex.svg +12 -0
- package/app-template/public/apple-pay.svg +16 -0
- package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
- package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
- package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
- package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
- package/app-template/public/google-pay.svg +16 -0
- package/app-template/public/locales/en/account.json +6 -3
- package/app-template/public/locales/en/auth.json +6 -7
- package/app-template/public/locales/en/basket.json +6 -6
- package/app-template/public/locales/en/blog.json +7 -0
- package/app-template/public/locales/en/category.json +3 -1
- package/app-template/public/locales/en/checkout.json +5 -4
- package/app-template/public/locales/en/common.json +11 -2
- package/app-template/public/locales/en/forgot_password.json +6 -7
- package/app-template/public/locales/en/product.json +4 -3
- package/app-template/public/locales/tr/account.json +6 -3
- package/app-template/public/locales/tr/auth.json +16 -17
- package/app-template/public/locales/tr/basket.json +4 -4
- package/app-template/public/locales/tr/blog.json +7 -0
- package/app-template/public/locales/tr/category.json +3 -1
- package/app-template/public/locales/tr/checkout.json +39 -38
- package/app-template/public/locales/tr/common.json +10 -1
- package/app-template/public/locales/tr/forgot_password.json +12 -13
- package/app-template/public/locales/tr/product.json +1 -0
- package/app-template/public/logo.svg +3 -27
- package/app-template/public/mastercard.svg +14 -0
- package/app-template/public/promotion-banner.jpg +0 -0
- package/app-template/public/shop-pay.svg +12 -0
- package/app-template/public/visa.svg +12 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/blog/[slug]/page.tsx +118 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +15 -0
- package/app-template/src/app/api/theme-settings/route.ts +12 -0
- package/app-template/src/assets/fonts/pz-icon.css +211 -49
- package/app-template/src/assets/fonts/pz-icon.eot +0 -0
- package/app-template/src/assets/fonts/pz-icon.html +486 -0
- package/app-template/src/assets/fonts/pz-icon.scss +373 -49
- package/app-template/src/assets/fonts/pz-icon.svg +215 -53
- package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
- package/app-template/src/assets/fonts/pz-icon.woff +0 -0
- package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
- package/app-template/src/assets/globals.scss +4 -0
- package/app-template/src/assets/icons/arrow-right.svg +3 -0
- package/app-template/src/assets/icons/cart.svg +4 -12
- package/app-template/src/assets/icons/check.svg +2 -18
- package/app-template/src/assets/icons/chevron-down.svg +2 -7
- package/app-template/src/assets/icons/delete.svg +3 -0
- package/app-template/src/assets/icons/facebook.svg +2 -8
- package/app-template/src/assets/icons/fav-off.svg +5 -0
- package/app-template/src/assets/icons/fav-on.svg +5 -0
- package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
- package/app-template/src/assets/icons/heart.svg +3 -0
- package/app-template/src/assets/icons/instagram.svg +2 -13
- package/app-template/src/assets/icons/materials.svg +3 -0
- package/app-template/src/assets/icons/person.svg +4 -0
- package/app-template/src/assets/icons/pinterest.svg +5 -11
- package/app-template/src/assets/icons/ruler.svg +3 -0
- package/app-template/src/assets/icons/search.svg +8 -11
- package/app-template/src/assets/icons/share.svg +2 -9
- package/app-template/src/assets/icons/snapchat.svg +3 -0
- package/app-template/src/assets/icons/tiktok.svg +3 -0
- package/app-template/src/assets/icons/tumblr.svg +6 -0
- package/app-template/src/assets/icons/twitter.svg +2 -10
- package/app-template/src/assets/icons/vimeo.svg +3 -0
- package/app-template/src/assets/icons/youtube.svg +3 -0
- package/app-template/src/assets/icons/zoom.svg +8 -0
- package/app-template/src/components/accordion.tsx +33 -11
- package/app-template/src/components/action-tooltip.tsx +160 -0
- package/app-template/src/components/currency-select.tsx +149 -4
- package/app-template/src/components/icon.tsx +5 -6
- package/app-template/src/components/index.ts +4 -1
- package/app-template/src/components/language-select.tsx +88 -2
- package/app-template/src/components/pagination.tsx +132 -20
- package/app-template/src/components/quantity-input.tsx +63 -0
- package/app-template/src/components/quantity-selector.tsx +203 -0
- package/app-template/src/components/route-handler.tsx +50 -0
- package/app-template/src/components/select.tsx +89 -69
- package/app-template/src/components/types/index.ts +26 -0
- package/app-template/src/components/widget-content.tsx +323 -0
- package/app-template/src/data/server/theme.ts +70 -0
- package/app-template/src/hooks/use-fav-button.tsx +5 -2
- package/app-template/src/hooks/use-product-cart.ts +11 -8
- package/app-template/src/hooks/use-theme-settings.ts +42 -0
- package/app-template/src/lib/fonts.ts +149 -0
- package/app-template/src/settings.js +2 -2
- package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
- package/app-template/src/types/widget.ts +169 -0
- package/app-template/src/utils/formatDate.ts +48 -0
- package/app-template/src/utils/styles.ts +71 -0
- package/app-template/src/views/account/contact-form.tsx +147 -130
- package/app-template/src/views/basket/basket-item.tsx +691 -107
- package/app-template/src/views/basket/basket-summary-context.tsx +560 -0
- package/app-template/src/views/basket/designer-context.tsx +617 -0
- package/app-template/src/views/basket/index.ts +2 -0
- package/app-template/src/views/basket/summary.tsx +496 -75
- package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +190 -0
- package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +286 -0
- package/app-template/src/views/breadcrumb/constants.ts +15 -0
- package/app-template/src/views/breadcrumb/index.tsx +127 -0
- package/app-template/src/views/breadcrumb.tsx +13 -38
- package/app-template/src/views/category/category-banner.tsx +4 -23
- package/app-template/src/views/category/category-header.tsx +289 -66
- package/app-template/src/views/category/category-info.tsx +173 -24
- package/app-template/src/views/category/filters/filter-item.tsx +138 -42
- package/app-template/src/views/category/filters/index.tsx +208 -48
- package/app-template/src/views/category/layout.tsx +7 -4
- package/app-template/src/views/category/native-widget-context.tsx +257 -0
- package/app-template/src/views/category/product-list-registrar.tsx +665 -0
- package/app-template/src/views/checkout/auth.tsx +64 -40
- package/app-template/src/views/checkout/checkout-address-registrar.tsx +254 -0
- package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +183 -0
- package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +259 -0
- package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +253 -0
- package/app-template/src/views/checkout/checkout-summary-registrar.tsx +183 -0
- package/app-template/src/views/checkout/constants.ts +5 -0
- package/app-template/src/views/checkout/index.tsx +5 -0
- package/app-template/src/views/checkout/layout/header.tsx +9 -5
- package/app-template/src/views/checkout/steps/payment/index.tsx +5 -2
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +72 -1
- package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +15 -0
- package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +18 -0
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +171 -40
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +74 -12
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +128 -45
- package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +232 -27
- package/app-template/src/views/checkout/summary.tsx +303 -29
- package/app-template/src/views/footer/footer-app-banner-context.tsx +326 -0
- package/app-template/src/views/footer/footer-bottom-context.tsx +215 -0
- package/app-template/src/views/footer/footer-bottom-wrapper.tsx +74 -0
- package/app-template/src/views/footer/footer-layout-constants.ts +35 -0
- package/app-template/src/views/footer/footer-layout-registrar.tsx +342 -0
- package/app-template/src/views/footer/footer-layout-switcher.tsx +110 -0
- package/app-template/src/views/footer/footer-menu-context.tsx +211 -0
- package/app-template/src/views/footer/footer-native-widgets.tsx +60 -0
- package/app-template/src/views/footer/footer-social-context.tsx +254 -0
- package/app-template/src/views/footer/footer-subscription-context.tsx +210 -0
- package/app-template/src/views/footer/footer-utils.ts +43 -0
- package/app-template/src/views/footer/footer-value-props-context.tsx +326 -0
- package/app-template/src/views/footer/logo-settings.ts +183 -0
- package/app-template/src/views/footer/native-widget-config.ts +262 -0
- package/app-template/src/views/footer/subscription-settings.ts +122 -0
- package/app-template/src/views/footer/use-footer-logo.ts +162 -0
- package/app-template/src/views/footer.tsx +415 -13
- package/app-template/src/views/guest-login/index.tsx +62 -58
- package/app-template/src/views/header/action-menu.tsx +277 -45
- package/app-template/src/views/header/band.tsx +6 -21
- package/app-template/src/views/header/designer-context.tsx +261 -0
- package/app-template/src/views/header/header-announcement-registrar.tsx +267 -0
- package/app-template/src/views/header/header-client-wrapper.tsx +496 -0
- package/app-template/src/views/header/header-content.tsx +1026 -0
- package/app-template/src/views/header/header-currency-registrar.tsx +348 -0
- package/app-template/src/views/header/header-icons-context.tsx +262 -0
- package/app-template/src/views/header/header-language-registrar.tsx +348 -0
- package/app-template/src/views/header/header-layout-context.tsx +143 -0
- package/app-template/src/views/header/header-layout-registrar.tsx +658 -0
- package/app-template/src/views/header/header-logo-context.tsx +228 -0
- package/app-template/src/views/header/header-logo.tsx +118 -0
- package/app-template/src/views/header/header-mini-basket-context.tsx +524 -0
- package/app-template/src/views/header/header-search-registrar.tsx +511 -0
- package/app-template/src/views/header/header-text-slider-registrar.tsx +382 -0
- package/app-template/src/views/header/index.tsx +109 -47
- package/app-template/src/views/header/inline-search.tsx +262 -0
- package/app-template/src/views/header/mini-basket.tsx +819 -44
- package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
- package/app-template/src/views/header/mobile-menu.tsx +12 -0
- package/app-template/src/views/header/navbar-menu-context.tsx +219 -0
- package/app-template/src/views/header/navbar.tsx +178 -111
- package/app-template/src/views/header/search/index.tsx +71 -32
- package/app-template/src/views/header/search/results.tsx +127 -65
- package/app-template/src/views/header/search/search-input.tsx +61 -0
- package/app-template/src/views/header/server-settings-parser.ts +1105 -0
- package/app-template/src/views/header/use-header-icons.ts +241 -0
- package/app-template/src/views/header/use-header-logo.ts +213 -0
- package/app-template/src/views/header/use-navbar-menu.ts +179 -0
- package/app-template/src/views/login/index.tsx +54 -46
- package/app-template/src/views/product/accordion-section.tsx +61 -0
- package/app-template/src/views/product/accordion-wrapper.tsx +135 -43
- package/app-template/src/views/product/custom-button-group.tsx +69 -0
- package/app-template/src/views/product/favorites-button-section.tsx +69 -0
- package/app-template/src/views/product/find-in-store-section.tsx +60 -0
- package/app-template/src/views/product/index.ts +1 -0
- package/app-template/src/views/product/layout.tsx +6 -5
- package/app-template/src/views/product/misc-buttons.tsx +339 -25
- package/app-template/src/views/product/price-wrapper.tsx +3 -29
- package/app-template/src/views/product/product-actions.tsx +137 -8
- package/app-template/src/views/product/product-info-section.tsx +140 -0
- package/app-template/src/views/product/product-info.tsx +69 -31
- package/app-template/src/views/product/product-share.tsx +13 -8
- package/app-template/src/views/product/product-variants.tsx +2 -2
- package/app-template/src/views/product/quantity-section.tsx +73 -0
- package/app-template/src/views/product/sale-tag.tsx +10 -0
- package/app-template/src/views/product/share-section.tsx +357 -0
- package/app-template/src/views/product/slider.tsx +117 -79
- package/app-template/src/views/product/variant.tsx +69 -41
- package/app-template/src/views/product/variants-section.tsx +126 -0
- package/app-template/src/views/product-detail/constants.ts +272 -0
- package/app-template/src/views/product-detail/index.ts +10 -0
- package/app-template/src/views/product-detail/product-detail-registrar.tsx +616 -0
- package/app-template/src/views/product-item/index.tsx +119 -46
- package/app-template/src/views/register/index.tsx +14 -25
- package/app-template/src/views/share/index.tsx +9 -6
- package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
- package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
- package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
- package/app-template/src/widgets/footer-app-banner.tsx +444 -0
- package/app-template/src/widgets/footer-bottom.tsx +127 -0
- package/app-template/src/widgets/footer-menu-compact.tsx +238 -0
- package/app-template/src/widgets/footer-menu-two.tsx +298 -0
- package/app-template/src/widgets/footer-social-client.tsx +251 -0
- package/app-template/src/widgets/footer-social.tsx +47 -16
- package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
- package/app-template/src/widgets/footer-subscription/index.tsx +183 -17
- package/app-template/src/widgets/footer-value-props.tsx +201 -0
- package/app-template/src/widgets/index.ts +7 -0
- package/app-template/src/widgets/schemas/about-us.json +46 -0
- package/app-template/src/widgets/schemas/blog-list.json +37 -0
- package/app-template/src/widgets/schemas/blog.json +29 -0
- package/app-template/tailwind.config.js +18 -2
- package/package.json +1 -1
|
@@ -0,0 +1,1105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side parsers for header widget settings
|
|
3
|
+
* These parse widget data fetched on the server to provide initial values
|
|
4
|
+
* to client components, avoiding the flash of default styles.
|
|
5
|
+
*
|
|
6
|
+
* Note: This file exports types that are also used by client components,
|
|
7
|
+
* but the parser functions should only be called on the server.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Block IDs matching header-icons-context.tsx
|
|
11
|
+
const ICON_BLOCK_IDS = {
|
|
12
|
+
search: 'header-icon-search',
|
|
13
|
+
profile: 'header-icon-profile',
|
|
14
|
+
cart: 'header-icon-cart'
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
const LOGO_BLOCK_ID = 'header-logo-image';
|
|
18
|
+
|
|
19
|
+
// Default values
|
|
20
|
+
const DEFAULT_ICON_SIZE = 20;
|
|
21
|
+
const DEFAULT_ICON_COLOR = 'currentColor';
|
|
22
|
+
const DEFAULT_LOGO_WIDTH = 120;
|
|
23
|
+
const DEFAULT_LOGO_HEIGHT = 40;
|
|
24
|
+
|
|
25
|
+
export interface IconSettings {
|
|
26
|
+
size: number;
|
|
27
|
+
color: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface HeaderIconSettings {
|
|
31
|
+
search: IconSettings;
|
|
32
|
+
profile: IconSettings;
|
|
33
|
+
cart: IconSettings;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface LogoSettings {
|
|
37
|
+
src: string;
|
|
38
|
+
width: number | string;
|
|
39
|
+
height: number | string;
|
|
40
|
+
maxWidth?: string;
|
|
41
|
+
maxHeight?: string;
|
|
42
|
+
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const defaultIconSettings: HeaderIconSettings = {
|
|
46
|
+
search: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR },
|
|
47
|
+
profile: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR },
|
|
48
|
+
cart: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR }
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const defaultLogoSettings: LogoSettings = {
|
|
52
|
+
src: '/logo.svg',
|
|
53
|
+
width: DEFAULT_LOGO_WIDTH,
|
|
54
|
+
height: DEFAULT_LOGO_HEIGHT,
|
|
55
|
+
objectFit: 'contain'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse icon settings from widget data (server-side)
|
|
60
|
+
*/
|
|
61
|
+
export function parseServerIconSettings(
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
widgetData: any
|
|
64
|
+
): HeaderIconSettings {
|
|
65
|
+
if (!widgetData?.attributes) {
|
|
66
|
+
return defaultIconSettings;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const result = { ...defaultIconSettings };
|
|
70
|
+
const attrs = widgetData.attributes;
|
|
71
|
+
|
|
72
|
+
Object.entries(ICON_BLOCK_IDS).forEach(([key, blockId]) => {
|
|
73
|
+
const attrData = attrs[blockId];
|
|
74
|
+
if (!attrData) return;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const blockData =
|
|
78
|
+
typeof attrData === 'string'
|
|
79
|
+
? JSON.parse(attrData)
|
|
80
|
+
: typeof attrData === 'object' && 'value' in attrData
|
|
81
|
+
? JSON.parse((attrData as { value: string }).value)
|
|
82
|
+
: null;
|
|
83
|
+
|
|
84
|
+
if (blockData) {
|
|
85
|
+
// Support both old format (direct styles) and new format ({ styles, properties, value })
|
|
86
|
+
const styles = blockData.styles || blockData;
|
|
87
|
+
const properties = blockData.properties || {};
|
|
88
|
+
|
|
89
|
+
const color =
|
|
90
|
+
styles?.color?.desktop || styles?.color || DEFAULT_ICON_COLOR;
|
|
91
|
+
const sizeValue =
|
|
92
|
+
properties?.iconSize?.desktop ||
|
|
93
|
+
properties?.iconSize ||
|
|
94
|
+
DEFAULT_ICON_SIZE;
|
|
95
|
+
const size =
|
|
96
|
+
typeof sizeValue === 'string' ? parseInt(sizeValue, 10) : sizeValue;
|
|
97
|
+
|
|
98
|
+
result[key as keyof HeaderIconSettings] = {
|
|
99
|
+
size: isNaN(size) ? DEFAULT_ICON_SIZE : size,
|
|
100
|
+
color
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
// Ignore parse errors
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parse logo settings from widget data (server-side)
|
|
113
|
+
*/
|
|
114
|
+
export function parseServerLogoSettings(
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
116
|
+
widgetData: any,
|
|
117
|
+
fallbackSrc?: string
|
|
118
|
+
): LogoSettings {
|
|
119
|
+
const result: LogoSettings = {
|
|
120
|
+
...defaultLogoSettings,
|
|
121
|
+
src: fallbackSrc || defaultLogoSettings.src
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (!widgetData?.attributes) {
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const blockData = widgetData.attributes[LOGO_BLOCK_ID];
|
|
129
|
+
if (!blockData) return result;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const data =
|
|
133
|
+
typeof blockData === 'string'
|
|
134
|
+
? JSON.parse(blockData)
|
|
135
|
+
: typeof blockData === 'object' && 'value' in blockData
|
|
136
|
+
? JSON.parse((blockData as { value: string }).value)
|
|
137
|
+
: null;
|
|
138
|
+
|
|
139
|
+
if (!data) return result;
|
|
140
|
+
|
|
141
|
+
// Support both old format (direct styles) and new format ({ styles, properties, value })
|
|
142
|
+
const styles = data.styles || data;
|
|
143
|
+
const value = data.value;
|
|
144
|
+
|
|
145
|
+
// Get logo src from value
|
|
146
|
+
if (value) {
|
|
147
|
+
const src = typeof value === 'object' ? value.desktop : value;
|
|
148
|
+
if (src) result.src = src;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Get styles
|
|
152
|
+
if (styles.width) {
|
|
153
|
+
result.width =
|
|
154
|
+
typeof styles.width === 'object' ? styles.width.desktop : styles.width;
|
|
155
|
+
}
|
|
156
|
+
if (styles.height) {
|
|
157
|
+
result.height =
|
|
158
|
+
typeof styles.height === 'object'
|
|
159
|
+
? styles.height.desktop
|
|
160
|
+
: styles.height;
|
|
161
|
+
}
|
|
162
|
+
if (styles.maxWidth || styles['max-width']) {
|
|
163
|
+
const maxWidth = styles.maxWidth || styles['max-width'];
|
|
164
|
+
result.maxWidth =
|
|
165
|
+
typeof maxWidth === 'object' ? maxWidth.desktop : maxWidth;
|
|
166
|
+
}
|
|
167
|
+
if (styles.maxHeight || styles['max-height']) {
|
|
168
|
+
const maxHeight = styles.maxHeight || styles['max-height'];
|
|
169
|
+
result.maxHeight =
|
|
170
|
+
typeof maxHeight === 'object' ? maxHeight.desktop : maxHeight;
|
|
171
|
+
}
|
|
172
|
+
if (styles.objectFit || styles['object-fit']) {
|
|
173
|
+
const objectFit = styles.objectFit || styles['object-fit'];
|
|
174
|
+
result.objectFit =
|
|
175
|
+
typeof objectFit === 'object' ? objectFit.desktop : objectFit;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// Ignore parse errors
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ============================================
|
|
185
|
+
// Navbar Menu Settings
|
|
186
|
+
// ============================================
|
|
187
|
+
|
|
188
|
+
const NAVBAR_MENU_BLOCK_ID = 'header-nav-menu-item';
|
|
189
|
+
|
|
190
|
+
// Default navbar menu values
|
|
191
|
+
const DEFAULT_FONT_SIZE = '14px';
|
|
192
|
+
const DEFAULT_NAV_COLOR = 'currentColor';
|
|
193
|
+
const DEFAULT_HOVER_COLOR = '';
|
|
194
|
+
const DEFAULT_FONT_WEIGHT = '400';
|
|
195
|
+
|
|
196
|
+
export interface NavbarMenuSettings {
|
|
197
|
+
fontSize: string;
|
|
198
|
+
color: string;
|
|
199
|
+
hoverColor: string;
|
|
200
|
+
fontWeight: string;
|
|
201
|
+
textTransform?: string;
|
|
202
|
+
letterSpacing?: string;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const defaultNavbarMenuSettings: NavbarMenuSettings = {
|
|
206
|
+
fontSize: DEFAULT_FONT_SIZE,
|
|
207
|
+
color: DEFAULT_NAV_COLOR,
|
|
208
|
+
hoverColor: DEFAULT_HOVER_COLOR,
|
|
209
|
+
fontWeight: DEFAULT_FONT_WEIGHT
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Parse navbar menu settings from widget data (server-side)
|
|
214
|
+
*/
|
|
215
|
+
export function parseServerNavbarMenuSettings(
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
217
|
+
widgetData: any
|
|
218
|
+
): NavbarMenuSettings {
|
|
219
|
+
if (!widgetData?.attributes) {
|
|
220
|
+
return defaultNavbarMenuSettings;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = { ...defaultNavbarMenuSettings };
|
|
224
|
+
const blockData = widgetData.attributes[NAVBAR_MENU_BLOCK_ID];
|
|
225
|
+
|
|
226
|
+
if (!blockData) return result;
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const data =
|
|
230
|
+
typeof blockData === 'string'
|
|
231
|
+
? JSON.parse(blockData)
|
|
232
|
+
: typeof blockData === 'object' && 'value' in blockData
|
|
233
|
+
? JSON.parse((blockData as { value: string }).value)
|
|
234
|
+
: null;
|
|
235
|
+
|
|
236
|
+
if (!data) return result;
|
|
237
|
+
|
|
238
|
+
// Support both old format (direct styles) and new format ({ styles, properties })
|
|
239
|
+
const styles = data.styles || data;
|
|
240
|
+
|
|
241
|
+
// Font size
|
|
242
|
+
if (styles.fontSize || styles['font-size']) {
|
|
243
|
+
const fontSize = styles.fontSize || styles['font-size'];
|
|
244
|
+
result.fontSize =
|
|
245
|
+
typeof fontSize === 'object' ? fontSize.desktop : fontSize;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Color
|
|
249
|
+
if (styles.color) {
|
|
250
|
+
result.color =
|
|
251
|
+
typeof styles.color === 'object' ? styles.color.desktop : styles.color;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Hover color
|
|
255
|
+
if (styles.hoverColor || styles['hover-color']) {
|
|
256
|
+
const hoverColor = styles.hoverColor || styles['hover-color'];
|
|
257
|
+
result.hoverColor =
|
|
258
|
+
typeof hoverColor === 'object' ? hoverColor.desktop : hoverColor;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Font weight
|
|
262
|
+
if (styles.fontWeight || styles['font-weight']) {
|
|
263
|
+
const fontWeight = styles.fontWeight || styles['font-weight'];
|
|
264
|
+
result.fontWeight =
|
|
265
|
+
typeof fontWeight === 'object' ? fontWeight.desktop : fontWeight;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Text transform
|
|
269
|
+
if (styles.textTransform || styles['text-transform']) {
|
|
270
|
+
const textTransform = styles.textTransform || styles['text-transform'];
|
|
271
|
+
result.textTransform =
|
|
272
|
+
typeof textTransform === 'object'
|
|
273
|
+
? textTransform.desktop
|
|
274
|
+
: textTransform;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Letter spacing
|
|
278
|
+
if (styles.letterSpacing || styles['letter-spacing']) {
|
|
279
|
+
const letterSpacing = styles.letterSpacing || styles['letter-spacing'];
|
|
280
|
+
result.letterSpacing =
|
|
281
|
+
typeof letterSpacing === 'object'
|
|
282
|
+
? letterSpacing.desktop
|
|
283
|
+
: letterSpacing;
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
// Ignore parse errors
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ============================================
|
|
293
|
+
// Header Layout Settings
|
|
294
|
+
// ============================================
|
|
295
|
+
|
|
296
|
+
const HEADER_LAYOUT_SECTION_ID = 'header-layout';
|
|
297
|
+
|
|
298
|
+
// Block IDs for header layout rows
|
|
299
|
+
const HEADER_LAYOUT_BLOCK_IDS = {
|
|
300
|
+
UTILITY_ROW: 'header-utility-row',
|
|
301
|
+
MAIN_ROW: 'header-main-row',
|
|
302
|
+
TOP_ROW: 'header-top-row',
|
|
303
|
+
BOTTOM_ROW: 'header-bottom-row'
|
|
304
|
+
} as const;
|
|
305
|
+
|
|
306
|
+
export type HeaderLayoutType = 'default' | 'two-row';
|
|
307
|
+
export type MenuPositionType = 'left' | 'center' | 'right';
|
|
308
|
+
export type SearchPositionType = 'left' | 'center' | 'right';
|
|
309
|
+
export type UtilityPositionType = 'left' | 'right';
|
|
310
|
+
|
|
311
|
+
export interface BlockStylesMap {
|
|
312
|
+
[blockId: string]: Record<string, string>;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface HeaderLayoutSettings {
|
|
316
|
+
layout: HeaderLayoutType;
|
|
317
|
+
menuPosition: MenuPositionType;
|
|
318
|
+
searchPosition: SearchPositionType;
|
|
319
|
+
utilityPosition: UtilityPositionType;
|
|
320
|
+
sticky: boolean;
|
|
321
|
+
blockStyles: BlockStylesMap;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const defaultLayoutSettings: HeaderLayoutSettings = {
|
|
325
|
+
layout: 'default',
|
|
326
|
+
menuPosition: 'center',
|
|
327
|
+
searchPosition: 'center',
|
|
328
|
+
utilityPosition: 'right',
|
|
329
|
+
sticky: true,
|
|
330
|
+
blockStyles: {}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Parse header layout settings from widget data (server-side)
|
|
335
|
+
*/
|
|
336
|
+
export function parseServerLayoutSettings(
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
338
|
+
widgetData: any
|
|
339
|
+
): HeaderLayoutSettings {
|
|
340
|
+
if (!widgetData?.attributes) {
|
|
341
|
+
return defaultLayoutSettings;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const result: HeaderLayoutSettings = {
|
|
345
|
+
...defaultLayoutSettings,
|
|
346
|
+
blockStyles: {}
|
|
347
|
+
};
|
|
348
|
+
const attrs = widgetData.attributes;
|
|
349
|
+
|
|
350
|
+
// Parse section-level properties
|
|
351
|
+
const sectionData = attrs[HEADER_LAYOUT_SECTION_ID];
|
|
352
|
+
|
|
353
|
+
if (sectionData) {
|
|
354
|
+
try {
|
|
355
|
+
const data =
|
|
356
|
+
typeof sectionData === 'string'
|
|
357
|
+
? JSON.parse(sectionData)
|
|
358
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
359
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
360
|
+
: null;
|
|
361
|
+
|
|
362
|
+
if (data?.properties) {
|
|
363
|
+
const properties = data.properties;
|
|
364
|
+
|
|
365
|
+
// Layout
|
|
366
|
+
if (properties.layout) {
|
|
367
|
+
const layout = properties.layout;
|
|
368
|
+
result.layout = (
|
|
369
|
+
typeof layout === 'object' ? layout.desktop : layout
|
|
370
|
+
) as HeaderLayoutType;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Menu position
|
|
374
|
+
if (properties.menuPosition) {
|
|
375
|
+
const menuPosition = properties.menuPosition;
|
|
376
|
+
result.menuPosition = (
|
|
377
|
+
typeof menuPosition === 'object'
|
|
378
|
+
? menuPosition.desktop
|
|
379
|
+
: menuPosition
|
|
380
|
+
) as MenuPositionType;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Search position
|
|
384
|
+
if (properties.searchPosition) {
|
|
385
|
+
const searchPosition = properties.searchPosition;
|
|
386
|
+
result.searchPosition = (
|
|
387
|
+
typeof searchPosition === 'object'
|
|
388
|
+
? searchPosition.desktop
|
|
389
|
+
: searchPosition
|
|
390
|
+
) as SearchPositionType;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Utility position (Language/Currency selects)
|
|
394
|
+
if (properties.utilityPosition) {
|
|
395
|
+
const utilityPosition = properties.utilityPosition;
|
|
396
|
+
result.utilityPosition = (
|
|
397
|
+
typeof utilityPosition === 'object'
|
|
398
|
+
? utilityPosition.desktop
|
|
399
|
+
: utilityPosition
|
|
400
|
+
) as UtilityPositionType;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Sticky
|
|
404
|
+
if (properties.sticky !== undefined) {
|
|
405
|
+
const sticky = properties.sticky;
|
|
406
|
+
const stickyValue =
|
|
407
|
+
typeof sticky === 'object' ? sticky.desktop : sticky;
|
|
408
|
+
result.sticky = stickyValue === 'true' || stickyValue === true;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
} catch {
|
|
412
|
+
// Ignore parse errors
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Parse block styles (TOP_ROW, BOTTOM_ROW, MAIN_ROW)
|
|
417
|
+
Object.values(HEADER_LAYOUT_BLOCK_IDS).forEach((blockId) => {
|
|
418
|
+
const blockData = attrs[blockId];
|
|
419
|
+
if (!blockData) return;
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const data =
|
|
423
|
+
typeof blockData === 'string'
|
|
424
|
+
? JSON.parse(blockData)
|
|
425
|
+
: typeof blockData === 'object' && 'value' in blockData
|
|
426
|
+
? JSON.parse((blockData as { value: string }).value)
|
|
427
|
+
: null;
|
|
428
|
+
|
|
429
|
+
if (data?.styles) {
|
|
430
|
+
// Convert responsive styles to flat styles (desktop first)
|
|
431
|
+
const flatStyles: Record<string, string> = {};
|
|
432
|
+
Object.entries(data.styles).forEach(([key, value]) => {
|
|
433
|
+
if (typeof value === 'object' && value !== null) {
|
|
434
|
+
const responsiveValue = value as Record<string, string>;
|
|
435
|
+
flatStyles[key] =
|
|
436
|
+
responsiveValue.desktop ||
|
|
437
|
+
responsiveValue.mobile ||
|
|
438
|
+
Object.values(responsiveValue)[0] ||
|
|
439
|
+
'';
|
|
440
|
+
} else if (typeof value === 'string') {
|
|
441
|
+
flatStyles[key] = value;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
if (Object.keys(flatStyles).length > 0) {
|
|
446
|
+
result.blockStyles[blockId] = flatStyles;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} catch {
|
|
450
|
+
// Ignore parse errors
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ============================================================
|
|
458
|
+
// SEARCH SECTION SETTINGS PARSER
|
|
459
|
+
// ============================================================
|
|
460
|
+
|
|
461
|
+
const HEADER_SEARCH_SECTION_ID = 'header-search';
|
|
462
|
+
const HEADER_SEARCH_ICON_BLOCK_ID = 'header-search-icon';
|
|
463
|
+
|
|
464
|
+
export interface SearchSectionStyles {
|
|
465
|
+
width?: string | number;
|
|
466
|
+
maxWidth?: string;
|
|
467
|
+
height?: string | number;
|
|
468
|
+
backgroundColor?: string;
|
|
469
|
+
borderColor?: string;
|
|
470
|
+
borderWidth?: string;
|
|
471
|
+
borderRadius?: string;
|
|
472
|
+
fontSize?: string;
|
|
473
|
+
paddingLeft?: string;
|
|
474
|
+
paddingRight?: string;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export interface SearchIconStyles {
|
|
478
|
+
color?: string;
|
|
479
|
+
iconSize?: number;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export interface HeaderSearchSettings {
|
|
483
|
+
sectionStyles: SearchSectionStyles;
|
|
484
|
+
iconStyles: SearchIconStyles;
|
|
485
|
+
properties: {
|
|
486
|
+
placeholder?: string;
|
|
487
|
+
visible?: boolean;
|
|
488
|
+
width?: string | number;
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const defaultSearchSettings: HeaderSearchSettings = {
|
|
493
|
+
sectionStyles: {},
|
|
494
|
+
iconStyles: {},
|
|
495
|
+
properties: {}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Helper to extract responsive value (desktop first)
|
|
500
|
+
*/
|
|
501
|
+
function extractResponsiveValue(
|
|
502
|
+
value: unknown
|
|
503
|
+
): string | number | boolean | undefined {
|
|
504
|
+
if (value === null || value === undefined) return undefined;
|
|
505
|
+
if (
|
|
506
|
+
typeof value === 'string' ||
|
|
507
|
+
typeof value === 'number' ||
|
|
508
|
+
typeof value === 'boolean'
|
|
509
|
+
)
|
|
510
|
+
return value;
|
|
511
|
+
if (typeof value === 'object' && value !== null) {
|
|
512
|
+
const obj = value as Record<string, string | number | boolean>;
|
|
513
|
+
// Use nullish coalescing to properly handle false values
|
|
514
|
+
if ('desktop' in obj) return obj.desktop;
|
|
515
|
+
if ('mobile' in obj) return obj.mobile;
|
|
516
|
+
return Object.values(obj)[0];
|
|
517
|
+
}
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Parse search section settings from widget data (server-side)
|
|
523
|
+
*/
|
|
524
|
+
export function parseServerSearchSettings(
|
|
525
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
526
|
+
widgetData: any
|
|
527
|
+
): HeaderSearchSettings {
|
|
528
|
+
if (!widgetData?.attributes) {
|
|
529
|
+
return defaultSearchSettings;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const result: HeaderSearchSettings = {
|
|
533
|
+
sectionStyles: {},
|
|
534
|
+
iconStyles: {},
|
|
535
|
+
properties: {}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const attrs = widgetData.attributes;
|
|
539
|
+
|
|
540
|
+
// Parse section styles and properties
|
|
541
|
+
const sectionData = attrs[HEADER_SEARCH_SECTION_ID];
|
|
542
|
+
if (sectionData) {
|
|
543
|
+
try {
|
|
544
|
+
const data =
|
|
545
|
+
typeof sectionData === 'string'
|
|
546
|
+
? JSON.parse(sectionData)
|
|
547
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
548
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
549
|
+
: null;
|
|
550
|
+
|
|
551
|
+
if (data) {
|
|
552
|
+
// Extract properties
|
|
553
|
+
if (data.properties) {
|
|
554
|
+
const width = extractResponsiveValue(data.properties.width);
|
|
555
|
+
result.properties = {
|
|
556
|
+
placeholder: data.properties.placeholder,
|
|
557
|
+
visible: data.properties.visible,
|
|
558
|
+
width: typeof width === 'boolean' ? undefined : width
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Extract styles
|
|
563
|
+
if (data.styles) {
|
|
564
|
+
const styles = data.styles;
|
|
565
|
+
const sectionStyles: SearchSectionStyles = {};
|
|
566
|
+
|
|
567
|
+
// Map theme editor style keys to CSS properties
|
|
568
|
+
const styleKeys = [
|
|
569
|
+
'width',
|
|
570
|
+
'max-width',
|
|
571
|
+
'height',
|
|
572
|
+
'background-color',
|
|
573
|
+
'border-color',
|
|
574
|
+
'border-width',
|
|
575
|
+
'border-radius',
|
|
576
|
+
'font-size',
|
|
577
|
+
'padding-left',
|
|
578
|
+
'padding-right'
|
|
579
|
+
];
|
|
580
|
+
|
|
581
|
+
styleKeys.forEach((key) => {
|
|
582
|
+
const value = extractResponsiveValue(styles[key]);
|
|
583
|
+
if (value !== undefined) {
|
|
584
|
+
// Convert kebab-case to camelCase for CSS-in-JS
|
|
585
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
586
|
+
letter.toUpperCase()
|
|
587
|
+
) as keyof SearchSectionStyles;
|
|
588
|
+
// @ts-expect-error - dynamic assignment
|
|
589
|
+
sectionStyles[camelKey] = value;
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
result.sectionStyles = sectionStyles;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
} catch {
|
|
597
|
+
// Ignore parse errors
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Parse icon block styles
|
|
602
|
+
const iconData = attrs[HEADER_SEARCH_ICON_BLOCK_ID];
|
|
603
|
+
if (iconData) {
|
|
604
|
+
try {
|
|
605
|
+
const data =
|
|
606
|
+
typeof iconData === 'string'
|
|
607
|
+
? JSON.parse(iconData)
|
|
608
|
+
: typeof iconData === 'object' && 'value' in iconData
|
|
609
|
+
? JSON.parse((iconData as { value: string }).value)
|
|
610
|
+
: null;
|
|
611
|
+
|
|
612
|
+
if (data) {
|
|
613
|
+
const iconStyles: SearchIconStyles = {};
|
|
614
|
+
|
|
615
|
+
// Get color from styles
|
|
616
|
+
if (data.styles?.color) {
|
|
617
|
+
const color = extractResponsiveValue(data.styles.color);
|
|
618
|
+
if (typeof color === 'string') {
|
|
619
|
+
iconStyles.color = color;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Get iconSize from properties
|
|
624
|
+
if (data.properties?.iconSize) {
|
|
625
|
+
const size = extractResponsiveValue(data.properties.iconSize);
|
|
626
|
+
if (size !== undefined && typeof size !== 'boolean') {
|
|
627
|
+
iconStyles.iconSize =
|
|
628
|
+
typeof size === 'string' ? parseInt(size, 10) : size;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
result.iconStyles = iconStyles;
|
|
633
|
+
}
|
|
634
|
+
} catch {
|
|
635
|
+
// Ignore parse errors
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// ============================================
|
|
643
|
+
// Language Select Settings
|
|
644
|
+
// ============================================
|
|
645
|
+
|
|
646
|
+
const HEADER_LANGUAGE_SECTION_ID = 'header-language';
|
|
647
|
+
|
|
648
|
+
export interface LanguageSectionStyles {
|
|
649
|
+
[key: string]: unknown;
|
|
650
|
+
width?: string;
|
|
651
|
+
height?: string;
|
|
652
|
+
fontSize?: string;
|
|
653
|
+
color?: string;
|
|
654
|
+
backgroundColor?: string;
|
|
655
|
+
borderColor?: string;
|
|
656
|
+
borderWidth?: string;
|
|
657
|
+
borderRadius?: string;
|
|
658
|
+
paddingLeft?: string;
|
|
659
|
+
paddingRight?: string;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export interface HeaderLanguageSettings {
|
|
663
|
+
sectionStyles?: LanguageSectionStyles;
|
|
664
|
+
properties?: {
|
|
665
|
+
showIcon?: boolean;
|
|
666
|
+
visible?: boolean;
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Parse language select settings from widget data (server-side)
|
|
672
|
+
*/
|
|
673
|
+
export function parseServerLanguageSettings(
|
|
674
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
675
|
+
widgetData: any
|
|
676
|
+
): HeaderLanguageSettings {
|
|
677
|
+
const result: HeaderLanguageSettings = {};
|
|
678
|
+
|
|
679
|
+
if (!widgetData?.attributes) {
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const attrs = widgetData.attributes;
|
|
684
|
+
|
|
685
|
+
// Parse section-level styles and properties
|
|
686
|
+
const sectionData = attrs[HEADER_LANGUAGE_SECTION_ID];
|
|
687
|
+
if (sectionData) {
|
|
688
|
+
try {
|
|
689
|
+
const data =
|
|
690
|
+
typeof sectionData === 'string'
|
|
691
|
+
? JSON.parse(sectionData)
|
|
692
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
693
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
694
|
+
: null;
|
|
695
|
+
|
|
696
|
+
if (data) {
|
|
697
|
+
// Parse properties (showIcon, visible)
|
|
698
|
+
if (data.properties) {
|
|
699
|
+
const properties: HeaderLanguageSettings['properties'] = {};
|
|
700
|
+
|
|
701
|
+
if (data.properties.showIcon !== undefined) {
|
|
702
|
+
const showIcon = extractResponsiveValue(data.properties.showIcon);
|
|
703
|
+
properties.showIcon = showIcon === 'true' || Boolean(showIcon);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (data.properties.visible !== undefined) {
|
|
707
|
+
const visible = extractResponsiveValue(data.properties.visible);
|
|
708
|
+
properties.visible = visible === 'true' || Boolean(visible);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
result.properties = properties;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Parse styles
|
|
715
|
+
if (data.styles) {
|
|
716
|
+
const styles = data.styles;
|
|
717
|
+
const sectionStyles: LanguageSectionStyles = {};
|
|
718
|
+
|
|
719
|
+
const styleKeys = [
|
|
720
|
+
'width',
|
|
721
|
+
'height',
|
|
722
|
+
'min-width',
|
|
723
|
+
'font-size',
|
|
724
|
+
'color',
|
|
725
|
+
'background-color',
|
|
726
|
+
'border-color',
|
|
727
|
+
'border-width',
|
|
728
|
+
'border-radius',
|
|
729
|
+
'padding-left',
|
|
730
|
+
'padding-right'
|
|
731
|
+
];
|
|
732
|
+
|
|
733
|
+
styleKeys.forEach((key) => {
|
|
734
|
+
const value = extractResponsiveValue(styles[key]);
|
|
735
|
+
if (value !== undefined) {
|
|
736
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
737
|
+
letter.toUpperCase()
|
|
738
|
+
) as keyof LanguageSectionStyles;
|
|
739
|
+
sectionStyles[camelKey] = value;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
result.sectionStyles = sectionStyles;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} catch {
|
|
747
|
+
// Ignore parse errors
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// ============================================
|
|
755
|
+
// Currency Select Settings
|
|
756
|
+
// ============================================
|
|
757
|
+
|
|
758
|
+
const HEADER_CURRENCY_SECTION_ID = 'header-currency';
|
|
759
|
+
|
|
760
|
+
export interface CurrencySectionStyles {
|
|
761
|
+
[key: string]: unknown;
|
|
762
|
+
width?: string;
|
|
763
|
+
height?: string;
|
|
764
|
+
fontSize?: string;
|
|
765
|
+
color?: string;
|
|
766
|
+
backgroundColor?: string;
|
|
767
|
+
borderColor?: string;
|
|
768
|
+
borderWidth?: string;
|
|
769
|
+
borderRadius?: string;
|
|
770
|
+
paddingLeft?: string;
|
|
771
|
+
paddingRight?: string;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export interface HeaderCurrencySettings {
|
|
775
|
+
sectionStyles?: CurrencySectionStyles;
|
|
776
|
+
properties?: {
|
|
777
|
+
showIcon?: boolean;
|
|
778
|
+
visible?: boolean;
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Parse currency select settings from widget data (server-side)
|
|
784
|
+
*/
|
|
785
|
+
export function parseServerCurrencySettings(
|
|
786
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
787
|
+
widgetData: any
|
|
788
|
+
): HeaderCurrencySettings {
|
|
789
|
+
const result: HeaderCurrencySettings = {};
|
|
790
|
+
|
|
791
|
+
if (!widgetData?.attributes) {
|
|
792
|
+
return result;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const attrs = widgetData.attributes;
|
|
796
|
+
|
|
797
|
+
// Parse section-level styles and properties
|
|
798
|
+
const sectionData = attrs[HEADER_CURRENCY_SECTION_ID];
|
|
799
|
+
if (sectionData) {
|
|
800
|
+
try {
|
|
801
|
+
const data =
|
|
802
|
+
typeof sectionData === 'string'
|
|
803
|
+
? JSON.parse(sectionData)
|
|
804
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
805
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
806
|
+
: null;
|
|
807
|
+
|
|
808
|
+
if (data) {
|
|
809
|
+
// Parse properties (showIcon, visible)
|
|
810
|
+
if (data.properties) {
|
|
811
|
+
const properties: HeaderCurrencySettings['properties'] = {};
|
|
812
|
+
|
|
813
|
+
if (data.properties.showIcon !== undefined) {
|
|
814
|
+
const showIcon = extractResponsiveValue(data.properties.showIcon);
|
|
815
|
+
properties.showIcon = showIcon === 'true' || Boolean(showIcon);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (data.properties.visible !== undefined) {
|
|
819
|
+
const visible = extractResponsiveValue(data.properties.visible);
|
|
820
|
+
properties.visible = visible === 'true' || Boolean(visible);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
result.properties = properties;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Parse styles
|
|
827
|
+
if (data.styles) {
|
|
828
|
+
const styles = data.styles;
|
|
829
|
+
const sectionStyles: CurrencySectionStyles = {};
|
|
830
|
+
|
|
831
|
+
const styleKeys = [
|
|
832
|
+
'width',
|
|
833
|
+
'height',
|
|
834
|
+
'font-size',
|
|
835
|
+
'color',
|
|
836
|
+
'background-color',
|
|
837
|
+
'border-color',
|
|
838
|
+
'border-width',
|
|
839
|
+
'border-radius',
|
|
840
|
+
'padding-left',
|
|
841
|
+
'padding-right'
|
|
842
|
+
];
|
|
843
|
+
|
|
844
|
+
styleKeys.forEach((key) => {
|
|
845
|
+
const value = extractResponsiveValue(styles[key]);
|
|
846
|
+
if (value !== undefined) {
|
|
847
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
848
|
+
letter.toUpperCase()
|
|
849
|
+
) as keyof CurrencySectionStyles;
|
|
850
|
+
sectionStyles[camelKey] = value;
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
result.sectionStyles = sectionStyles;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
} catch {
|
|
858
|
+
// Ignore parse errors
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return result;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Text Slider section ID
|
|
866
|
+
const HEADER_TEXT_SLIDER_SECTION_ID = 'header-text-slider';
|
|
867
|
+
|
|
868
|
+
export interface TextSliderItem {
|
|
869
|
+
text: string;
|
|
870
|
+
link?: string;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
export interface TextSliderSectionStyles extends Record<string, unknown> {
|
|
874
|
+
fontSize?: string;
|
|
875
|
+
fontWeight?: string;
|
|
876
|
+
color?: string;
|
|
877
|
+
backgroundColor?: string;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
export interface HeaderTextSliderSettings {
|
|
881
|
+
sectionStyles?: Record<string, unknown>;
|
|
882
|
+
properties?: {
|
|
883
|
+
visible?: boolean;
|
|
884
|
+
items?: TextSliderItem[];
|
|
885
|
+
autoPlay?: boolean;
|
|
886
|
+
autoPlayInterval?: number;
|
|
887
|
+
showArrows?: boolean;
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Parse text slider settings from widget data (server-side)
|
|
893
|
+
*/
|
|
894
|
+
export function parseServerTextSliderSettings(
|
|
895
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
896
|
+
widgetData: any
|
|
897
|
+
): HeaderTextSliderSettings {
|
|
898
|
+
const result: HeaderTextSliderSettings = {};
|
|
899
|
+
|
|
900
|
+
if (!widgetData?.attributes) {
|
|
901
|
+
return result;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const attrs = widgetData.attributes;
|
|
905
|
+
|
|
906
|
+
// Parse section-level styles and properties
|
|
907
|
+
const sectionData = attrs[HEADER_TEXT_SLIDER_SECTION_ID];
|
|
908
|
+
if (sectionData) {
|
|
909
|
+
try {
|
|
910
|
+
const data =
|
|
911
|
+
typeof sectionData === 'string'
|
|
912
|
+
? JSON.parse(sectionData)
|
|
913
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
914
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
915
|
+
: null;
|
|
916
|
+
|
|
917
|
+
if (data) {
|
|
918
|
+
// Parse properties
|
|
919
|
+
if (data.properties) {
|
|
920
|
+
const properties: HeaderTextSliderSettings['properties'] = {};
|
|
921
|
+
|
|
922
|
+
if (data.properties.visible !== undefined) {
|
|
923
|
+
const visible = extractResponsiveValue(data.properties.visible);
|
|
924
|
+
properties.visible = visible === 'true' || Boolean(visible);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
if (data.properties.items !== undefined) {
|
|
928
|
+
properties.items = data.properties.items;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (data.properties.autoPlay !== undefined) {
|
|
932
|
+
const autoPlay = extractResponsiveValue(data.properties.autoPlay);
|
|
933
|
+
properties.autoPlay = autoPlay === 'true' || Boolean(autoPlay);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (data.properties.autoPlayInterval !== undefined) {
|
|
937
|
+
const interval = extractResponsiveValue(
|
|
938
|
+
data.properties.autoPlayInterval
|
|
939
|
+
);
|
|
940
|
+
properties.autoPlayInterval =
|
|
941
|
+
typeof interval === 'number'
|
|
942
|
+
? interval
|
|
943
|
+
: parseInt(String(interval), 10) || 3000;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (data.properties.showArrows !== undefined) {
|
|
947
|
+
const showArrows = extractResponsiveValue(
|
|
948
|
+
data.properties.showArrows
|
|
949
|
+
);
|
|
950
|
+
properties.showArrows =
|
|
951
|
+
showArrows === 'true' || Boolean(showArrows);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
result.properties = properties;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Parse styles
|
|
958
|
+
if (data.styles) {
|
|
959
|
+
const styles = data.styles;
|
|
960
|
+
const sectionStyles: TextSliderSectionStyles = {};
|
|
961
|
+
|
|
962
|
+
const styleKeys = [
|
|
963
|
+
'font-size',
|
|
964
|
+
'font-weight',
|
|
965
|
+
'color',
|
|
966
|
+
'background-color'
|
|
967
|
+
];
|
|
968
|
+
|
|
969
|
+
styleKeys.forEach((key) => {
|
|
970
|
+
const value = extractResponsiveValue(styles[key]);
|
|
971
|
+
if (value !== undefined) {
|
|
972
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
973
|
+
letter.toUpperCase()
|
|
974
|
+
) as keyof TextSliderSectionStyles;
|
|
975
|
+
sectionStyles[camelKey] = value;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
result.sectionStyles = sectionStyles;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
} catch {
|
|
983
|
+
// Ignore parse errors
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
return result;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// ============================================
|
|
991
|
+
// Announcement Bar Settings
|
|
992
|
+
// ============================================
|
|
993
|
+
|
|
994
|
+
const HEADER_ANNOUNCEMENT_SECTION_ID = 'header-announcement-bar';
|
|
995
|
+
|
|
996
|
+
export interface HeaderAnnouncementSettings {
|
|
997
|
+
sectionStyles?: Record<string, unknown>;
|
|
998
|
+
properties?: {
|
|
999
|
+
visible?: boolean;
|
|
1000
|
+
text?: string;
|
|
1001
|
+
link?: string;
|
|
1002
|
+
target?: string;
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Parse announcement bar settings from widget data (server-side)
|
|
1008
|
+
*/
|
|
1009
|
+
export function parseServerAnnouncementSettings(
|
|
1010
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1011
|
+
widgetData: any
|
|
1012
|
+
): HeaderAnnouncementSettings {
|
|
1013
|
+
const result: HeaderAnnouncementSettings = {};
|
|
1014
|
+
|
|
1015
|
+
const parseAnnouncementData = (data: any) => {
|
|
1016
|
+
if (!data) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
if (data.properties) {
|
|
1021
|
+
const properties: HeaderAnnouncementSettings['properties'] = {};
|
|
1022
|
+
|
|
1023
|
+
if (data.properties.visible !== undefined) {
|
|
1024
|
+
const visible = extractResponsiveValue(data.properties.visible);
|
|
1025
|
+
properties.visible = visible === 'true' || Boolean(visible);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (data.properties.text !== undefined) {
|
|
1029
|
+
const text = extractResponsiveValue(data.properties.text);
|
|
1030
|
+
if (typeof text === 'string') {
|
|
1031
|
+
properties.text = text;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (data.properties.link !== undefined) {
|
|
1036
|
+
const link = extractResponsiveValue(data.properties.link);
|
|
1037
|
+
if (typeof link === 'string') {
|
|
1038
|
+
properties.link = link;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (data.properties.target !== undefined) {
|
|
1043
|
+
const target = extractResponsiveValue(data.properties.target);
|
|
1044
|
+
if (typeof target === 'string') {
|
|
1045
|
+
properties.target = target;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
result.properties = properties;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if (data.styles) {
|
|
1053
|
+
const styles = data.styles as Record<string, unknown>;
|
|
1054
|
+
const sectionStyles: Record<string, unknown> = {};
|
|
1055
|
+
|
|
1056
|
+
const styleKeys = [
|
|
1057
|
+
'background-color',
|
|
1058
|
+
'color',
|
|
1059
|
+
'font-size',
|
|
1060
|
+
'font-weight',
|
|
1061
|
+
'text-align',
|
|
1062
|
+
'padding-top',
|
|
1063
|
+
'padding-bottom',
|
|
1064
|
+
'padding-left',
|
|
1065
|
+
'padding-right'
|
|
1066
|
+
];
|
|
1067
|
+
|
|
1068
|
+
styleKeys.forEach((key) => {
|
|
1069
|
+
const value = extractResponsiveValue(styles[key]);
|
|
1070
|
+
if (value !== undefined) {
|
|
1071
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
1072
|
+
letter.toUpperCase()
|
|
1073
|
+
);
|
|
1074
|
+
sectionStyles[camelKey] = value;
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
result.sectionStyles = sectionStyles;
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
const attrs = widgetData?.attributes;
|
|
1083
|
+
const sectionData = attrs?.[HEADER_ANNOUNCEMENT_SECTION_ID];
|
|
1084
|
+
|
|
1085
|
+
try {
|
|
1086
|
+
const parsedAttributeData =
|
|
1087
|
+
typeof sectionData === 'string'
|
|
1088
|
+
? JSON.parse(sectionData)
|
|
1089
|
+
: typeof sectionData === 'object' && 'value' in sectionData
|
|
1090
|
+
? JSON.parse((sectionData as { value: string }).value)
|
|
1091
|
+
: null;
|
|
1092
|
+
if (parsedAttributeData) {
|
|
1093
|
+
parseAnnouncementData(parsedAttributeData);
|
|
1094
|
+
return result;
|
|
1095
|
+
}
|
|
1096
|
+
} catch {
|
|
1097
|
+
// Ignore parse errors for attributes and continue with schema metadata fallback
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const sectionSchemaMetadata =
|
|
1101
|
+
widgetData?.widget_type?.schema?.[HEADER_ANNOUNCEMENT_SECTION_ID]?.metadata;
|
|
1102
|
+
parseAnnouncementData(sectionSchemaMetadata);
|
|
1103
|
+
|
|
1104
|
+
return result;
|
|
1105
|
+
}
|