@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 Language Section Registrar
|
|
5
|
+
*
|
|
6
|
+
* This component registers the "Language Select" section for the header placeholder.
|
|
7
|
+
* The language 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_LANGUAGE_PLACEHOLDER_ID = 'header';
|
|
21
|
+
export const HEADER_LANGUAGE_SECTION_ID = 'header-language';
|
|
22
|
+
export const HEADER_LANGUAGE_WIDGET_SLUG = 'header-language-settings-2';
|
|
23
|
+
|
|
24
|
+
// Global flag to track if registration has been done
|
|
25
|
+
declare global {
|
|
26
|
+
interface Window {
|
|
27
|
+
__headerLanguageRegistered?: 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 HeaderLanguageProperties {
|
|
52
|
+
showIcon?: boolean;
|
|
53
|
+
visible?: boolean;
|
|
54
|
+
labelFormat?: 'full' | 'short' | 'code';
|
|
55
|
+
/** Custom SVG icon content */
|
|
56
|
+
icon?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface HeaderLanguageContextValue {
|
|
60
|
+
isDesigner: boolean;
|
|
61
|
+
isLanguageSectionSelected: boolean;
|
|
62
|
+
isSectionVisible: boolean;
|
|
63
|
+
properties: HeaderLanguageProperties;
|
|
64
|
+
sectionStyles: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const HeaderLanguageContext = createContext<HeaderLanguageContextValue>({
|
|
68
|
+
isDesigner: false,
|
|
69
|
+
isLanguageSectionSelected: 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 useHeaderLanguage = () => useContext(HeaderLanguageContext);
|
|
76
|
+
|
|
77
|
+
interface HeaderLanguageRegistrarProps extends PropsWithChildren {
|
|
78
|
+
/**
|
|
79
|
+
* Initial settings from server-side parsing (to avoid flash)
|
|
80
|
+
*/
|
|
81
|
+
initialSettings?: {
|
|
82
|
+
sectionStyles?: Record<string, unknown>;
|
|
83
|
+
properties?: HeaderLanguageProperties;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* HeaderLanguageRegistrar
|
|
89
|
+
*
|
|
90
|
+
* Registers the Language 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 HeaderLanguageRegistrar({
|
|
94
|
+
children,
|
|
95
|
+
initialSettings = {}
|
|
96
|
+
}: HeaderLanguageRegistrarProps) {
|
|
97
|
+
const isDesignerRef = useRef(false);
|
|
98
|
+
const [sectionProperties, setSectionProperties] =
|
|
99
|
+
useState<HeaderLanguageProperties>(
|
|
100
|
+
initialSettings.properties || { showIcon: true }
|
|
101
|
+
);
|
|
102
|
+
const [sectionStyles, setSectionStyles] = useState<Record<string, unknown>>(
|
|
103
|
+
initialSettings.sectionStyles || {}
|
|
104
|
+
);
|
|
105
|
+
const [isLanguageSelected, setIsLanguageSelected] = 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.__headerLanguageRegistered &&
|
|
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_LANGUAGE_PLACEHOLDER_ID,
|
|
147
|
+
autoAdd: false,
|
|
148
|
+
section: {
|
|
149
|
+
id: HEADER_LANGUAGE_SECTION_ID,
|
|
150
|
+
type: 'native',
|
|
151
|
+
label: 'Language 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.__headerLanguageRegistered = true;
|
|
168
|
+
hasRegisteredNativeWidget.current = true;
|
|
169
|
+
}, [sectionProperties, sectionStyles]);
|
|
170
|
+
|
|
171
|
+
// Apply highlight style to language container when section is selected
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (typeof window === 'undefined') return;
|
|
174
|
+
|
|
175
|
+
const languageContainer = document.querySelector(
|
|
176
|
+
'[data-section-id="header-language"]'
|
|
177
|
+
);
|
|
178
|
+
if (!languageContainer) return;
|
|
179
|
+
|
|
180
|
+
if (isLanguageSelected) {
|
|
181
|
+
(languageContainer as HTMLElement).style.outline = '2px solid #3b82f6';
|
|
182
|
+
(languageContainer as HTMLElement).style.outlineOffset = '-2px';
|
|
183
|
+
} else {
|
|
184
|
+
(languageContainer as HTMLElement).style.outline = '';
|
|
185
|
+
(languageContainer as HTMLElement).style.outlineOffset = '';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return () => {
|
|
189
|
+
(languageContainer as HTMLElement).style.outline = '';
|
|
190
|
+
(languageContainer as HTMLElement).style.outlineOffset = '';
|
|
191
|
+
};
|
|
192
|
+
}, [isLanguageSelected]);
|
|
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_LANGUAGE_PLACEHOLDER_ID
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const languageSection = placeholder?.sections?.find(
|
|
211
|
+
(s: ThemeSection) => s.id === HEADER_LANGUAGE_SECTION_ID
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (languageSection) {
|
|
215
|
+
// Check section visibility from properties first
|
|
216
|
+
// Section properties.visible determines if section should be shown
|
|
217
|
+
const sectionVisible = languageSection.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 languageSection.visible === 'boolean') {
|
|
227
|
+
// Fallback to section.visible flag
|
|
228
|
+
setIsSectionVisible(languageSection.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 (languageSection.properties) {
|
|
241
|
+
setSectionProperties(
|
|
242
|
+
languageSection.properties as HeaderLanguageProperties
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Update section styles
|
|
247
|
+
if (languageSection.styles) {
|
|
248
|
+
setSectionStyles(languageSection.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_LANGUAGE_SECTION_ID) {
|
|
262
|
+
if (key && value !== undefined) {
|
|
263
|
+
setSectionProperties((prev) => ({
|
|
264
|
+
...prev,
|
|
265
|
+
[key]: value
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
if (properties) {
|
|
269
|
+
setSectionProperties(properties as HeaderLanguageProperties);
|
|
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_LANGUAGE_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_LANGUAGE_PLACEHOLDER_ID &&
|
|
297
|
+
sectionId === HEADER_LANGUAGE_SECTION_ID;
|
|
298
|
+
|
|
299
|
+
setIsLanguageSelected(isSelected);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Handle block selection
|
|
303
|
+
if (type === 'SELECT_BLOCK') {
|
|
304
|
+
const { sectionId } = data || {};
|
|
305
|
+
if (sectionId === HEADER_LANGUAGE_SECTION_ID) {
|
|
306
|
+
setIsLanguageSelected(true);
|
|
307
|
+
} else {
|
|
308
|
+
setIsLanguageSelected(false);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Handle deselection
|
|
313
|
+
if (type === 'DESELECT' || type === 'CLEAR_SELECTION') {
|
|
314
|
+
setIsLanguageSelected(false);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Handle visibility toggle
|
|
318
|
+
if (type === 'TOGGLE_SECTION_VISIBILITY') {
|
|
319
|
+
const { sectionId } = data || {};
|
|
320
|
+
if (sectionId === HEADER_LANGUAGE_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
|
+
<HeaderLanguageContext.Provider
|
|
337
|
+
value={{
|
|
338
|
+
isDesigner,
|
|
339
|
+
isLanguageSectionSelected: isLanguageSelected,
|
|
340
|
+
isSectionVisible,
|
|
341
|
+
properties: sectionProperties,
|
|
342
|
+
sectionStyles
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
{children}
|
|
346
|
+
</HeaderLanguageContext.Provider>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Header Layout Designer Context
|
|
5
|
+
*
|
|
6
|
+
* Enables the entire header to be selected via Theme Editor
|
|
7
|
+
* for layout configuration (e.g., layout type dropdown).
|
|
8
|
+
* Uses the native widget pattern - NO blocks, section-level properties only.
|
|
9
|
+
*
|
|
10
|
+
* When this section is selected, the entire header should appear selected.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
createContext,
|
|
15
|
+
PropsWithChildren,
|
|
16
|
+
useContext,
|
|
17
|
+
useEffect,
|
|
18
|
+
useRef,
|
|
19
|
+
useState
|
|
20
|
+
} from 'react';
|
|
21
|
+
import { useExternalDesigner } from '@akinon/next/components/theme-editor/hooks/use-external-designer';
|
|
22
|
+
|
|
23
|
+
// Constants - Header layout is a section within the header placeholder
|
|
24
|
+
export const HEADER_LAYOUT_PLACEHOLDER_ID = 'header';
|
|
25
|
+
export const HEADER_LAYOUT_SECTION_ID = 'header-layout';
|
|
26
|
+
export const HEADER_LAYOUT_WIDGET_SLUG = 'header-layout-settings-2';
|
|
27
|
+
|
|
28
|
+
// Section properties definition - Layout dropdown options will be added later
|
|
29
|
+
export interface HeaderLayoutProperties {
|
|
30
|
+
layout?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface HeaderLayoutContextValue {
|
|
34
|
+
isDesigner: boolean;
|
|
35
|
+
selectedSectionId: string | null;
|
|
36
|
+
isLayoutSectionSelected: boolean;
|
|
37
|
+
properties: HeaderLayoutProperties;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const HeaderLayoutContext = createContext<HeaderLayoutContextValue>({
|
|
41
|
+
isDesigner: false,
|
|
42
|
+
selectedSectionId: null,
|
|
43
|
+
isLayoutSectionSelected: false,
|
|
44
|
+
properties: {}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const HeaderLayoutProvider = ({ children }: PropsWithChildren) => {
|
|
48
|
+
const designerState = useExternalDesigner({
|
|
49
|
+
placeholderId: HEADER_LAYOUT_PLACEHOLDER_ID
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const [sectionProperties, setSectionProperties] =
|
|
53
|
+
useState<HeaderLayoutProperties>({});
|
|
54
|
+
const hasReceivedThemeProps = useRef(false);
|
|
55
|
+
|
|
56
|
+
// Register native widget with Theme Editor - only once
|
|
57
|
+
// This section has NO blocks - only section-level properties
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const isInIframe =
|
|
60
|
+
typeof window !== 'undefined' && window.self !== window.top;
|
|
61
|
+
if (!isInIframe || !window.parent) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Send native widget registration to Theme Editor
|
|
66
|
+
// Note: blocks array is empty - this is a section-only widget
|
|
67
|
+
const nativeWidgetConfig = {
|
|
68
|
+
placeholderId: HEADER_LAYOUT_PLACEHOLDER_ID,
|
|
69
|
+
section: {
|
|
70
|
+
id: HEADER_LAYOUT_SECTION_ID,
|
|
71
|
+
type: 'native',
|
|
72
|
+
label: 'Layout',
|
|
73
|
+
blocks: [] // No blocks - entire header selected when this section is selected
|
|
74
|
+
// Don't send properties - Theme Editor will load them from widget API
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
window.parent.postMessage(
|
|
79
|
+
{
|
|
80
|
+
type: 'REGISTER_NATIVE_WIDGETS',
|
|
81
|
+
data: { widgets: [nativeWidgetConfig] }
|
|
82
|
+
},
|
|
83
|
+
'*'
|
|
84
|
+
);
|
|
85
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
+
}, []); // Only register once
|
|
87
|
+
|
|
88
|
+
// Widget data is now fetched server-side and passed via HeaderLayoutRegistrar
|
|
89
|
+
// This context only handles designer mode selection state
|
|
90
|
+
|
|
91
|
+
// Listen for theme updates from Theme Editor
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (typeof window === 'undefined') return;
|
|
94
|
+
|
|
95
|
+
const handleMessage = (event: MessageEvent) => {
|
|
96
|
+
const { type, data } = event.data || {};
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
100
|
+
data?.theme?.placeholders
|
|
101
|
+
) {
|
|
102
|
+
const placeholder = data.theme.placeholders?.find(
|
|
103
|
+
(p: { slug: string }) => p.slug === HEADER_LAYOUT_PLACEHOLDER_ID
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const layoutSection = placeholder?.sections?.find(
|
|
107
|
+
(s: { id: string }) => s.id === HEADER_LAYOUT_SECTION_ID
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (layoutSection) {
|
|
111
|
+
hasReceivedThemeProps.current = true;
|
|
112
|
+
|
|
113
|
+
// Update section properties
|
|
114
|
+
if (layoutSection.properties) {
|
|
115
|
+
setSectionProperties(layoutSection.properties);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
window.addEventListener('message', handleMessage);
|
|
122
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
// Check if the layout section is currently selected
|
|
126
|
+
const isLayoutSectionSelected =
|
|
127
|
+
designerState.selectedSectionId === HEADER_LAYOUT_SECTION_ID;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<HeaderLayoutContext.Provider
|
|
131
|
+
value={{
|
|
132
|
+
isDesigner: designerState.isDesigner,
|
|
133
|
+
selectedSectionId: designerState.selectedSectionId,
|
|
134
|
+
isLayoutSectionSelected,
|
|
135
|
+
properties: sectionProperties
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
{children}
|
|
139
|
+
</HeaderLayoutContext.Provider>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const useHeaderLayoutDesigner = () => useContext(HeaderLayoutContext);
|