@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.
Files changed (223) hide show
  1. package/CHANGELOG.md +9 -7
  2. package/app-template/CHANGELOG.md +251 -204
  3. package/app-template/akinon.json +1 -1
  4. package/app-template/package.json +28 -28
  5. package/app-template/public/amex.svg +12 -0
  6. package/app-template/public/apple-pay.svg +16 -0
  7. package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
  8. package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
  9. package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
  10. package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
  11. package/app-template/public/google-pay.svg +16 -0
  12. package/app-template/public/locales/en/account.json +6 -3
  13. package/app-template/public/locales/en/auth.json +6 -7
  14. package/app-template/public/locales/en/basket.json +6 -6
  15. package/app-template/public/locales/en/blog.json +7 -0
  16. package/app-template/public/locales/en/category.json +3 -1
  17. package/app-template/public/locales/en/checkout.json +5 -4
  18. package/app-template/public/locales/en/common.json +11 -2
  19. package/app-template/public/locales/en/forgot_password.json +6 -7
  20. package/app-template/public/locales/en/product.json +4 -3
  21. package/app-template/public/locales/tr/account.json +6 -3
  22. package/app-template/public/locales/tr/auth.json +16 -17
  23. package/app-template/public/locales/tr/basket.json +4 -4
  24. package/app-template/public/locales/tr/blog.json +7 -0
  25. package/app-template/public/locales/tr/category.json +3 -1
  26. package/app-template/public/locales/tr/checkout.json +39 -38
  27. package/app-template/public/locales/tr/common.json +10 -1
  28. package/app-template/public/locales/tr/forgot_password.json +12 -13
  29. package/app-template/public/locales/tr/product.json +1 -0
  30. package/app-template/public/logo.svg +3 -27
  31. package/app-template/public/mastercard.svg +14 -0
  32. package/app-template/public/promotion-banner.jpg +0 -0
  33. package/app-template/public/shop-pay.svg +12 -0
  34. package/app-template/public/visa.svg +12 -0
  35. package/app-template/src/app/[commerce]/[locale]/[currency]/blog/[slug]/page.tsx +118 -0
  36. package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +15 -0
  37. package/app-template/src/app/api/theme-settings/route.ts +12 -0
  38. package/app-template/src/assets/fonts/pz-icon.css +211 -49
  39. package/app-template/src/assets/fonts/pz-icon.eot +0 -0
  40. package/app-template/src/assets/fonts/pz-icon.html +486 -0
  41. package/app-template/src/assets/fonts/pz-icon.scss +373 -49
  42. package/app-template/src/assets/fonts/pz-icon.svg +215 -53
  43. package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
  44. package/app-template/src/assets/fonts/pz-icon.woff +0 -0
  45. package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
  46. package/app-template/src/assets/globals.scss +4 -0
  47. package/app-template/src/assets/icons/arrow-right.svg +3 -0
  48. package/app-template/src/assets/icons/cart.svg +4 -12
  49. package/app-template/src/assets/icons/check.svg +2 -18
  50. package/app-template/src/assets/icons/chevron-down.svg +2 -7
  51. package/app-template/src/assets/icons/delete.svg +3 -0
  52. package/app-template/src/assets/icons/facebook.svg +2 -8
  53. package/app-template/src/assets/icons/fav-off.svg +5 -0
  54. package/app-template/src/assets/icons/fav-on.svg +5 -0
  55. package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
  56. package/app-template/src/assets/icons/heart.svg +3 -0
  57. package/app-template/src/assets/icons/instagram.svg +2 -13
  58. package/app-template/src/assets/icons/materials.svg +3 -0
  59. package/app-template/src/assets/icons/person.svg +4 -0
  60. package/app-template/src/assets/icons/pinterest.svg +5 -11
  61. package/app-template/src/assets/icons/ruler.svg +3 -0
  62. package/app-template/src/assets/icons/search.svg +8 -11
  63. package/app-template/src/assets/icons/share.svg +2 -9
  64. package/app-template/src/assets/icons/snapchat.svg +3 -0
  65. package/app-template/src/assets/icons/tiktok.svg +3 -0
  66. package/app-template/src/assets/icons/tumblr.svg +6 -0
  67. package/app-template/src/assets/icons/twitter.svg +2 -10
  68. package/app-template/src/assets/icons/vimeo.svg +3 -0
  69. package/app-template/src/assets/icons/youtube.svg +3 -0
  70. package/app-template/src/assets/icons/zoom.svg +8 -0
  71. package/app-template/src/components/accordion.tsx +33 -11
  72. package/app-template/src/components/action-tooltip.tsx +160 -0
  73. package/app-template/src/components/currency-select.tsx +149 -4
  74. package/app-template/src/components/icon.tsx +5 -6
  75. package/app-template/src/components/index.ts +4 -1
  76. package/app-template/src/components/language-select.tsx +88 -2
  77. package/app-template/src/components/pagination.tsx +132 -20
  78. package/app-template/src/components/quantity-input.tsx +63 -0
  79. package/app-template/src/components/quantity-selector.tsx +203 -0
  80. package/app-template/src/components/route-handler.tsx +50 -0
  81. package/app-template/src/components/select.tsx +89 -69
  82. package/app-template/src/components/types/index.ts +26 -0
  83. package/app-template/src/components/widget-content.tsx +323 -0
  84. package/app-template/src/data/server/theme.ts +70 -0
  85. package/app-template/src/hooks/use-fav-button.tsx +5 -2
  86. package/app-template/src/hooks/use-product-cart.ts +11 -8
  87. package/app-template/src/hooks/use-theme-settings.ts +42 -0
  88. package/app-template/src/lib/fonts.ts +149 -0
  89. package/app-template/src/settings.js +2 -2
  90. package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
  91. package/app-template/src/types/widget.ts +169 -0
  92. package/app-template/src/utils/formatDate.ts +48 -0
  93. package/app-template/src/utils/styles.ts +71 -0
  94. package/app-template/src/views/account/contact-form.tsx +147 -130
  95. package/app-template/src/views/basket/basket-item.tsx +691 -107
  96. package/app-template/src/views/basket/basket-summary-context.tsx +560 -0
  97. package/app-template/src/views/basket/designer-context.tsx +617 -0
  98. package/app-template/src/views/basket/index.ts +2 -0
  99. package/app-template/src/views/basket/summary.tsx +496 -75
  100. package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +190 -0
  101. package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +286 -0
  102. package/app-template/src/views/breadcrumb/constants.ts +15 -0
  103. package/app-template/src/views/breadcrumb/index.tsx +127 -0
  104. package/app-template/src/views/breadcrumb.tsx +13 -38
  105. package/app-template/src/views/category/category-banner.tsx +4 -23
  106. package/app-template/src/views/category/category-header.tsx +289 -66
  107. package/app-template/src/views/category/category-info.tsx +173 -24
  108. package/app-template/src/views/category/filters/filter-item.tsx +138 -42
  109. package/app-template/src/views/category/filters/index.tsx +208 -48
  110. package/app-template/src/views/category/layout.tsx +7 -4
  111. package/app-template/src/views/category/native-widget-context.tsx +257 -0
  112. package/app-template/src/views/category/product-list-registrar.tsx +665 -0
  113. package/app-template/src/views/checkout/auth.tsx +64 -40
  114. package/app-template/src/views/checkout/checkout-address-registrar.tsx +254 -0
  115. package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +183 -0
  116. package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +259 -0
  117. package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +253 -0
  118. package/app-template/src/views/checkout/checkout-summary-registrar.tsx +183 -0
  119. package/app-template/src/views/checkout/constants.ts +5 -0
  120. package/app-template/src/views/checkout/index.tsx +5 -0
  121. package/app-template/src/views/checkout/layout/header.tsx +9 -5
  122. package/app-template/src/views/checkout/steps/payment/index.tsx +5 -2
  123. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +72 -1
  124. package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +15 -0
  125. package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +18 -0
  126. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +171 -40
  127. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +74 -12
  128. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +128 -45
  129. package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +232 -27
  130. package/app-template/src/views/checkout/summary.tsx +303 -29
  131. package/app-template/src/views/footer/footer-app-banner-context.tsx +326 -0
  132. package/app-template/src/views/footer/footer-bottom-context.tsx +215 -0
  133. package/app-template/src/views/footer/footer-bottom-wrapper.tsx +74 -0
  134. package/app-template/src/views/footer/footer-layout-constants.ts +35 -0
  135. package/app-template/src/views/footer/footer-layout-registrar.tsx +342 -0
  136. package/app-template/src/views/footer/footer-layout-switcher.tsx +110 -0
  137. package/app-template/src/views/footer/footer-menu-context.tsx +211 -0
  138. package/app-template/src/views/footer/footer-native-widgets.tsx +60 -0
  139. package/app-template/src/views/footer/footer-social-context.tsx +254 -0
  140. package/app-template/src/views/footer/footer-subscription-context.tsx +210 -0
  141. package/app-template/src/views/footer/footer-utils.ts +43 -0
  142. package/app-template/src/views/footer/footer-value-props-context.tsx +326 -0
  143. package/app-template/src/views/footer/logo-settings.ts +183 -0
  144. package/app-template/src/views/footer/native-widget-config.ts +262 -0
  145. package/app-template/src/views/footer/subscription-settings.ts +122 -0
  146. package/app-template/src/views/footer/use-footer-logo.ts +162 -0
  147. package/app-template/src/views/footer.tsx +415 -13
  148. package/app-template/src/views/guest-login/index.tsx +62 -58
  149. package/app-template/src/views/header/action-menu.tsx +277 -45
  150. package/app-template/src/views/header/band.tsx +6 -21
  151. package/app-template/src/views/header/designer-context.tsx +261 -0
  152. package/app-template/src/views/header/header-announcement-registrar.tsx +267 -0
  153. package/app-template/src/views/header/header-client-wrapper.tsx +496 -0
  154. package/app-template/src/views/header/header-content.tsx +1026 -0
  155. package/app-template/src/views/header/header-currency-registrar.tsx +348 -0
  156. package/app-template/src/views/header/header-icons-context.tsx +262 -0
  157. package/app-template/src/views/header/header-language-registrar.tsx +348 -0
  158. package/app-template/src/views/header/header-layout-context.tsx +143 -0
  159. package/app-template/src/views/header/header-layout-registrar.tsx +658 -0
  160. package/app-template/src/views/header/header-logo-context.tsx +228 -0
  161. package/app-template/src/views/header/header-logo.tsx +118 -0
  162. package/app-template/src/views/header/header-mini-basket-context.tsx +524 -0
  163. package/app-template/src/views/header/header-search-registrar.tsx +511 -0
  164. package/app-template/src/views/header/header-text-slider-registrar.tsx +382 -0
  165. package/app-template/src/views/header/index.tsx +109 -47
  166. package/app-template/src/views/header/inline-search.tsx +262 -0
  167. package/app-template/src/views/header/mini-basket.tsx +819 -44
  168. package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
  169. package/app-template/src/views/header/mobile-menu.tsx +12 -0
  170. package/app-template/src/views/header/navbar-menu-context.tsx +219 -0
  171. package/app-template/src/views/header/navbar.tsx +178 -111
  172. package/app-template/src/views/header/search/index.tsx +71 -32
  173. package/app-template/src/views/header/search/results.tsx +127 -65
  174. package/app-template/src/views/header/search/search-input.tsx +61 -0
  175. package/app-template/src/views/header/server-settings-parser.ts +1105 -0
  176. package/app-template/src/views/header/use-header-icons.ts +241 -0
  177. package/app-template/src/views/header/use-header-logo.ts +213 -0
  178. package/app-template/src/views/header/use-navbar-menu.ts +179 -0
  179. package/app-template/src/views/login/index.tsx +54 -46
  180. package/app-template/src/views/product/accordion-section.tsx +61 -0
  181. package/app-template/src/views/product/accordion-wrapper.tsx +135 -43
  182. package/app-template/src/views/product/custom-button-group.tsx +69 -0
  183. package/app-template/src/views/product/favorites-button-section.tsx +69 -0
  184. package/app-template/src/views/product/find-in-store-section.tsx +60 -0
  185. package/app-template/src/views/product/index.ts +1 -0
  186. package/app-template/src/views/product/layout.tsx +6 -5
  187. package/app-template/src/views/product/misc-buttons.tsx +339 -25
  188. package/app-template/src/views/product/price-wrapper.tsx +3 -29
  189. package/app-template/src/views/product/product-actions.tsx +137 -8
  190. package/app-template/src/views/product/product-info-section.tsx +140 -0
  191. package/app-template/src/views/product/product-info.tsx +69 -31
  192. package/app-template/src/views/product/product-share.tsx +13 -8
  193. package/app-template/src/views/product/product-variants.tsx +2 -2
  194. package/app-template/src/views/product/quantity-section.tsx +73 -0
  195. package/app-template/src/views/product/sale-tag.tsx +10 -0
  196. package/app-template/src/views/product/share-section.tsx +357 -0
  197. package/app-template/src/views/product/slider.tsx +117 -79
  198. package/app-template/src/views/product/variant.tsx +69 -41
  199. package/app-template/src/views/product/variants-section.tsx +126 -0
  200. package/app-template/src/views/product-detail/constants.ts +272 -0
  201. package/app-template/src/views/product-detail/index.ts +10 -0
  202. package/app-template/src/views/product-detail/product-detail-registrar.tsx +616 -0
  203. package/app-template/src/views/product-item/index.tsx +119 -46
  204. package/app-template/src/views/register/index.tsx +14 -25
  205. package/app-template/src/views/share/index.tsx +9 -6
  206. package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
  207. package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
  208. package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
  209. package/app-template/src/widgets/footer-app-banner.tsx +444 -0
  210. package/app-template/src/widgets/footer-bottom.tsx +127 -0
  211. package/app-template/src/widgets/footer-menu-compact.tsx +238 -0
  212. package/app-template/src/widgets/footer-menu-two.tsx +298 -0
  213. package/app-template/src/widgets/footer-social-client.tsx +251 -0
  214. package/app-template/src/widgets/footer-social.tsx +47 -16
  215. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
  216. package/app-template/src/widgets/footer-subscription/index.tsx +183 -17
  217. package/app-template/src/widgets/footer-value-props.tsx +201 -0
  218. package/app-template/src/widgets/index.ts +7 -0
  219. package/app-template/src/widgets/schemas/about-us.json +46 -0
  220. package/app-template/src/widgets/schemas/blog-list.json +37 -0
  221. package/app-template/src/widgets/schemas/blog.json +29 -0
  222. package/app-template/tailwind.config.js +18 -2
  223. package/package.json +1 -1
@@ -0,0 +1,382 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Header Text Slider Section Registrar
5
+ *
6
+ * This component registers the "Text Slider" section for the header placeholder.
7
+ * The text slider section is a native widget that can be added via "Add Section" button.
8
+ * It displays scrolling/sliding text messages in the utility bar.
9
+ */
10
+
11
+ import {
12
+ createContext,
13
+ useContext,
14
+ useEffect,
15
+ useRef,
16
+ useState,
17
+ PropsWithChildren
18
+ } from 'react';
19
+
20
+ // Constants
21
+ export const HEADER_TEXT_SLIDER_PLACEHOLDER_ID = 'header';
22
+ export const HEADER_TEXT_SLIDER_SECTION_ID = 'header-text-slider';
23
+ export const HEADER_TEXT_SLIDER_WIDGET_SLUG = 'header-text-slider-settings-2';
24
+
25
+ // Global flag to track if registration has been done
26
+ declare global {
27
+ interface Window {
28
+ __headerTextSliderRegistered?: boolean;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Check if running inside designer iframe
34
+ */
35
+ function isInDesignerMode(): boolean {
36
+ if (typeof window === 'undefined') return false;
37
+ return window.self !== window.top;
38
+ }
39
+
40
+ interface ThemeSection {
41
+ id: string;
42
+ visible?: boolean;
43
+ properties?: Record<string, unknown>;
44
+ styles?: Record<string, unknown>;
45
+ }
46
+
47
+ interface ThemePlaceholder {
48
+ slug: string;
49
+ sections: ThemeSection[];
50
+ }
51
+
52
+ export interface TextSliderItem {
53
+ text: string;
54
+ link?: string;
55
+ }
56
+
57
+ export interface HeaderTextSliderProperties {
58
+ visible?: boolean;
59
+ items?: TextSliderItem[];
60
+ autoPlay?: boolean;
61
+ autoPlayInterval?: number;
62
+ showArrows?: boolean;
63
+ }
64
+
65
+ interface HeaderTextSliderContextValue {
66
+ isDesigner: boolean;
67
+ isTextSliderSectionSelected: boolean;
68
+ isSectionVisible: boolean;
69
+ properties: HeaderTextSliderProperties;
70
+ sectionStyles: Record<string, unknown>;
71
+ }
72
+
73
+ const HeaderTextSliderContext = createContext<HeaderTextSliderContextValue>({
74
+ isDesigner: false,
75
+ isTextSliderSectionSelected: false,
76
+ isSectionVisible: false,
77
+ properties: {
78
+ items: [{ text: 'Welcome to our store!' }],
79
+ autoPlay: true,
80
+ autoPlayInterval: 3000,
81
+ showArrows: true
82
+ },
83
+ sectionStyles: {}
84
+ });
85
+
86
+ export const useHeaderTextSlider = () => useContext(HeaderTextSliderContext);
87
+
88
+ interface HeaderTextSliderRegistrarProps extends PropsWithChildren {
89
+ /**
90
+ * Initial settings from server-side parsing (to avoid flash)
91
+ */
92
+ initialSettings?: {
93
+ sectionStyles?: Record<string, unknown>;
94
+ properties?: HeaderTextSliderProperties;
95
+ };
96
+ }
97
+
98
+ /**
99
+ * HeaderTextSliderRegistrar
100
+ *
101
+ * Registers the Text Slider native section with Theme Editor.
102
+ * The section can be added via "Add Section" button in the header placeholder.
103
+ */
104
+ export default function HeaderTextSliderRegistrar({
105
+ children,
106
+ initialSettings = {}
107
+ }: HeaderTextSliderRegistrarProps) {
108
+ const isDesignerRef = useRef(false);
109
+ const [sectionProperties, setSectionProperties] =
110
+ useState<HeaderTextSliderProperties>(
111
+ initialSettings.properties || {
112
+ items: [{ text: 'Welcome to our store!' }],
113
+ autoPlay: true,
114
+ autoPlayInterval: 3000,
115
+ showArrows: true
116
+ }
117
+ );
118
+ const [sectionStyles, setSectionStyles] = useState<Record<string, unknown>>(
119
+ initialSettings.sectionStyles || {}
120
+ );
121
+ const [isTextSliderSelected, setIsTextSliderSelected] = useState(false);
122
+ const [isSectionVisible, setIsSectionVisible] = useState(
123
+ initialSettings.properties?.visible ?? false
124
+ );
125
+ const hasRegisteredNativeWidget = useRef(false);
126
+
127
+ const hasInitialSettings = !!(
128
+ (initialSettings.sectionStyles &&
129
+ Object.keys(initialSettings.sectionStyles).length > 0) ||
130
+ (initialSettings.properties &&
131
+ Object.keys(initialSettings.properties).length > 0)
132
+ );
133
+
134
+ useEffect(() => {
135
+ isDesignerRef.current = isInDesignerMode();
136
+ }, []);
137
+
138
+ const isDesigner = isDesignerRef.current;
139
+
140
+ // Register native widget - only once on mount
141
+ useEffect(() => {
142
+ const isInIframe =
143
+ typeof window !== 'undefined' && window.self !== window.top;
144
+ if (!isInIframe || !window.parent) {
145
+ return;
146
+ }
147
+
148
+ // Skip if already registered
149
+ if (
150
+ typeof window !== 'undefined' &&
151
+ window.__headerTextSliderRegistered &&
152
+ hasRegisteredNativeWidget.current
153
+ ) {
154
+ return;
155
+ }
156
+
157
+ // Send native widget registration to Theme Editor
158
+ // Use initial settings for registration, not dynamic state
159
+ const nativeWidgetConfig = {
160
+ placeholderId: HEADER_TEXT_SLIDER_PLACEHOLDER_ID,
161
+ autoAdd: false,
162
+ section: {
163
+ id: HEADER_TEXT_SLIDER_SECTION_ID,
164
+ type: 'native',
165
+ label: 'Text Slider',
166
+ blocks: [],
167
+ properties: initialSettings.properties || {
168
+ items: [{ text: 'Welcome to our store!' }],
169
+ autoPlay: true,
170
+ autoPlayInterval: 3000,
171
+ showArrows: true
172
+ },
173
+ styles: initialSettings.sectionStyles || {}
174
+ }
175
+ };
176
+
177
+ window.parent.postMessage(
178
+ {
179
+ type: 'REGISTER_NATIVE_WIDGETS',
180
+ data: { widgets: [nativeWidgetConfig] }
181
+ },
182
+ '*'
183
+ );
184
+
185
+ // Mark as registered
186
+ window.__headerTextSliderRegistered = true;
187
+ hasRegisteredNativeWidget.current = true;
188
+ // eslint-disable-next-line react-hooks/exhaustive-deps
189
+ }, []);
190
+
191
+ // Apply highlight style to text slider container when section is selected
192
+ useEffect(() => {
193
+ if (typeof window === 'undefined') return;
194
+
195
+ const textSliderContainer = document.querySelector(
196
+ '[data-section-id="header-text-slider"]'
197
+ );
198
+ if (!textSliderContainer) return;
199
+
200
+ if (isTextSliderSelected) {
201
+ (textSliderContainer as HTMLElement).style.outline = '2px solid #3b82f6';
202
+ (textSliderContainer as HTMLElement).style.outlineOffset = '-2px';
203
+ } else {
204
+ (textSliderContainer as HTMLElement).style.outline = '';
205
+ (textSliderContainer as HTMLElement).style.outlineOffset = '';
206
+ }
207
+
208
+ return () => {
209
+ (textSliderContainer as HTMLElement).style.outline = '';
210
+ (textSliderContainer as HTMLElement).style.outlineOffset = '';
211
+ };
212
+ }, [isTextSliderSelected]);
213
+
214
+ const hasReceivedInitialTheme = useRef(false);
215
+
216
+ // Listen for theme updates and selection changes from Theme Editor
217
+ useEffect(() => {
218
+ if (typeof window === 'undefined') return;
219
+
220
+ const handleMessage = (event: MessageEvent) => {
221
+ const { type, data } = event.data || {};
222
+
223
+ // Handle theme updates
224
+ if (
225
+ (type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
226
+ data?.theme?.placeholders
227
+ ) {
228
+ const placeholder = data.theme.placeholders?.find(
229
+ (p: ThemePlaceholder) => p.slug === HEADER_TEXT_SLIDER_PLACEHOLDER_ID
230
+ );
231
+
232
+ const textSliderSection = placeholder?.sections?.find(
233
+ (s: ThemeSection) => s.id === HEADER_TEXT_SLIDER_SECTION_ID
234
+ );
235
+
236
+ if (textSliderSection) {
237
+ const sectionVisible = textSliderSection.properties?.visible;
238
+ if (sectionVisible !== undefined) {
239
+ const isVisible =
240
+ typeof sectionVisible === 'boolean'
241
+ ? sectionVisible
242
+ : sectionVisible?.desktop === true ||
243
+ sectionVisible?.mobile === true;
244
+ setIsSectionVisible(isVisible);
245
+ } else if (typeof textSliderSection.visible === 'boolean') {
246
+ setIsSectionVisible(textSliderSection.visible);
247
+ } else {
248
+ setIsSectionVisible(true);
249
+ }
250
+
251
+ // Skip property/style updates if we have initial settings and haven't
252
+ // received the first theme yet (to avoid format mismatch issues)
253
+ // After first theme load, we use UPDATE_SECTION_PROPERTY for changes
254
+ if (hasInitialSettings && !hasReceivedInitialTheme.current) {
255
+ hasReceivedInitialTheme.current = true;
256
+ // Don't apply properties/styles from UPDATE_THEME when we have server-side initial settings
257
+ // Individual property changes will come via UPDATE_SECTION_PROPERTY
258
+ return;
259
+ }
260
+
261
+ // Only apply updates if we don't have initial settings (fresh load)
262
+ if (!hasInitialSettings) {
263
+ if (textSliderSection.properties) {
264
+ setSectionProperties((prev) => {
265
+ const newProps =
266
+ textSliderSection.properties as HeaderTextSliderProperties;
267
+ if (JSON.stringify(prev) === JSON.stringify(newProps)) {
268
+ return prev;
269
+ }
270
+ return newProps;
271
+ });
272
+ }
273
+
274
+ if (textSliderSection.styles) {
275
+ setSectionStyles((prev) => {
276
+ if (
277
+ JSON.stringify(prev) ===
278
+ JSON.stringify(textSliderSection.styles)
279
+ ) {
280
+ return prev;
281
+ }
282
+ return textSliderSection.styles;
283
+ });
284
+ }
285
+ }
286
+ } else if (placeholder) {
287
+ setIsSectionVisible(false);
288
+ }
289
+ }
290
+
291
+ // Handle property updates
292
+ if (type === 'UPDATE_SECTION_PROPERTY' || type === 'UPDATE_PROPERTY') {
293
+ const { sectionId, key, value, properties } = data || {};
294
+
295
+ if (sectionId === HEADER_TEXT_SLIDER_SECTION_ID) {
296
+ if (key && value !== undefined) {
297
+ setSectionProperties((prev) => ({
298
+ ...prev,
299
+ [key]: value
300
+ }));
301
+ }
302
+ if (properties) {
303
+ setSectionProperties(properties as HeaderTextSliderProperties);
304
+ }
305
+ }
306
+ }
307
+
308
+ // Handle style updates for the section
309
+ if (type === 'UPDATE_SECTION_STYLE' || type === 'UPDATE_STYLE') {
310
+ const { sectionId, key, value, styles } = data || {};
311
+
312
+ if (sectionId === HEADER_TEXT_SLIDER_SECTION_ID) {
313
+ if (key && value !== undefined) {
314
+ setSectionStyles((prev) => ({
315
+ ...prev,
316
+ [key]: value
317
+ }));
318
+ }
319
+ if (styles) {
320
+ setSectionStyles(styles);
321
+ }
322
+ }
323
+ }
324
+
325
+ // Handle selection changes
326
+ if (type === 'SELECT_SECTION') {
327
+ const { placeholderId, sectionId } = data || {};
328
+
329
+ const isSelected =
330
+ placeholderId === HEADER_TEXT_SLIDER_PLACEHOLDER_ID &&
331
+ sectionId === HEADER_TEXT_SLIDER_SECTION_ID;
332
+
333
+ setIsTextSliderSelected(isSelected);
334
+ }
335
+
336
+ // Handle block selection
337
+ if (type === 'SELECT_BLOCK') {
338
+ const { sectionId } = data || {};
339
+ if (sectionId === HEADER_TEXT_SLIDER_SECTION_ID) {
340
+ setIsTextSliderSelected(true);
341
+ } else {
342
+ setIsTextSliderSelected(false);
343
+ }
344
+ }
345
+
346
+ // Handle deselection
347
+ if (type === 'DESELECT' || type === 'CLEAR_SELECTION') {
348
+ setIsTextSliderSelected(false);
349
+ }
350
+
351
+ // Handle visibility toggle
352
+ if (type === 'TOGGLE_SECTION_VISIBILITY') {
353
+ const { sectionId } = data || {};
354
+ if (sectionId === HEADER_TEXT_SLIDER_SECTION_ID) {
355
+ setIsSectionVisible((prev) => !prev);
356
+ }
357
+ }
358
+ };
359
+
360
+ window.addEventListener('message', handleMessage);
361
+ return () => window.removeEventListener('message', handleMessage);
362
+ }, [hasInitialSettings, isDesigner]);
363
+
364
+ // If no children, just return null (registration-only mode)
365
+ if (!children) {
366
+ return null;
367
+ }
368
+
369
+ return (
370
+ <HeaderTextSliderContext.Provider
371
+ value={{
372
+ isDesigner,
373
+ isTextSliderSectionSelected: isTextSliderSelected,
374
+ isSectionVisible,
375
+ properties: sectionProperties,
376
+ sectionStyles
377
+ }}
378
+ >
379
+ {children}
380
+ </HeaderTextSliderContext.Provider>
381
+ );
382
+ }
@@ -1,66 +1,128 @@
1
1
  import 'server-only';
2
2
 
3
- import { Link } from '@theme/components';
4
- import clsx from 'clsx';
5
- import { ROUTES } from '@theme/routes';
6
3
  import { menuGenerator } from '@akinon/next/utils';
7
4
 
8
5
  import HeaderBand from './band';
9
6
  import MobileHamburgerButton from './mobile-hamburger-button';
10
7
  import MobileMenu from './mobile-menu';
11
8
  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 { Image } from '@akinon/next/components/image';
13
+ import { getThemeSettings } from '@theme/data/server/theme';
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';
14
26
 
15
27
  export default async function Header() {
16
28
  const response = await getMenu({ depth: 3 });
17
29
  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';
18
83
 
19
84
  return (
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
- ])}
85
+ <header
86
+ className="relative"
87
+ style={{
88
+ position:
89
+ `var(--theme-header-position, ${position})` as React.CSSProperties['position'],
90
+ top: `var(--theme-header-top, ${top})`,
91
+ zIndex:
92
+ `var(--theme-header-z-index, ${zIndex})` as React.CSSProperties['zIndex'],
93
+ width: '100%',
94
+ backgroundColor: 'var(--theme-background, #fff)'
95
+ }}
96
+ >
97
+ {/* Register Layout section and provide layout context */}
98
+ <HeaderLayoutRegistrar
99
+ initialLayout={initialLayoutSettings.layout}
100
+ initialMenuPosition={initialLayoutSettings.menuPosition}
101
+ initialSearchPosition={initialLayoutSettings.searchPosition}
102
+ initialUtilityPosition={initialLayoutSettings.utilityPosition}
103
+ initialSticky={initialLayoutSettings.sticky}
104
+ initialBlockStyles={initialLayoutSettings.blockStyles}
42
105
  >
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>
106
+ <HeaderContent
107
+ logo={
108
+ <HeaderLogo
109
+ fallbackSrc={themeSettings.logo || '/logo.svg'}
110
+ initialSettings={initialLogoSettings}
111
+ />
112
+ }
113
+ navbar={
114
+ <Navbar menu={menu} initialNavSettings={initialNavSettings} />
115
+ }
116
+ icons={<HeaderBand initialIconSettings={initialIconSettings} />}
117
+ mobileHamburger={<MobileHamburgerButton />}
118
+ mobileMenu={<MobileMenu menu={menu} />}
119
+ initialSearchSettings={initialSearchSettings}
120
+ initialLanguageSettings={initialLanguageSettings}
121
+ initialCurrencySettings={initialCurrencySettings}
122
+ initialTextSliderSettings={initialTextSliderSettings}
123
+ initialAnnouncementSettings={initialAnnouncementSettings}
124
+ />
125
+ </HeaderLayoutRegistrar>
64
126
  </header>
65
127
  );
66
128
  }