@akinon/next 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.
- package/CHANGELOG.md +20 -13
- package/assets/styles/index.scss +84 -0
- package/components/client-root.tsx +107 -1
- package/components/link.tsx +46 -16
- package/components/theme-editor/blocks/accordion-block.tsx +136 -0
- package/components/theme-editor/blocks/block-renderer-registry.tsx +77 -0
- package/components/theme-editor/blocks/button-block.tsx +593 -0
- package/components/theme-editor/blocks/counter-block.tsx +348 -0
- package/components/theme-editor/blocks/divider-block.tsx +20 -0
- package/components/theme-editor/blocks/embed-block.tsx +208 -0
- package/components/theme-editor/blocks/group-block.tsx +116 -0
- package/components/theme-editor/blocks/hotspot-block.tsx +147 -0
- package/components/theme-editor/blocks/icon-block.tsx +230 -0
- package/components/theme-editor/blocks/image-block.tsx +137 -0
- package/components/theme-editor/blocks/image-gallery-block.tsx +269 -0
- package/components/theme-editor/blocks/input-block.tsx +123 -0
- package/components/theme-editor/blocks/link-block.tsx +216 -0
- package/components/theme-editor/blocks/lottie-block.tsx +325 -0
- package/components/theme-editor/blocks/map-block.tsx +89 -0
- package/components/theme-editor/blocks/slider-block.tsx +595 -0
- package/components/theme-editor/blocks/tab-block.tsx +10 -0
- package/components/theme-editor/blocks/text-block.tsx +52 -0
- package/components/theme-editor/blocks/video-block.tsx +122 -0
- package/components/theme-editor/components/action-toolbar.tsx +305 -0
- package/components/theme-editor/components/designer-overlay.tsx +74 -0
- package/components/theme-editor/components/with-designer-features.tsx +142 -0
- package/components/theme-editor/dynamic-font-loader.tsx +79 -0
- package/components/theme-editor/hooks/use-designer-features.tsx +100 -0
- package/components/theme-editor/hooks/use-external-designer.tsx +95 -0
- package/components/theme-editor/hooks/use-native-widget-data.ts +188 -0
- package/components/theme-editor/hooks/use-visibility-context.ts +27 -0
- package/components/theme-editor/placeholder-registry.ts +31 -0
- package/components/theme-editor/sections/before-after-section.tsx +245 -0
- package/components/theme-editor/sections/contact-form-section.tsx +563 -0
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +433 -0
- package/components/theme-editor/sections/coupon-banner-section.tsx +710 -0
- package/components/theme-editor/sections/divider-section.tsx +62 -0
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +507 -0
- package/components/theme-editor/sections/find-in-store-section.tsx +1995 -0
- package/components/theme-editor/sections/hover-showcase-section.tsx +326 -0
- package/components/theme-editor/sections/image-hotspot-section.tsx +142 -0
- package/components/theme-editor/sections/installment-options-section.tsx +1065 -0
- package/components/theme-editor/sections/notification-banner-section.tsx +173 -0
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +1379 -0
- package/components/theme-editor/sections/posts-slider-section.tsx +472 -0
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +663 -0
- package/components/theme-editor/sections/section-renderer-registry.tsx +89 -0
- package/components/theme-editor/sections/section-wrapper.tsx +135 -0
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +586 -0
- package/components/theme-editor/sections/stats-counter-section.tsx +486 -0
- package/components/theme-editor/sections/tabs-section.tsx +578 -0
- package/components/theme-editor/theme-block.tsx +102 -0
- package/components/theme-editor/theme-placeholder-client.tsx +218 -0
- package/components/theme-editor/theme-placeholder-wrapper.tsx +732 -0
- package/components/theme-editor/theme-placeholder.tsx +288 -0
- package/components/theme-editor/theme-section.tsx +1224 -0
- package/components/theme-editor/theme-settings-context.tsx +13 -0
- package/components/theme-editor/utils/index.ts +792 -0
- package/components/theme-editor/utils/iterator-utils.ts +234 -0
- package/components/theme-editor/utils/publish-window.ts +86 -0
- package/components/theme-editor/utils/visibility-rules.ts +188 -0
- package/data/client/misc.ts +13 -1
- package/data/server/widget.ts +68 -1
- package/data/urls.ts +3 -1
- package/hooks/use-router.ts +53 -19
- package/lib/cache.ts +1 -0
- package/package.json +4 -2
- package/redux/reducers/index.ts +2 -0
- package/redux/reducers/widget.ts +80 -0
- package/types/commerce/widget.ts +33 -0
- package/types/widget.ts +80 -0
- package/utils/widget-styles.ts +107 -0
- package/with-pz-config.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
-
## 2.0.0-beta.
|
|
3
|
+
## 2.0.0-beta.20
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cf3a9901: ZERO-3585: Fix cookie handling for widget builder in ClientRoot component
|
|
8
|
+
- 9076e50e: ZERO-3136: Refactor DynamicWidgetContainer and WidgetContent for improved styling and component tagging
|
|
9
|
+
- d0e1ef78: ZERO-3232: Integrate widget styles generation directly in WidgetPlaceholder
|
|
10
|
+
- 1d10dde2: ZERO-3553: Add alignment style for image components
|
|
11
|
+
- 74c6a237: ZERO-3060: Drag and drop functionality on the site implemented
|
|
12
|
+
|
|
13
|
+
## 1.125.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
|
6
16
|
|
|
7
17
|
- cd68a97a: ZERO-4126: Enhance error handling in appFetch and related data handlers to throw notFound on 404 and 422 errors
|
|
8
18
|
|
|
9
|
-
##
|
|
19
|
+
## 1.124.0
|
|
10
20
|
|
|
11
21
|
### Minor Changes
|
|
12
22
|
|
|
@@ -16,7 +26,7 @@
|
|
|
16
26
|
- bd431e36: ZERO-3278: improve checkout validation error messages for better user guidance
|
|
17
27
|
- 54eac86b: ZERO-3271: add development logger system
|
|
18
28
|
|
|
19
|
-
##
|
|
29
|
+
## 1.123.0
|
|
20
30
|
|
|
21
31
|
### Minor Changes
|
|
22
32
|
|
|
@@ -28,7 +38,7 @@
|
|
|
28
38
|
- 94a86fcc: ZERO-4065: Expand skipSegments array to include additional segments for route generation
|
|
29
39
|
- bcaad120: ZERO-4158: Add logging for OAuth login and callback redirects
|
|
30
40
|
|
|
31
|
-
##
|
|
41
|
+
## 1.122.0
|
|
32
42
|
|
|
33
43
|
### Minor Changes
|
|
34
44
|
|
|
@@ -38,7 +48,7 @@
|
|
|
38
48
|
- 9b7d0de6: ZERO-3393: Improve error handling in checkout middleware to support both object and array error formats
|
|
39
49
|
- 5ec0faf8: ZERO-3355: Refactor Redis client creation to include optional password in configuration
|
|
40
50
|
|
|
41
|
-
##
|
|
51
|
+
## 1.121.0
|
|
42
52
|
|
|
43
53
|
### Minor Changes
|
|
44
54
|
|
|
@@ -46,21 +56,18 @@
|
|
|
46
56
|
- d7e5178b: ZERO-3985: Add query string handling for orders redirection in middleware
|
|
47
57
|
- b59fdd1c: ZERO-4009: Add password reset token validation
|
|
48
58
|
|
|
49
|
-
##
|
|
59
|
+
## 1.120.0
|
|
50
60
|
|
|
51
61
|
### Minor Changes
|
|
52
62
|
|
|
63
|
+
- b12527ec: ZERO-4102: Add support for post-checkout redirect in middleware
|
|
53
64
|
- aef81c5d: ZERO-4034: Refactor checkout API to use dynamic store imports for improved performance
|
|
54
65
|
- 0754c835: ZERO-4063: Add support for optional Redis password in cache handlers
|
|
55
|
-
- c6c5c1cd: ZERO-4128: Use cookies API for pz-pos-error to ensure delivery on 303 redirects
|
|
56
|
-
|
|
57
|
-
## 2.0.0-beta.13
|
|
58
|
-
|
|
59
|
-
### Minor Changes
|
|
60
|
-
|
|
61
|
-
- b12527ec: ZERO-4102: Add support for post-checkout redirect in middleware
|
|
62
66
|
- a9f5cdb1: ZERO-4102: Fix post-checkout condition to additionally check for empty search parameters.
|
|
63
67
|
- 01ee41f1: ZERO-4102: Implement a post-checkout flow by dynamically determining checkout paths and managing a pz-post-checkout-flow cookie.
|
|
68
|
+
- c6c5c1cd: ZERO-4128: Use cookies API for pz-pos-error to ensure delivery on 303 redirects
|
|
69
|
+
|
|
70
|
+
## 1.119.0
|
|
64
71
|
|
|
65
72
|
## 1.118.0
|
|
66
73
|
|
package/assets/styles/index.scss
CHANGED
|
@@ -51,3 +51,87 @@
|
|
|
51
51
|
z-index: 1001;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
.container-droppable {
|
|
56
|
+
outline: 2px dashed #4482ff !important;
|
|
57
|
+
outline-offset: -2px;
|
|
58
|
+
background-color: rgba(96, 165, 250, 0.1) !important;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.container-dragover {
|
|
62
|
+
background-color: rgba(59, 130, 246, 0.1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.container-dragover::after {
|
|
66
|
+
content: '';
|
|
67
|
+
position: absolute;
|
|
68
|
+
top: 0;
|
|
69
|
+
left: 0;
|
|
70
|
+
right: 0;
|
|
71
|
+
bottom: 0;
|
|
72
|
+
border: 2px solid #4482ff;
|
|
73
|
+
pointer-events: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dragging * {
|
|
77
|
+
cursor: default !important;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.dragging [data-component='true'] {
|
|
81
|
+
cursor: grabbing !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dragging .drop-target {
|
|
85
|
+
cursor: grab !important;
|
|
86
|
+
outline: 2px dashed #4482ff !important;
|
|
87
|
+
outline-offset: -2px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.dragging .no-drop-target {
|
|
91
|
+
cursor: no-drop !important;
|
|
92
|
+
outline: 2px dashed #e63946 !important;
|
|
93
|
+
outline-offset: -2px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[data-component='true'] {
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.carousel-container {
|
|
101
|
+
li[aria-hidden='false'] {
|
|
102
|
+
div {
|
|
103
|
+
display: block;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.posts-slider-carousel {
|
|
109
|
+
width: 100%;
|
|
110
|
+
|
|
111
|
+
.react-multi-carousel-track {
|
|
112
|
+
align-items: stretch;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.posts-slider-dots {
|
|
117
|
+
position: relative;
|
|
118
|
+
display: flex;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
gap: 8px;
|
|
121
|
+
margin-top: 16px;
|
|
122
|
+
padding: 0;
|
|
123
|
+
list-style: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.posts-slider-dots li button {
|
|
127
|
+
width: 8px;
|
|
128
|
+
height: 8px;
|
|
129
|
+
border-radius: 9999px;
|
|
130
|
+
border: none;
|
|
131
|
+
background: rgba(0, 0, 0, 0.25);
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.posts-slider-dots li.react-multi-carousel-dot--active button {
|
|
136
|
+
background: rgba(0, 0, 0, 0.6);
|
|
137
|
+
}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
+
import { useCallback, useEffect } from 'react';
|
|
4
5
|
import { useMobileIframeHandler } from '../hooks';
|
|
6
|
+
import { useAppDispatch, useAppSelector } from '../redux/hooks';
|
|
7
|
+
import {
|
|
8
|
+
setComponents,
|
|
9
|
+
setDataSources,
|
|
10
|
+
setDesignMode,
|
|
11
|
+
setDraggingActive,
|
|
12
|
+
setPlaceholders,
|
|
13
|
+
setResponsive,
|
|
14
|
+
setSelectedComponentId,
|
|
15
|
+
setSelectedPlaceholder,
|
|
16
|
+
setSelectedWidget
|
|
17
|
+
} from '../redux/reducers/widget';
|
|
5
18
|
import { LoggerPopup } from './logger-popup';
|
|
6
19
|
import { LoggerProvider } from '../hooks/use-logger-context';
|
|
7
20
|
import * as Sentry from '@sentry/nextjs';
|
|
8
21
|
import { initSentry } from '../sentry';
|
|
9
|
-
import { useEffect } from 'react';
|
|
10
22
|
|
|
11
23
|
export default function ClientRoot({
|
|
12
24
|
children,
|
|
@@ -18,6 +30,100 @@ export default function ClientRoot({
|
|
|
18
30
|
const { preventPageRender } = useMobileIframeHandler({
|
|
19
31
|
sessionId: sessionId || ''
|
|
20
32
|
});
|
|
33
|
+
const { components } = useAppSelector((state) => state.widget);
|
|
34
|
+
const dispatch = useAppDispatch();
|
|
35
|
+
|
|
36
|
+
const postMessage = (message: {
|
|
37
|
+
type: string;
|
|
38
|
+
data: Record<string, unknown>;
|
|
39
|
+
}) => {
|
|
40
|
+
window.parent.postMessage(message, '*');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleWidgetMessage = useCallback(
|
|
44
|
+
(event: MessageEvent) => {
|
|
45
|
+
if (event.data.type === 'UPDATE_COMPONENTS') {
|
|
46
|
+
const { components } = event.data.data;
|
|
47
|
+
|
|
48
|
+
dispatch(setComponents(components));
|
|
49
|
+
} else if (event.data.type === 'UPDATE_WIDGET_SYSTEM') {
|
|
50
|
+
const {
|
|
51
|
+
responsive,
|
|
52
|
+
designMode,
|
|
53
|
+
selectedComponentId,
|
|
54
|
+
placeholders,
|
|
55
|
+
dataSources
|
|
56
|
+
} = event.data.data;
|
|
57
|
+
|
|
58
|
+
dispatch(setResponsive(responsive));
|
|
59
|
+
dispatch(setDesignMode(designMode));
|
|
60
|
+
dispatch(setSelectedComponentId(selectedComponentId));
|
|
61
|
+
dispatch(setPlaceholders(placeholders));
|
|
62
|
+
dispatch(setDataSources(dataSources));
|
|
63
|
+
} else if (event.data.type === 'SELECT_PLACEHOLDER') {
|
|
64
|
+
const { placeholderSlug } = event.data.data;
|
|
65
|
+
|
|
66
|
+
dispatch(setSelectedPlaceholder(placeholderSlug));
|
|
67
|
+
} else if (event.data.type === 'SELECT_WIDGET') {
|
|
68
|
+
const { widgetSlug } = event.data.data;
|
|
69
|
+
|
|
70
|
+
dispatch(setSelectedWidget(widgetSlug));
|
|
71
|
+
|
|
72
|
+
postMessage({
|
|
73
|
+
type: 'SELECT_COMPONENT',
|
|
74
|
+
data: {
|
|
75
|
+
componentId: null
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
} else if (event.data.type === 'DRAG_START') {
|
|
79
|
+
dispatch(setDraggingActive(true));
|
|
80
|
+
} else if (event.data.type === 'DRAG_END') {
|
|
81
|
+
dispatch(setDraggingActive(false));
|
|
82
|
+
} else if (event.data.type === 'GET_DROP_TARGET') {
|
|
83
|
+
const { x, y, component } = event.data.data;
|
|
84
|
+
const elements = document.elementsFromPoint(x, y);
|
|
85
|
+
const dropTargetId = elements
|
|
86
|
+
.find((element) => element.hasAttribute('data-component'))
|
|
87
|
+
?.getAttribute('data-id');
|
|
88
|
+
|
|
89
|
+
postMessage({
|
|
90
|
+
type: 'DROP_TARGET_RESPONSE',
|
|
91
|
+
data: {
|
|
92
|
+
targetId: dropTargetId,
|
|
93
|
+
component
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
} else if (event.data.type === 'SET_COOKIE') {
|
|
97
|
+
const { key, value } = event.data.data.value;
|
|
98
|
+
|
|
99
|
+
if (key && value) {
|
|
100
|
+
let cookieString = '';
|
|
101
|
+
if (window.parent !== window) {
|
|
102
|
+
cookieString = `${key}=${value}; path=/; SameSite=None; Secure`;
|
|
103
|
+
} else if (key === 'widget_builder') {
|
|
104
|
+
cookieString = `${key}=false; path=/; SameSite=None; Secure`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (cookieString) {
|
|
108
|
+
document.cookie = cookieString;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[dispatch, components]
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
window.addEventListener('message', handleWidgetMessage);
|
|
118
|
+
|
|
119
|
+
if (window.parent === window) {
|
|
120
|
+
document.cookie = 'widget_builder=false; path=/; SameSite=None; Secure';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return () => {
|
|
124
|
+
window.removeEventListener('message', handleWidgetMessage);
|
|
125
|
+
};
|
|
126
|
+
}, [handleWidgetMessage]);
|
|
21
127
|
|
|
22
128
|
const initializeSentry = async () => {
|
|
23
129
|
const response = await fetch('/api/sentry', { next: { revalidate: 0 } });
|
package/components/link.tsx
CHANGED
|
@@ -19,26 +19,56 @@ export const Link = ({ children, href, ...rest }: LinkProps) => {
|
|
|
19
19
|
return '#';
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
if (typeof href === 'string') {
|
|
23
|
+
const trimmedHref = href.trim();
|
|
24
|
+
if (!trimmedHref) {
|
|
25
|
+
return '#';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (urlSchemes.some((scheme) => trimmedHref.startsWith(scheme))) {
|
|
29
|
+
if (
|
|
30
|
+
trimmedHref.startsWith('mailto:') ||
|
|
31
|
+
trimmedHref.startsWith('tel:')
|
|
32
|
+
) {
|
|
33
|
+
return trimmedHref;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
new URL(trimmedHref);
|
|
38
|
+
return trimmedHref;
|
|
39
|
+
} catch {
|
|
40
|
+
return '#';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
try {
|
|
45
|
+
new URL(trimmedHref, 'http://localhost');
|
|
46
|
+
} catch {
|
|
47
|
+
return '#';
|
|
48
|
+
}
|
|
31
49
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
const pathnameWithoutLocale = trimmedHref.replace(
|
|
51
|
+
urlLocaleMatcherRegex,
|
|
52
|
+
''
|
|
53
|
+
);
|
|
54
|
+
const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
|
|
55
|
+
|
|
56
|
+
if (localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
57
|
+
return hrefWithLocale;
|
|
58
|
+
} else if (
|
|
59
|
+
localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale &&
|
|
60
|
+
locale !== defaultLocaleValue
|
|
61
|
+
) {
|
|
62
|
+
return hrefWithLocale;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return trimmedHref || '#';
|
|
39
66
|
}
|
|
40
67
|
|
|
41
|
-
|
|
68
|
+
if (typeof href !== 'string') {
|
|
69
|
+
return href;
|
|
70
|
+
}
|
|
71
|
+
return '#';
|
|
42
72
|
}, [href, defaultLocaleValue, locale, localeUrlStrategy]);
|
|
43
73
|
|
|
44
74
|
return (
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { BlockRendererProps } from './block-renderer-registry';
|
|
3
|
+
import ThemeBlock from '../theme-block';
|
|
4
|
+
|
|
5
|
+
const AccordionBlock: React.FC<BlockRendererProps> = ({
|
|
6
|
+
block,
|
|
7
|
+
placeholderId,
|
|
8
|
+
sectionId,
|
|
9
|
+
isDesigner,
|
|
10
|
+
selectedBlockId,
|
|
11
|
+
currentBreakpoint = 'desktop'
|
|
12
|
+
}) => {
|
|
13
|
+
const childBlocks = block.blocks || [];
|
|
14
|
+
|
|
15
|
+
const defaultOpen = block.properties?.defaultOpen === true;
|
|
16
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
17
|
+
|
|
18
|
+
const headingBlock = childBlocks.find((b) => b.label === 'Accordion Heading');
|
|
19
|
+
const contentBlock = childBlocks.find((b) => b.label === 'Accordion Content');
|
|
20
|
+
|
|
21
|
+
if (!headingBlock && !contentBlock) {
|
|
22
|
+
return (
|
|
23
|
+
<div className="p-4 text-gray-400 border border-dashed border-gray-300 rounded">
|
|
24
|
+
No accordion content
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const renderHeading = () => {
|
|
30
|
+
if (!headingBlock) return null;
|
|
31
|
+
|
|
32
|
+
return headingBlock.blocks
|
|
33
|
+
?.filter((childBlock) => (isDesigner ? true : !childBlock.hidden))
|
|
34
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0))
|
|
35
|
+
.map((childBlock, index) => {
|
|
36
|
+
const modifiedBlock =
|
|
37
|
+
childBlock.type === 'icon'
|
|
38
|
+
? {
|
|
39
|
+
...childBlock,
|
|
40
|
+
styles: {
|
|
41
|
+
...childBlock.styles,
|
|
42
|
+
transform: {
|
|
43
|
+
desktop: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
44
|
+
mobile: isOpen ? 'rotate(180deg)' : 'rotate(0deg)'
|
|
45
|
+
},
|
|
46
|
+
transition: {
|
|
47
|
+
desktop: 'transform 0.3s ease-in-out',
|
|
48
|
+
mobile: 'transform 0.3s ease-in-out'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
: childBlock;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<ThemeBlock
|
|
56
|
+
key={childBlock.id || `heading-block-${index}`}
|
|
57
|
+
block={modifiedBlock}
|
|
58
|
+
placeholderId={placeholderId}
|
|
59
|
+
sectionId={sectionId}
|
|
60
|
+
isDesigner={isDesigner}
|
|
61
|
+
isSelected={selectedBlockId === childBlock.id}
|
|
62
|
+
selectedBlockId={selectedBlockId}
|
|
63
|
+
currentBreakpoint={currentBreakpoint}
|
|
64
|
+
onMoveUp={undefined}
|
|
65
|
+
onMoveDown={undefined}
|
|
66
|
+
onDuplicate={undefined}
|
|
67
|
+
onToggleVisibility={undefined}
|
|
68
|
+
onDelete={undefined}
|
|
69
|
+
onRename={undefined}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const renderContent = () => {
|
|
76
|
+
if (!contentBlock) return null;
|
|
77
|
+
|
|
78
|
+
return contentBlock.blocks
|
|
79
|
+
?.filter((childBlock) => (isDesigner ? true : !childBlock.hidden))
|
|
80
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0))
|
|
81
|
+
.map((childBlock, index) => {
|
|
82
|
+
return (
|
|
83
|
+
<ThemeBlock
|
|
84
|
+
key={childBlock.id || `content-block-${index}`}
|
|
85
|
+
block={childBlock}
|
|
86
|
+
placeholderId={placeholderId}
|
|
87
|
+
sectionId={sectionId}
|
|
88
|
+
isDesigner={isDesigner}
|
|
89
|
+
isSelected={selectedBlockId === childBlock.id}
|
|
90
|
+
selectedBlockId={selectedBlockId}
|
|
91
|
+
currentBreakpoint={currentBreakpoint}
|
|
92
|
+
onMoveUp={undefined}
|
|
93
|
+
onMoveDown={undefined}
|
|
94
|
+
onDuplicate={undefined}
|
|
95
|
+
onToggleVisibility={undefined}
|
|
96
|
+
onDelete={undefined}
|
|
97
|
+
onRename={undefined}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const toggleAccordion = (e: React.MouseEvent) => {
|
|
104
|
+
if (isDesigner) {
|
|
105
|
+
const target = e.target as HTMLElement;
|
|
106
|
+
if (
|
|
107
|
+
target.closest('[data-action-toolbar]') ||
|
|
108
|
+
target.closest('button') ||
|
|
109
|
+
target.closest('input')
|
|
110
|
+
) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
setIsOpen(!isOpen);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<>
|
|
119
|
+
<div style={{ cursor: 'pointer' }} onClick={toggleAccordion}>
|
|
120
|
+
{renderHeading()}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div
|
|
124
|
+
style={{
|
|
125
|
+
display: 'grid',
|
|
126
|
+
gridTemplateRows: isOpen ? '1fr' : '0fr',
|
|
127
|
+
transition: 'grid-template-rows 0.3s ease-in-out'
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
<div style={{ overflow: 'hidden' }}>{renderContent()}</div>
|
|
131
|
+
</div>
|
|
132
|
+
</>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export default AccordionBlock;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Block } from '../theme-block';
|
|
3
|
+
import TextBlock from './text-block';
|
|
4
|
+
import ImageBlock from './image-block';
|
|
5
|
+
import DividerBlock from './divider-block';
|
|
6
|
+
import GroupBlock from './group-block';
|
|
7
|
+
import ButtonBlock from './button-block';
|
|
8
|
+
import IconBlock from './icon-block';
|
|
9
|
+
import VideoBlock from './video-block';
|
|
10
|
+
import AccordionBlock from './accordion-block';
|
|
11
|
+
import TabBlock from './tab-block';
|
|
12
|
+
import CounterBlock from './counter-block';
|
|
13
|
+
import ImageGalleryBlock from './image-gallery-block';
|
|
14
|
+
import InputBlock from './input-block';
|
|
15
|
+
import SliderBlock from './slider-block';
|
|
16
|
+
import HotspotBlock from './hotspot-block';
|
|
17
|
+
import LinkBlock from './link-block';
|
|
18
|
+
import LottieBlock from './lottie-block';
|
|
19
|
+
import EmbedBlock from './embed-block';
|
|
20
|
+
import MapBlock from './map-block';
|
|
21
|
+
|
|
22
|
+
export interface BlockRendererProps {
|
|
23
|
+
block: Block;
|
|
24
|
+
placeholderId: string;
|
|
25
|
+
sectionId: string;
|
|
26
|
+
isDesigner: boolean;
|
|
27
|
+
isSelected?: boolean;
|
|
28
|
+
selectedBlockId?: string | null;
|
|
29
|
+
currentBreakpoint?: string;
|
|
30
|
+
onMoveUp?: () => void;
|
|
31
|
+
onMoveDown?: () => void;
|
|
32
|
+
onDuplicate?: () => void;
|
|
33
|
+
onToggleVisibility?: () => void;
|
|
34
|
+
onDelete?: () => void;
|
|
35
|
+
onRename?: (newLabel: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type BlockRenderer = React.ComponentType<BlockRendererProps>;
|
|
39
|
+
|
|
40
|
+
class BlockRendererRegistry {
|
|
41
|
+
private renderers: Map<string, BlockRenderer> = new Map();
|
|
42
|
+
|
|
43
|
+
register(type: string, renderer: BlockRenderer) {
|
|
44
|
+
this.renderers.set(type, renderer);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getRenderer(type: string): BlockRenderer | undefined {
|
|
48
|
+
return this.renderers.get(type);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
hasRenderer(type: string): boolean {
|
|
52
|
+
return this.renderers.has(type);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const blockRendererRegistry = new BlockRendererRegistry();
|
|
57
|
+
|
|
58
|
+
blockRendererRegistry.register('text', TextBlock);
|
|
59
|
+
blockRendererRegistry.register('image', ImageBlock);
|
|
60
|
+
blockRendererRegistry.register('divider', DividerBlock);
|
|
61
|
+
blockRendererRegistry.register('group', GroupBlock);
|
|
62
|
+
blockRendererRegistry.register('button', ButtonBlock);
|
|
63
|
+
blockRendererRegistry.register('icon', IconBlock);
|
|
64
|
+
blockRendererRegistry.register('video', VideoBlock);
|
|
65
|
+
blockRendererRegistry.register('accordion', AccordionBlock);
|
|
66
|
+
blockRendererRegistry.register('tab', TabBlock);
|
|
67
|
+
blockRendererRegistry.register('counter', CounterBlock);
|
|
68
|
+
blockRendererRegistry.register('image-gallery', ImageGalleryBlock);
|
|
69
|
+
blockRendererRegistry.register('input', InputBlock);
|
|
70
|
+
blockRendererRegistry.register('slider', SliderBlock);
|
|
71
|
+
blockRendererRegistry.register('hotspot', HotspotBlock);
|
|
72
|
+
blockRendererRegistry.register('link', LinkBlock);
|
|
73
|
+
blockRendererRegistry.register('lottie', LottieBlock);
|
|
74
|
+
blockRendererRegistry.register('embed', EmbedBlock);
|
|
75
|
+
blockRendererRegistry.register('map', MapBlock);
|
|
76
|
+
|
|
77
|
+
export default blockRendererRegistry;
|