@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,348 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Header Currency Section Registrar
|
|
5
|
+
*
|
|
6
|
+
* This component registers the "Currency Select" section for the header placeholder.
|
|
7
|
+
* The currency section is a native widget that can be added via "Add Section" button.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
createContext,
|
|
12
|
+
useContext,
|
|
13
|
+
useEffect,
|
|
14
|
+
useRef,
|
|
15
|
+
useState,
|
|
16
|
+
PropsWithChildren
|
|
17
|
+
} from 'react';
|
|
18
|
+
|
|
19
|
+
// Constants
|
|
20
|
+
export const HEADER_CURRENCY_PLACEHOLDER_ID = 'header';
|
|
21
|
+
export const HEADER_CURRENCY_SECTION_ID = 'header-currency';
|
|
22
|
+
export const HEADER_CURRENCY_WIDGET_SLUG = 'header-currency-settings-2';
|
|
23
|
+
|
|
24
|
+
// Global flag to track if registration has been done
|
|
25
|
+
declare global {
|
|
26
|
+
interface Window {
|
|
27
|
+
__headerCurrencyRegistered?: boolean;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if running inside designer iframe
|
|
33
|
+
*/
|
|
34
|
+
function isInDesignerMode(): boolean {
|
|
35
|
+
if (typeof window === 'undefined') return false;
|
|
36
|
+
return window.self !== window.top;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ThemeSection {
|
|
40
|
+
id: string;
|
|
41
|
+
visible?: boolean;
|
|
42
|
+
properties?: Record<string, unknown>;
|
|
43
|
+
styles?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ThemePlaceholder {
|
|
47
|
+
slug: string;
|
|
48
|
+
sections: ThemeSection[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface HeaderCurrencyProperties {
|
|
52
|
+
showIcon?: boolean;
|
|
53
|
+
visible?: boolean;
|
|
54
|
+
labelFormat?: 'full' | 'symbol' | 'code';
|
|
55
|
+
/** Custom SVG icon content */
|
|
56
|
+
icon?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface HeaderCurrencyContextValue {
|
|
60
|
+
isDesigner: boolean;
|
|
61
|
+
isCurrencySectionSelected: boolean;
|
|
62
|
+
isSectionVisible: boolean;
|
|
63
|
+
properties: HeaderCurrencyProperties;
|
|
64
|
+
sectionStyles: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const HeaderCurrencyContext = createContext<HeaderCurrencyContextValue>({
|
|
68
|
+
isDesigner: false,
|
|
69
|
+
isCurrencySectionSelected: false,
|
|
70
|
+
isSectionVisible: false, // Not visible by default, must be added via Add Section
|
|
71
|
+
properties: { showIcon: true },
|
|
72
|
+
sectionStyles: {}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const useHeaderCurrency = () => useContext(HeaderCurrencyContext);
|
|
76
|
+
|
|
77
|
+
interface HeaderCurrencyRegistrarProps extends PropsWithChildren {
|
|
78
|
+
/**
|
|
79
|
+
* Initial settings from server-side parsing (to avoid flash)
|
|
80
|
+
*/
|
|
81
|
+
initialSettings?: {
|
|
82
|
+
sectionStyles?: Record<string, unknown>;
|
|
83
|
+
properties?: HeaderCurrencyProperties;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* HeaderCurrencyRegistrar
|
|
89
|
+
*
|
|
90
|
+
* Registers the Currency Select native section with Theme Editor.
|
|
91
|
+
* The section can be added via "Add Section" button in the header placeholder.
|
|
92
|
+
*/
|
|
93
|
+
export default function HeaderCurrencyRegistrar({
|
|
94
|
+
children,
|
|
95
|
+
initialSettings = {}
|
|
96
|
+
}: HeaderCurrencyRegistrarProps) {
|
|
97
|
+
const isDesignerRef = useRef(false);
|
|
98
|
+
const [sectionProperties, setSectionProperties] =
|
|
99
|
+
useState<HeaderCurrencyProperties>(
|
|
100
|
+
initialSettings.properties || { showIcon: true }
|
|
101
|
+
);
|
|
102
|
+
const [sectionStyles, setSectionStyles] = useState<Record<string, unknown>>(
|
|
103
|
+
initialSettings.sectionStyles || {}
|
|
104
|
+
);
|
|
105
|
+
const [isCurrencySelected, setIsCurrencySelected] = useState(false);
|
|
106
|
+
// Initialize visibility from server-side settings - defaults to false if no settings
|
|
107
|
+
const [isSectionVisible, setIsSectionVisible] = useState(
|
|
108
|
+
initialSettings.properties?.visible ?? false
|
|
109
|
+
);
|
|
110
|
+
const hasRegisteredNativeWidget = useRef(false);
|
|
111
|
+
|
|
112
|
+
const hasInitialSettings = !!(
|
|
113
|
+
(initialSettings.sectionStyles &&
|
|
114
|
+
Object.keys(initialSettings.sectionStyles).length > 0) ||
|
|
115
|
+
(initialSettings.properties &&
|
|
116
|
+
Object.keys(initialSettings.properties).length > 0)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
isDesignerRef.current = isInDesignerMode();
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
const isDesigner = isDesignerRef.current;
|
|
124
|
+
|
|
125
|
+
// Register native widget
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
const isInIframe =
|
|
128
|
+
typeof window !== 'undefined' && window.self !== window.top;
|
|
129
|
+
if (!isInIframe || !window.parent) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Skip if already registered
|
|
134
|
+
if (
|
|
135
|
+
typeof window !== 'undefined' &&
|
|
136
|
+
window.__headerCurrencyRegistered &&
|
|
137
|
+
hasRegisteredNativeWidget.current
|
|
138
|
+
) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Send native widget registration to Theme Editor
|
|
143
|
+
// autoAdd: false means this section won't be auto-inserted when registered
|
|
144
|
+
// It must be manually added via "Add Section" button
|
|
145
|
+
const nativeWidgetConfig = {
|
|
146
|
+
placeholderId: HEADER_CURRENCY_PLACEHOLDER_ID,
|
|
147
|
+
autoAdd: false,
|
|
148
|
+
section: {
|
|
149
|
+
id: HEADER_CURRENCY_SECTION_ID,
|
|
150
|
+
type: 'native',
|
|
151
|
+
label: 'Currency Select',
|
|
152
|
+
blocks: [],
|
|
153
|
+
properties: sectionProperties,
|
|
154
|
+
styles: sectionStyles
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
window.parent.postMessage(
|
|
159
|
+
{
|
|
160
|
+
type: 'REGISTER_NATIVE_WIDGETS',
|
|
161
|
+
data: { widgets: [nativeWidgetConfig] }
|
|
162
|
+
},
|
|
163
|
+
'*'
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Mark as registered
|
|
167
|
+
window.__headerCurrencyRegistered = true;
|
|
168
|
+
hasRegisteredNativeWidget.current = true;
|
|
169
|
+
}, [sectionProperties, sectionStyles]);
|
|
170
|
+
|
|
171
|
+
// Apply highlight style to currency container when section is selected
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (typeof window === 'undefined') return;
|
|
174
|
+
|
|
175
|
+
const currencyContainer = document.querySelector(
|
|
176
|
+
'[data-section-id="header-currency"]'
|
|
177
|
+
);
|
|
178
|
+
if (!currencyContainer) return;
|
|
179
|
+
|
|
180
|
+
if (isCurrencySelected) {
|
|
181
|
+
(currencyContainer as HTMLElement).style.outline = '2px solid #3b82f6';
|
|
182
|
+
(currencyContainer as HTMLElement).style.outlineOffset = '-2px';
|
|
183
|
+
} else {
|
|
184
|
+
(currencyContainer as HTMLElement).style.outline = '';
|
|
185
|
+
(currencyContainer as HTMLElement).style.outlineOffset = '';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return () => {
|
|
189
|
+
(currencyContainer as HTMLElement).style.outline = '';
|
|
190
|
+
(currencyContainer as HTMLElement).style.outlineOffset = '';
|
|
191
|
+
};
|
|
192
|
+
}, [isCurrencySelected]);
|
|
193
|
+
|
|
194
|
+
// Listen for theme updates and selection changes from Theme Editor
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (typeof window === 'undefined') return;
|
|
197
|
+
|
|
198
|
+
const handleMessage = (event: MessageEvent) => {
|
|
199
|
+
const { type, data } = event.data || {};
|
|
200
|
+
|
|
201
|
+
// Handle theme updates
|
|
202
|
+
if (
|
|
203
|
+
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
204
|
+
data?.theme?.placeholders
|
|
205
|
+
) {
|
|
206
|
+
const placeholder = data.theme.placeholders?.find(
|
|
207
|
+
(p: ThemePlaceholder) => p.slug === HEADER_CURRENCY_PLACEHOLDER_ID
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const currencySection = placeholder?.sections?.find(
|
|
211
|
+
(s: ThemeSection) => s.id === HEADER_CURRENCY_SECTION_ID
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (currencySection) {
|
|
215
|
+
// Check section visibility from properties first
|
|
216
|
+
// Section properties.visible determines if section should be shown
|
|
217
|
+
const sectionVisible = currencySection.properties?.visible;
|
|
218
|
+
if (sectionVisible !== undefined) {
|
|
219
|
+
// Handle responsive format { desktop: bool, mobile: bool } or direct boolean
|
|
220
|
+
const isVisible =
|
|
221
|
+
typeof sectionVisible === 'boolean'
|
|
222
|
+
? sectionVisible
|
|
223
|
+
: sectionVisible?.desktop === true ||
|
|
224
|
+
sectionVisible?.mobile === true;
|
|
225
|
+
setIsSectionVisible(isVisible);
|
|
226
|
+
} else if (typeof currencySection.visible === 'boolean') {
|
|
227
|
+
// Fallback to section.visible flag
|
|
228
|
+
setIsSectionVisible(currencySection.visible);
|
|
229
|
+
} else {
|
|
230
|
+
// Section exists in theme without explicit visibility, show it
|
|
231
|
+
setIsSectionVisible(true);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// In designer mode, always apply updates from Theme Editor
|
|
235
|
+
// In standalone mode, only apply if no initial settings (to avoid flash)
|
|
236
|
+
const shouldApplyUpdates = isDesigner || !hasInitialSettings;
|
|
237
|
+
|
|
238
|
+
if (shouldApplyUpdates) {
|
|
239
|
+
// Update section properties
|
|
240
|
+
if (currencySection.properties) {
|
|
241
|
+
setSectionProperties(
|
|
242
|
+
currencySection.properties as HeaderCurrencyProperties
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Update section styles
|
|
247
|
+
if (currencySection.styles) {
|
|
248
|
+
setSectionStyles(currencySection.styles);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} else if (placeholder) {
|
|
252
|
+
// Section not in placeholder, hide it
|
|
253
|
+
setIsSectionVisible(false);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Handle property updates
|
|
258
|
+
if (type === 'UPDATE_SECTION_PROPERTY' || type === 'UPDATE_PROPERTY') {
|
|
259
|
+
const { sectionId, key, value, properties } = data || {};
|
|
260
|
+
|
|
261
|
+
if (sectionId === HEADER_CURRENCY_SECTION_ID) {
|
|
262
|
+
if (key && value !== undefined) {
|
|
263
|
+
setSectionProperties((prev) => ({
|
|
264
|
+
...prev,
|
|
265
|
+
[key]: value
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
if (properties) {
|
|
269
|
+
setSectionProperties(properties as HeaderCurrencyProperties);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Handle style updates for the section
|
|
275
|
+
if (type === 'UPDATE_SECTION_STYLE' || type === 'UPDATE_STYLE') {
|
|
276
|
+
const { sectionId, key, value, styles } = data || {};
|
|
277
|
+
|
|
278
|
+
if (sectionId === HEADER_CURRENCY_SECTION_ID) {
|
|
279
|
+
if (key && value !== undefined) {
|
|
280
|
+
setSectionStyles((prev) => ({
|
|
281
|
+
...prev,
|
|
282
|
+
[key]: value
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
if (styles) {
|
|
286
|
+
setSectionStyles(styles);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Handle selection changes
|
|
292
|
+
if (type === 'SELECT_SECTION') {
|
|
293
|
+
const { placeholderId, sectionId } = data || {};
|
|
294
|
+
|
|
295
|
+
const isSelected =
|
|
296
|
+
placeholderId === HEADER_CURRENCY_PLACEHOLDER_ID &&
|
|
297
|
+
sectionId === HEADER_CURRENCY_SECTION_ID;
|
|
298
|
+
|
|
299
|
+
setIsCurrencySelected(isSelected);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Handle block selection
|
|
303
|
+
if (type === 'SELECT_BLOCK') {
|
|
304
|
+
const { sectionId } = data || {};
|
|
305
|
+
if (sectionId === HEADER_CURRENCY_SECTION_ID) {
|
|
306
|
+
setIsCurrencySelected(true);
|
|
307
|
+
} else {
|
|
308
|
+
setIsCurrencySelected(false);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Handle deselection
|
|
313
|
+
if (type === 'DESELECT' || type === 'CLEAR_SELECTION') {
|
|
314
|
+
setIsCurrencySelected(false);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Handle visibility toggle
|
|
318
|
+
if (type === 'TOGGLE_SECTION_VISIBILITY') {
|
|
319
|
+
const { sectionId } = data || {};
|
|
320
|
+
if (sectionId === HEADER_CURRENCY_SECTION_ID) {
|
|
321
|
+
setIsSectionVisible((prev) => !prev);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
window.addEventListener('message', handleMessage);
|
|
327
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
328
|
+
}, [hasInitialSettings, isDesigner]);
|
|
329
|
+
|
|
330
|
+
// If no children, just return null (registration-only mode)
|
|
331
|
+
if (!children) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<HeaderCurrencyContext.Provider
|
|
337
|
+
value={{
|
|
338
|
+
isDesigner,
|
|
339
|
+
isCurrencySectionSelected: isCurrencySelected,
|
|
340
|
+
isSectionVisible,
|
|
341
|
+
properties: sectionProperties,
|
|
342
|
+
sectionStyles
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
{children}
|
|
346
|
+
</HeaderCurrencyContext.Provider>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
PropsWithChildren,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { useExternalDesigner } from '@akinon/next/components/theme-editor/hooks/use-external-designer';
|
|
13
|
+
import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
|
|
14
|
+
import { useHeaderLayout } from './header-layout-registrar';
|
|
15
|
+
|
|
16
|
+
export const HEADER_ICONS_PLACEHOLDER_ID = 'header';
|
|
17
|
+
export const HEADER_ICONS_SECTION_ID = 'header-icons';
|
|
18
|
+
export const HEADER_ICONS_WIDGET_SLUG = 'header-icon-settings-2';
|
|
19
|
+
|
|
20
|
+
export const HEADER_ICON_BLOCKS = {
|
|
21
|
+
SEARCH: {
|
|
22
|
+
id: 'header-icon-search',
|
|
23
|
+
type: 'icon-button',
|
|
24
|
+
label: 'Search Icon'
|
|
25
|
+
},
|
|
26
|
+
PROFILE: {
|
|
27
|
+
id: 'header-icon-profile',
|
|
28
|
+
type: 'icon-button',
|
|
29
|
+
label: 'Profile Icon'
|
|
30
|
+
},
|
|
31
|
+
CART: { id: 'header-icon-cart', type: 'icon-button', label: 'Cart Icon' }
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
const BLOCK_META = [
|
|
35
|
+
HEADER_ICON_BLOCKS.SEARCH,
|
|
36
|
+
HEADER_ICON_BLOCKS.PROFILE,
|
|
37
|
+
HEADER_ICON_BLOCKS.CART
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
interface ThemeBlock {
|
|
41
|
+
id: string;
|
|
42
|
+
type: string;
|
|
43
|
+
label: string;
|
|
44
|
+
styles?: Record<string, unknown>;
|
|
45
|
+
properties?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ThemeSection {
|
|
49
|
+
id: string;
|
|
50
|
+
blocks: ThemeBlock[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ThemePlaceholder {
|
|
54
|
+
slug: string;
|
|
55
|
+
sections: ThemeSection[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface HeaderIconsContextValue {
|
|
59
|
+
isDesigner: boolean;
|
|
60
|
+
selectedBlockId: string | null;
|
|
61
|
+
getBlockStyles: (blockId: string) => Record<string, unknown> | undefined;
|
|
62
|
+
getBlockProperties: (blockId: string) => Record<string, unknown> | undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const HeaderIconsContext = createContext<HeaderIconsContextValue>({
|
|
66
|
+
isDesigner: false,
|
|
67
|
+
selectedBlockId: null,
|
|
68
|
+
getBlockStyles: () => undefined,
|
|
69
|
+
getBlockProperties: () => undefined
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const HeaderIconsProvider = ({ children }: PropsWithChildren) => {
|
|
73
|
+
const designerState = useExternalDesigner({
|
|
74
|
+
placeholderId: HEADER_ICONS_PLACEHOLDER_ID
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const { layout } = useHeaderLayout();
|
|
78
|
+
const previousLayoutRef = useRef(layout);
|
|
79
|
+
|
|
80
|
+
const isDesignerRef = useRef(false);
|
|
81
|
+
const [isDesignerChecked, setIsDesignerChecked] = useState(false);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (typeof window === 'undefined') return;
|
|
85
|
+
isDesignerRef.current = window.self !== window.top;
|
|
86
|
+
setIsDesignerChecked(true);
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
const isDesigner = isDesignerRef.current;
|
|
90
|
+
|
|
91
|
+
const [themeBlocks, setThemeBlocks] = useState<Map<string, ThemeBlock>>(
|
|
92
|
+
new Map()
|
|
93
|
+
);
|
|
94
|
+
const hasReceivedThemeStyles = useRef(false);
|
|
95
|
+
const themeBlocksRef = useRef<Map<string, ThemeBlock>>(new Map());
|
|
96
|
+
const hasRegisteredNativeWidget = useRef(false);
|
|
97
|
+
|
|
98
|
+
const widgetData = useNativeWidgetData({
|
|
99
|
+
widgetSlug: HEADER_ICONS_WIDGET_SLUG,
|
|
100
|
+
sectionId: HEADER_ICONS_SECTION_ID,
|
|
101
|
+
skip: !isDesignerChecked || isDesigner,
|
|
102
|
+
blockMeta: BLOCK_META
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const getBlockWithStyles = useCallback(
|
|
106
|
+
(blockDef: typeof HEADER_ICON_BLOCKS[keyof typeof HEADER_ICON_BLOCKS]) => {
|
|
107
|
+
const existingBlock = themeBlocksRef.current.get(blockDef.id);
|
|
108
|
+
return {
|
|
109
|
+
id: blockDef.id,
|
|
110
|
+
type: blockDef.type,
|
|
111
|
+
label: blockDef.label,
|
|
112
|
+
properties: existingBlock?.properties || {},
|
|
113
|
+
styles: existingBlock?.styles || {}
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
[]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const isInIframe =
|
|
121
|
+
typeof window !== 'undefined' && window.self !== window.top;
|
|
122
|
+
if (!isInIframe || !window.parent) return;
|
|
123
|
+
|
|
124
|
+
const blocks =
|
|
125
|
+
layout === 'two-row'
|
|
126
|
+
? [
|
|
127
|
+
getBlockWithStyles(HEADER_ICON_BLOCKS.PROFILE),
|
|
128
|
+
getBlockWithStyles(HEADER_ICON_BLOCKS.CART)
|
|
129
|
+
]
|
|
130
|
+
: [
|
|
131
|
+
getBlockWithStyles(HEADER_ICON_BLOCKS.SEARCH),
|
|
132
|
+
getBlockWithStyles(HEADER_ICON_BLOCKS.PROFILE),
|
|
133
|
+
getBlockWithStyles(HEADER_ICON_BLOCKS.CART)
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
const nativeWidgetConfig = {
|
|
137
|
+
placeholderId: HEADER_ICONS_PLACEHOLDER_ID,
|
|
138
|
+
section: {
|
|
139
|
+
id: HEADER_ICONS_SECTION_ID,
|
|
140
|
+
type: 'native',
|
|
141
|
+
label: 'Header Icons',
|
|
142
|
+
blocks
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (
|
|
147
|
+
hasRegisteredNativeWidget.current &&
|
|
148
|
+
previousLayoutRef.current !== layout
|
|
149
|
+
) {
|
|
150
|
+
window.parent.postMessage(
|
|
151
|
+
{
|
|
152
|
+
type: 'UPDATE_SECTION_BLOCKS',
|
|
153
|
+
data: {
|
|
154
|
+
placeholderId: HEADER_ICONS_PLACEHOLDER_ID,
|
|
155
|
+
sectionId: HEADER_ICONS_SECTION_ID,
|
|
156
|
+
blocks
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
'*'
|
|
160
|
+
);
|
|
161
|
+
previousLayoutRef.current = layout;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
window.parent.postMessage(
|
|
166
|
+
{
|
|
167
|
+
type: 'REGISTER_NATIVE_WIDGETS',
|
|
168
|
+
data: { widgets: [nativeWidgetConfig] }
|
|
169
|
+
},
|
|
170
|
+
'*'
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
hasRegisteredNativeWidget.current = true;
|
|
174
|
+
previousLayoutRef.current = layout;
|
|
175
|
+
}, [layout, getBlockWithStyles]);
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!isDesignerChecked || isDesigner || widgetData.isLoading) return;
|
|
179
|
+
if (hasReceivedThemeStyles.current) return;
|
|
180
|
+
|
|
181
|
+
const blockMap = new Map<string, ThemeBlock>();
|
|
182
|
+
widgetData.blocks.forEach((block) => {
|
|
183
|
+
blockMap.set(block.id, {
|
|
184
|
+
id: block.id,
|
|
185
|
+
type: block.type || 'icon-button',
|
|
186
|
+
label: block.label || block.id,
|
|
187
|
+
styles: block.styles,
|
|
188
|
+
properties: block.properties
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (blockMap.size > 0) {
|
|
193
|
+
setThemeBlocks(blockMap);
|
|
194
|
+
themeBlocksRef.current = blockMap;
|
|
195
|
+
}
|
|
196
|
+
}, [isDesignerChecked, isDesigner, widgetData.isLoading, widgetData.blocks]);
|
|
197
|
+
|
|
198
|
+
const indexBlocks = useCallback(
|
|
199
|
+
(blocks: ThemeBlock[], map: Map<string, ThemeBlock>) => {
|
|
200
|
+
blocks.forEach((block) => {
|
|
201
|
+
map.set(block.id, block);
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
[]
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
const handleMessage = (event: MessageEvent) => {
|
|
209
|
+
const { type, data } = event.data || {};
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
213
|
+
data?.theme?.placeholders
|
|
214
|
+
) {
|
|
215
|
+
const placeholder = data.theme.placeholders?.find(
|
|
216
|
+
(p: ThemePlaceholder) => p.slug === HEADER_ICONS_PLACEHOLDER_ID
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (placeholder) {
|
|
220
|
+
const blockMap = new Map<string, ThemeBlock>();
|
|
221
|
+
|
|
222
|
+
placeholder.sections.forEach((section: ThemeSection) => {
|
|
223
|
+
indexBlocks(section.blocks, blockMap);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (blockMap.size > 0) {
|
|
227
|
+
hasReceivedThemeStyles.current = true;
|
|
228
|
+
setThemeBlocks(blockMap);
|
|
229
|
+
themeBlocksRef.current = blockMap;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
window.addEventListener('message', handleMessage);
|
|
236
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
237
|
+
}, [indexBlocks]);
|
|
238
|
+
|
|
239
|
+
const getBlockStyles = useCallback(
|
|
240
|
+
(blockId: string) => themeBlocks.get(blockId)?.styles,
|
|
241
|
+
[themeBlocks]
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const getBlockProperties = useCallback(
|
|
245
|
+
(blockId: string) => themeBlocks.get(blockId)?.properties,
|
|
246
|
+
[themeBlocks]
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<HeaderIconsContext.Provider
|
|
251
|
+
value={{
|
|
252
|
+
...designerState,
|
|
253
|
+
getBlockStyles,
|
|
254
|
+
getBlockProperties
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
{children}
|
|
258
|
+
</HeaderIconsContext.Provider>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const useHeaderIconsDesigner = () => useContext(HeaderIconsContext);
|