@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,496 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Header Client Wrapper
|
|
5
|
+
*
|
|
6
|
+
* This component wraps header elements with Theme Editor styling capabilities.
|
|
7
|
+
* It provides the HeaderDesignerProvider context and WithDesignerFeatures wrappers.
|
|
8
|
+
*
|
|
9
|
+
* @see .github/instructions/native-widget.instructions.md for implementation guide
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { PropsWithChildren, ReactNode, useEffect } from 'react';
|
|
13
|
+
import { WithDesignerFeatures } from '@akinon/next/components/theme-editor/components/with-designer-features';
|
|
14
|
+
import {
|
|
15
|
+
HeaderDesignerProvider,
|
|
16
|
+
useHeaderDesigner,
|
|
17
|
+
HEADER_PLACEHOLDER_ID,
|
|
18
|
+
HEADER_SECTION_ID,
|
|
19
|
+
HEADER_BLOCKS
|
|
20
|
+
} from './designer-context';
|
|
21
|
+
import {
|
|
22
|
+
HeaderLayoutProvider,
|
|
23
|
+
useHeaderLayoutDesigner,
|
|
24
|
+
HEADER_LAYOUT_SECTION_ID
|
|
25
|
+
} from './header-layout-context';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Helper to extract value from responsive object or direct value
|
|
29
|
+
* Theme Editor sends values in format { desktop: "value", mobile: "value" }
|
|
30
|
+
*/
|
|
31
|
+
function getResponsiveValue<T>(value: unknown, fallback: T): T {
|
|
32
|
+
if (value === null || value === undefined) return fallback;
|
|
33
|
+
|
|
34
|
+
// If it's a responsive object, get desktop value first, then any available value
|
|
35
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
36
|
+
const obj = value as Record<string, unknown>;
|
|
37
|
+
if ('desktop' in obj) return (obj.desktop as T) ?? fallback;
|
|
38
|
+
if ('mobile' in obj) return (obj.mobile as T) ?? fallback;
|
|
39
|
+
// Return first available value
|
|
40
|
+
const firstValue = Object.values(obj)[0];
|
|
41
|
+
if (firstValue !== undefined) return firstValue as T;
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return value as T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse icon size from properties - handles both string and responsive object formats
|
|
50
|
+
*/
|
|
51
|
+
function parseIconSize(value: unknown, fallback: number): number {
|
|
52
|
+
const strValue = getResponsiveValue<string>(value, String(fallback));
|
|
53
|
+
const parsed = parseInt(strValue, 10);
|
|
54
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface HeaderWrapperProps {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Main Header Wrapper with Provider
|
|
63
|
+
*/
|
|
64
|
+
export function HeaderWrapper({ children }: HeaderWrapperProps) {
|
|
65
|
+
return (
|
|
66
|
+
<HeaderDesignerProvider>
|
|
67
|
+
<HeaderLayoutProvider>
|
|
68
|
+
<HeaderContent>{children}</HeaderContent>
|
|
69
|
+
</HeaderLayoutProvider>
|
|
70
|
+
</HeaderDesignerProvider>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Header Content with Designer Features
|
|
76
|
+
*
|
|
77
|
+
* Uses CSS to disable pointer events on interactive elements (links, buttons)
|
|
78
|
+
* while keeping the WithDesignerFeatures wrapper clickable for block selection.
|
|
79
|
+
*
|
|
80
|
+
* When the Layout section is selected, the entire header gets a selection highlight.
|
|
81
|
+
*/
|
|
82
|
+
function HeaderContent({ children }: PropsWithChildren) {
|
|
83
|
+
const { isDesigner, selectedBlockId, getBlockStyles } = useHeaderDesigner();
|
|
84
|
+
const { isLayoutSectionSelected } = useHeaderLayoutDesigner();
|
|
85
|
+
|
|
86
|
+
// Inject styles to disable pointer events on interactive elements in designer mode
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!isDesigner) return;
|
|
89
|
+
|
|
90
|
+
const styleId = 'header-designer-styles';
|
|
91
|
+
let styleEl = document.getElementById(styleId) as HTMLStyleElement | null;
|
|
92
|
+
|
|
93
|
+
if (!styleEl) {
|
|
94
|
+
styleEl = document.createElement('style');
|
|
95
|
+
styleEl.id = styleId;
|
|
96
|
+
document.head.appendChild(styleEl);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
styleEl.textContent = `
|
|
100
|
+
[data-block-id] a,
|
|
101
|
+
[data-block-id] button:not([data-designer-action]),
|
|
102
|
+
[data-block-id] input,
|
|
103
|
+
[data-block-id] [role="button"] {
|
|
104
|
+
pointer-events: none !important;
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
styleEl?.remove();
|
|
110
|
+
};
|
|
111
|
+
}, [isDesigner]);
|
|
112
|
+
|
|
113
|
+
// If not in designer mode, just render children without wrapper
|
|
114
|
+
if (!isDesigner) {
|
|
115
|
+
return <>{children}</>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const containerBlock = {
|
|
119
|
+
...HEADER_BLOCKS.CONTAINER,
|
|
120
|
+
styles: getBlockStyles(HEADER_BLOCKS.CONTAINER.id)
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Check if header should be highlighted (either container block or layout section selected)
|
|
124
|
+
const isHeaderSelected =
|
|
125
|
+
selectedBlockId === HEADER_BLOCKS.CONTAINER.id || isLayoutSectionSelected;
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<WithDesignerFeatures
|
|
129
|
+
block={containerBlock}
|
|
130
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
131
|
+
sectionId={
|
|
132
|
+
isLayoutSectionSelected ? HEADER_LAYOUT_SECTION_ID : HEADER_SECTION_ID
|
|
133
|
+
}
|
|
134
|
+
isDesigner={isDesigner}
|
|
135
|
+
isSelected={isHeaderSelected}
|
|
136
|
+
className="relative"
|
|
137
|
+
>
|
|
138
|
+
{children}
|
|
139
|
+
</WithDesignerFeatures>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface HeaderLogoWrapperProps {
|
|
144
|
+
children: ReactNode;
|
|
145
|
+
className?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Logo Wrapper with Designer Features
|
|
150
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
151
|
+
*/
|
|
152
|
+
export function HeaderLogoWrapper({
|
|
153
|
+
children,
|
|
154
|
+
className
|
|
155
|
+
}: HeaderLogoWrapperProps) {
|
|
156
|
+
const { isDesigner, selectedBlockId, getBlockStyles } = useHeaderDesigner();
|
|
157
|
+
|
|
158
|
+
// If not in designer mode, just render children without wrapper
|
|
159
|
+
if (!isDesigner) {
|
|
160
|
+
return <>{children}</>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const logoBlock = {
|
|
164
|
+
...HEADER_BLOCKS.LOGO,
|
|
165
|
+
styles: getBlockStyles(HEADER_BLOCKS.LOGO.id)
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<WithDesignerFeatures
|
|
170
|
+
block={logoBlock}
|
|
171
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
172
|
+
sectionId={HEADER_SECTION_ID}
|
|
173
|
+
isDesigner={isDesigner}
|
|
174
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.LOGO.id}
|
|
175
|
+
className={className}
|
|
176
|
+
>
|
|
177
|
+
{children}
|
|
178
|
+
</WithDesignerFeatures>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface HeaderNavbarWrapperProps {
|
|
183
|
+
children: ReactNode;
|
|
184
|
+
className?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Navbar Wrapper with Designer Features
|
|
189
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
190
|
+
*/
|
|
191
|
+
export function HeaderNavbarWrapper({
|
|
192
|
+
children,
|
|
193
|
+
className
|
|
194
|
+
}: HeaderNavbarWrapperProps) {
|
|
195
|
+
const { isDesigner, selectedBlockId, getBlockStyles } = useHeaderDesigner();
|
|
196
|
+
|
|
197
|
+
// If not in designer mode, just render children without wrapper
|
|
198
|
+
if (!isDesigner) {
|
|
199
|
+
return <>{children}</>;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const navbarBlock = {
|
|
203
|
+
...HEADER_BLOCKS.NAVBAR,
|
|
204
|
+
styles: getBlockStyles(HEADER_BLOCKS.NAVBAR.id)
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<WithDesignerFeatures
|
|
209
|
+
block={navbarBlock}
|
|
210
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
211
|
+
sectionId={HEADER_SECTION_ID}
|
|
212
|
+
isDesigner={isDesigner}
|
|
213
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.NAVBAR.id}
|
|
214
|
+
className={className}
|
|
215
|
+
>
|
|
216
|
+
{children}
|
|
217
|
+
</WithDesignerFeatures>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
interface HeaderNavItemWrapperProps {
|
|
222
|
+
children: ReactNode;
|
|
223
|
+
className?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Navigation Item Wrapper with Designer Features
|
|
228
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
229
|
+
*/
|
|
230
|
+
export function HeaderNavItemWrapper({
|
|
231
|
+
children,
|
|
232
|
+
className
|
|
233
|
+
}: HeaderNavItemWrapperProps) {
|
|
234
|
+
const { isDesigner, selectedBlockId, getBlockStyles } = useHeaderDesigner();
|
|
235
|
+
|
|
236
|
+
// If not in designer mode, just render children without wrapper
|
|
237
|
+
if (!isDesigner) {
|
|
238
|
+
return <>{children}</>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Use base ID for shared styles across all nav items
|
|
242
|
+
const navItemBlock = {
|
|
243
|
+
...HEADER_BLOCKS.NAV_ITEM,
|
|
244
|
+
styles: getBlockStyles(HEADER_BLOCKS.NAV_ITEM.id)
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const isSelected =
|
|
248
|
+
selectedBlockId === HEADER_BLOCKS.NAV_ITEM.id ||
|
|
249
|
+
selectedBlockId?.startsWith(HEADER_BLOCKS.NAV_ITEM.id + '-');
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<WithDesignerFeatures
|
|
253
|
+
block={navItemBlock}
|
|
254
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
255
|
+
sectionId={HEADER_SECTION_ID}
|
|
256
|
+
isDesigner={isDesigner}
|
|
257
|
+
isSelected={isSelected}
|
|
258
|
+
className={className}
|
|
259
|
+
>
|
|
260
|
+
{children}
|
|
261
|
+
</WithDesignerFeatures>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
interface HeaderSearchWrapperProps {
|
|
266
|
+
children: ReactNode;
|
|
267
|
+
className?: string;
|
|
268
|
+
renderIcon?: (icon: string, size: number) => ReactNode;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Search Wrapper with Designer Features
|
|
273
|
+
* Supports customizable icon via Theme Editor
|
|
274
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
275
|
+
*/
|
|
276
|
+
export function HeaderSearchWrapper({
|
|
277
|
+
children,
|
|
278
|
+
className,
|
|
279
|
+
renderIcon
|
|
280
|
+
}: HeaderSearchWrapperProps) {
|
|
281
|
+
const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
|
|
282
|
+
useHeaderDesigner();
|
|
283
|
+
|
|
284
|
+
// If not in designer mode, render children (ignore renderIcon, use original)
|
|
285
|
+
if (!isDesigner) {
|
|
286
|
+
return <>{children}</>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const properties = getBlockProperties(HEADER_BLOCKS.SEARCH.id);
|
|
290
|
+
const searchBlock = {
|
|
291
|
+
...HEADER_BLOCKS.SEARCH,
|
|
292
|
+
styles: getBlockStyles(HEADER_BLOCKS.SEARCH.id),
|
|
293
|
+
properties: properties || HEADER_BLOCKS.SEARCH.properties
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const icon = getResponsiveValue<string>(
|
|
297
|
+
properties?.icon,
|
|
298
|
+
(HEADER_BLOCKS.SEARCH.properties?.icon as string) || 'search'
|
|
299
|
+
);
|
|
300
|
+
const iconSize = parseIconSize(
|
|
301
|
+
properties?.iconSize,
|
|
302
|
+
parseInt((HEADER_BLOCKS.SEARCH.properties?.iconSize as string) || '20', 10)
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<WithDesignerFeatures
|
|
307
|
+
block={searchBlock}
|
|
308
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
309
|
+
sectionId={HEADER_SECTION_ID}
|
|
310
|
+
isDesigner={isDesigner}
|
|
311
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.SEARCH.id}
|
|
312
|
+
className={className}
|
|
313
|
+
>
|
|
314
|
+
{renderIcon ? renderIcon(icon, iconSize) : children}
|
|
315
|
+
</WithDesignerFeatures>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
interface HeaderUserMenuWrapperProps {
|
|
320
|
+
children: ReactNode;
|
|
321
|
+
className?: string;
|
|
322
|
+
renderIcon?: (icon: string, size: number) => ReactNode;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* User Menu Wrapper with Designer Features
|
|
327
|
+
* Supports customizable icon via Theme Editor
|
|
328
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
329
|
+
*/
|
|
330
|
+
export function HeaderUserMenuWrapper({
|
|
331
|
+
children,
|
|
332
|
+
className,
|
|
333
|
+
renderIcon
|
|
334
|
+
}: HeaderUserMenuWrapperProps) {
|
|
335
|
+
const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
|
|
336
|
+
useHeaderDesigner();
|
|
337
|
+
|
|
338
|
+
// If not in designer mode, render children (ignore renderIcon, use original)
|
|
339
|
+
if (!isDesigner) {
|
|
340
|
+
return <>{children}</>;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const properties = getBlockProperties(HEADER_BLOCKS.USER_MENU.id);
|
|
344
|
+
const userMenuBlock = {
|
|
345
|
+
...HEADER_BLOCKS.USER_MENU,
|
|
346
|
+
styles: getBlockStyles(HEADER_BLOCKS.USER_MENU.id),
|
|
347
|
+
properties: properties || HEADER_BLOCKS.USER_MENU.properties
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const icon = getResponsiveValue<string>(
|
|
351
|
+
properties?.icon,
|
|
352
|
+
(HEADER_BLOCKS.USER_MENU.properties?.icon as string) || 'person'
|
|
353
|
+
);
|
|
354
|
+
const iconSize = parseIconSize(
|
|
355
|
+
properties?.iconSize,
|
|
356
|
+
parseInt(
|
|
357
|
+
(HEADER_BLOCKS.USER_MENU.properties?.iconSize as string) || '20',
|
|
358
|
+
10
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
return (
|
|
363
|
+
<WithDesignerFeatures
|
|
364
|
+
block={userMenuBlock}
|
|
365
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
366
|
+
sectionId={HEADER_SECTION_ID}
|
|
367
|
+
isDesigner={isDesigner}
|
|
368
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.USER_MENU.id}
|
|
369
|
+
className={className}
|
|
370
|
+
>
|
|
371
|
+
{renderIcon ? renderIcon(icon, iconSize) : children}
|
|
372
|
+
</WithDesignerFeatures>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
interface HeaderMiniBasketWrapperProps {
|
|
377
|
+
children: ReactNode;
|
|
378
|
+
className?: string;
|
|
379
|
+
renderIcon?: (icon: string, size: number) => ReactNode;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Mini Basket Wrapper with Designer Features
|
|
384
|
+
* Supports customizable icon via Theme Editor
|
|
385
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
386
|
+
*/
|
|
387
|
+
export function HeaderMiniBasketWrapper({
|
|
388
|
+
children,
|
|
389
|
+
className,
|
|
390
|
+
renderIcon
|
|
391
|
+
}: HeaderMiniBasketWrapperProps) {
|
|
392
|
+
const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
|
|
393
|
+
useHeaderDesigner();
|
|
394
|
+
|
|
395
|
+
// If not in designer mode, render children (ignore renderIcon, use original)
|
|
396
|
+
if (!isDesigner) {
|
|
397
|
+
return <>{children}</>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const properties = getBlockProperties(HEADER_BLOCKS.MINI_BASKET.id);
|
|
401
|
+
const miniBasketBlock = {
|
|
402
|
+
...HEADER_BLOCKS.MINI_BASKET,
|
|
403
|
+
styles: getBlockStyles(HEADER_BLOCKS.MINI_BASKET.id),
|
|
404
|
+
properties: properties || HEADER_BLOCKS.MINI_BASKET.properties
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const icon = getResponsiveValue<string>(
|
|
408
|
+
properties?.icon,
|
|
409
|
+
(HEADER_BLOCKS.MINI_BASKET.properties?.icon as string) || 'cart'
|
|
410
|
+
);
|
|
411
|
+
const iconSize = parseIconSize(
|
|
412
|
+
properties?.iconSize,
|
|
413
|
+
parseInt(
|
|
414
|
+
(HEADER_BLOCKS.MINI_BASKET.properties?.iconSize as string) || '20',
|
|
415
|
+
10
|
|
416
|
+
)
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
<WithDesignerFeatures
|
|
421
|
+
block={miniBasketBlock}
|
|
422
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
423
|
+
sectionId={HEADER_SECTION_ID}
|
|
424
|
+
isDesigner={isDesigner}
|
|
425
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.MINI_BASKET.id}
|
|
426
|
+
className={className}
|
|
427
|
+
>
|
|
428
|
+
{renderIcon ? renderIcon(icon, iconSize) : children}
|
|
429
|
+
</WithDesignerFeatures>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
interface HeaderMobileMenuButtonWrapperProps {
|
|
434
|
+
children: ReactNode;
|
|
435
|
+
className?: string;
|
|
436
|
+
renderIcon?: (icon: string, size: number) => ReactNode;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Mobile Menu Button Wrapper with Designer Features
|
|
441
|
+
* Supports customizable icon via Theme Editor
|
|
442
|
+
* Note: In non-designer mode, className is applied to inner elements, not wrapper
|
|
443
|
+
*/
|
|
444
|
+
export function HeaderMobileMenuButtonWrapper({
|
|
445
|
+
children,
|
|
446
|
+
className,
|
|
447
|
+
renderIcon
|
|
448
|
+
}: HeaderMobileMenuButtonWrapperProps) {
|
|
449
|
+
const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
|
|
450
|
+
useHeaderDesigner();
|
|
451
|
+
|
|
452
|
+
// If not in designer mode, render children (ignore renderIcon, use original)
|
|
453
|
+
if (!isDesigner) {
|
|
454
|
+
return <>{children}</>;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const properties = getBlockProperties(HEADER_BLOCKS.MOBILE_MENU_BUTTON.id);
|
|
458
|
+
const mobileMenuButtonBlock = {
|
|
459
|
+
...HEADER_BLOCKS.MOBILE_MENU_BUTTON,
|
|
460
|
+
styles: getBlockStyles(HEADER_BLOCKS.MOBILE_MENU_BUTTON.id),
|
|
461
|
+
properties: properties || HEADER_BLOCKS.MOBILE_MENU_BUTTON.properties
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const icon = getResponsiveValue<string>(
|
|
465
|
+
properties?.icon,
|
|
466
|
+
(HEADER_BLOCKS.MOBILE_MENU_BUTTON.properties?.icon as string) || 'hamburger'
|
|
467
|
+
);
|
|
468
|
+
const iconSize = parseIconSize(
|
|
469
|
+
properties?.iconSize,
|
|
470
|
+
parseInt(
|
|
471
|
+
(HEADER_BLOCKS.MOBILE_MENU_BUTTON.properties?.iconSize as string) || '18',
|
|
472
|
+
10
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
return (
|
|
477
|
+
<WithDesignerFeatures
|
|
478
|
+
block={mobileMenuButtonBlock}
|
|
479
|
+
placeholderId={HEADER_PLACEHOLDER_ID}
|
|
480
|
+
sectionId={HEADER_SECTION_ID}
|
|
481
|
+
isDesigner={isDesigner}
|
|
482
|
+
isSelected={selectedBlockId === HEADER_BLOCKS.MOBILE_MENU_BUTTON.id}
|
|
483
|
+
className={className}
|
|
484
|
+
>
|
|
485
|
+
{renderIcon ? renderIcon(icon, iconSize) : children}
|
|
486
|
+
</WithDesignerFeatures>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Re-export for convenience
|
|
491
|
+
export {
|
|
492
|
+
useHeaderDesigner,
|
|
493
|
+
HEADER_BLOCKS,
|
|
494
|
+
HEADER_PLACEHOLDER_ID,
|
|
495
|
+
HEADER_SECTION_ID
|
|
496
|
+
};
|