@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.
- package/CHANGELOG.md +25 -0
- package/api/auth.ts +292 -60
- package/bin/pz-install-plugins.js +1 -1
- package/package.json +3 -3
- package/types/index.ts +19 -6
- package/types/next-auth.d.ts +1 -1
- package/with-pz-config.js +8 -1
- package/components/theme-editor/blocks/accordion-block.tsx +0 -136
- package/components/theme-editor/blocks/block-renderer-registry.tsx +0 -77
- package/components/theme-editor/blocks/button-block.tsx +0 -593
- package/components/theme-editor/blocks/counter-block.tsx +0 -348
- package/components/theme-editor/blocks/divider-block.tsx +0 -20
- package/components/theme-editor/blocks/embed-block.tsx +0 -208
- package/components/theme-editor/blocks/group-block.tsx +0 -116
- package/components/theme-editor/blocks/hotspot-block.tsx +0 -147
- package/components/theme-editor/blocks/icon-block.tsx +0 -230
- package/components/theme-editor/blocks/image-block.tsx +0 -137
- package/components/theme-editor/blocks/image-gallery-block.tsx +0 -269
- package/components/theme-editor/blocks/input-block.tsx +0 -123
- package/components/theme-editor/blocks/link-block.tsx +0 -216
- package/components/theme-editor/blocks/lottie-block.tsx +0 -325
- package/components/theme-editor/blocks/map-block.tsx +0 -89
- package/components/theme-editor/blocks/slider-block.tsx +0 -595
- package/components/theme-editor/blocks/tab-block.tsx +0 -10
- package/components/theme-editor/blocks/text-block.tsx +0 -52
- package/components/theme-editor/blocks/video-block.tsx +0 -122
- package/components/theme-editor/components/action-toolbar.tsx +0 -305
- package/components/theme-editor/components/designer-overlay.tsx +0 -74
- package/components/theme-editor/components/with-designer-features.tsx +0 -142
- package/components/theme-editor/dynamic-font-loader.tsx +0 -79
- package/components/theme-editor/hooks/use-designer-features.tsx +0 -100
- package/components/theme-editor/hooks/use-external-designer.tsx +0 -95
- package/components/theme-editor/hooks/use-native-widget-data.ts +0 -188
- package/components/theme-editor/hooks/use-visibility-context.ts +0 -27
- package/components/theme-editor/placeholder-registry.ts +0 -31
- package/components/theme-editor/sections/before-after-section.tsx +0 -245
- package/components/theme-editor/sections/contact-form-section.tsx +0 -563
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +0 -433
- package/components/theme-editor/sections/coupon-banner-section.tsx +0 -710
- package/components/theme-editor/sections/divider-section.tsx +0 -62
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +0 -507
- package/components/theme-editor/sections/find-in-store-section.tsx +0 -1995
- package/components/theme-editor/sections/hover-showcase-section.tsx +0 -326
- package/components/theme-editor/sections/image-hotspot-section.tsx +0 -142
- package/components/theme-editor/sections/installment-options-section.tsx +0 -1065
- package/components/theme-editor/sections/notification-banner-section.tsx +0 -173
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +0 -1379
- package/components/theme-editor/sections/posts-slider-section.tsx +0 -472
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +0 -663
- package/components/theme-editor/sections/section-renderer-registry.tsx +0 -89
- package/components/theme-editor/sections/section-wrapper.tsx +0 -135
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +0 -586
- package/components/theme-editor/sections/stats-counter-section.tsx +0 -486
- package/components/theme-editor/sections/tabs-section.tsx +0 -578
- package/components/theme-editor/theme-block.tsx +0 -102
- package/components/theme-editor/theme-placeholder-client.tsx +0 -218
- package/components/theme-editor/theme-placeholder-wrapper.tsx +0 -732
- package/components/theme-editor/theme-placeholder.tsx +0 -288
- package/components/theme-editor/theme-section.tsx +0 -1224
- package/components/theme-editor/theme-settings-context.tsx +0 -13
- package/components/theme-editor/utils/index.ts +0 -792
- package/components/theme-editor/utils/iterator-utils.ts +0 -234
- package/components/theme-editor/utils/publish-window.ts +0 -86
- package/components/theme-editor/utils/visibility-rules.ts +0 -188
|
@@ -1,595 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import Carousel from 'react-multi-carousel';
|
|
3
|
-
import 'react-multi-carousel/lib/styles.css';
|
|
4
|
-
import clsx from 'clsx';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
|
-
|
|
7
|
-
import ThemeBlock from '../theme-block';
|
|
8
|
-
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
9
|
-
import {
|
|
10
|
-
getCSSStyles,
|
|
11
|
-
getResponsiveValue,
|
|
12
|
-
resolveThemeCssVariables
|
|
13
|
-
} from '../utils';
|
|
14
|
-
import { BlockRendererProps } from './block-renderer-registry';
|
|
15
|
-
|
|
16
|
-
const parseNumber = (value: unknown, fallback: number) => {
|
|
17
|
-
if (value === undefined || value === null || value === '') return fallback;
|
|
18
|
-
const parsed = typeof value === 'string' ? Number(value) : Number(value);
|
|
19
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const clampNumber = (value: number, min: number, max: number) =>
|
|
23
|
-
Math.min(Math.max(value, min), max);
|
|
24
|
-
|
|
25
|
-
const SliderBlock: React.FC<BlockRendererProps> = ({
|
|
26
|
-
block,
|
|
27
|
-
placeholderId,
|
|
28
|
-
sectionId,
|
|
29
|
-
isDesigner = false,
|
|
30
|
-
selectedBlockId = null,
|
|
31
|
-
currentBreakpoint = 'desktop',
|
|
32
|
-
onMoveUp,
|
|
33
|
-
onMoveDown,
|
|
34
|
-
onDuplicate,
|
|
35
|
-
onToggleVisibility,
|
|
36
|
-
onDelete,
|
|
37
|
-
onRename
|
|
38
|
-
}) => {
|
|
39
|
-
const themeSettings = useThemeSettingsContext();
|
|
40
|
-
const carouselRef = React.useRef<Carousel>(null);
|
|
41
|
-
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
42
|
-
|
|
43
|
-
const leftArrowIconBlock = block.blocks?.find(
|
|
44
|
-
(b: any) => b.type === 'icon' && b.label === 'Left Arrow Icon'
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const rightArrowIconBlock = block.blocks?.find(
|
|
48
|
-
(b: any) => b.type === 'icon' && b.label === 'Right Arrow Icon'
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const getConfigEntry = (key: string) =>
|
|
52
|
-
block.properties?.[key] ?? block.styles?.[key];
|
|
53
|
-
|
|
54
|
-
const normalizeResponsiveNumber = (
|
|
55
|
-
value: unknown,
|
|
56
|
-
fallback: { desktop: number; mobile: number }
|
|
57
|
-
) => {
|
|
58
|
-
if (value === undefined || value === null || value === '') {
|
|
59
|
-
return { ...fallback };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (typeof value === 'number' || typeof value === 'string') {
|
|
63
|
-
const parsed = parseNumber(value, fallback.desktop);
|
|
64
|
-
return { desktop: parsed, mobile: parsed };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (typeof value === 'object') {
|
|
68
|
-
const responsiveValue = value as Record<string, unknown>;
|
|
69
|
-
const candidate =
|
|
70
|
-
typeof responsiveValue.desktop === 'object' &&
|
|
71
|
-
responsiveValue.desktop !== null
|
|
72
|
-
? (responsiveValue.desktop as Record<string, unknown>)
|
|
73
|
-
: responsiveValue;
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
desktop: parseNumber(candidate.desktop, fallback.desktop),
|
|
77
|
-
mobile: parseNumber(candidate.mobile, fallback.mobile)
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return { ...fallback };
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const itemsConfig = normalizeResponsiveNumber(
|
|
85
|
-
getConfigEntry('items-per-slide'),
|
|
86
|
-
{ desktop: 4, mobile: 1 }
|
|
87
|
-
);
|
|
88
|
-
const slidesConfig = normalizeResponsiveNumber(
|
|
89
|
-
getConfigEntry('slides-to-slide'),
|
|
90
|
-
{ desktop: 1, mobile: 1 }
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const sortedBlocks = [...(block.blocks || [])]
|
|
94
|
-
.filter(
|
|
95
|
-
(childBlock) =>
|
|
96
|
-
childBlock.type !== 'icon' && (isDesigner ? true : !childBlock.hidden)
|
|
97
|
-
)
|
|
98
|
-
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
99
|
-
const visibleCount = sortedBlocks.length;
|
|
100
|
-
|
|
101
|
-
const desktopItems = clampNumber(itemsConfig.desktop, 1, 8);
|
|
102
|
-
const mobileItems = clampNumber(itemsConfig.mobile, 1, 4);
|
|
103
|
-
const desktopSlides = clampNumber(slidesConfig.desktop, 1, 8);
|
|
104
|
-
const mobileSlides = clampNumber(slidesConfig.mobile, 1, 8);
|
|
105
|
-
|
|
106
|
-
const carouselResponsive = {
|
|
107
|
-
desktop: {
|
|
108
|
-
breakpoint: { max: 4000, min: 768 },
|
|
109
|
-
items: desktopItems
|
|
110
|
-
},
|
|
111
|
-
mobile: {
|
|
112
|
-
breakpoint: { max: 768, min: 0 },
|
|
113
|
-
items: mobileItems
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const currentSlidesToSlide =
|
|
118
|
-
currentBreakpoint === 'mobile' ? mobileSlides : desktopSlides;
|
|
119
|
-
|
|
120
|
-
const showArrows = getResponsiveValue(
|
|
121
|
-
getConfigEntry('show-arrows'),
|
|
122
|
-
currentBreakpoint,
|
|
123
|
-
true
|
|
124
|
-
);
|
|
125
|
-
const showDots = getResponsiveValue(
|
|
126
|
-
getConfigEntry('show-dots'),
|
|
127
|
-
currentBreakpoint,
|
|
128
|
-
false
|
|
129
|
-
);
|
|
130
|
-
const useAutoPlay = getResponsiveValue(
|
|
131
|
-
getConfigEntry('autoplay'),
|
|
132
|
-
currentBreakpoint,
|
|
133
|
-
false
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const useInfinite = useAutoPlay;
|
|
137
|
-
|
|
138
|
-
const autoPlaySpeedRaw = getConfigEntry('autoplay-speed');
|
|
139
|
-
const autoPlaySpeed = parseNumber(autoPlaySpeedRaw, 3000);
|
|
140
|
-
|
|
141
|
-
const slideGap = parseNumber(getConfigEntry('slide-gap'), 16);
|
|
142
|
-
const slidePadding = slideGap / 2;
|
|
143
|
-
|
|
144
|
-
const blockStyles = block.styles || {};
|
|
145
|
-
const sectionStyles = getCSSStyles(
|
|
146
|
-
typeof blockStyles === 'object' && blockStyles !== null
|
|
147
|
-
? (blockStyles as Record<string, unknown>)
|
|
148
|
-
: {},
|
|
149
|
-
themeSettings,
|
|
150
|
-
currentBreakpoint
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
const containerStylesRaw = blockStyles['max-width'];
|
|
154
|
-
const hasMaxWidth =
|
|
155
|
-
containerStylesRaw !== undefined &&
|
|
156
|
-
containerStylesRaw !== null &&
|
|
157
|
-
containerStylesRaw !== '';
|
|
158
|
-
const maxWidthValue = getResponsiveValue(
|
|
159
|
-
containerStylesRaw,
|
|
160
|
-
currentBreakpoint,
|
|
161
|
-
'1200px'
|
|
162
|
-
);
|
|
163
|
-
const maxWidthClass = hasMaxWidth ? `max-w-[${maxWidthValue}]` : '';
|
|
164
|
-
|
|
165
|
-
const blockAction = (action: string, blockId: string, label?: string) => {
|
|
166
|
-
if (!isDesigner) return;
|
|
167
|
-
window.parent.postMessage(
|
|
168
|
-
{
|
|
169
|
-
type: 'BLOCK_ACTION',
|
|
170
|
-
action,
|
|
171
|
-
blockId,
|
|
172
|
-
sectionId,
|
|
173
|
-
placeholderId,
|
|
174
|
-
label
|
|
175
|
-
},
|
|
176
|
-
'*'
|
|
177
|
-
);
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const modifySVGColor = (svgContent: string, color: string) => {
|
|
181
|
-
if (color === 'currentColor' || !color) return svgContent;
|
|
182
|
-
|
|
183
|
-
return svgContent
|
|
184
|
-
.replace(/fill\s*=\s*["'][^"']*["']/gi, `fill="${color}"`)
|
|
185
|
-
.replace(/stroke\s*=\s*["'][^"']*["']/gi, `stroke="${color}"`)
|
|
186
|
-
.replace(/fill\s*:\s*[^;}\s]+/gi, `fill: ${color}`)
|
|
187
|
-
.replace(/stroke\s*:\s*[^;}\s]+/gi, `stroke: ${color}`)
|
|
188
|
-
.replace(/<path(?![^>]*fill)/gi, `<path fill="${color}"`)
|
|
189
|
-
.replace(/<circle(?![^>]*fill)/gi, `<circle fill="${color}"`)
|
|
190
|
-
.replace(/<rect(?![^>]*fill)/gi, `<rect fill="${color}"`)
|
|
191
|
-
.replace(/<polygon(?![^>]*fill)/gi, `<polygon fill="${color}"`)
|
|
192
|
-
.replace(/<ellipse(?![^>]*fill)/gi, `<ellipse fill="${color}"`);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const isInlineSvg = (value: string): boolean => {
|
|
196
|
-
return (
|
|
197
|
-
typeof value === 'string' &&
|
|
198
|
-
(value.includes('<svg') || value.includes('<?xml'))
|
|
199
|
-
);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const processInlineSVG = (svgContent: string, color: string) => {
|
|
203
|
-
try {
|
|
204
|
-
const modifiedSVG = modifySVGColor(svgContent, color);
|
|
205
|
-
const base64 = btoa(modifiedSVG);
|
|
206
|
-
return `data:image/svg+xml;base64,${base64}`;
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error('Error processing inline SVG:', error);
|
|
209
|
-
return '';
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const processBase64SVG = (base64String: string, color: string) => {
|
|
214
|
-
try {
|
|
215
|
-
const base64Content = base64String.replace(
|
|
216
|
-
/^data:image\/svg\+xml;base64,/,
|
|
217
|
-
''
|
|
218
|
-
);
|
|
219
|
-
const svgContent = atob(base64Content);
|
|
220
|
-
const modifiedSVG = modifySVGColor(svgContent, color);
|
|
221
|
-
const modifiedBase64 = `data:image/svg+xml;base64,${btoa(modifiedSVG)}`;
|
|
222
|
-
return modifiedBase64;
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.error('Error processing SVG base64:', error);
|
|
225
|
-
return base64String;
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const getIconUrl = (iconBlock: any): string => {
|
|
230
|
-
if (!iconBlock?.value) return '';
|
|
231
|
-
const value = iconBlock.value;
|
|
232
|
-
|
|
233
|
-
if (typeof value === 'string') {
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (typeof value === 'object') {
|
|
238
|
-
// Handle responsive value structure
|
|
239
|
-
const responsiveUrl = getResponsiveValue(value, currentBreakpoint, '');
|
|
240
|
-
if (typeof responsiveUrl === 'string') {
|
|
241
|
-
return responsiveUrl;
|
|
242
|
-
}
|
|
243
|
-
// Handle {url: '...', alt: '...'} structure
|
|
244
|
-
if (value.url) {
|
|
245
|
-
return value.url;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return '';
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const defaultLeftArrowSvg =
|
|
253
|
-
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1IDZMOSAxMkwxNSAxOCIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPg==';
|
|
254
|
-
const defaultRightArrowSvg =
|
|
255
|
-
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkgNkwxNSAxMkw5IDE4IiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+';
|
|
256
|
-
|
|
257
|
-
const leftArrowIconUrl = getIconUrl(leftArrowIconBlock);
|
|
258
|
-
const rightArrowIconUrl = getIconUrl(rightArrowIconBlock);
|
|
259
|
-
|
|
260
|
-
const leftArrowIconSize = String(
|
|
261
|
-
getResponsiveValue(leftArrowIconBlock?.styles?.size, '24')
|
|
262
|
-
);
|
|
263
|
-
const rightArrowIconSize = String(
|
|
264
|
-
getResponsiveValue(rightArrowIconBlock?.styles?.size, '24')
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
const leftArrowIconColor = resolveThemeCssVariables(
|
|
268
|
-
String(
|
|
269
|
-
getResponsiveValue(leftArrowIconBlock?.styles?.['color'], '#ffffff')
|
|
270
|
-
),
|
|
271
|
-
themeSettings
|
|
272
|
-
);
|
|
273
|
-
const rightArrowIconColor = resolveThemeCssVariables(
|
|
274
|
-
String(
|
|
275
|
-
getResponsiveValue(rightArrowIconBlock?.styles?.['color'], '#ffffff')
|
|
276
|
-
),
|
|
277
|
-
themeSettings
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
const leftArrowIconOpacity =
|
|
281
|
-
parseFloat(
|
|
282
|
-
String(getResponsiveValue(leftArrowIconBlock?.styles?.['opacity'], '1'))
|
|
283
|
-
) || 1;
|
|
284
|
-
const rightArrowIconOpacity =
|
|
285
|
-
parseFloat(
|
|
286
|
-
String(getResponsiveValue(rightArrowIconBlock?.styles?.['opacity'], '1'))
|
|
287
|
-
) || 1;
|
|
288
|
-
|
|
289
|
-
// Process SVGs with color
|
|
290
|
-
const processedLeftArrowUrl = isInlineSvg(leftArrowIconUrl)
|
|
291
|
-
? processInlineSVG(leftArrowIconUrl, leftArrowIconColor)
|
|
292
|
-
: (leftArrowIconUrl || defaultLeftArrowSvg).includes(
|
|
293
|
-
'data:image/svg+xml;base64,'
|
|
294
|
-
)
|
|
295
|
-
? processBase64SVG(
|
|
296
|
-
leftArrowIconUrl || defaultLeftArrowSvg,
|
|
297
|
-
leftArrowIconColor
|
|
298
|
-
)
|
|
299
|
-
: leftArrowIconUrl || defaultLeftArrowSvg;
|
|
300
|
-
|
|
301
|
-
const processedRightArrowUrl = isInlineSvg(rightArrowIconUrl)
|
|
302
|
-
? processInlineSVG(rightArrowIconUrl, rightArrowIconColor)
|
|
303
|
-
: (rightArrowIconUrl || defaultRightArrowSvg).includes(
|
|
304
|
-
'data:image/svg+xml;base64,'
|
|
305
|
-
)
|
|
306
|
-
? processBase64SVG(
|
|
307
|
-
rightArrowIconUrl || defaultRightArrowSvg,
|
|
308
|
-
rightArrowIconColor
|
|
309
|
-
)
|
|
310
|
-
: rightArrowIconUrl || defaultRightArrowSvg;
|
|
311
|
-
|
|
312
|
-
const leftArrowIcon = {
|
|
313
|
-
url: processedLeftArrowUrl,
|
|
314
|
-
alt: 'Previous'
|
|
315
|
-
};
|
|
316
|
-
const rightArrowIcon = {
|
|
317
|
-
url: processedRightArrowUrl,
|
|
318
|
-
alt: 'Next'
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
const leftArrowBackground = resolveThemeCssVariables(
|
|
322
|
-
String(
|
|
323
|
-
getResponsiveValue(
|
|
324
|
-
leftArrowIconBlock?.styles?.['background'] ??
|
|
325
|
-
leftArrowIconBlock?.styles?.['backgroundColor'],
|
|
326
|
-
'rgba(0, 0, 0, 0.5)'
|
|
327
|
-
)
|
|
328
|
-
),
|
|
329
|
-
themeSettings
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
const rightArrowBackground = resolveThemeCssVariables(
|
|
333
|
-
String(
|
|
334
|
-
getResponsiveValue(
|
|
335
|
-
rightArrowIconBlock?.styles?.['background'] ??
|
|
336
|
-
rightArrowIconBlock?.styles?.['backgroundColor'],
|
|
337
|
-
'rgba(0, 0, 0, 0.5)'
|
|
338
|
-
)
|
|
339
|
-
),
|
|
340
|
-
themeSettings
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
const handleLeftArrowClick = (e: React.MouseEvent) => {
|
|
344
|
-
e.preventDefault();
|
|
345
|
-
e.stopPropagation();
|
|
346
|
-
|
|
347
|
-
carouselRef.current?.previous?.(1);
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const handleRightArrowClick = (e: React.MouseEvent) => {
|
|
351
|
-
e.preventDefault();
|
|
352
|
-
e.stopPropagation();
|
|
353
|
-
|
|
354
|
-
carouselRef.current?.next?.(1);
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const CustomLeftArrow = ({ onClick }: any) => (
|
|
358
|
-
<button
|
|
359
|
-
onClick={(e) => {
|
|
360
|
-
e.stopPropagation();
|
|
361
|
-
if (!isDesigner) {
|
|
362
|
-
onClick?.();
|
|
363
|
-
}
|
|
364
|
-
handleLeftArrowClick(e);
|
|
365
|
-
}}
|
|
366
|
-
className={clsx(
|
|
367
|
-
'absolute left-4 top-1/2 -translate-y-1/2 z-10',
|
|
368
|
-
isDesigner &&
|
|
369
|
-
selectedBlockId === leftArrowIconBlock?.id &&
|
|
370
|
-
'ring-2 ring-blue-500 ring-offset-2'
|
|
371
|
-
)}
|
|
372
|
-
style={{
|
|
373
|
-
backgroundColor: leftArrowBackground,
|
|
374
|
-
border: 'none',
|
|
375
|
-
borderRadius: '50%',
|
|
376
|
-
width: '40px',
|
|
377
|
-
height: '40px',
|
|
378
|
-
display: 'flex',
|
|
379
|
-
alignItems: 'center',
|
|
380
|
-
justifyContent: 'center',
|
|
381
|
-
cursor: 'pointer',
|
|
382
|
-
padding: 0
|
|
383
|
-
}}
|
|
384
|
-
aria-label="Previous"
|
|
385
|
-
>
|
|
386
|
-
<img
|
|
387
|
-
src={leftArrowIcon.url}
|
|
388
|
-
alt={leftArrowIcon.alt}
|
|
389
|
-
style={{
|
|
390
|
-
width: `${leftArrowIconSize}px`,
|
|
391
|
-
height: `${leftArrowIconSize}px`,
|
|
392
|
-
opacity: leftArrowIconOpacity,
|
|
393
|
-
pointerEvents: 'none'
|
|
394
|
-
}}
|
|
395
|
-
/>
|
|
396
|
-
</button>
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
const CustomRightArrow = ({ onClick }: any) => (
|
|
400
|
-
<button
|
|
401
|
-
onClick={(e) => {
|
|
402
|
-
e.stopPropagation();
|
|
403
|
-
if (!isDesigner) {
|
|
404
|
-
onClick?.();
|
|
405
|
-
}
|
|
406
|
-
handleRightArrowClick(e);
|
|
407
|
-
}}
|
|
408
|
-
className={clsx(
|
|
409
|
-
'absolute right-4 top-1/2 -translate-y-1/2 z-10',
|
|
410
|
-
isDesigner &&
|
|
411
|
-
selectedBlockId === rightArrowIconBlock?.id &&
|
|
412
|
-
'ring-2 ring-blue-500 ring-offset-2'
|
|
413
|
-
)}
|
|
414
|
-
style={{
|
|
415
|
-
backgroundColor: rightArrowBackground,
|
|
416
|
-
border: 'none',
|
|
417
|
-
borderRadius: '50%',
|
|
418
|
-
width: '40px',
|
|
419
|
-
height: '40px',
|
|
420
|
-
display: 'flex',
|
|
421
|
-
alignItems: 'center',
|
|
422
|
-
justifyContent: 'center',
|
|
423
|
-
cursor: 'pointer',
|
|
424
|
-
padding: 0
|
|
425
|
-
}}
|
|
426
|
-
aria-label="Next"
|
|
427
|
-
>
|
|
428
|
-
<img
|
|
429
|
-
src={rightArrowIcon.url}
|
|
430
|
-
alt={rightArrowIcon.alt}
|
|
431
|
-
style={{
|
|
432
|
-
width: `${rightArrowIconSize}px`,
|
|
433
|
-
height: `${rightArrowIconSize}px`,
|
|
434
|
-
opacity: rightArrowIconOpacity,
|
|
435
|
-
pointerEvents: 'none'
|
|
436
|
-
}}
|
|
437
|
-
/>
|
|
438
|
-
</button>
|
|
439
|
-
);
|
|
440
|
-
|
|
441
|
-
if (visibleCount === 0) {
|
|
442
|
-
return (
|
|
443
|
-
<div
|
|
444
|
-
className="p-4 text-gray-400 border border-dashed border-gray-300 rounded"
|
|
445
|
-
style={sectionStyles}
|
|
446
|
-
>
|
|
447
|
-
No items available
|
|
448
|
-
</div>
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
return (
|
|
453
|
-
<div
|
|
454
|
-
className={twMerge(
|
|
455
|
-
clsx('relative z-10 w-full', hasMaxWidth && 'mx-auto', maxWidthClass)
|
|
456
|
-
)}
|
|
457
|
-
style={sectionStyles}
|
|
458
|
-
>
|
|
459
|
-
<div style={{ position: 'relative' }}>
|
|
460
|
-
<Carousel
|
|
461
|
-
ref={carouselRef}
|
|
462
|
-
key={`${desktopItems}-${mobileItems}-${desktopSlides}-${mobileSlides}-${visibleCount}-${useInfinite}-${useAutoPlay}-${autoPlaySpeed}`}
|
|
463
|
-
responsive={carouselResponsive}
|
|
464
|
-
slidesToSlide={currentSlidesToSlide}
|
|
465
|
-
swipeable={true}
|
|
466
|
-
draggable={true}
|
|
467
|
-
arrows={isDesigner ? false : Boolean(showArrows)}
|
|
468
|
-
showDots={Boolean(showDots)}
|
|
469
|
-
renderDotsOutside={Boolean(showDots)}
|
|
470
|
-
infinite={Boolean(useInfinite)}
|
|
471
|
-
rewind={Boolean(useInfinite)}
|
|
472
|
-
rewindWithAnimation={Boolean(useInfinite)}
|
|
473
|
-
autoPlay={Boolean(useAutoPlay)}
|
|
474
|
-
autoPlaySpeed={autoPlaySpeed}
|
|
475
|
-
containerClass="slider-carousel"
|
|
476
|
-
dotListClass="slider-dots"
|
|
477
|
-
customLeftArrow={
|
|
478
|
-
!isDesigner && showArrows ? <CustomLeftArrow /> : undefined
|
|
479
|
-
}
|
|
480
|
-
customRightArrow={
|
|
481
|
-
!isDesigner && showArrows ? <CustomRightArrow /> : undefined
|
|
482
|
-
}
|
|
483
|
-
>
|
|
484
|
-
{sortedBlocks.map((childBlock) => (
|
|
485
|
-
<div
|
|
486
|
-
key={childBlock.id}
|
|
487
|
-
style={{
|
|
488
|
-
paddingLeft: slidePadding,
|
|
489
|
-
paddingRight: slidePadding
|
|
490
|
-
}}
|
|
491
|
-
>
|
|
492
|
-
<ThemeBlock
|
|
493
|
-
block={childBlock}
|
|
494
|
-
placeholderId={placeholderId}
|
|
495
|
-
sectionId={sectionId}
|
|
496
|
-
isDesigner={isDesigner}
|
|
497
|
-
isSelected={selectedBlockId === childBlock.id}
|
|
498
|
-
selectedBlockId={selectedBlockId}
|
|
499
|
-
currentBreakpoint={currentBreakpoint}
|
|
500
|
-
onMoveUp={() => blockAction('MOVE_BLOCK_UP', childBlock.id)}
|
|
501
|
-
onMoveDown={() => blockAction('MOVE_BLOCK_DOWN', childBlock.id)}
|
|
502
|
-
onDuplicate={() =>
|
|
503
|
-
blockAction('DUPLICATE_BLOCK', childBlock.id)
|
|
504
|
-
}
|
|
505
|
-
onToggleVisibility={() =>
|
|
506
|
-
blockAction('TOGGLE_BLOCK_VISIBILITY', childBlock.id)
|
|
507
|
-
}
|
|
508
|
-
onDelete={() => blockAction('DELETE_BLOCK', childBlock.id)}
|
|
509
|
-
onRename={(newLabel: string) =>
|
|
510
|
-
blockAction('RENAME_BLOCK', childBlock.id, newLabel)
|
|
511
|
-
}
|
|
512
|
-
/>
|
|
513
|
-
</div>
|
|
514
|
-
))}
|
|
515
|
-
</Carousel>
|
|
516
|
-
|
|
517
|
-
{isDesigner &&
|
|
518
|
-
showArrows &&
|
|
519
|
-
sortedBlocks.length > 0 &&
|
|
520
|
-
leftArrowIconBlock &&
|
|
521
|
-
rightArrowIconBlock ? (
|
|
522
|
-
<>
|
|
523
|
-
<button
|
|
524
|
-
onClick={handleLeftArrowClick}
|
|
525
|
-
className={clsx(
|
|
526
|
-
'absolute left-4 top-1/2 -translate-y-1/2 z-10',
|
|
527
|
-
selectedBlockId === leftArrowIconBlock?.id &&
|
|
528
|
-
'ring-2 ring-blue-500 ring-offset-2'
|
|
529
|
-
)}
|
|
530
|
-
style={{
|
|
531
|
-
backgroundColor: leftArrowBackground,
|
|
532
|
-
border: 'none',
|
|
533
|
-
borderRadius: '50%',
|
|
534
|
-
width: '40px',
|
|
535
|
-
height: '40px',
|
|
536
|
-
display: 'flex',
|
|
537
|
-
alignItems: 'center',
|
|
538
|
-
justifyContent: 'center',
|
|
539
|
-
cursor: 'pointer',
|
|
540
|
-
padding: 0
|
|
541
|
-
}}
|
|
542
|
-
aria-label="Previous"
|
|
543
|
-
>
|
|
544
|
-
<img
|
|
545
|
-
src={leftArrowIcon.url}
|
|
546
|
-
alt={leftArrowIcon.alt}
|
|
547
|
-
style={{
|
|
548
|
-
width: `${leftArrowIconSize}px`,
|
|
549
|
-
height: `${leftArrowIconSize}px`,
|
|
550
|
-
opacity: leftArrowIconOpacity,
|
|
551
|
-
pointerEvents: 'none'
|
|
552
|
-
}}
|
|
553
|
-
/>
|
|
554
|
-
</button>
|
|
555
|
-
|
|
556
|
-
<button
|
|
557
|
-
onClick={handleRightArrowClick}
|
|
558
|
-
className={clsx(
|
|
559
|
-
'absolute right-4 top-1/2 -translate-y-1/2 z-10',
|
|
560
|
-
selectedBlockId === rightArrowIconBlock?.id &&
|
|
561
|
-
'ring-2 ring-blue-500 ring-offset-2'
|
|
562
|
-
)}
|
|
563
|
-
style={{
|
|
564
|
-
backgroundColor: rightArrowBackground,
|
|
565
|
-
border: 'none',
|
|
566
|
-
borderRadius: '50%',
|
|
567
|
-
width: '40px',
|
|
568
|
-
height: '40px',
|
|
569
|
-
display: 'flex',
|
|
570
|
-
alignItems: 'center',
|
|
571
|
-
justifyContent: 'center',
|
|
572
|
-
cursor: 'pointer',
|
|
573
|
-
padding: 0
|
|
574
|
-
}}
|
|
575
|
-
aria-label="Next"
|
|
576
|
-
>
|
|
577
|
-
<img
|
|
578
|
-
src={rightArrowIcon.url}
|
|
579
|
-
alt={rightArrowIcon.alt}
|
|
580
|
-
style={{
|
|
581
|
-
width: `${rightArrowIconSize}px`,
|
|
582
|
-
height: `${rightArrowIconSize}px`,
|
|
583
|
-
opacity: rightArrowIconOpacity,
|
|
584
|
-
pointerEvents: 'none'
|
|
585
|
-
}}
|
|
586
|
-
/>
|
|
587
|
-
</button>
|
|
588
|
-
</>
|
|
589
|
-
) : null}
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
);
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
export default SliderBlock;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { BlockRendererProps } from './block-renderer-registry';
|
|
3
|
-
|
|
4
|
-
// Tab blocks are rendered by TabsSection, not individually
|
|
5
|
-
// This component should not be used directly
|
|
6
|
-
const TabBlock: React.FC<BlockRendererProps> = () => {
|
|
7
|
-
return null;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default TabBlock;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
4
|
-
import React, { useMemo } from 'react';
|
|
5
|
-
import { BlockRendererProps } from './block-renderer-registry';
|
|
6
|
-
import { getCSSStyles } from '../utils';
|
|
7
|
-
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
8
|
-
|
|
9
|
-
const TextBlock = ({
|
|
10
|
-
block,
|
|
11
|
-
currentBreakpoint = 'desktop'
|
|
12
|
-
}: BlockRendererProps) => {
|
|
13
|
-
const { locale } = useLocalization();
|
|
14
|
-
const defaultLocale = process.env.NEXT_PUBLIC_DEFAULT_LOCALE || 'en';
|
|
15
|
-
const themeSettings = useThemeSettingsContext();
|
|
16
|
-
|
|
17
|
-
const getLocalizedContent = (): string => {
|
|
18
|
-
let value = block.value;
|
|
19
|
-
|
|
20
|
-
if (typeof value === 'string' && value.startsWith('{')) {
|
|
21
|
-
try {
|
|
22
|
-
value = JSON.parse(value);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (typeof value === 'object' && value !== null) {
|
|
29
|
-
return (
|
|
30
|
-
value[locale] || value[defaultLocale] || Object.values(value)[0] || ''
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (typeof value === 'string') {
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return '';
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const content = getLocalizedContent();
|
|
42
|
-
|
|
43
|
-
const wrapperStyles = useMemo(() => {
|
|
44
|
-
return getCSSStyles(block.styles || {}, themeSettings, currentBreakpoint);
|
|
45
|
-
}, [block.styles, themeSettings, currentBreakpoint]);
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div style={wrapperStyles} dangerouslySetInnerHTML={{ __html: content }} />
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export default TextBlock;
|