@akinon/next 2.0.0-beta.20 → 2.0.0-beta.22

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 (64) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/api/auth.ts +292 -60
  3. package/bin/pz-install-plugins.js +1 -1
  4. package/package.json +3 -3
  5. package/types/index.ts +19 -6
  6. package/types/next-auth.d.ts +1 -1
  7. package/with-pz-config.js +8 -1
  8. package/components/theme-editor/blocks/accordion-block.tsx +0 -136
  9. package/components/theme-editor/blocks/block-renderer-registry.tsx +0 -77
  10. package/components/theme-editor/blocks/button-block.tsx +0 -593
  11. package/components/theme-editor/blocks/counter-block.tsx +0 -348
  12. package/components/theme-editor/blocks/divider-block.tsx +0 -20
  13. package/components/theme-editor/blocks/embed-block.tsx +0 -208
  14. package/components/theme-editor/blocks/group-block.tsx +0 -116
  15. package/components/theme-editor/blocks/hotspot-block.tsx +0 -147
  16. package/components/theme-editor/blocks/icon-block.tsx +0 -230
  17. package/components/theme-editor/blocks/image-block.tsx +0 -137
  18. package/components/theme-editor/blocks/image-gallery-block.tsx +0 -269
  19. package/components/theme-editor/blocks/input-block.tsx +0 -123
  20. package/components/theme-editor/blocks/link-block.tsx +0 -216
  21. package/components/theme-editor/blocks/lottie-block.tsx +0 -325
  22. package/components/theme-editor/blocks/map-block.tsx +0 -89
  23. package/components/theme-editor/blocks/slider-block.tsx +0 -595
  24. package/components/theme-editor/blocks/tab-block.tsx +0 -10
  25. package/components/theme-editor/blocks/text-block.tsx +0 -52
  26. package/components/theme-editor/blocks/video-block.tsx +0 -122
  27. package/components/theme-editor/components/action-toolbar.tsx +0 -305
  28. package/components/theme-editor/components/designer-overlay.tsx +0 -74
  29. package/components/theme-editor/components/with-designer-features.tsx +0 -142
  30. package/components/theme-editor/dynamic-font-loader.tsx +0 -79
  31. package/components/theme-editor/hooks/use-designer-features.tsx +0 -100
  32. package/components/theme-editor/hooks/use-external-designer.tsx +0 -95
  33. package/components/theme-editor/hooks/use-native-widget-data.ts +0 -188
  34. package/components/theme-editor/hooks/use-visibility-context.ts +0 -27
  35. package/components/theme-editor/placeholder-registry.ts +0 -31
  36. package/components/theme-editor/sections/before-after-section.tsx +0 -245
  37. package/components/theme-editor/sections/contact-form-section.tsx +0 -563
  38. package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +0 -433
  39. package/components/theme-editor/sections/coupon-banner-section.tsx +0 -710
  40. package/components/theme-editor/sections/divider-section.tsx +0 -62
  41. package/components/theme-editor/sections/featured-product-spotlight-section.tsx +0 -507
  42. package/components/theme-editor/sections/find-in-store-section.tsx +0 -1995
  43. package/components/theme-editor/sections/hover-showcase-section.tsx +0 -326
  44. package/components/theme-editor/sections/image-hotspot-section.tsx +0 -142
  45. package/components/theme-editor/sections/installment-options-section.tsx +0 -1065
  46. package/components/theme-editor/sections/notification-banner-section.tsx +0 -173
  47. package/components/theme-editor/sections/order-tracking-lookup-section.tsx +0 -1379
  48. package/components/theme-editor/sections/posts-slider-section.tsx +0 -472
  49. package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +0 -663
  50. package/components/theme-editor/sections/section-renderer-registry.tsx +0 -89
  51. package/components/theme-editor/sections/section-wrapper.tsx +0 -135
  52. package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +0 -586
  53. package/components/theme-editor/sections/stats-counter-section.tsx +0 -486
  54. package/components/theme-editor/sections/tabs-section.tsx +0 -578
  55. package/components/theme-editor/theme-block.tsx +0 -102
  56. package/components/theme-editor/theme-placeholder-client.tsx +0 -218
  57. package/components/theme-editor/theme-placeholder-wrapper.tsx +0 -732
  58. package/components/theme-editor/theme-placeholder.tsx +0 -288
  59. package/components/theme-editor/theme-section.tsx +0 -1224
  60. package/components/theme-editor/theme-settings-context.tsx +0 -13
  61. package/components/theme-editor/utils/index.ts +0 -792
  62. package/components/theme-editor/utils/iterator-utils.ts +0 -234
  63. package/components/theme-editor/utils/publish-window.ts +0 -86
  64. package/components/theme-editor/utils/visibility-rules.ts +0 -188
@@ -1,122 +0,0 @@
1
- import React from 'react';
2
- import { BlockRendererProps } from './block-renderer-registry';
3
-
4
- const getVideoEmbedUrl = (
5
- url: string,
6
- properties?: {
7
- autoplay?: boolean;
8
- loop?: boolean;
9
- muted?: boolean;
10
- controls?: boolean;
11
- }
12
- ) => {
13
- if (!url) return null;
14
-
15
- if (url.includes('youtube.com') || url.includes('youtu.be')) {
16
- let videoId = '';
17
- if (url.includes('youtu.be/')) {
18
- videoId = url.split('youtu.be/')[1].split('?')[0];
19
- } else if (url.includes('watch?v=')) {
20
- videoId = url.split('watch?v=')[1].split('&')[0];
21
- } else if (url.includes('embed/')) {
22
- videoId = url.split('embed/')[1].split('?')[0];
23
- }
24
-
25
- if (!videoId) return null;
26
-
27
- const params = new URLSearchParams();
28
- if (properties?.autoplay) params.append('autoplay', '1');
29
- if (properties?.loop) {
30
- params.append('loop', '1');
31
- params.append('playlist', videoId);
32
- }
33
- if (properties?.muted) params.append('mute', '1');
34
- if (properties?.controls === false) params.append('controls', '0');
35
-
36
- const queryString = params.toString();
37
- return `https://www.youtube.com/embed/${videoId}${
38
- queryString ? '?' + queryString : ''
39
- }`;
40
- }
41
-
42
- if (url.includes('vimeo.com')) {
43
- const videoId = url.split('vimeo.com/')[1]?.split('?')[0];
44
-
45
- if (!videoId) return null;
46
-
47
- const params = new URLSearchParams();
48
- if (properties?.autoplay) params.append('autoplay', '1');
49
- if (properties?.loop) params.append('loop', '1');
50
- if (properties?.muted) params.append('muted', '1');
51
- if (properties?.controls === false) params.append('controls', '0');
52
-
53
- const queryString = params.toString();
54
- return `https://player.vimeo.com/video/${videoId}${
55
- queryString ? '?' + queryString : ''
56
- }`;
57
- }
58
-
59
- if (url.match(/\.(mp4|webm|ogg)$/i)) {
60
- return url;
61
- }
62
-
63
- return null;
64
- };
65
-
66
- const VideoBlock = ({ block }: BlockRendererProps) => {
67
- const videoUrl = block.value;
68
- const properties = block.properties || {};
69
-
70
- const embedUrl = getVideoEmbedUrl(videoUrl, properties);
71
-
72
- let content;
73
-
74
- if (!videoUrl || !embedUrl) {
75
- content = (
76
- <div
77
- style={{
78
- minHeight: '300px',
79
- display: 'flex',
80
- alignItems: 'center',
81
- justifyContent: 'center',
82
- backgroundColor: '#f0f0f0',
83
- color: '#666',
84
- fontSize: '14px'
85
- }}
86
- >
87
- {!videoUrl ? 'No video URL provided' : 'Invalid video URL'}
88
- </div>
89
- );
90
- } else if (embedUrl.includes('youtube.com') || embedUrl.includes('vimeo.com')) {
91
- content = (
92
- <iframe
93
- src={embedUrl}
94
- style={{
95
- width: '100%',
96
- height: '400px',
97
- border: 'none'
98
- }}
99
- allow="autoplay; fullscreen; picture-in-picture"
100
- allowFullScreen
101
- />
102
- );
103
- } else {
104
- content = (
105
- <video
106
- src={embedUrl}
107
- controls={properties.controls !== false}
108
- autoPlay={properties.autoplay}
109
- loop={properties.loop}
110
- muted={properties.muted}
111
- style={{
112
- width: '100%',
113
- height: 'auto'
114
- }}
115
- />
116
- );
117
- }
118
-
119
- return content;
120
- };
121
-
122
- export default VideoBlock;
@@ -1,305 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useRef, useEffect } from 'react';
4
-
5
- interface ActionToolbarProps {
6
- label: string;
7
- isLocked?: boolean;
8
- zIndex?: number;
9
- onMoveUp?: () => void;
10
- onMoveDown?: () => void;
11
- onDuplicate?: () => void;
12
- onToggleVisibility?: () => void;
13
- onDelete?: () => void;
14
- onRename?: (newLabel: string) => void;
15
- }
16
-
17
- const LockIcon = () => (
18
- <svg
19
- width="14"
20
- height="14"
21
- viewBox="0 0 24 24"
22
- fill="none"
23
- xmlns="http://www.w3.org/2000/svg"
24
- aria-hidden="true"
25
- >
26
- <path
27
- d="M7 10V7.5C7 4.46243 9.46243 2 12.5 2C15.5376 2 18 4.46243 18 7.5V10"
28
- stroke="currentColor"
29
- strokeWidth="1.7"
30
- strokeLinecap="round"
31
- />
32
- <path
33
- d="M7 10H17C18.1046 10 19 10.8954 19 12V19C19 20.1046 18.1046 21 17 21H7C5.89543 21 5 20.1046 5 19V12C5 10.8954 5.89543 10 7 10Z"
34
- stroke="currentColor"
35
- strokeWidth="1.7"
36
- />
37
- <path
38
- d="M12 14V17"
39
- stroke="currentColor"
40
- strokeWidth="1.7"
41
- strokeLinecap="round"
42
- />
43
- </svg>
44
- );
45
-
46
- export default function ActionToolbar({
47
- label,
48
- isLocked = false,
49
- zIndex = 30,
50
- onMoveUp,
51
- onMoveDown,
52
- onDuplicate,
53
- onToggleVisibility,
54
- onDelete,
55
- onRename
56
- }: ActionToolbarProps) {
57
- const [isRenaming, setIsRenaming] = useState(false);
58
- const [newLabel, setNewLabel] = useState(label);
59
- const inputRef = useRef<HTMLInputElement>(null);
60
-
61
- useEffect(() => {
62
- if (isRenaming && inputRef.current) {
63
- inputRef.current.focus();
64
- inputRef.current.select();
65
- }
66
- }, [isRenaming]);
67
-
68
- const handleStartRename = (e: React.MouseEvent) => {
69
- e.stopPropagation();
70
- setNewLabel(label);
71
- setIsRenaming(true);
72
- };
73
-
74
- const handleSaveRename = () => {
75
- if (newLabel.trim() && onRename) {
76
- onRename(newLabel.trim());
77
- }
78
- setIsRenaming(false);
79
- };
80
-
81
- const handleCancelRename = () => {
82
- setNewLabel(label);
83
- setIsRenaming(false);
84
- };
85
-
86
- const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
87
- e.stopPropagation();
88
- if (e.key === 'Enter') {
89
- handleSaveRename();
90
- } else if (e.key === 'Escape') {
91
- handleCancelRename();
92
- }
93
- };
94
- return (
95
- <div
96
- className="absolute -top-0.5 left-0 -translate-y-full flex items-center pointer-events-auto gap-1 min-w-max"
97
- style={{ zIndex }}
98
- >
99
- <div className="flex items-center gap-1">
100
- {isRenaming ? (
101
- <input
102
- ref={inputRef}
103
- type="text"
104
- value={newLabel}
105
- onChange={(e) => setNewLabel(e.target.value)}
106
- onBlur={handleSaveRename}
107
- onKeyDown={handleKeyDown}
108
- onClick={(e) => e.stopPropagation()}
109
- className="bg-[#4482ff] text-white text-sm font-semibold p-2.5 rounded outline-none border-2 border-white"
110
- />
111
- ) : (
112
- <div
113
- className="bg-[#4482ff] text-white text-sm font-semibold p-2.5 rounded cursor-text"
114
- onClick={handleStartRename}
115
- title="Click to rename"
116
- >
117
- {label}
118
- </div>
119
- )}
120
- {isLocked && (
121
- <div
122
- className="flex h-10 w-10 items-center justify-center rounded border border-amber-300/25 bg-amber-400/15 text-amber-100"
123
- title="Locked"
124
- aria-label="Locked"
125
- >
126
- <LockIcon />
127
- </div>
128
- )}
129
- </div>
130
- <div className="h-10 bg-black rounded text-white grid grid-cols-5 items-center gap-3 px-5">
131
- <button
132
- className={
133
- isLocked
134
- ? 'cursor-not-allowed opacity-40'
135
- : 'hover:opacity-70 transition-opacity'
136
- }
137
- title="Move Up"
138
- disabled={isLocked}
139
- onClick={(e) => {
140
- e.stopPropagation();
141
- onMoveUp?.();
142
- }}
143
- >
144
- <svg
145
- id="a"
146
- xmlns="http://www.w3.org/2000/svg"
147
- viewBox="0 0 9.23 13"
148
- height={14}
149
- width={14}
150
- >
151
- <path
152
- d="M3.73,3.02l-2.24,2.24c-.36.33-.92.31-1.25-.04-.32-.34-.32-.87,0-1.21L3.99.26c.35-.34.91-.34,1.25,0l3.75,3.75c.33.36.3.92-.06,1.25-.34.31-.86.31-1.19,0l-2.24-2.24v9.13c-.02.49-.43.87-.92.85-.46-.02-.84-.39-.85-.85V3.02Z"
153
- fill="#fff"
154
- fillRule="evenodd"
155
- />
156
- </svg>
157
- </button>
158
- <button
159
- className={
160
- isLocked
161
- ? 'cursor-not-allowed opacity-40'
162
- : 'hover:opacity-70 transition-opacity'
163
- }
164
- title="Move Down"
165
- disabled={isLocked}
166
- onClick={(e) => {
167
- e.stopPropagation();
168
- onMoveDown?.();
169
- }}
170
- >
171
- <svg
172
- id="a"
173
- xmlns="http://www.w3.org/2000/svg"
174
- viewBox="0 0 9.23 13"
175
- width={14}
176
- height={14}
177
- >
178
- <path
179
- d="M5.49,9.98l2.24-2.24c.36-.33.92-.31,1.25.04.32.34.32.87,0,1.21l-3.75,3.75c-.35.34-.91.34-1.25,0L.23,8.99c-.33-.36-.3-.92.06-1.25.34-.31.86-.31,1.19,0l2.24,2.24V.85C3.74.37,4.15-.02,4.64,0c.46.02.84.39.85.85v9.13Z"
180
- fill="#fff"
181
- fillRule="evenodd"
182
- />
183
- </svg>
184
- </button>
185
- <button
186
- className={
187
- isLocked
188
- ? 'cursor-not-allowed opacity-40'
189
- : 'hover:opacity-70 transition-opacity'
190
- }
191
- title="Duplicate"
192
- disabled={isLocked}
193
- onClick={(e) => {
194
- e.stopPropagation();
195
- onDuplicate?.();
196
- }}
197
- >
198
- <svg
199
- id="a"
200
- xmlns="http://www.w3.org/2000/svg"
201
- viewBox="0 0 13 13"
202
- width={14}
203
- height={14}
204
- >
205
- <path
206
- d="M8.5,3H1.5c-.8,0-1.5.7-1.5,1.5v7c0,.8.7,1.5,1.5,1.5h7c.8,0,1.5-.7,1.5-1.5v-7c0-.8-.7-1.5-1.5-1.5ZM9,11.5c0,.3-.2.5-.5.5H1.5c-.3,0-.5-.2-.5-.5v-7c0-.3.2-.5.5-.5h7c.3,0,.5.2.5.5v7Z"
207
- fill="#fff"
208
- />
209
- <path
210
- d="M3.5,2.5c.3,0,.5-.2.5-.5v-.5c0-.3.2-.5.5-.5h.5c.3,0,.5-.2.5-.5s-.2-.5-.5-.5h-.5c-.8,0-1.5.7-1.5,1.5v.5c0,.3.2.5.5.5Z"
211
- fill="#fff"
212
- />
213
- <path
214
- d="M12.5,7.5c-.3,0-.5.2-.5.5v.5c0,.3-.2.5-.5.5h-.5c-.3,0-.5.2-.5.5s.2.5.5.5h.5c.8,0,1.5-.7,1.5-1.5v-.5c0-.3-.2-.5-.5-.5Z"
215
- fill="#fff"
216
- />
217
- <path
218
- d="M11.5,0h-.5c-.3,0-.5.2-.5.5s.2.5.5.5h.5c.3,0,.5.2.5.5v.5c0,.3.2.5.5.5s.5-.2.5-.5v-.5c0-.8-.7-1.5-1.5-1.5Z"
219
- fill="#fff"
220
- />
221
- <path
222
- d="M12.5,3.5c-.3,0-.5.2-.5.5v2c0,.3.2.5.5.5s.5-.2.5-.5v-2c0-.3-.2-.5-.5-.5Z"
223
- fill="#fff"
224
- />
225
- <path
226
- d="M7,1h2c.3,0,.5-.2.5-.5s-.2-.5-.5-.5h-2c-.3,0-.5.2-.5.5s.2.5.5.5Z"
227
- fill="#fff"
228
- />
229
- <path
230
- d="M6.5,7.5h-1v-1c0-.3-.2-.5-.5-.5s-.5.2-.5.5v1h-1c-.3,0-.5.2-.5.5s.2.5.5.5h1v1c0,.3.2.5.5.5s.5-.2.5-.5v-1h1c.3,0,.5-.2.5-.5s-.2-.5-.5-.5Z"
231
- fill="#fff"
232
- />
233
- </svg>
234
- </button>
235
- <button
236
- className="hover:opacity-70 transition-opacity"
237
- title="Show/Hide"
238
- onClick={(e) => {
239
- e.stopPropagation();
240
- onToggleVisibility?.();
241
- }}
242
- >
243
- <svg
244
- id="a"
245
- xmlns="http://www.w3.org/2000/svg"
246
- viewBox="0 0 512 327.2"
247
- width={14}
248
- height={14}
249
- >
250
- <g id="b">
251
- <g id="c">
252
- <g id="d">
253
- <g id="e">
254
- <path
255
- id="f"
256
- d="M259.2,327.2C90.7,327.2,6.5,178.2,0,171.7v-16.2C3.2,149,87.5,0,256,0s252.8,149.1,256,155.5v16.2c-3.2,6.5-87.5,155.5-256,155.5h3.2ZM35.6,165.2c19.4,29.2,94,129.6,223.6,129.6s204.2-100.5,223.6-129.6c-19.4-29.2-94-129.6-223.6-129.6S55.1,136.1,35.6,165.2ZM259.2,262.4c-55.1,0-97.2-42.1-97.2-97.2s42.1-97.2,97.2-97.2,97.2,42.1,97.2,97.2-42.1,97.2-97.2,97.2ZM259.2,100.4c-35.6,0-64.8,29.2-64.8,64.8s29.2,64.8,64.8,64.8,64.8-29.2,64.8-64.8-29.2-64.8-64.8-64.8h0Z"
257
- fill="#fff"
258
- />
259
- </g>
260
- </g>
261
- </g>
262
- </g>
263
- </svg>
264
- </button>
265
- <button
266
- className={
267
- isLocked
268
- ? 'cursor-not-allowed opacity-40'
269
- : 'hover:opacity-70 transition-opacity'
270
- }
271
- title="Delete"
272
- disabled={isLocked}
273
- onClick={(e) => {
274
- e.stopPropagation();
275
- onDelete?.();
276
- }}
277
- >
278
- <svg
279
- id="a"
280
- xmlns="http://www.w3.org/2000/svg"
281
- viewBox="0 0 11.87 12.98"
282
- width={14}
283
- height={14}
284
- >
285
- <g id="b">
286
- <g id="c">
287
- <g id="d">
288
- <g id="e">
289
- <g id="f">
290
- <path
291
- id="g"
292
- d="M11.21,2.43h-2.36v-.66c0-.96-.81-1.77-1.77-1.77h-2.36c-.96,0-1.77.81-1.77,1.77v.66H.59c-.29,0-.59.22-.59.59s.29.52.59.52h.66v7.74c0,.96.81,1.7,1.77,1.7h5.9c.96,0,1.7-.81,1.7-1.7V3.54h.66c.29,0,.59-.22.59-.59s-.37-.52-.66-.52h0ZM4.13,1.77c0-.37.29-.66.66-.66h2.36c.37,0,.66.29.66.66v.66h-3.69v-.66h0ZM9.44,11.21c0,.37-.29.59-.59.59H2.95c-.37,0-.66-.29-.66-.59V3.54h7.15v7.67ZM4.72,5.38c-.29,0-.59.22-.59.59v3.47c0,.29.22.59.59.59s.59-.22.59-.59v-3.54c0-.29-.29-.52-.59-.52h0ZM7.08,5.38c-.29,0-.59.22-.59.52v3.54c0,.29.22.59.59.59s.59-.22.59-.59v-3.54c-.07-.29-.29-.52-.59-.52Z"
293
- fill="#fff"
294
- />
295
- </g>
296
- </g>
297
- </g>
298
- </g>
299
- </g>
300
- </svg>
301
- </button>
302
- </div>
303
- </div>
304
- );
305
- }
@@ -1,74 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
- import { twMerge } from 'tailwind-merge';
5
- import clsx from 'clsx';
6
- import ActionToolbar from './action-toolbar';
7
-
8
- interface DesignerOverlayProps {
9
- label: string;
10
- isSelected: boolean;
11
- isLocked?: boolean;
12
- isHovered?: boolean;
13
- onMoveUp?: () => void;
14
- onMoveDown?: () => void;
15
- onDuplicate?: () => void;
16
- onToggleVisibility?: () => void;
17
- onDelete?: () => void;
18
- onRename?: (newLabel: string) => void;
19
- }
20
-
21
- export default function DesignerOverlay({
22
- label,
23
- isSelected,
24
- isLocked = false,
25
- onMoveUp,
26
- onMoveDown,
27
- onDuplicate,
28
- onToggleVisibility,
29
- onDelete,
30
- onRename
31
- }: DesignerOverlayProps) {
32
- const lockedClasses = isLocked
33
- ? isSelected
34
- ? 'border-dashed border-amber-400 bg-amber-400/[0.12] shadow-[0_0_0_1px_rgba(251,191,36,0.4)]'
35
- : 'border-dashed border-amber-300/25 bg-amber-400/[0.03] group-hover:border-amber-300/55 group-hover:bg-amber-400/[0.07]'
36
- : null;
37
- const defaultClasses = !isLocked
38
- ? isSelected
39
- ? 'border-[#1b57f0] bg-[#1b57f0]/20 shadow-[0_0_0_1px_rgba(27,87,240,0.35)]'
40
- : 'border-transparent group-hover:border-[#7fb2ff] group-hover:bg-[#7fb2ff]/15'
41
- : null;
42
-
43
- return (
44
- <>
45
- <div
46
- className={twMerge(
47
- clsx(
48
- 'absolute inset-0 pointer-events-none border-2 transition-all duration-150 z-50',
49
- defaultClasses,
50
- lockedClasses
51
- )
52
- )}
53
- />
54
- {isSelected &&
55
- (onMoveUp ||
56
- onMoveDown ||
57
- onDuplicate ||
58
- onToggleVisibility ||
59
- onDelete ||
60
- onRename) && (
61
- <ActionToolbar
62
- label={label}
63
- isLocked={isLocked}
64
- onMoveUp={onMoveUp}
65
- onMoveDown={onMoveDown}
66
- onDuplicate={onDuplicate}
67
- onToggleVisibility={onToggleVisibility}
68
- onDelete={onDelete}
69
- onRename={onRename}
70
- />
71
- )}
72
- </>
73
- );
74
- }
@@ -1,142 +0,0 @@
1
- 'use client';
2
-
3
- import clsx from 'clsx';
4
- import React, { useEffect, useRef } from 'react';
5
- import { getResponsiveValue } from '../utils';
6
- import DesignerOverlay from './designer-overlay';
7
- import { useDesignerFeatures } from '../hooks/use-designer-features';
8
-
9
- interface WithDesignerFeaturesProps {
10
- block: any;
11
- placeholderId: string;
12
- sectionId: string;
13
- isDesigner: boolean;
14
- isSelected?: boolean;
15
- currentBreakpoint?: string;
16
- className?: string;
17
- style?: React.CSSProperties;
18
- onMoveUp?: () => void;
19
- onMoveDown?: () => void;
20
- onDuplicate?: () => void;
21
- onToggleVisibility?: () => void;
22
- onDelete?: () => void;
23
- onRename?: (newLabel: string) => void;
24
- // Note: repo has mixed React type versions; keep this permissive to avoid ReactNode incompatibilities.
25
- children: any;
26
- }
27
-
28
- export function WithDesignerFeatures({
29
- block,
30
- placeholderId,
31
- sectionId,
32
- isDesigner,
33
- isSelected = false,
34
- currentBreakpoint = 'desktop',
35
- className,
36
- style,
37
- onMoveUp,
38
- onMoveDown,
39
- onDuplicate,
40
- onToggleVisibility,
41
- onDelete,
42
- onRename,
43
- children
44
- }: WithDesignerFeaturesProps) {
45
- const blockRef = useRef<HTMLDivElement>(null);
46
- const actionBlockId = block.styleSourceId || block.id;
47
- const { handleClick } = useDesignerFeatures({
48
- blockId: actionBlockId,
49
- placeholderId,
50
- sectionId,
51
- isDesigner,
52
- blockInfo: {
53
- id: actionBlockId,
54
- type: block.type,
55
- label: block.label
56
- }
57
- });
58
-
59
- useEffect(() => {
60
- if (!isDesigner) return;
61
-
62
- const handleMessage = (event: MessageEvent) => {
63
- if (
64
- event.data?.type === 'SCROLL_TO_BLOCK' &&
65
- event.data?.data?.blockId === actionBlockId
66
- ) {
67
- blockRef.current?.scrollIntoView({
68
- behavior: 'smooth',
69
- block: 'center'
70
- });
71
- }
72
- };
73
-
74
- window.addEventListener('message', handleMessage);
75
- return () => window.removeEventListener('message', handleMessage);
76
- }, [actionBlockId, isDesigner]);
77
-
78
- const isButtonBlock = block.type === 'button';
79
-
80
- const buttonDisplay = isButtonBlock
81
- ? getResponsiveValue(
82
- block.styles?.display,
83
- currentBreakpoint,
84
- 'inline-block'
85
- )
86
- : null;
87
- const buttonAlign =
88
- isButtonBlock && block.properties?.align
89
- ? getResponsiveValue(block.properties.align, currentBreakpoint, 'left')
90
- : 'left';
91
-
92
- // In designer mode, keep hidden blocks mounted (prevents remount-driven side effects)
93
- // but visually hide them.
94
- const mergedStyle: React.CSSProperties = {
95
- ...(style || {}),
96
- ...(isDesigner && block.hidden ? { display: 'none' } : null)
97
- };
98
-
99
- return (
100
- <div
101
- ref={blockRef}
102
- data-block-id={block.styleSourceId || block.id}
103
- className={clsx(
104
- 'relative',
105
- className,
106
- isDesigner && 'cursor-pointer group',
107
- block.type === 'tab' && 'h-full'
108
- )}
109
- style={mergedStyle}
110
- onClick={handleClick}
111
- >
112
- {isDesigner && (
113
- <DesignerOverlay
114
- label={block.label}
115
- isSelected={isSelected}
116
- isLocked={block.locked === true}
117
- onMoveUp={onMoveUp}
118
- onMoveDown={onMoveDown}
119
- onDuplicate={onDuplicate}
120
- onToggleVisibility={onToggleVisibility}
121
- onDelete={onDelete}
122
- onRename={onRename}
123
- />
124
- )}
125
- {isButtonBlock ? (
126
- <div
127
- style={{
128
- display: buttonDisplay === 'inline-block' ? 'block' : undefined,
129
- textAlign:
130
- buttonDisplay === 'inline-block'
131
- ? (buttonAlign as 'left' | 'center' | 'right')
132
- : undefined
133
- }}
134
- >
135
- {children}
136
- </div>
137
- ) : (
138
- children
139
- )}
140
- </div>
141
- );
142
- }