@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,326 @@
1
+ 'use client';
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ type PropsWithChildren
12
+ } from 'react';
13
+ import { useExternalDesigner } from '@akinon/next/components/theme-editor/hooks/use-external-designer';
14
+ import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
15
+
16
+ import {
17
+ FOOTER_PLACEHOLDER_ID,
18
+ FOOTER_VALUE_PROPS_SECTION_ID,
19
+ FOOTER_VALUE_PROPS_WIDGET_SLUG,
20
+ FOOTER_VALUE_PROPS_LIST_BLOCK_ID,
21
+ type FooterNativeWidgetBlock
22
+ } from './native-widget-config';
23
+
24
+ export { FOOTER_VALUE_PROPS_LIST_BLOCK_ID };
25
+
26
+ export interface ValuePropItem {
27
+ icon: string;
28
+ title: string;
29
+ description?: string;
30
+ }
31
+
32
+ type FooterValuePropsBlockState = FooterNativeWidgetBlock;
33
+
34
+ interface FooterValuePropsContextValue {
35
+ isDesigner: boolean;
36
+ selectedSectionId: string | null;
37
+ selectedBlockId: string | null;
38
+ getBlock: (blockId: string) => FooterValuePropsBlockState | undefined;
39
+ getSectionProperty: (key: string) => unknown;
40
+ getSectionStyle: (key: string) => unknown;
41
+ blockVersion: number;
42
+ }
43
+
44
+ const FooterValuePropsContext = createContext<FooterValuePropsContextValue>({
45
+ isDesigner: false,
46
+ selectedSectionId: null,
47
+ selectedBlockId: null,
48
+ getBlock: () => undefined,
49
+ getSectionProperty: () => undefined,
50
+ getSectionStyle: () => undefined,
51
+ blockVersion: 0
52
+ });
53
+
54
+ const BLOCK_META = [
55
+ {
56
+ id: FOOTER_VALUE_PROPS_LIST_BLOCK_ID,
57
+ type: 'value-props-list',
58
+ label: 'Value Props List'
59
+ }
60
+ ];
61
+
62
+ const DEFAULT_VALUE_PROPS: ValuePropItem[] = [
63
+ { icon: 'home', title: 'Free Shipping', description: 'On orders over $50' },
64
+ {
65
+ icon: 'add',
66
+ title: 'Secure Payment',
67
+ description: '256-bit SSL encryption'
68
+ },
69
+ { icon: 'search', title: 'Easy Returns', description: '30-day return policy' }
70
+ ];
71
+
72
+ const toBlockState = (
73
+ block: Partial<FooterNativeWidgetBlock>,
74
+ defaultItems?: ValuePropItem[]
75
+ ): FooterValuePropsBlockState => {
76
+ const fallback = BLOCK_META.find((meta) => meta.id === block.id);
77
+
78
+ const defaultProperties =
79
+ block.id === FOOTER_VALUE_PROPS_LIST_BLOCK_ID && defaultItems?.length
80
+ ? { items: defaultItems }
81
+ : undefined;
82
+
83
+ return {
84
+ id: block.id ?? fallback?.id ?? '',
85
+ type: block.type ?? fallback?.type,
86
+ label: block.label ?? fallback?.label,
87
+ styles: block.styles,
88
+ properties: block.properties || defaultProperties,
89
+ value: block.value
90
+ } as FooterValuePropsBlockState;
91
+ };
92
+
93
+ const mapFromSnapshot = (
94
+ blocks?: FooterNativeWidgetBlock[],
95
+ defaultItems?: ValuePropItem[]
96
+ ): Map<string, FooterValuePropsBlockState> => {
97
+ const map = new Map<string, FooterValuePropsBlockState>();
98
+ blocks?.forEach((block) => {
99
+ map.set(block.id, toBlockState(block, defaultItems));
100
+ });
101
+
102
+ BLOCK_META.forEach((meta) => {
103
+ if (!map.has(meta.id)) {
104
+ map.set(meta.id, toBlockState(meta, defaultItems));
105
+ } else if (
106
+ meta.id === FOOTER_VALUE_PROPS_LIST_BLOCK_ID &&
107
+ defaultItems?.length
108
+ ) {
109
+ const existing = map.get(meta.id);
110
+ const existingItems = (
111
+ existing?.properties as { items?: ValuePropItem[] }
112
+ )?.items;
113
+ if (!existingItems || existingItems.length === 0) {
114
+ map.set(
115
+ meta.id,
116
+ toBlockState({
117
+ ...existing,
118
+ properties: { ...existing?.properties, items: defaultItems }
119
+ })
120
+ );
121
+ }
122
+ }
123
+ });
124
+
125
+ return map;
126
+ };
127
+
128
+ interface FooterValuePropsProviderProps {
129
+ initialBlocks?: FooterNativeWidgetBlock[];
130
+ initialProperties?: Record<string, unknown>;
131
+ initialStyles?: Record<string, unknown>;
132
+ defaultItems?: ValuePropItem[];
133
+ }
134
+
135
+ export function FooterValuePropsProvider({
136
+ initialBlocks,
137
+ initialProperties,
138
+ initialStyles,
139
+ defaultItems = DEFAULT_VALUE_PROPS,
140
+ children
141
+ }: PropsWithChildren<FooterValuePropsProviderProps>) {
142
+ const designerState = useExternalDesigner({
143
+ placeholderId: FOOTER_PLACEHOLDER_ID
144
+ });
145
+
146
+ const [blockMap, setBlockMap] = useState(() =>
147
+ mapFromSnapshot(initialBlocks, defaultItems)
148
+ );
149
+ const blockMapRef = useRef(blockMap);
150
+ const [blockVersion, setBlockVersion] = useState(0);
151
+ const [sectionProperties, setSectionProperties] = useState<
152
+ Record<string, unknown>
153
+ >(initialProperties || {});
154
+ const [sectionStyles, setSectionStyles] = useState<Record<string, unknown>>(
155
+ initialStyles || {}
156
+ );
157
+
158
+ const isDesignerRef = useRef(false);
159
+ const [isDesignerChecked, setIsDesignerChecked] = useState(false);
160
+
161
+ useEffect(() => {
162
+ if (typeof window === 'undefined') return;
163
+ isDesignerRef.current = window.self !== window.top;
164
+ setIsDesignerChecked(true);
165
+ }, []);
166
+
167
+ const isDesigner = isDesignerRef.current;
168
+
169
+ const widgetData = useNativeWidgetData({
170
+ widgetSlug: FOOTER_VALUE_PROPS_WIDGET_SLUG,
171
+ sectionId: FOOTER_VALUE_PROPS_SECTION_ID,
172
+ skip: !isDesignerChecked || isDesigner,
173
+ blockMeta: BLOCK_META
174
+ });
175
+
176
+ const mergeBlocks = useCallback(
177
+ (blocks: Partial<FooterNativeWidgetBlock>[] | undefined) => {
178
+ if (!blocks?.length) return;
179
+
180
+ setBlockMap((prev) => {
181
+ const next = new Map(prev);
182
+ blocks.forEach((block) => {
183
+ if (!block.id) return;
184
+ const existing = next.get(block.id);
185
+
186
+ next.set(
187
+ block.id,
188
+ toBlockState({
189
+ ...existing,
190
+ ...block,
191
+ styles:
192
+ block.styles && Object.keys(block.styles).length > 0
193
+ ? block.styles
194
+ : existing?.styles,
195
+ properties:
196
+ block.properties && Object.keys(block.properties).length > 0
197
+ ? block.properties
198
+ : existing?.properties,
199
+ value: block.value !== undefined ? block.value : existing?.value
200
+ })
201
+ );
202
+ });
203
+ blockMapRef.current = next;
204
+ return next;
205
+ });
206
+ setBlockVersion((prev) => prev + 1);
207
+ },
208
+ []
209
+ );
210
+
211
+ useEffect(() => {
212
+ if (!isDesignerChecked || isDesigner || widgetData.isLoading) return;
213
+
214
+ if (
215
+ widgetData.sectionProperties &&
216
+ Object.keys(widgetData.sectionProperties).length > 0
217
+ ) {
218
+ setSectionProperties(widgetData.sectionProperties);
219
+ }
220
+ if (
221
+ widgetData.sectionStyles &&
222
+ Object.keys(widgetData.sectionStyles).length > 0
223
+ ) {
224
+ setSectionStyles(widgetData.sectionStyles);
225
+ }
226
+
227
+ const blocksToMerge: Partial<FooterNativeWidgetBlock>[] = [];
228
+ widgetData.blocks.forEach((block) => {
229
+ blocksToMerge.push(block as FooterNativeWidgetBlock);
230
+ });
231
+
232
+ if (blocksToMerge.length > 0) {
233
+ mergeBlocks(blocksToMerge);
234
+ }
235
+ }, [
236
+ isDesigner,
237
+ isDesignerChecked,
238
+ widgetData.isLoading,
239
+ widgetData.blocks,
240
+ widgetData.sectionProperties,
241
+ widgetData.sectionStyles,
242
+ mergeBlocks
243
+ ]);
244
+
245
+ useEffect(() => {
246
+ const handleMessage = (event: MessageEvent) => {
247
+ const { type, data } = event.data || {};
248
+
249
+ if (
250
+ (type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
251
+ data?.theme?.placeholders
252
+ ) {
253
+ const placeholder = data.theme.placeholders.find(
254
+ (p: { slug: string }) => p.slug === FOOTER_PLACEHOLDER_ID
255
+ );
256
+
257
+ const section = placeholder?.sections?.find(
258
+ (s: { id: string }) => s.id === FOOTER_VALUE_PROPS_SECTION_ID
259
+ );
260
+
261
+ if (section) {
262
+ mergeBlocks(section.blocks);
263
+
264
+ if (section.properties) {
265
+ setSectionProperties(section.properties);
266
+ }
267
+
268
+ if (section.styles) {
269
+ setSectionStyles(section.styles);
270
+ }
271
+ }
272
+ }
273
+ };
274
+
275
+ window.addEventListener('message', handleMessage);
276
+ return () => window.removeEventListener('message', handleMessage);
277
+ }, [mergeBlocks]);
278
+
279
+ const getBlock = useCallback(
280
+ (blockId: string) => blockMapRef.current.get(blockId),
281
+ []
282
+ );
283
+
284
+ const getSectionProperty = useCallback(
285
+ (key: string) => {
286
+ const value = sectionProperties[key];
287
+ if (typeof value === 'object' && value !== null && 'desktop' in value) {
288
+ return (value as Record<string, unknown>).desktop;
289
+ }
290
+ return value;
291
+ },
292
+ [sectionProperties]
293
+ );
294
+
295
+ const getSectionStyle = useCallback(
296
+ (key: string) => {
297
+ const value = sectionStyles[key];
298
+ if (typeof value === 'object' && value !== null && 'desktop' in value) {
299
+ return (value as Record<string, unknown>).desktop;
300
+ }
301
+ return value;
302
+ },
303
+ [sectionStyles]
304
+ );
305
+
306
+ const contextValue = useMemo(
307
+ () => ({
308
+ ...designerState,
309
+ getBlock,
310
+ getSectionProperty,
311
+ getSectionStyle,
312
+ blockVersion
313
+ }),
314
+ [designerState, getBlock, getSectionProperty, getSectionStyle, blockVersion]
315
+ );
316
+
317
+ return (
318
+ <FooterValuePropsContext.Provider value={contextValue}>
319
+ {children}
320
+ </FooterValuePropsContext.Provider>
321
+ );
322
+ }
323
+
324
+ export function useFooterValuePropsDesigner() {
325
+ return useContext(FooterValuePropsContext);
326
+ }
@@ -0,0 +1,183 @@
1
+ import type {
2
+ FooterNativeWidgetBlock,
3
+ FooterNativeWidgetSection
4
+ } from './native-widget-config';
5
+ import {
6
+ FOOTER_PLACEHOLDER_ID,
7
+ FOOTER_MENU_SECTION_ID,
8
+ FOOTER_MENU_LOGO_BLOCK_ID,
9
+ FOOTER_MENU_WIDGET_SLUG
10
+ } from './native-widget-config';
11
+
12
+ export interface FooterLogoSettings {
13
+ src: string;
14
+ width: number | string;
15
+ height: number | string;
16
+ maxWidth?: string | number;
17
+ maxHeight?: string | number;
18
+ objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down' | string;
19
+ }
20
+
21
+ const DEFAULT_LOGO_SRC = '/logo.svg';
22
+ const DEFAULT_LOGO_WIDTH = 80;
23
+ const DEFAULT_LOGO_HEIGHT = 20;
24
+
25
+ export const defaultFooterLogoSettings: FooterLogoSettings = {
26
+ src: DEFAULT_LOGO_SRC,
27
+ width: DEFAULT_LOGO_WIDTH,
28
+ height: DEFAULT_LOGO_HEIGHT,
29
+ objectFit: 'contain'
30
+ };
31
+
32
+ type BlockLike = Pick<FooterNativeWidgetBlock, 'styles' | 'value'> & {
33
+ styles?: Record<string, unknown>;
34
+ value?: unknown;
35
+ };
36
+
37
+ type ResponsiveValue<T = unknown> =
38
+ | T
39
+ | {
40
+ desktop?: T;
41
+ mobile?: T;
42
+ tablet?: T;
43
+ };
44
+
45
+ const getResponsiveValue = <T = unknown>(value: ResponsiveValue<T>) => {
46
+ if (value && typeof value === 'object' && 'desktop' in value) {
47
+ return value.desktop as T;
48
+ }
49
+ return value as T;
50
+ };
51
+
52
+ const readStyleValue = (
53
+ styles: Record<string, unknown> | undefined,
54
+ keys: string[]
55
+ ) => {
56
+ if (!styles) return undefined;
57
+ for (const key of keys) {
58
+ if (key in styles) return styles[key];
59
+ }
60
+ return undefined;
61
+ };
62
+
63
+ export function parseFooterLogoBlockPayload(
64
+ block?: BlockLike
65
+ ): Partial<FooterLogoSettings> {
66
+ if (!block) return {};
67
+
68
+ const overrides: Partial<FooterLogoSettings> = {};
69
+
70
+ if (block.value !== undefined && block.value !== null) {
71
+ const value = getResponsiveValue(block.value as ResponsiveValue<string>);
72
+ if (typeof value === 'string' && value.trim().length > 0) {
73
+ overrides.src = value;
74
+ }
75
+ }
76
+
77
+ const styles = block.styles as Record<string, unknown> | undefined;
78
+ if (styles) {
79
+ const width = readStyleValue(styles, ['width']);
80
+ if (width !== undefined) {
81
+ overrides.width = getResponsiveValue(
82
+ width as ResponsiveValue<string | number>
83
+ );
84
+ }
85
+
86
+ const height = readStyleValue(styles, ['height']);
87
+ if (height !== undefined) {
88
+ overrides.height = getResponsiveValue(
89
+ height as ResponsiveValue<string | number>
90
+ );
91
+ }
92
+
93
+ const maxWidth = readStyleValue(styles, ['maxWidth', 'max-width']);
94
+ if (maxWidth !== undefined) {
95
+ overrides.maxWidth = getResponsiveValue(
96
+ maxWidth as ResponsiveValue<string | number>
97
+ );
98
+ }
99
+
100
+ const maxHeight = readStyleValue(styles, ['maxHeight', 'max-height']);
101
+ if (maxHeight !== undefined) {
102
+ overrides.maxHeight = getResponsiveValue(
103
+ maxHeight as ResponsiveValue<string | number>
104
+ );
105
+ }
106
+
107
+ const objectFit = readStyleValue(styles, ['objectFit', 'object-fit']);
108
+ if (objectFit !== undefined) {
109
+ overrides.objectFit = getResponsiveValue(
110
+ objectFit as ResponsiveValue<string>
111
+ ) as FooterLogoSettings['objectFit'];
112
+ }
113
+ }
114
+
115
+ return overrides;
116
+ }
117
+
118
+ export function parseFooterLogoWidgetAttribute(
119
+ attrData: unknown
120
+ ): Partial<FooterLogoSettings> {
121
+ if (!attrData) return {};
122
+
123
+ let parsed: unknown = attrData;
124
+ if (typeof attrData === 'string') {
125
+ try {
126
+ parsed = JSON.parse(attrData);
127
+ } catch {
128
+ return {};
129
+ }
130
+ } else if (
131
+ typeof attrData === 'object' &&
132
+ attrData !== null &&
133
+ 'value' in (attrData as Record<string, unknown>)
134
+ ) {
135
+ const value = (attrData as { value?: unknown }).value;
136
+ if (typeof value === 'string') {
137
+ try {
138
+ parsed = JSON.parse(value);
139
+ } catch {
140
+ parsed = value;
141
+ }
142
+ } else {
143
+ parsed = value;
144
+ }
145
+ }
146
+
147
+ if (!parsed || typeof parsed !== 'object') {
148
+ return {};
149
+ }
150
+
151
+ return parseFooterLogoBlockPayload(parsed as BlockLike);
152
+ }
153
+
154
+ export function getFooterLogoInitialSettingsFromSections(
155
+ sections: FooterNativeWidgetSection[],
156
+ fallbackSrc?: string
157
+ ): FooterLogoSettings {
158
+ const footerMenuSection = sections.find(
159
+ (section) => section.sectionId === FOOTER_MENU_SECTION_ID
160
+ );
161
+ const logoBlock = footerMenuSection?.blocks.find(
162
+ (block) => block.id === FOOTER_MENU_LOGO_BLOCK_ID
163
+ );
164
+
165
+ const overrides = parseFooterLogoBlockPayload(logoBlock);
166
+ const base: FooterLogoSettings = {
167
+ ...defaultFooterLogoSettings,
168
+ src: fallbackSrc || defaultFooterLogoSettings.src
169
+ };
170
+
171
+ return {
172
+ ...base,
173
+ ...overrides,
174
+ src: overrides.src ?? base.src
175
+ };
176
+ }
177
+
178
+ export {
179
+ FOOTER_PLACEHOLDER_ID as FOOTER_LOGO_PLACEHOLDER_ID,
180
+ FOOTER_MENU_SECTION_ID as FOOTER_LOGO_SECTION_ID,
181
+ FOOTER_MENU_LOGO_BLOCK_ID as FOOTER_LOGO_BLOCK_ID,
182
+ FOOTER_MENU_WIDGET_SLUG as FOOTER_LOGO_WIDGET_SLUG
183
+ };