@akinon/projectzero 2.0.0-beta.20 → 2.0.0-beta.21
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 +8 -0
- package/app-template/CHANGELOG.md +138 -0
- package/app-template/next.config.mjs +0 -1
- package/app-template/package.json +31 -30
- package/app-template/src/app/[pz]/[...prettyurl]/page.tsx +2 -2
- package/app-template/src/app/[pz]/account/layout.tsx +2 -1
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/blog/[slug]/page.tsx +4 -2
- package/app-template/src/app/[pz]/category/[pk]/page.tsx +11 -1
- package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +2 -2
- package/app-template/src/app/[pz]/layout.tsx +3 -1
- package/app-template/src/app/[pz]/list/page.tsx +11 -1
- package/app-template/src/app/[pz]/page.tsx +13 -35
- package/app-template/src/app/[pz]/pages/[slug]/page.tsx +19 -0
- package/app-template/src/app/[pz]/product/[pk]/page.tsx +2 -2
- package/app-template/src/app/api/barcode-search/route.ts +1 -1
- package/app-template/src/app/api/cache/route.ts +1 -1
- package/app-template/src/app/api/image-proxy/route.ts +1 -1
- package/app-template/src/app/api/logout/route.ts +1 -1
- package/app-template/src/app/api/product-categories/route.ts +1 -1
- package/app-template/src/app/api/similar-product-list/route.ts +1 -1
- package/app-template/src/app/api/similar-products/route.ts +1 -1
- package/app-template/src/app/api/virtual-try-on/route.ts +1 -1
- package/app-template/src/app/api/web-vitals/route.ts +1 -1
- package/app-template/src/components/quantity-selector.tsx +16 -4
- package/app-template/src/components/widget-content.tsx +3 -3
- package/app-template/src/routes/index.ts +6 -6
- package/app-template/src/utils/__tests__/theme-page-context.test.ts +145 -0
- package/app-template/src/utils/theme-page-context.ts +309 -0
- package/app-template/src/views/basket/basket-item.tsx +107 -691
- package/app-template/src/views/basket/index.ts +0 -2
- package/app-template/src/views/basket/summary.tsx +75 -496
- package/app-template/src/views/breadcrumb.tsx +38 -13
- package/app-template/src/views/category/category-header.tsx +66 -289
- package/app-template/src/views/category/category-info.tsx +24 -173
- package/app-template/src/views/category/filters/index.tsx +48 -208
- package/app-template/src/views/category/layout.tsx +5 -7
- package/app-template/src/views/checkout/index.tsx +0 -5
- package/app-template/src/views/checkout/steps/payment/index.tsx +2 -5
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -72
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +40 -171
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +12 -74
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +45 -128
- package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +27 -232
- package/app-template/src/views/checkout/summary.tsx +29 -303
- package/app-template/src/views/footer.tsx +13 -415
- package/app-template/src/views/guest-login/index.tsx +1 -1
- package/app-template/src/views/header/action-menu.tsx +45 -277
- package/app-template/src/views/header/band.tsx +21 -6
- package/app-template/src/views/header/index.tsx +47 -109
- package/app-template/src/views/header/mini-basket.tsx +45 -820
- package/app-template/src/views/header/navbar.tsx +111 -178
- package/app-template/src/views/header/search/index.tsx +32 -71
- package/app-template/src/views/header/search/results.tsx +65 -127
- package/app-template/src/views/product/accordion-wrapper.tsx +43 -135
- package/app-template/src/views/product/index.ts +1 -1
- package/app-template/src/views/product/layout.tsx +7 -2
- package/app-template/src/views/product/misc-buttons.tsx +25 -339
- package/app-template/src/views/product/product-actions.tsx +8 -137
- package/app-template/src/views/product/product-info.tsx +31 -69
- package/app-template/src/views/product/product-share.tsx +8 -11
- package/app-template/src/views/product/slider.tsx +79 -117
- package/app-template/src/views/product-item/index.tsx +46 -119
- package/app-template/src/widgets/footer-social.tsx +16 -47
- package/app-template/src/widgets/footer-subscription/index.tsx +17 -183
- package/dist/commands/plugins.js +23 -2
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +0 -15
- package/app-template/src/views/basket/basket-summary-context.tsx +0 -560
- package/app-template/src/views/basket/designer-context.tsx +0 -617
- package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +0 -190
- package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +0 -286
- package/app-template/src/views/breadcrumb/constants.ts +0 -15
- package/app-template/src/views/breadcrumb/index.tsx +0 -127
- package/app-template/src/views/category/native-widget-context.tsx +0 -257
- package/app-template/src/views/category/product-list-registrar.tsx +0 -665
- package/app-template/src/views/checkout/checkout-address-registrar.tsx +0 -254
- package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +0 -183
- package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +0 -259
- package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +0 -253
- package/app-template/src/views/checkout/checkout-summary-registrar.tsx +0 -183
- package/app-template/src/views/checkout/constants.ts +0 -5
- package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +0 -15
- package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +0 -18
- package/app-template/src/views/footer/footer-app-banner-context.tsx +0 -326
- package/app-template/src/views/footer/footer-bottom-context.tsx +0 -215
- package/app-template/src/views/footer/footer-bottom-wrapper.tsx +0 -74
- package/app-template/src/views/footer/footer-layout-constants.ts +0 -35
- package/app-template/src/views/footer/footer-layout-registrar.tsx +0 -342
- package/app-template/src/views/footer/footer-layout-switcher.tsx +0 -110
- package/app-template/src/views/footer/footer-menu-context.tsx +0 -211
- package/app-template/src/views/footer/footer-native-widgets.tsx +0 -60
- package/app-template/src/views/footer/footer-social-context.tsx +0 -254
- package/app-template/src/views/footer/footer-subscription-context.tsx +0 -210
- package/app-template/src/views/footer/footer-utils.ts +0 -43
- package/app-template/src/views/footer/footer-value-props-context.tsx +0 -326
- package/app-template/src/views/footer/logo-settings.ts +0 -183
- package/app-template/src/views/footer/native-widget-config.ts +0 -262
- package/app-template/src/views/footer/subscription-settings.ts +0 -122
- package/app-template/src/views/footer/use-footer-logo.ts +0 -162
- package/app-template/src/views/header/designer-context.tsx +0 -261
- package/app-template/src/views/header/header-announcement-registrar.tsx +0 -267
- package/app-template/src/views/header/header-client-wrapper.tsx +0 -496
- package/app-template/src/views/header/header-content.tsx +0 -1026
- package/app-template/src/views/header/header-currency-registrar.tsx +0 -348
- package/app-template/src/views/header/header-icons-context.tsx +0 -262
- package/app-template/src/views/header/header-language-registrar.tsx +0 -348
- package/app-template/src/views/header/header-layout-context.tsx +0 -143
- package/app-template/src/views/header/header-layout-registrar.tsx +0 -658
- package/app-template/src/views/header/header-logo-context.tsx +0 -228
- package/app-template/src/views/header/header-logo.tsx +0 -118
- package/app-template/src/views/header/header-mini-basket-context.tsx +0 -524
- package/app-template/src/views/header/header-search-registrar.tsx +0 -511
- package/app-template/src/views/header/header-text-slider-registrar.tsx +0 -382
- package/app-template/src/views/header/inline-search.tsx +0 -262
- package/app-template/src/views/header/navbar-menu-context.tsx +0 -219
- package/app-template/src/views/header/search/search-input.tsx +0 -61
- package/app-template/src/views/header/server-settings-parser.ts +0 -1105
- package/app-template/src/views/header/use-header-icons.ts +0 -241
- package/app-template/src/views/header/use-header-logo.ts +0 -213
- package/app-template/src/views/header/use-navbar-menu.ts +0 -179
- package/app-template/src/views/product/accordion-section.tsx +0 -61
- package/app-template/src/views/product/custom-button-group.tsx +0 -69
- package/app-template/src/views/product/favorites-button-section.tsx +0 -69
- package/app-template/src/views/product/find-in-store-section.tsx +0 -60
- package/app-template/src/views/product/product-info-section.tsx +0 -140
- package/app-template/src/views/product/quantity-section.tsx +0 -73
- package/app-template/src/views/product/sale-tag.tsx +0 -10
- package/app-template/src/views/product/share-section.tsx +0 -357
- package/app-template/src/views/product/variants-section.tsx +0 -126
- package/app-template/src/views/product-detail/constants.ts +0 -272
- package/app-template/src/views/product-detail/index.ts +0 -10
- package/app-template/src/views/product-detail/product-detail-registrar.tsx +0 -616
- package/app-template/src/widgets/footer-app-banner.tsx +0 -444
- package/app-template/src/widgets/footer-bottom.tsx +0 -127
- package/app-template/src/widgets/footer-menu-compact.tsx +0 -238
- package/app-template/src/widgets/footer-menu-two.tsx +0 -298
- package/app-template/src/widgets/footer-social-client.tsx +0 -251
- package/app-template/src/widgets/footer-value-props.tsx +0 -201
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState, useRef } from 'react';
|
|
4
|
-
import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
|
|
5
|
-
import { useGetWidgetQuery } from '@akinon/next/data/client/misc';
|
|
6
|
-
|
|
7
|
-
const THEME_CONFIG_SLUG = 'theme-config';
|
|
8
|
-
const HEADER_ICONS_WIDGET_SLUG = 'header-icon-settings-2';
|
|
9
|
-
const HEADER_PLACEHOLDER_ID = 'header';
|
|
10
|
-
const HEADER_ICONS_SECTION_ID = 'header-icons';
|
|
11
|
-
|
|
12
|
-
const BLOCK_IDS = {
|
|
13
|
-
search: 'header-icon-search',
|
|
14
|
-
profile: 'header-icon-profile',
|
|
15
|
-
cart: 'header-icon-cart'
|
|
16
|
-
} as const;
|
|
17
|
-
|
|
18
|
-
const BLOCK_META = [
|
|
19
|
-
{ id: BLOCK_IDS.search, type: 'icon-button', label: 'Search Icon' },
|
|
20
|
-
{ id: BLOCK_IDS.profile, type: 'icon-button', label: 'Profile Icon' },
|
|
21
|
-
{ id: BLOCK_IDS.cart, type: 'icon-button', label: 'Cart Icon' }
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const DEFAULT_ICON_SIZE = 20;
|
|
25
|
-
const DEFAULT_ICON_COLOR = 'currentColor';
|
|
26
|
-
|
|
27
|
-
interface IconSettings {
|
|
28
|
-
size: number;
|
|
29
|
-
color: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface HeaderIconSettings {
|
|
33
|
-
search: IconSettings;
|
|
34
|
-
profile: IconSettings;
|
|
35
|
-
cart: IconSettings;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface ThemeSettings {
|
|
39
|
-
headerIcons?: {
|
|
40
|
-
search?: { size?: number; color?: string };
|
|
41
|
-
profile?: { size?: number; color?: string };
|
|
42
|
-
cart?: { size?: number; color?: string };
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const defaultSettings: HeaderIconSettings = {
|
|
47
|
-
search: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR },
|
|
48
|
-
profile: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR },
|
|
49
|
-
cart: { size: DEFAULT_ICON_SIZE, color: DEFAULT_ICON_COLOR }
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function parseHeaderIconSettings(
|
|
53
|
-
themeSettings?: ThemeSettings
|
|
54
|
-
): HeaderIconSettings {
|
|
55
|
-
if (!themeSettings?.headerIcons) {
|
|
56
|
-
return defaultSettings;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const { headerIcons } = themeSettings;
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
search: {
|
|
63
|
-
size: headerIcons.search?.size ?? DEFAULT_ICON_SIZE,
|
|
64
|
-
color: headerIcons.search?.color ?? DEFAULT_ICON_COLOR
|
|
65
|
-
},
|
|
66
|
-
profile: {
|
|
67
|
-
size: headerIcons.profile?.size ?? DEFAULT_ICON_SIZE,
|
|
68
|
-
color: headerIcons.profile?.color ?? DEFAULT_ICON_COLOR
|
|
69
|
-
},
|
|
70
|
-
cart: {
|
|
71
|
-
size: headerIcons.cart?.size ?? DEFAULT_ICON_SIZE,
|
|
72
|
-
color: headerIcons.cart?.color ?? DEFAULT_ICON_COLOR
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function parseBlockStyles(
|
|
78
|
-
block:
|
|
79
|
-
| { styles?: Record<string, unknown>; properties?: Record<string, unknown> }
|
|
80
|
-
| undefined
|
|
81
|
-
): IconSettings | null {
|
|
82
|
-
if (!block) return null;
|
|
83
|
-
|
|
84
|
-
const hasColor = block.styles?.color;
|
|
85
|
-
const hasSize = block.properties?.iconSize;
|
|
86
|
-
|
|
87
|
-
if (!hasColor && !hasSize) return null;
|
|
88
|
-
|
|
89
|
-
let color: string | undefined;
|
|
90
|
-
if (hasColor) {
|
|
91
|
-
const colorValue = block.styles!.color as string | { desktop: string };
|
|
92
|
-
color = typeof colorValue === 'object' ? colorValue.desktop : colorValue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let size: number | undefined;
|
|
96
|
-
if (hasSize) {
|
|
97
|
-
const sizeValue = block.properties!.iconSize as
|
|
98
|
-
| number
|
|
99
|
-
| string
|
|
100
|
-
| { desktop: number | string };
|
|
101
|
-
const rawSize =
|
|
102
|
-
typeof sizeValue === 'object' ? sizeValue.desktop : sizeValue;
|
|
103
|
-
const parsed =
|
|
104
|
-
typeof rawSize === 'string' ? parseInt(rawSize, 10) : rawSize;
|
|
105
|
-
if (!isNaN(parsed)) {
|
|
106
|
-
size = parsed;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (color === undefined && size === undefined) return null;
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
size: size ?? DEFAULT_ICON_SIZE,
|
|
114
|
-
color: color ?? DEFAULT_ICON_COLOR
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function useHeaderIconSettings(
|
|
119
|
-
initialSettings?: HeaderIconSettings
|
|
120
|
-
): HeaderIconSettings {
|
|
121
|
-
const isDesignerRef = useRef(false);
|
|
122
|
-
const [isDesignerChecked, setIsDesignerChecked] = useState(false);
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (typeof window === 'undefined') return;
|
|
126
|
-
isDesignerRef.current = window.self !== window.top;
|
|
127
|
-
setIsDesignerChecked(true);
|
|
128
|
-
}, []);
|
|
129
|
-
|
|
130
|
-
const isDesigner = isDesignerRef.current;
|
|
131
|
-
|
|
132
|
-
const { data: themeConfigData } = useGetWidgetQuery(THEME_CONFIG_SLUG, {
|
|
133
|
-
skip: !isDesignerChecked || isDesigner
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const widgetData = useNativeWidgetData({
|
|
137
|
-
widgetSlug: HEADER_ICONS_WIDGET_SLUG,
|
|
138
|
-
sectionId: HEADER_ICONS_SECTION_ID,
|
|
139
|
-
skip: !isDesignerChecked || isDesigner,
|
|
140
|
-
blockMeta: BLOCK_META
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const [settings, setSettings] = useState<HeaderIconSettings>(
|
|
144
|
-
initialSettings || defaultSettings
|
|
145
|
-
);
|
|
146
|
-
const hasInitialSettings = !!initialSettings;
|
|
147
|
-
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
if (!isDesignerChecked) return;
|
|
150
|
-
if (isDesigner) return;
|
|
151
|
-
if (hasInitialSettings) return;
|
|
152
|
-
if (widgetData.isLoading && !themeConfigData) return;
|
|
153
|
-
|
|
154
|
-
let newSettings = { ...defaultSettings };
|
|
155
|
-
|
|
156
|
-
if (themeConfigData?.attributes?.theme_settings?.value) {
|
|
157
|
-
try {
|
|
158
|
-
const themeSettings: ThemeSettings = JSON.parse(
|
|
159
|
-
themeConfigData.attributes.theme_settings.value
|
|
160
|
-
);
|
|
161
|
-
newSettings = parseHeaderIconSettings(themeSettings);
|
|
162
|
-
} catch {
|
|
163
|
-
// ignore
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
Object.keys(BLOCK_IDS).forEach((key) => {
|
|
168
|
-
const blockId = BLOCK_IDS[key as keyof typeof BLOCK_IDS];
|
|
169
|
-
const block = widgetData.getBlock(blockId);
|
|
170
|
-
const parsed = parseBlockStyles(block);
|
|
171
|
-
|
|
172
|
-
if (parsed) {
|
|
173
|
-
newSettings[key as keyof HeaderIconSettings] = parsed;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
setSettings(newSettings);
|
|
178
|
-
}, [
|
|
179
|
-
themeConfigData,
|
|
180
|
-
widgetData,
|
|
181
|
-
isDesignerChecked,
|
|
182
|
-
isDesigner,
|
|
183
|
-
hasInitialSettings
|
|
184
|
-
]);
|
|
185
|
-
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
const handleMessage = (event: MessageEvent) => {
|
|
188
|
-
const { type, data } = event.data || {};
|
|
189
|
-
|
|
190
|
-
if (type === 'UPDATE_THEME_SETTINGS' && data?.settings?.headerIcons) {
|
|
191
|
-
setSettings(parseHeaderIconSettings(data.settings));
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
197
|
-
data?.theme?.placeholders
|
|
198
|
-
) {
|
|
199
|
-
const placeholder = data.theme.placeholders?.find(
|
|
200
|
-
(p: { slug: string }) => p.slug === HEADER_PLACEHOLDER_ID
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
const headerIconsSection = placeholder?.sections?.find(
|
|
204
|
-
(s: { id: string }) => s.id === HEADER_ICONS_SECTION_ID
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
if (headerIconsSection?.blocks) {
|
|
208
|
-
const blocks = headerIconsSection.blocks;
|
|
209
|
-
|
|
210
|
-
setSettings((prev) => {
|
|
211
|
-
const newSettings = { ...prev };
|
|
212
|
-
let hasUpdates = false;
|
|
213
|
-
|
|
214
|
-
Object.keys(BLOCK_IDS).forEach((key) => {
|
|
215
|
-
const blockId = BLOCK_IDS[key as keyof typeof BLOCK_IDS];
|
|
216
|
-
const block = blocks.find(
|
|
217
|
-
(b: { id: string }) => b.id === blockId
|
|
218
|
-
);
|
|
219
|
-
const parsed = parseBlockStyles(block);
|
|
220
|
-
|
|
221
|
-
if (parsed) {
|
|
222
|
-
newSettings[key as keyof HeaderIconSettings] = parsed;
|
|
223
|
-
hasUpdates = true;
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
return hasUpdates ? newSettings : prev;
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
window.addEventListener('message', handleMessage);
|
|
234
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
235
|
-
}, []);
|
|
236
|
-
|
|
237
|
-
return settings;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export { DEFAULT_ICON_SIZE, DEFAULT_ICON_COLOR };
|
|
241
|
-
export type { IconSettings, HeaderIconSettings };
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState, useRef } from 'react';
|
|
4
|
-
import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
|
|
5
|
-
import { useGetWidgetQuery } from '@akinon/next/data/client/misc';
|
|
6
|
-
|
|
7
|
-
const THEME_CONFIG_SLUG = 'theme-config';
|
|
8
|
-
const HEADER_LOGO_WIDGET_SLUG = 'header-logo-settings-2';
|
|
9
|
-
const HEADER_PLACEHOLDER_ID = 'header';
|
|
10
|
-
const HEADER_LOGO_SECTION_ID = 'header-logo';
|
|
11
|
-
const HEADER_LOGO_BLOCK_ID = 'header-logo-image';
|
|
12
|
-
|
|
13
|
-
const DEFAULT_LOGO_SRC = '/logo.svg';
|
|
14
|
-
const DEFAULT_LOGO_WIDTH = 120;
|
|
15
|
-
const DEFAULT_LOGO_HEIGHT = 40;
|
|
16
|
-
|
|
17
|
-
const BLOCK_META = [
|
|
18
|
-
{ id: HEADER_LOGO_BLOCK_ID, type: 'image', label: 'Logo Image' }
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
interface LogoSettings {
|
|
22
|
-
src: string;
|
|
23
|
-
width: number | string;
|
|
24
|
-
height: number | string;
|
|
25
|
-
maxWidth?: string;
|
|
26
|
-
maxHeight?: string;
|
|
27
|
-
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const defaultSettings: LogoSettings = {
|
|
31
|
-
src: DEFAULT_LOGO_SRC,
|
|
32
|
-
width: DEFAULT_LOGO_WIDTH,
|
|
33
|
-
height: DEFAULT_LOGO_HEIGHT,
|
|
34
|
-
objectFit: 'contain'
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
function parseThemeConfigLogo(themeSettings?: {
|
|
38
|
-
logo?: string;
|
|
39
|
-
}): Partial<LogoSettings> {
|
|
40
|
-
if (!themeSettings?.logo) {
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
return { src: themeSettings.logo };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function parseBlockStyles(
|
|
47
|
-
block: { styles?: Record<string, unknown>; value?: unknown } | undefined
|
|
48
|
-
): Partial<LogoSettings> {
|
|
49
|
-
if (!block) return {};
|
|
50
|
-
|
|
51
|
-
const result: Partial<LogoSettings> = {};
|
|
52
|
-
|
|
53
|
-
if (block.value) {
|
|
54
|
-
const value =
|
|
55
|
-
typeof block.value === 'object'
|
|
56
|
-
? (block.value as { desktop?: string }).desktop
|
|
57
|
-
: block.value;
|
|
58
|
-
if (value) {
|
|
59
|
-
result.src = value as string;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (block.styles) {
|
|
64
|
-
const styles = block.styles;
|
|
65
|
-
|
|
66
|
-
if (styles.width) {
|
|
67
|
-
const width =
|
|
68
|
-
typeof styles.width === 'object'
|
|
69
|
-
? (styles.width as { desktop: string | number }).desktop
|
|
70
|
-
: styles.width;
|
|
71
|
-
result.width = width as string | number;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (styles.height) {
|
|
75
|
-
const height =
|
|
76
|
-
typeof styles.height === 'object'
|
|
77
|
-
? (styles.height as { desktop: string | number }).desktop
|
|
78
|
-
: styles.height;
|
|
79
|
-
result.height = height as string | number;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (styles.maxWidth || styles['max-width']) {
|
|
83
|
-
const maxWidth = (styles.maxWidth || styles['max-width']) as
|
|
84
|
-
| string
|
|
85
|
-
| { desktop: string };
|
|
86
|
-
result.maxWidth =
|
|
87
|
-
typeof maxWidth === 'object' ? maxWidth.desktop : maxWidth;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (styles.maxHeight || styles['max-height']) {
|
|
91
|
-
const maxHeight = (styles.maxHeight || styles['max-height']) as
|
|
92
|
-
| string
|
|
93
|
-
| { desktop: string };
|
|
94
|
-
result.maxHeight =
|
|
95
|
-
typeof maxHeight === 'object' ? maxHeight.desktop : maxHeight;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (styles.objectFit || styles['object-fit']) {
|
|
99
|
-
const objectFit = (styles.objectFit || styles['object-fit']) as
|
|
100
|
-
| string
|
|
101
|
-
| { desktop: string };
|
|
102
|
-
result.objectFit = (
|
|
103
|
-
typeof objectFit === 'object' ? objectFit.desktop : objectFit
|
|
104
|
-
) as LogoSettings['objectFit'];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function useHeaderLogoSettings(
|
|
112
|
-
initialSettings?: LogoSettings
|
|
113
|
-
): LogoSettings {
|
|
114
|
-
const isDesignerRef = useRef(false);
|
|
115
|
-
const [isDesignerChecked, setIsDesignerChecked] = useState(false);
|
|
116
|
-
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
if (typeof window === 'undefined') return;
|
|
119
|
-
isDesignerRef.current = window.self !== window.top;
|
|
120
|
-
setIsDesignerChecked(true);
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
|
-
const isDesigner = isDesignerRef.current;
|
|
124
|
-
|
|
125
|
-
const { data: themeConfigData } = useGetWidgetQuery(THEME_CONFIG_SLUG, {
|
|
126
|
-
skip: !isDesignerChecked || isDesigner
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const widgetData = useNativeWidgetData({
|
|
130
|
-
widgetSlug: HEADER_LOGO_WIDGET_SLUG,
|
|
131
|
-
sectionId: HEADER_LOGO_SECTION_ID,
|
|
132
|
-
skip: !isDesignerChecked || isDesigner,
|
|
133
|
-
blockMeta: BLOCK_META
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const [settings, setSettings] = useState<LogoSettings>(
|
|
137
|
-
initialSettings || defaultSettings
|
|
138
|
-
);
|
|
139
|
-
const hasInitialSettings = !!initialSettings;
|
|
140
|
-
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
if (!isDesignerChecked) return;
|
|
143
|
-
if (isDesigner) return;
|
|
144
|
-
if (hasInitialSettings) return;
|
|
145
|
-
if (widgetData.isLoading && !themeConfigData) return;
|
|
146
|
-
|
|
147
|
-
let newSettings = { ...defaultSettings };
|
|
148
|
-
|
|
149
|
-
if (themeConfigData?.attributes?.theme_settings?.value) {
|
|
150
|
-
try {
|
|
151
|
-
const themeSettings = JSON.parse(
|
|
152
|
-
themeConfigData.attributes.theme_settings.value
|
|
153
|
-
);
|
|
154
|
-
const themeConfigLogo = parseThemeConfigLogo(themeSettings);
|
|
155
|
-
newSettings = { ...newSettings, ...themeConfigLogo };
|
|
156
|
-
} catch {
|
|
157
|
-
// ignore
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const block = widgetData.getBlock(HEADER_LOGO_BLOCK_ID);
|
|
162
|
-
if (block) {
|
|
163
|
-
const parsed = parseBlockStyles(block);
|
|
164
|
-
newSettings = { ...newSettings, ...parsed };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
setSettings(newSettings);
|
|
168
|
-
}, [
|
|
169
|
-
themeConfigData,
|
|
170
|
-
widgetData,
|
|
171
|
-
isDesignerChecked,
|
|
172
|
-
isDesigner,
|
|
173
|
-
hasInitialSettings
|
|
174
|
-
]);
|
|
175
|
-
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
const handleMessage = (event: MessageEvent) => {
|
|
178
|
-
const { type, data } = event.data || {};
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
182
|
-
data?.theme?.placeholders
|
|
183
|
-
) {
|
|
184
|
-
const placeholder = data.theme.placeholders?.find(
|
|
185
|
-
(p: { slug: string }) => p.slug === HEADER_PLACEHOLDER_ID
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const headerLogoSection = placeholder?.sections?.find(
|
|
189
|
-
(s: { id: string }) => s.id === HEADER_LOGO_SECTION_ID
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
if (headerLogoSection?.blocks) {
|
|
193
|
-
const block = headerLogoSection.blocks.find(
|
|
194
|
-
(b: { id: string }) => b.id === HEADER_LOGO_BLOCK_ID
|
|
195
|
-
);
|
|
196
|
-
const parsed = parseBlockStyles(block);
|
|
197
|
-
|
|
198
|
-
if (Object.keys(parsed).length > 0) {
|
|
199
|
-
setSettings((prev) => ({ ...prev, ...parsed }));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
window.addEventListener('message', handleMessage);
|
|
206
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
207
|
-
}, []);
|
|
208
|
-
|
|
209
|
-
return settings;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export { DEFAULT_LOGO_SRC, DEFAULT_LOGO_WIDTH, DEFAULT_LOGO_HEIGHT };
|
|
213
|
-
export type { LogoSettings };
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState, useRef } from 'react';
|
|
4
|
-
import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
|
|
5
|
-
|
|
6
|
-
const NAVBAR_MENU_WIDGET_SLUG = 'header-nav-settings-2';
|
|
7
|
-
const HEADER_PLACEHOLDER_ID = 'header';
|
|
8
|
-
const NAVBAR_MENU_SECTION_ID = 'header-nav';
|
|
9
|
-
const NAVBAR_MENU_BLOCK_ID = 'header-nav-menu-item';
|
|
10
|
-
|
|
11
|
-
const DEFAULT_FONT_SIZE = '14px';
|
|
12
|
-
const DEFAULT_COLOR = 'currentColor';
|
|
13
|
-
const DEFAULT_HOVER_COLOR = '';
|
|
14
|
-
const DEFAULT_FONT_WEIGHT = '400';
|
|
15
|
-
|
|
16
|
-
const BLOCK_META = [
|
|
17
|
-
{ id: NAVBAR_MENU_BLOCK_ID, type: 'text', label: 'Menu Items' }
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
export interface NavbarMenuSettings {
|
|
21
|
-
fontSize: string;
|
|
22
|
-
color: string;
|
|
23
|
-
hoverColor: string;
|
|
24
|
-
fontWeight: string;
|
|
25
|
-
textTransform?: string;
|
|
26
|
-
letterSpacing?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const defaultSettings: NavbarMenuSettings = {
|
|
30
|
-
fontSize: DEFAULT_FONT_SIZE,
|
|
31
|
-
color: DEFAULT_COLOR,
|
|
32
|
-
hoverColor: DEFAULT_HOVER_COLOR,
|
|
33
|
-
fontWeight: DEFAULT_FONT_WEIGHT
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
function parseBlockStyles(
|
|
37
|
-
blockData:
|
|
38
|
-
| { styles?: Record<string, unknown>; properties?: Record<string, unknown> }
|
|
39
|
-
| undefined
|
|
40
|
-
): Partial<NavbarMenuSettings> {
|
|
41
|
-
if (!blockData) return {};
|
|
42
|
-
|
|
43
|
-
const styles = blockData.styles || {};
|
|
44
|
-
const result: Partial<NavbarMenuSettings> = {};
|
|
45
|
-
|
|
46
|
-
if (styles.fontSize || styles['font-size']) {
|
|
47
|
-
const fontSize = (styles.fontSize || styles['font-size']) as
|
|
48
|
-
| string
|
|
49
|
-
| { desktop: string };
|
|
50
|
-
result.fontSize =
|
|
51
|
-
typeof fontSize === 'object' ? fontSize.desktop : fontSize;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (styles.color) {
|
|
55
|
-
const color = styles.color as string | { desktop: string };
|
|
56
|
-
result.color = typeof color === 'object' ? color.desktop : color;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (styles.hoverColor || styles['hover-color']) {
|
|
60
|
-
const hoverColor = (styles.hoverColor || styles['hover-color']) as
|
|
61
|
-
| string
|
|
62
|
-
| { desktop: string };
|
|
63
|
-
result.hoverColor =
|
|
64
|
-
typeof hoverColor === 'object' ? hoverColor.desktop : hoverColor;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (styles.fontWeight || styles['font-weight']) {
|
|
68
|
-
const fontWeight = (styles.fontWeight || styles['font-weight']) as
|
|
69
|
-
| string
|
|
70
|
-
| { desktop: string };
|
|
71
|
-
result.fontWeight =
|
|
72
|
-
typeof fontWeight === 'object' ? fontWeight.desktop : fontWeight;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (styles.textTransform || styles['text-transform']) {
|
|
76
|
-
const textTransform = (styles.textTransform || styles['text-transform']) as
|
|
77
|
-
| string
|
|
78
|
-
| { desktop: string };
|
|
79
|
-
result.textTransform =
|
|
80
|
-
typeof textTransform === 'object' ? textTransform.desktop : textTransform;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (styles.letterSpacing || styles['letter-spacing']) {
|
|
84
|
-
const letterSpacing = (styles.letterSpacing || styles['letter-spacing']) as
|
|
85
|
-
| string
|
|
86
|
-
| { desktop: string };
|
|
87
|
-
result.letterSpacing =
|
|
88
|
-
typeof letterSpacing === 'object' ? letterSpacing.desktop : letterSpacing;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function useNavbarMenuSettings(
|
|
95
|
-
initialSettings?: NavbarMenuSettings
|
|
96
|
-
): NavbarMenuSettings {
|
|
97
|
-
const isDesignerRef = useRef(false);
|
|
98
|
-
const [isDesignerChecked, setIsDesignerChecked] = useState(false);
|
|
99
|
-
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
if (typeof window === 'undefined') return;
|
|
102
|
-
isDesignerRef.current = window.self !== window.top;
|
|
103
|
-
setIsDesignerChecked(true);
|
|
104
|
-
}, []);
|
|
105
|
-
|
|
106
|
-
const isDesigner = isDesignerRef.current;
|
|
107
|
-
|
|
108
|
-
const widgetData = useNativeWidgetData({
|
|
109
|
-
widgetSlug: NAVBAR_MENU_WIDGET_SLUG,
|
|
110
|
-
sectionId: NAVBAR_MENU_SECTION_ID,
|
|
111
|
-
skip: !isDesignerChecked || isDesigner,
|
|
112
|
-
blockMeta: BLOCK_META
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const [settings, setSettings] = useState<NavbarMenuSettings>(
|
|
116
|
-
initialSettings || defaultSettings
|
|
117
|
-
);
|
|
118
|
-
const hasInitialSettings = !!initialSettings;
|
|
119
|
-
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
if (!isDesignerChecked) return;
|
|
122
|
-
if (isDesigner) return;
|
|
123
|
-
if (hasInitialSettings) return;
|
|
124
|
-
if (widgetData.isLoading) return;
|
|
125
|
-
|
|
126
|
-
const block = widgetData.getBlock(NAVBAR_MENU_BLOCK_ID);
|
|
127
|
-
if (block) {
|
|
128
|
-
const parsed = parseBlockStyles(block);
|
|
129
|
-
if (Object.keys(parsed).length > 0) {
|
|
130
|
-
setSettings((prev) => ({ ...prev, ...parsed }));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}, [widgetData, isDesignerChecked, isDesigner, hasInitialSettings]);
|
|
134
|
-
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
const handleMessage = (event: MessageEvent) => {
|
|
137
|
-
const { type, data } = event.data || {};
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
141
|
-
data?.theme?.placeholders
|
|
142
|
-
) {
|
|
143
|
-
const placeholder = data.theme.placeholders?.find(
|
|
144
|
-
(p: { slug: string }) => p.slug === HEADER_PLACEHOLDER_ID
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const navSection = placeholder?.sections?.find(
|
|
148
|
-
(s: { id: string }) => s.id === NAVBAR_MENU_SECTION_ID
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
if (navSection?.blocks) {
|
|
152
|
-
const block = navSection.blocks.find(
|
|
153
|
-
(b: { id: string }) => b.id === NAVBAR_MENU_BLOCK_ID
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
if (block) {
|
|
157
|
-
const parsed = parseBlockStyles(block);
|
|
158
|
-
if (Object.keys(parsed).length > 0) {
|
|
159
|
-
setSettings((prev) => ({ ...prev, ...parsed }));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
window.addEventListener('message', handleMessage);
|
|
167
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
168
|
-
}, []);
|
|
169
|
-
|
|
170
|
-
return settings;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export { defaultSettings as defaultNavbarMenuSettings };
|
|
174
|
-
export {
|
|
175
|
-
DEFAULT_FONT_SIZE,
|
|
176
|
-
DEFAULT_COLOR,
|
|
177
|
-
DEFAULT_HOVER_COLOR,
|
|
178
|
-
DEFAULT_FONT_WEIGHT
|
|
179
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Accordion Section Wrapper
|
|
5
|
-
*
|
|
6
|
-
* Wraps accordion components with Theme Editor styling support.
|
|
7
|
-
* Applies CSS variables for customization via Theme Editor.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { PropsWithChildren, CSSProperties, useMemo } from 'react';
|
|
11
|
-
import clsx from 'clsx';
|
|
12
|
-
import {
|
|
13
|
-
useProductDetail,
|
|
14
|
-
ACCORDION_SECTION_ID
|
|
15
|
-
} from '@theme/views/product-detail';
|
|
16
|
-
|
|
17
|
-
export default function AccordionSection({ children }: PropsWithChildren) {
|
|
18
|
-
const { isDesigner, selectedSectionId, accordionStyles } = useProductDetail();
|
|
19
|
-
|
|
20
|
-
const isSelected = isDesigner && selectedSectionId === ACCORDION_SECTION_ID;
|
|
21
|
-
|
|
22
|
-
const cssVariables = useMemo((): CSSProperties => {
|
|
23
|
-
if (!accordionStyles) return {};
|
|
24
|
-
|
|
25
|
-
const vars: Record<string, string> = {};
|
|
26
|
-
Object.entries(accordionStyles).forEach(([key, value]) => {
|
|
27
|
-
if (value) {
|
|
28
|
-
vars[`--accordion-${key}`] = value;
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
return vars as CSSProperties;
|
|
32
|
-
}, [accordionStyles]);
|
|
33
|
-
|
|
34
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
35
|
-
if (isDesigner) {
|
|
36
|
-
e.stopPropagation();
|
|
37
|
-
window.parent?.postMessage(
|
|
38
|
-
{
|
|
39
|
-
type: 'SELECT_SECTION',
|
|
40
|
-
data: {
|
|
41
|
-
placeholderId: 'product-detail',
|
|
42
|
-
sectionId: ACCORDION_SECTION_ID
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
'*'
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<div
|
|
52
|
-
className={clsx('accordion-section-wrapper', {
|
|
53
|
-
'ring-2 ring-blue-500 ring-offset-2': isSelected
|
|
54
|
-
})}
|
|
55
|
-
style={cssVariables}
|
|
56
|
-
onClick={handleClick}
|
|
57
|
-
>
|
|
58
|
-
{children}
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|