@akinon/pz-theme 2.0.35-rc.0 → 2.0.35

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 (39) hide show
  1. package/CHANGELOG.md +12 -28
  2. package/package.json +4 -3
  3. package/src/__tests__/theme-contract.test.ts +130 -0
  4. package/src/blocks/__tests__/text-block.test.tsx +8 -1
  5. package/src/blocks/__tests__/video-block.test.tsx +14 -7
  6. package/src/blocks/counter-block.tsx +44 -56
  7. package/src/blocks/group-block.tsx +23 -22
  8. package/src/blocks/hotspot-block.tsx +171 -80
  9. package/src/blocks/input-block.tsx +15 -17
  10. package/src/blocks/link-block.tsx +18 -21
  11. package/src/blocks/slider-block.tsx +15 -12
  12. package/src/components/with-designer-features.tsx +2 -0
  13. package/src/editor-bridge.ts +160 -0
  14. package/src/hooks/use-designer-features.tsx +30 -37
  15. package/src/placeholder-registry.ts +5 -7
  16. package/src/sections/__tests__/countdown-campaign-banner-section.test.tsx +8 -4
  17. package/src/sections/__tests__/coupon-banner-section.test.tsx +4 -2
  18. package/src/sections/before-after-section.tsx +189 -134
  19. package/src/sections/contact-form-section.tsx +185 -60
  20. package/src/sections/countdown-campaign-banner-section.tsx +73 -27
  21. package/src/sections/coupon-banner-section.tsx +152 -48
  22. package/src/sections/featured-product-spotlight-section.tsx +67 -66
  23. package/src/sections/find-in-store-section.tsx +37 -25
  24. package/src/sections/hover-showcase-section.tsx +221 -89
  25. package/src/sections/image-hotspot-section.tsx +208 -94
  26. package/src/sections/installment-options-section.tsx +268 -259
  27. package/src/sections/notification-banner-section.tsx +111 -25
  28. package/src/sections/order-tracking-lookup-section.tsx +272 -247
  29. package/src/sections/posts-slider-section.tsx +17 -16
  30. package/src/sections/pre-order-launch-banner-section.tsx +31 -50
  31. package/src/sections/section-wrapper.tsx +9 -10
  32. package/src/sections/shipping-threshold-progress-section.tsx +72 -29
  33. package/src/sections/stats-counter-section.tsx +68 -30
  34. package/src/sections/tabs-section.tsx +124 -147
  35. package/src/theme-placeholder-client.tsx +11 -14
  36. package/src/theme-placeholder-wrapper.tsx +42 -51
  37. package/src/theme-section.tsx +27 -34
  38. package/src/utils/__tests__/transform-styles.test.ts +97 -0
  39. package/src/utils/index.ts +9 -0
@@ -8,6 +8,7 @@ import { twMerge } from 'tailwind-merge';
8
8
  import clsx from 'clsx';
9
9
  import ThemeBlock from '../theme-block';
10
10
 
11
+ import { postToEditor } from '../editor-bridge';
11
12
  interface ImageHotspotSectionProps {
12
13
  section: Section;
13
14
  currentBreakpoint?: string;
@@ -24,17 +25,30 @@ export default function ImageHotspotSection({
24
25
  selectedBlockId = null
25
26
  }: ImageHotspotSectionProps) {
26
27
  const themeSettings = useThemeSettingsContext();
27
-
28
+
28
29
  const styles = section.styles || {};
29
30
  const properties = section.properties || {};
30
31
 
31
- const imageBlock = section.blocks?.find(b => b.type === 'image');
32
+ const imageBlock = section.blocks?.find((b) => b.type === 'image');
32
33
 
33
- const hotspotBlocks = section.blocks?.filter(b => b.type === 'hotspot') || [];
34
+ const hotspotBlocks =
35
+ section.blocks?.filter((b) => b.type === 'hotspot') || [];
34
36
 
35
- const height = getResponsiveValue(styles.height, currentBreakpoint, '600px') as string | number;
36
- const width = getResponsiveValue(styles.width, currentBreakpoint, '100%') as string;
37
- const maxWidth = getResponsiveValue(styles['max-width'], currentBreakpoint, 'normal') as string;
37
+ const height = getResponsiveValue(
38
+ styles.height,
39
+ currentBreakpoint,
40
+ '600px'
41
+ ) as string | number;
42
+ const width = getResponsiveValue(
43
+ styles.width,
44
+ currentBreakpoint,
45
+ '100%'
46
+ ) as string;
47
+ const maxWidth = getResponsiveValue(
48
+ styles['max-width'],
49
+ currentBreakpoint,
50
+ 'normal'
51
+ ) as string;
38
52
 
39
53
  const maxWidthClass =
40
54
  maxWidth === 'narrow'
@@ -44,99 +58,199 @@ export default function ImageHotspotSection({
44
58
  : maxWidth === 'full'
45
59
  ? 'w-full'
46
60
  : '';
47
-
61
+
48
62
  const hasMaxWidth = maxWidth !== 'none' && maxWidth !== 'full';
49
63
 
50
64
  return (
51
- <div
52
- className={twMerge(
53
- clsx(
54
- 'relative mx-auto overflow-hidden group/hotspot-section',
55
- hasMaxWidth && 'mx-auto',
56
- maxWidthClass
57
- )
58
- )}
59
- style={{
60
- height: typeof height === 'number' ? `${height}px` : height,
61
- width: width === 'fill' ? '100%' : width,
62
- maxWidth: maxWidth === 'none' ? 'none' : undefined,
63
- position: 'relative'
64
- }}
65
+ <div
66
+ className={twMerge(
67
+ clsx(
68
+ 'relative mx-auto overflow-hidden group/hotspot-section',
69
+ hasMaxWidth && 'mx-auto',
70
+ maxWidthClass
71
+ )
72
+ )}
73
+ style={{
74
+ height: typeof height === 'number' ? `${height}px` : height,
75
+ width: width === 'fill' ? '100%' : width,
76
+ maxWidth: maxWidth === 'none' ? 'none' : undefined,
77
+ position: 'relative'
78
+ }}
65
79
  >
80
+ <div className="absolute inset-0 w-full h-full z-0">
81
+ {imageBlock ? (
82
+ <div className="w-full h-full">
83
+ <ThemeBlock
84
+ key={imageBlock.id}
85
+ block={imageBlock}
86
+ placeholderId={placeholderId}
87
+ sectionId={section.id}
88
+ isDesigner={isDesigner}
89
+ isSelected={selectedBlockId === imageBlock.id}
90
+ selectedBlockId={selectedBlockId}
91
+ currentBreakpoint={currentBreakpoint}
92
+ onMoveUp={() => {
93
+ if (window.parent)
94
+ postToEditor({
95
+ type: 'MOVE_BLOCK_UP',
96
+ data: {
97
+ placeholderId,
98
+ sectionId: section.id,
99
+ blockId: imageBlock.id
100
+ }
101
+ });
102
+ }}
103
+ onMoveDown={() => {
104
+ if (window.parent)
105
+ postToEditor({
106
+ type: 'MOVE_BLOCK_DOWN',
107
+ data: {
108
+ placeholderId,
109
+ sectionId: section.id,
110
+ blockId: imageBlock.id
111
+ }
112
+ });
113
+ }}
114
+ onDuplicate={() => {
115
+ if (window.parent)
116
+ postToEditor({
117
+ type: 'DUPLICATE_BLOCK',
118
+ data: {
119
+ placeholderId,
120
+ sectionId: section.id,
121
+ blockId: imageBlock.id
122
+ }
123
+ });
124
+ }}
125
+ onToggleVisibility={() => {
126
+ if (window.parent)
127
+ postToEditor({
128
+ type: 'TOGGLE_BLOCK_VISIBILITY',
129
+ data: {
130
+ placeholderId,
131
+ sectionId: section.id,
132
+ blockId: imageBlock.id
133
+ }
134
+ });
135
+ }}
136
+ onDelete={() => {
137
+ if (window.parent)
138
+ postToEditor({
139
+ type: 'DELETE_BLOCK',
140
+ data: {
141
+ placeholderId,
142
+ sectionId: section.id,
143
+ blockId: imageBlock.id
144
+ }
145
+ });
146
+ }}
147
+ onRename={(newLabel) => {
148
+ if (window.parent)
149
+ postToEditor({
150
+ type: 'RENAME_BLOCK',
151
+ data: {
152
+ placeholderId,
153
+ sectionId: section.id,
154
+ blockId: imageBlock.id,
155
+ label: newLabel
156
+ }
157
+ });
158
+ }}
159
+ />
160
+ </div>
161
+ ) : (
162
+ <div className="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 select-none">
163
+ No Image Selected
164
+ </div>
165
+ )}
166
+ </div>
66
167
 
67
- <div className="absolute inset-0 w-full h-full z-0">
68
- {imageBlock ? (
69
- <div className="w-full h-full">
70
- <ThemeBlock
71
- key={imageBlock.id}
72
- block={imageBlock}
73
- placeholderId={placeholderId}
74
- sectionId={section.id}
75
- isDesigner={isDesigner}
76
- isSelected={selectedBlockId === imageBlock.id}
77
- selectedBlockId={selectedBlockId}
78
- currentBreakpoint={currentBreakpoint}
79
-
80
- onMoveUp={() => {
81
- if (window.parent) window.parent.postMessage({ type: 'MOVE_BLOCK_UP', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id } }, '*');
82
- }}
83
- onMoveDown={() => {
84
- if (window.parent) window.parent.postMessage({ type: 'MOVE_BLOCK_DOWN', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id } }, '*');
85
- }}
86
- onDuplicate={() => {
87
- if (window.parent) window.parent.postMessage({ type: 'DUPLICATE_BLOCK', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id } }, '*');
88
- }}
89
- onToggleVisibility={() => {
90
- if (window.parent) window.parent.postMessage({ type: 'TOGGLE_BLOCK_VISIBILITY', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id } }, '*');
91
- }}
92
- onDelete={() => {
93
- if (window.parent) window.parent.postMessage({ type: 'DELETE_BLOCK', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id } }, '*');
94
- }}
95
- onRename={(newLabel) => {
96
- if (window.parent) window.parent.postMessage({ type: 'RENAME_BLOCK', data: { placeholderId, sectionId: section.id, blockId: imageBlock.id, label: newLabel } }, '*');
97
- }}
98
- />
99
- </div>
100
- ) : (
101
- <div className="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 select-none">
102
- No Image Selected
103
- </div>
104
- )}
105
- </div>
106
-
107
- <div className="absolute inset-0 w-full h-full z-10 pointer-events-none">
108
- {hotspotBlocks.map(block => (
109
- <div key={block.id} className="absolute pointer-events-none" style={{ top: 0, left: 0, width: '100%', height: '100%' }}>
110
- <ThemeBlock
111
- block={block}
112
- placeholderId={placeholderId}
113
- sectionId={section.id}
114
- isDesigner={isDesigner}
115
- isSelected={selectedBlockId === block.id}
116
- selectedBlockId={selectedBlockId}
117
- currentBreakpoint={currentBreakpoint}
118
- onMoveUp={() => {
119
- if (window.parent) window.parent.postMessage({ type: 'MOVE_BLOCK_UP', data: { placeholderId, sectionId: section.id, blockId: block.id } }, '*');
120
- }}
121
- onMoveDown={() => {
122
- if (window.parent) window.parent.postMessage({ type: 'MOVE_BLOCK_DOWN', data: { placeholderId, sectionId: section.id, blockId: block.id } }, '*');
123
- }}
124
- onDuplicate={() => {
125
- if (window.parent) window.parent.postMessage({ type: 'DUPLICATE_BLOCK', data: { placeholderId, sectionId: section.id, blockId: block.id } }, '*');
126
- }}
127
- onToggleVisibility={() => {
128
- if (window.parent) window.parent.postMessage({ type: 'TOGGLE_BLOCK_VISIBILITY', data: { placeholderId, sectionId: section.id, blockId: block.id } }, '*');
129
- }}
130
- onDelete={() => {
131
- if (window.parent) window.parent.postMessage({ type: 'DELETE_BLOCK', data: { placeholderId, sectionId: section.id, blockId: block.id } }, '*');
132
- }}
133
- onRename={(newLabel) => {
134
- if (window.parent) window.parent.postMessage({ type: 'RENAME_BLOCK', data: { placeholderId, sectionId: section.id, blockId: block.id, label: newLabel } }, '*');
135
- }}
136
- />
137
- </div>
138
- ))}
139
- </div>
168
+ <div className="absolute inset-0 w-full h-full z-10 pointer-events-none">
169
+ {hotspotBlocks.map((block) => (
170
+ <div
171
+ key={block.id}
172
+ className="absolute pointer-events-none"
173
+ style={{ top: 0, left: 0, width: '100%', height: '100%' }}
174
+ >
175
+ <ThemeBlock
176
+ block={block}
177
+ placeholderId={placeholderId}
178
+ sectionId={section.id}
179
+ isDesigner={isDesigner}
180
+ isSelected={selectedBlockId === block.id}
181
+ selectedBlockId={selectedBlockId}
182
+ currentBreakpoint={currentBreakpoint}
183
+ onMoveUp={() => {
184
+ if (window.parent)
185
+ postToEditor({
186
+ type: 'MOVE_BLOCK_UP',
187
+ data: {
188
+ placeholderId,
189
+ sectionId: section.id,
190
+ blockId: block.id
191
+ }
192
+ });
193
+ }}
194
+ onMoveDown={() => {
195
+ if (window.parent)
196
+ postToEditor({
197
+ type: 'MOVE_BLOCK_DOWN',
198
+ data: {
199
+ placeholderId,
200
+ sectionId: section.id,
201
+ blockId: block.id
202
+ }
203
+ });
204
+ }}
205
+ onDuplicate={() => {
206
+ if (window.parent)
207
+ postToEditor({
208
+ type: 'DUPLICATE_BLOCK',
209
+ data: {
210
+ placeholderId,
211
+ sectionId: section.id,
212
+ blockId: block.id
213
+ }
214
+ });
215
+ }}
216
+ onToggleVisibility={() => {
217
+ if (window.parent)
218
+ postToEditor({
219
+ type: 'TOGGLE_BLOCK_VISIBILITY',
220
+ data: {
221
+ placeholderId,
222
+ sectionId: section.id,
223
+ blockId: block.id
224
+ }
225
+ });
226
+ }}
227
+ onDelete={() => {
228
+ if (window.parent)
229
+ postToEditor({
230
+ type: 'DELETE_BLOCK',
231
+ data: {
232
+ placeholderId,
233
+ sectionId: section.id,
234
+ blockId: block.id
235
+ }
236
+ });
237
+ }}
238
+ onRename={(newLabel) => {
239
+ if (window.parent)
240
+ postToEditor({
241
+ type: 'RENAME_BLOCK',
242
+ data: {
243
+ placeholderId,
244
+ sectionId: section.id,
245
+ blockId: block.id,
246
+ label: newLabel
247
+ }
248
+ });
249
+ }}
250
+ />
251
+ </div>
252
+ ))}
253
+ </div>
140
254
  </div>
141
255
  );
142
256
  }