@akinon/projectzero 2.0.0-beta.20 → 2.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/app-template/CHANGELOG.md +138 -0
- package/app-template/next.config.mjs +0 -1
- package/app-template/package.json +31 -30
- package/app-template/src/app/[pz]/[...prettyurl]/page.tsx +2 -2
- package/app-template/src/app/[pz]/account/layout.tsx +2 -1
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/blog/[slug]/page.tsx +4 -2
- package/app-template/src/app/[pz]/category/[pk]/page.tsx +11 -1
- package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +2 -2
- package/app-template/src/app/[pz]/layout.tsx +3 -1
- package/app-template/src/app/[pz]/list/page.tsx +11 -1
- package/app-template/src/app/[pz]/page.tsx +13 -35
- package/app-template/src/app/[pz]/pages/[slug]/page.tsx +19 -0
- package/app-template/src/app/[pz]/product/[pk]/page.tsx +2 -2
- package/app-template/src/app/api/barcode-search/route.ts +1 -1
- package/app-template/src/app/api/cache/route.ts +1 -1
- package/app-template/src/app/api/image-proxy/route.ts +1 -1
- package/app-template/src/app/api/logout/route.ts +1 -1
- package/app-template/src/app/api/product-categories/route.ts +1 -1
- package/app-template/src/app/api/similar-product-list/route.ts +1 -1
- package/app-template/src/app/api/similar-products/route.ts +1 -1
- package/app-template/src/app/api/virtual-try-on/route.ts +1 -1
- package/app-template/src/app/api/web-vitals/route.ts +1 -1
- package/app-template/src/components/quantity-selector.tsx +16 -4
- package/app-template/src/components/widget-content.tsx +3 -3
- package/app-template/src/routes/index.ts +6 -6
- package/app-template/src/utils/__tests__/theme-page-context.test.ts +145 -0
- package/app-template/src/utils/theme-page-context.ts +309 -0
- package/app-template/src/views/basket/basket-item.tsx +107 -691
- package/app-template/src/views/basket/index.ts +0 -2
- package/app-template/src/views/basket/summary.tsx +75 -496
- package/app-template/src/views/breadcrumb.tsx +38 -13
- package/app-template/src/views/category/category-header.tsx +66 -289
- package/app-template/src/views/category/category-info.tsx +24 -173
- package/app-template/src/views/category/filters/index.tsx +48 -208
- package/app-template/src/views/category/layout.tsx +5 -7
- package/app-template/src/views/checkout/index.tsx +0 -5
- package/app-template/src/views/checkout/steps/payment/index.tsx +2 -5
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -72
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +40 -171
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +12 -74
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +45 -128
- package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +27 -232
- package/app-template/src/views/checkout/summary.tsx +29 -303
- package/app-template/src/views/footer.tsx +13 -415
- package/app-template/src/views/guest-login/index.tsx +1 -1
- package/app-template/src/views/header/action-menu.tsx +45 -277
- package/app-template/src/views/header/band.tsx +21 -6
- package/app-template/src/views/header/index.tsx +47 -109
- package/app-template/src/views/header/mini-basket.tsx +45 -820
- package/app-template/src/views/header/navbar.tsx +111 -178
- package/app-template/src/views/header/search/index.tsx +32 -71
- package/app-template/src/views/header/search/results.tsx +65 -127
- package/app-template/src/views/product/accordion-wrapper.tsx +43 -135
- package/app-template/src/views/product/index.ts +1 -1
- package/app-template/src/views/product/layout.tsx +7 -2
- package/app-template/src/views/product/misc-buttons.tsx +25 -339
- package/app-template/src/views/product/product-actions.tsx +8 -137
- package/app-template/src/views/product/product-info.tsx +31 -69
- package/app-template/src/views/product/product-share.tsx +8 -11
- package/app-template/src/views/product/slider.tsx +79 -117
- package/app-template/src/views/product-item/index.tsx +46 -119
- package/app-template/src/widgets/footer-social.tsx +16 -47
- package/app-template/src/widgets/footer-subscription/index.tsx +17 -183
- package/dist/commands/plugins.js +23 -2
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +0 -15
- package/app-template/src/views/basket/basket-summary-context.tsx +0 -560
- package/app-template/src/views/basket/designer-context.tsx +0 -617
- package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +0 -190
- package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +0 -286
- package/app-template/src/views/breadcrumb/constants.ts +0 -15
- package/app-template/src/views/breadcrumb/index.tsx +0 -127
- package/app-template/src/views/category/native-widget-context.tsx +0 -257
- package/app-template/src/views/category/product-list-registrar.tsx +0 -665
- package/app-template/src/views/checkout/checkout-address-registrar.tsx +0 -254
- package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +0 -183
- package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +0 -259
- package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +0 -253
- package/app-template/src/views/checkout/checkout-summary-registrar.tsx +0 -183
- package/app-template/src/views/checkout/constants.ts +0 -5
- package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +0 -15
- package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +0 -18
- package/app-template/src/views/footer/footer-app-banner-context.tsx +0 -326
- package/app-template/src/views/footer/footer-bottom-context.tsx +0 -215
- package/app-template/src/views/footer/footer-bottom-wrapper.tsx +0 -74
- package/app-template/src/views/footer/footer-layout-constants.ts +0 -35
- package/app-template/src/views/footer/footer-layout-registrar.tsx +0 -342
- package/app-template/src/views/footer/footer-layout-switcher.tsx +0 -110
- package/app-template/src/views/footer/footer-menu-context.tsx +0 -211
- package/app-template/src/views/footer/footer-native-widgets.tsx +0 -60
- package/app-template/src/views/footer/footer-social-context.tsx +0 -254
- package/app-template/src/views/footer/footer-subscription-context.tsx +0 -210
- package/app-template/src/views/footer/footer-utils.ts +0 -43
- package/app-template/src/views/footer/footer-value-props-context.tsx +0 -326
- package/app-template/src/views/footer/logo-settings.ts +0 -183
- package/app-template/src/views/footer/native-widget-config.ts +0 -262
- package/app-template/src/views/footer/subscription-settings.ts +0 -122
- package/app-template/src/views/footer/use-footer-logo.ts +0 -162
- package/app-template/src/views/header/designer-context.tsx +0 -261
- package/app-template/src/views/header/header-announcement-registrar.tsx +0 -267
- package/app-template/src/views/header/header-client-wrapper.tsx +0 -496
- package/app-template/src/views/header/header-content.tsx +0 -1026
- package/app-template/src/views/header/header-currency-registrar.tsx +0 -348
- package/app-template/src/views/header/header-icons-context.tsx +0 -262
- package/app-template/src/views/header/header-language-registrar.tsx +0 -348
- package/app-template/src/views/header/header-layout-context.tsx +0 -143
- package/app-template/src/views/header/header-layout-registrar.tsx +0 -658
- package/app-template/src/views/header/header-logo-context.tsx +0 -228
- package/app-template/src/views/header/header-logo.tsx +0 -118
- package/app-template/src/views/header/header-mini-basket-context.tsx +0 -524
- package/app-template/src/views/header/header-search-registrar.tsx +0 -511
- package/app-template/src/views/header/header-text-slider-registrar.tsx +0 -382
- package/app-template/src/views/header/inline-search.tsx +0 -262
- package/app-template/src/views/header/navbar-menu-context.tsx +0 -219
- package/app-template/src/views/header/search/search-input.tsx +0 -61
- package/app-template/src/views/header/server-settings-parser.ts +0 -1105
- package/app-template/src/views/header/use-header-icons.ts +0 -241
- package/app-template/src/views/header/use-header-logo.ts +0 -213
- package/app-template/src/views/header/use-navbar-menu.ts +0 -179
- package/app-template/src/views/product/accordion-section.tsx +0 -61
- package/app-template/src/views/product/custom-button-group.tsx +0 -69
- package/app-template/src/views/product/favorites-button-section.tsx +0 -69
- package/app-template/src/views/product/find-in-store-section.tsx +0 -60
- package/app-template/src/views/product/product-info-section.tsx +0 -140
- package/app-template/src/views/product/quantity-section.tsx +0 -73
- package/app-template/src/views/product/sale-tag.tsx +0 -10
- package/app-template/src/views/product/share-section.tsx +0 -357
- package/app-template/src/views/product/variants-section.tsx +0 -126
- package/app-template/src/views/product-detail/constants.ts +0 -272
- package/app-template/src/views/product-detail/index.ts +0 -10
- package/app-template/src/views/product-detail/product-detail-registrar.tsx +0 -616
- package/app-template/src/widgets/footer-app-banner.tsx +0 -444
- package/app-template/src/widgets/footer-bottom.tsx +0 -127
- package/app-template/src/widgets/footer-menu-compact.tsx +0 -238
- package/app-template/src/widgets/footer-menu-two.tsx +0 -298
- package/app-template/src/widgets/footer-social-client.tsx +0 -251
- package/app-template/src/widgets/footer-value-props.tsx +0 -201
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ReactNode, useMemo, useRef, useCallback
|
|
4
|
-
import { useRouter } from '@akinon/next/hooks';
|
|
3
|
+
import { ReactNode, useMemo, useRef, useCallback } from 'react';
|
|
5
4
|
import { useGetMiniBasketQuery } from '@akinon/next/data/client/basket';
|
|
6
5
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
7
6
|
import {
|
|
@@ -15,104 +14,8 @@ import { ROUTES } from '@theme/routes';
|
|
|
15
14
|
import MiniBasket from './mini-basket';
|
|
16
15
|
import { Badge, Icon, Link } from '@theme/components';
|
|
17
16
|
import { useOnClickOutside } from '@akinon/next/hooks';
|
|
18
|
-
import { useSession } from 'next-auth/react';
|
|
19
|
-
import { useHeaderIconSettings } from './use-header-icons';
|
|
20
|
-
import { useDesignerFeatures } from '@akinon/next/components/theme-editor/hooks/use-designer-features';
|
|
21
|
-
import {
|
|
22
|
-
HeaderMiniBasketProvider,
|
|
23
|
-
useHeaderMiniBasket
|
|
24
|
-
} from './header-mini-basket-context';
|
|
25
|
-
import {
|
|
26
|
-
HeaderIconsProvider,
|
|
27
|
-
useHeaderIconsDesigner,
|
|
28
|
-
HEADER_ICONS_PLACEHOLDER_ID,
|
|
29
|
-
HEADER_ICONS_SECTION_ID,
|
|
30
|
-
HEADER_ICON_BLOCKS
|
|
31
|
-
} from './header-icons-context';
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Convert block styles to CSS properties
|
|
35
|
-
*/
|
|
36
|
-
function convertBlockStylesToCSS(
|
|
37
|
-
styles: Record<string, unknown> | undefined
|
|
38
|
-
): CSSProperties {
|
|
39
|
-
if (!styles) return {};
|
|
40
|
-
|
|
41
|
-
const result: Record<string, string | number> = {};
|
|
42
|
-
|
|
43
|
-
const styleMap: Record<string, string> = {
|
|
44
|
-
'margin-top': 'marginTop',
|
|
45
|
-
'margin-right': 'marginRight',
|
|
46
|
-
'margin-bottom': 'marginBottom',
|
|
47
|
-
'margin-left': 'marginLeft',
|
|
48
|
-
'background-color': 'backgroundColor'
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
Object.entries(styles).forEach(([key, value]) => {
|
|
52
|
-
const cssKey = styleMap[key] || key;
|
|
53
|
-
// Extract desktop value if responsive
|
|
54
|
-
if (value && typeof value === 'object' && 'desktop' in value) {
|
|
55
|
-
result[cssKey] = (value as Record<string, string>).desktop;
|
|
56
|
-
} else if (typeof value === 'string' || typeof value === 'number') {
|
|
57
|
-
result[cssKey] = value;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return result as CSSProperties;
|
|
62
|
-
}
|
|
63
|
-
import { HeaderIconSettings } from './server-settings-parser';
|
|
64
|
-
import { useHeaderLayout } from './header-layout-registrar';
|
|
65
|
-
|
|
66
|
-
// Inline selectable wrapper for header icons - doesn't add extra wrapper div
|
|
67
|
-
function SelectableIcon({
|
|
68
|
-
blockId,
|
|
69
|
-
blockType,
|
|
70
|
-
blockLabel,
|
|
71
|
-
isDesigner,
|
|
72
|
-
isSelected,
|
|
73
|
-
children
|
|
74
|
-
}: {
|
|
75
|
-
blockId: string;
|
|
76
|
-
blockType: string;
|
|
77
|
-
blockLabel: string;
|
|
78
|
-
isDesigner: boolean;
|
|
79
|
-
isSelected: boolean;
|
|
80
|
-
children: React.ReactElement;
|
|
81
|
-
}) {
|
|
82
|
-
const { handleClick } = useDesignerFeatures({
|
|
83
|
-
blockId,
|
|
84
|
-
placeholderId: HEADER_ICONS_PLACEHOLDER_ID,
|
|
85
|
-
sectionId: HEADER_ICONS_SECTION_ID,
|
|
86
|
-
isDesigner,
|
|
87
|
-
blockInfo: {
|
|
88
|
-
id: blockId,
|
|
89
|
-
type: blockType,
|
|
90
|
-
label: blockLabel
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (!isDesigner) {
|
|
95
|
-
return children;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Clone child and inject onClick and selection styles
|
|
99
|
-
return (
|
|
100
|
-
<span
|
|
101
|
-
data-block-id={blockId}
|
|
102
|
-
onClick={handleClick}
|
|
103
|
-
className={clsx(
|
|
104
|
-
'relative cursor-pointer',
|
|
105
|
-
isSelected && 'ring-2 ring-blue-500 ring-offset-1 rounded'
|
|
106
|
-
)}
|
|
107
|
-
style={{ display: 'inline-flex', alignItems: 'center' }}
|
|
108
|
-
>
|
|
109
|
-
{children}
|
|
110
|
-
</span>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
17
|
|
|
114
18
|
interface MenuItem {
|
|
115
|
-
id: 'search' | 'profile' | 'cart';
|
|
116
19
|
label: string;
|
|
117
20
|
url?: string;
|
|
118
21
|
action?: () => void;
|
|
@@ -121,49 +24,10 @@ interface MenuItem {
|
|
|
121
24
|
badge?: ReactNode;
|
|
122
25
|
miniBasket?: ReactNode;
|
|
123
26
|
dataTestId?: string;
|
|
124
|
-
blockId: string;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
interface ActionMenuContentProps {
|
|
128
|
-
initialIconSettings?: HeaderIconSettings;
|
|
129
27
|
}
|
|
130
28
|
|
|
131
|
-
|
|
132
|
-
* Inner ActionMenu component that uses the designer context
|
|
133
|
-
*/
|
|
134
|
-
function ActionMenuContent({ initialIconSettings }: ActionMenuContentProps) {
|
|
135
|
-
const router = useRouter();
|
|
29
|
+
export default function ActionMenu() {
|
|
136
30
|
const dispatch = useAppDispatch();
|
|
137
|
-
const { status } = useSession();
|
|
138
|
-
const iconSettings = useHeaderIconSettings(initialIconSettings);
|
|
139
|
-
const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
|
|
140
|
-
useHeaderIconsDesigner();
|
|
141
|
-
const { layout } = useHeaderLayout();
|
|
142
|
-
const { sectionProperties } = useHeaderMiniBasket();
|
|
143
|
-
|
|
144
|
-
// Extract visibility value from section properties (handles responsive format)
|
|
145
|
-
const extractVisibility = useCallback((visibilityProp: unknown): string => {
|
|
146
|
-
if (!visibilityProp) return 'show';
|
|
147
|
-
if (typeof visibilityProp === 'string') return visibilityProp;
|
|
148
|
-
if (typeof visibilityProp === 'object' && visibilityProp !== null) {
|
|
149
|
-
const obj = visibilityProp as Record<string, string>;
|
|
150
|
-
return obj.desktop || obj.mobile || Object.values(obj)[0] || 'show';
|
|
151
|
-
}
|
|
152
|
-
return 'show';
|
|
153
|
-
}, []);
|
|
154
|
-
|
|
155
|
-
// Check if mini basket should be shown based on theme editor settings
|
|
156
|
-
const shouldShowMiniBasket = useMemo(() => {
|
|
157
|
-
const miniBasketVisibility = extractVisibility(
|
|
158
|
-
sectionProperties?.visibility
|
|
159
|
-
);
|
|
160
|
-
const shouldShow = miniBasketVisibility === 'show';
|
|
161
|
-
|
|
162
|
-
return shouldShow;
|
|
163
|
-
}, [sectionProperties, extractVisibility]);
|
|
164
|
-
|
|
165
|
-
// Hide search icon when inline search is visible (two-row layout)
|
|
166
|
-
const hideSearchIcon = layout === 'two-row';
|
|
167
31
|
|
|
168
32
|
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
169
33
|
const totalQuantity = useMemo(
|
|
@@ -182,171 +46,75 @@ function ActionMenuContent({ initialIconSettings }: ActionMenuContentProps) {
|
|
|
182
46
|
|
|
183
47
|
const MenuItems: MenuItem[] = [
|
|
184
48
|
{
|
|
185
|
-
id: 'search',
|
|
186
|
-
blockId: HEADER_ICON_BLOCKS.SEARCH.id,
|
|
187
49
|
label: 'Search',
|
|
188
50
|
action: () => {
|
|
189
|
-
|
|
51
|
+
dispatch(openSearch());
|
|
190
52
|
},
|
|
191
53
|
icon: 'search',
|
|
54
|
+
className: 'sm:hidden',
|
|
192
55
|
dataTestId: 'header-search'
|
|
193
56
|
},
|
|
194
57
|
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
icon: 'person',
|
|
200
|
-
dataTestId: 'header-profile'
|
|
58
|
+
label: 'Favourite Products',
|
|
59
|
+
url: ROUTES.ACCOUNT_WISHLIST,
|
|
60
|
+
icon: 'heart-stroke',
|
|
61
|
+
dataTestId: 'header-favourite'
|
|
201
62
|
},
|
|
202
63
|
{
|
|
203
|
-
id: 'cart',
|
|
204
|
-
blockId: HEADER_ICON_BLOCKS.CART.id,
|
|
205
64
|
label: 'Basket',
|
|
206
|
-
action
|
|
207
|
-
|
|
208
|
-
dispatch(toggleMiniBasket());
|
|
209
|
-
} else if (!isDesigner) {
|
|
210
|
-
// Only navigate to basket page when not in designer mode
|
|
211
|
-
router.push(ROUTES.BASKET);
|
|
212
|
-
}
|
|
65
|
+
action() {
|
|
66
|
+
dispatch(toggleMiniBasket());
|
|
213
67
|
},
|
|
214
68
|
icon: 'cart',
|
|
215
69
|
dataTestId: 'header-basket',
|
|
216
|
-
className: 'pl-2.5',
|
|
217
70
|
badge: (
|
|
218
71
|
<Badge
|
|
219
72
|
className={clsx(
|
|
220
|
-
'w-4
|
|
73
|
+
'w-4',
|
|
74
|
+
totalQuantity === 0
|
|
75
|
+
? 'bg-primary text-gray-500'
|
|
76
|
+
: 'bg-secondary-500 text-white'
|
|
221
77
|
)}
|
|
222
78
|
>
|
|
223
79
|
<span data-testid="header-basket-count">{totalQuantity}</span>
|
|
224
80
|
</Badge>
|
|
225
81
|
),
|
|
226
|
-
miniBasket:
|
|
82
|
+
miniBasket: <MiniBasket />
|
|
227
83
|
}
|
|
228
84
|
];
|
|
229
85
|
|
|
230
|
-
const renderIconContent = (
|
|
231
|
-
menu: MenuItem,
|
|
232
|
-
settings: { size: number; color: string }
|
|
233
|
-
) => {
|
|
234
|
-
// Get custom icon from block properties
|
|
235
|
-
const blockProps = getBlockProperties(menu.blockId);
|
|
236
|
-
const customIcon = blockProps?.icon as
|
|
237
|
-
| string
|
|
238
|
-
| { desktop?: string }
|
|
239
|
-
| undefined;
|
|
240
|
-
|
|
241
|
-
// Handle responsive object format
|
|
242
|
-
const iconValue =
|
|
243
|
-
typeof customIcon === 'object' && customIcon !== null
|
|
244
|
-
? customIcon.desktop
|
|
245
|
-
: customIcon;
|
|
246
|
-
|
|
247
|
-
// Check if we have a custom SVG icon
|
|
248
|
-
const hasCustomSvgIcon =
|
|
249
|
-
iconValue && typeof iconValue === 'string' && iconValue.includes('<svg');
|
|
250
|
-
|
|
251
|
-
return (
|
|
252
|
-
<>
|
|
253
|
-
{hasCustomSvgIcon ? (
|
|
254
|
-
<div
|
|
255
|
-
className="flex items-center justify-center"
|
|
256
|
-
style={{
|
|
257
|
-
width: settings.size,
|
|
258
|
-
height: settings.size,
|
|
259
|
-
color:
|
|
260
|
-
settings.color !== 'currentColor' ? settings.color : undefined
|
|
261
|
-
}}
|
|
262
|
-
dangerouslySetInnerHTML={{ __html: iconValue }}
|
|
263
|
-
/>
|
|
264
|
-
) : (
|
|
265
|
-
<Icon
|
|
266
|
-
name={menu.icon}
|
|
267
|
-
size={settings.size}
|
|
268
|
-
style={{
|
|
269
|
-
color:
|
|
270
|
-
settings.color !== 'currentColor' ? settings.color : undefined
|
|
271
|
-
}}
|
|
272
|
-
/>
|
|
273
|
-
)}
|
|
274
|
-
{menu.badge}
|
|
275
|
-
</>
|
|
276
|
-
);
|
|
277
|
-
};
|
|
278
|
-
|
|
279
86
|
return (
|
|
280
|
-
<ul className="flex items-center">
|
|
281
|
-
{MenuItems.map((menu, index) =>
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
data-testid={menu.dataTestId}
|
|
304
|
-
>
|
|
305
|
-
{renderIconContent(menu, settings)}
|
|
306
|
-
</Link>
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
// Get block styles for margins
|
|
310
|
-
const blockStyles = getBlockStyles(menu.blockId);
|
|
311
|
-
const iconContainerStyles = convertBlockStylesToCSS(blockStyles);
|
|
312
|
-
|
|
313
|
-
return (
|
|
314
|
-
<li
|
|
315
|
-
key={index}
|
|
316
|
-
className={clsx('flex items-center relative', menu.className)}
|
|
317
|
-
style={iconContainerStyles}
|
|
318
|
-
ref={menu.miniBasket ? miniBasketRef : null}
|
|
319
|
-
>
|
|
320
|
-
<SelectableIcon
|
|
321
|
-
blockId={menu.blockId}
|
|
322
|
-
blockType="icon-button"
|
|
323
|
-
blockLabel={menu.label}
|
|
324
|
-
isDesigner={isDesigner}
|
|
325
|
-
isSelected={selectedBlockId === menu.blockId}
|
|
87
|
+
<ul className="flex items-center space-x-3 lg:space-x-10">
|
|
88
|
+
{MenuItems.map((menu, index) => (
|
|
89
|
+
<li
|
|
90
|
+
key={index}
|
|
91
|
+
className={clsx('flex items-center relative', menu.className)}
|
|
92
|
+
ref={menu.miniBasket ? miniBasketRef : null}
|
|
93
|
+
>
|
|
94
|
+
{menu.action ? (
|
|
95
|
+
<button onClick={menu.action} data-testid={menu.dataTestId}>
|
|
96
|
+
<Icon name={menu.icon} size={24} />
|
|
97
|
+
{menu.badge}
|
|
98
|
+
</button>
|
|
99
|
+
) : (
|
|
100
|
+
<Link
|
|
101
|
+
href={menu.url ?? '#'}
|
|
102
|
+
passHref={true}
|
|
103
|
+
onClick={(event) => {
|
|
104
|
+
if (menu.action) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
menu.action();
|
|
107
|
+
}
|
|
108
|
+
}}
|
|
109
|
+
data-testid={menu.dataTestId}
|
|
326
110
|
>
|
|
327
|
-
{
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
111
|
+
<Icon name={menu.icon} size={24} />
|
|
112
|
+
{menu.badge}
|
|
113
|
+
</Link>
|
|
114
|
+
)}
|
|
115
|
+
{menu.miniBasket}
|
|
116
|
+
</li>
|
|
117
|
+
))}
|
|
333
118
|
</ul>
|
|
334
119
|
);
|
|
335
120
|
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* ActionMenu with HeaderIcons and HeaderMiniBasket Provider wrappers
|
|
339
|
-
*/
|
|
340
|
-
interface ActionMenuProps {
|
|
341
|
-
initialIconSettings?: HeaderIconSettings;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export default function ActionMenu({ initialIconSettings }: ActionMenuProps) {
|
|
345
|
-
return (
|
|
346
|
-
<HeaderIconsProvider>
|
|
347
|
-
<HeaderMiniBasketProvider>
|
|
348
|
-
<ActionMenuContent initialIconSettings={initialIconSettings} />
|
|
349
|
-
</HeaderMiniBasketProvider>
|
|
350
|
-
</HeaderIconsProvider>
|
|
351
|
-
);
|
|
352
|
-
}
|
|
@@ -6,12 +6,27 @@ import ActionMenu from './action-menu';
|
|
|
6
6
|
import HeaderBandText from '@theme/widgets/header-band-text';
|
|
7
7
|
import { LanguageSelect } from '@theme/components';
|
|
8
8
|
import { CurrencySelect } from 'components/currency-select';
|
|
9
|
-
import { HeaderIconSettings } from './server-settings-parser';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export default function HeaderBand() {
|
|
11
|
+
return (
|
|
12
|
+
<div className="contents text-xs">
|
|
13
|
+
<div className="hidden justify-start sm:inline-flex sm:gap-x-5 sm:header-grid-area-band-l">
|
|
14
|
+
<LanguageSelect className="bg-transparent w-11 px-0 text-sm" />
|
|
15
|
+
<CurrencySelect className="bg-transparent w-12 px-0 text-sm" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div className="header-grid-area-nav bg-gray-100 h-auto p-3 sm:header-grid-area-band-m sm:h-9 sm:py-0">
|
|
19
|
+
<div className="text-center overflow-ellipsis line-clamp-2 h-full flex items-center justify-center">
|
|
20
|
+
<HeaderBandText />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
<div className="header-grid-area-main-r h-full pr-4 sm:header-grid-area-band-r sm:pr-0">
|
|
25
|
+
<div className="flex items-center justify-end h-full">
|
|
26
|
+
<UserMenu isMobile={false} />
|
|
27
|
+
<ActionMenu />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
17
32
|
}
|
|
@@ -1,128 +1,66 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
|
|
3
|
+
import { Link } from '@theme/components';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { ROUTES } from '@theme/routes';
|
|
3
6
|
import { menuGenerator } from '@akinon/next/utils';
|
|
4
7
|
|
|
5
8
|
import HeaderBand from './band';
|
|
6
9
|
import MobileHamburgerButton from './mobile-hamburger-button';
|
|
7
10
|
import MobileMenu from './mobile-menu';
|
|
8
11
|
import Navbar from './navbar';
|
|
9
|
-
import HeaderLogo from './header-logo';
|
|
10
|
-
import HeaderLayoutRegistrar from './header-layout-registrar';
|
|
11
|
-
import HeaderContent from './header-content';
|
|
12
12
|
import { getMenu } from '@akinon/next/data/server';
|
|
13
|
-
import {
|
|
14
|
-
import { getWidgetData } from '@akinon/next/data/server/widget';
|
|
15
|
-
import {
|
|
16
|
-
parseServerIconSettings,
|
|
17
|
-
parseServerLogoSettings,
|
|
18
|
-
parseServerNavbarMenuSettings,
|
|
19
|
-
parseServerLayoutSettings,
|
|
20
|
-
parseServerSearchSettings,
|
|
21
|
-
parseServerLanguageSettings,
|
|
22
|
-
parseServerCurrencySettings,
|
|
23
|
-
parseServerTextSliderSettings,
|
|
24
|
-
parseServerAnnouncementSettings
|
|
25
|
-
} from './server-settings-parser';
|
|
13
|
+
import { Image } from '@akinon/next/components/image';
|
|
26
14
|
|
|
27
15
|
export default async function Header() {
|
|
28
16
|
const response = await getMenu({ depth: 3 });
|
|
29
17
|
const menu = menuGenerator(response);
|
|
30
|
-
const themeSettings = await getThemeSettings();
|
|
31
|
-
|
|
32
|
-
// Fetch native widget settings server-side to avoid flash
|
|
33
|
-
const [
|
|
34
|
-
iconSettingsWidget,
|
|
35
|
-
logoSettingsWidget,
|
|
36
|
-
navSettingsWidget,
|
|
37
|
-
layoutSettingsWidget,
|
|
38
|
-
searchSettingsWidget,
|
|
39
|
-
languageSettingsWidget,
|
|
40
|
-
currencySettingsWidget,
|
|
41
|
-
textSliderSettingsWidget,
|
|
42
|
-
announcementSettingsWidget
|
|
43
|
-
] = await Promise.all([
|
|
44
|
-
getWidgetData({ slug: 'header-icon-settings-2' }).catch(() => null),
|
|
45
|
-
getWidgetData({ slug: 'header-logo-settings-2' }).catch(() => null),
|
|
46
|
-
getWidgetData({ slug: 'header-nav-settings-2' }).catch(() => null),
|
|
47
|
-
getWidgetData({ slug: 'header-layout-settings-2' }).catch(() => null),
|
|
48
|
-
getWidgetData({ slug: 'header-search-settings-2' }).catch(() => null),
|
|
49
|
-
getWidgetData({ slug: 'header-language-settings-2' }).catch(() => null),
|
|
50
|
-
getWidgetData({ slug: 'header-currency-settings-2' }).catch(() => null),
|
|
51
|
-
getWidgetData({ slug: 'header-text-slider-settings-2' }).catch(() => null),
|
|
52
|
-
getWidgetData({ slug: 'header-announcement-bar-settings-2' }).catch(
|
|
53
|
-
() => null
|
|
54
|
-
)
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
const initialIconSettings = parseServerIconSettings(iconSettingsWidget);
|
|
58
|
-
const initialLogoSettings = parseServerLogoSettings(
|
|
59
|
-
logoSettingsWidget,
|
|
60
|
-
themeSettings.logo
|
|
61
|
-
);
|
|
62
|
-
const initialNavSettings = parseServerNavbarMenuSettings(navSettingsWidget);
|
|
63
|
-
const initialLayoutSettings = parseServerLayoutSettings(layoutSettingsWidget);
|
|
64
|
-
const initialSearchSettings = parseServerSearchSettings(searchSettingsWidget);
|
|
65
|
-
const initialLanguageSettings = parseServerLanguageSettings(
|
|
66
|
-
languageSettingsWidget
|
|
67
|
-
);
|
|
68
|
-
const initialCurrencySettings = parseServerCurrencySettings(
|
|
69
|
-
currencySettingsWidget
|
|
70
|
-
);
|
|
71
|
-
const initialTextSliderSettings = parseServerTextSliderSettings(
|
|
72
|
-
textSliderSettingsWidget
|
|
73
|
-
);
|
|
74
|
-
const initialAnnouncementSettings = parseServerAnnouncementSettings(
|
|
75
|
-
announcementSettingsWidget
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// Use layout settings for sticky, fallback to theme settings
|
|
79
|
-
const isSticky = initialLayoutSettings.sticky;
|
|
80
|
-
const position = isSticky ? 'sticky' : 'relative';
|
|
81
|
-
const top = isSticky ? '0' : 'auto';
|
|
82
|
-
const zIndex = isSticky ? '40' : 'auto';
|
|
83
18
|
|
|
84
19
|
return (
|
|
85
|
-
<header
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
20
|
+
<header className="relative">
|
|
21
|
+
<div
|
|
22
|
+
className={clsx([
|
|
23
|
+
'grid',
|
|
24
|
+
'mx-auto',
|
|
25
|
+
'header-m-template-cols',
|
|
26
|
+
'header-grid-template-areas',
|
|
27
|
+
'border-b',
|
|
28
|
+
'border-gray-100',
|
|
29
|
+
'before:hidden',
|
|
30
|
+
'before:absolute',
|
|
31
|
+
'before:top-0',
|
|
32
|
+
'before:left-0',
|
|
33
|
+
'before:w-full',
|
|
34
|
+
'before:bg-gray-100',
|
|
35
|
+
'before:h-9',
|
|
36
|
+
'before:z-[-1]',
|
|
37
|
+
'before:content-[""]',
|
|
38
|
+
'sm:grid-cols-3',
|
|
39
|
+
'sm:container',
|
|
40
|
+
'sm:before:block'
|
|
41
|
+
])}
|
|
105
42
|
>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/>
|
|
125
|
-
|
|
43
|
+
<HeaderBand />
|
|
44
|
+
<MobileHamburgerButton />
|
|
45
|
+
<Link
|
|
46
|
+
href={ROUTES.HOME}
|
|
47
|
+
passHref={true}
|
|
48
|
+
className="self-center block w-40 pl-3 header-grid-area-main-m sm:mt-9 sm:w-72 sm:justify-self-center sm:p-0"
|
|
49
|
+
aria-label="Homepage"
|
|
50
|
+
>
|
|
51
|
+
<Image
|
|
52
|
+
src="/logo.svg"
|
|
53
|
+
alt="Project Zero"
|
|
54
|
+
width={285}
|
|
55
|
+
height={27}
|
|
56
|
+
className="block w-full"
|
|
57
|
+
style={{ height: 'auto', width: '100%' }}
|
|
58
|
+
unoptimized
|
|
59
|
+
/>
|
|
60
|
+
</Link>
|
|
61
|
+
<MobileMenu menu={menu} />
|
|
62
|
+
<Navbar menu={menu} />
|
|
63
|
+
</div>
|
|
126
64
|
</header>
|
|
127
65
|
);
|
|
128
66
|
}
|