@developer_tribe/react-builder 1.0.2 → 1.0.4
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/dist/AttributesEditor.d.ts +3 -1
- package/dist/RenderPage.d.ts +2 -1
- package/dist/android.svg +43 -0
- package/dist/apple.svg +16 -0
- package/dist/attributes-editor/Field.d.ts +4 -2
- package/dist/attributes-editor/SizeField.d.ts +9 -0
- package/dist/attributes-editor/SpecialCategorySection.d.ts +2 -1
- package/dist/build-components/BackgroundImage/BackgroundImage.d.ts +5 -0
- package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +45 -0
- package/dist/build-components/Button/ButtonProps.generated.d.ts +8 -0
- package/dist/build-components/Carousel/CarouselProps.generated.d.ts +8 -0
- package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +8 -0
- package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +8 -0
- package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +8 -0
- package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +8 -0
- package/dist/build-components/Image/ImageProps.generated.d.ts +8 -0
- package/dist/build-components/Onboard/OnboardProps.generated.d.ts +8 -0
- package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -1
- package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +8 -0
- package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +9 -3
- package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +8 -0
- package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +9 -1
- package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +8 -0
- package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +8 -1
- package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +8 -0
- package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +8 -0
- package/dist/build-components/Text/TextProps.generated.d.ts +8 -0
- package/dist/build-components/View/ViewProps.generated.d.ts +8 -0
- package/dist/build-components/index.d.ts +2 -1
- package/dist/build-components/patterns.generated.d.ts +1612 -46
- package/dist/components/AttributesEditorPanel.d.ts +3 -4
- package/dist/components/Builder.d.ts +2 -1
- package/dist/components/BuilderButton.d.ts +9 -0
- package/dist/components/JsonTextEditor.d.ts +9 -0
- package/dist/index.cjs.js +5 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +5 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/modals/ColorModal.d.ts +3 -1
- package/dist/pages/ProjectPage.d.ts +3 -3
- package/dist/pages/tabs/BuilderPanel.d.ts +8 -0
- package/dist/pages/tabs/SideTool.d.ts +8 -0
- package/dist/store.d.ts +9 -1
- package/dist/styles.css +1 -1
- package/dist/types/Project.d.ts +11 -0
- package/dist/utils/analyseNode.d.ts +1 -0
- package/dist/utils/extractImageStyle.d.ts +2 -1
- package/dist/utils/extractTextStyle.d.ts +8 -1
- package/dist/utils/extractViewStyle.d.ts +7 -1
- package/dist/utils/parseColor.d.ts +7 -0
- package/dist/utils/selection.d.ts +7 -0
- package/dist/utils/useMergedStyle.d.ts +2 -0
- package/package.json +2 -5
- package/src/.DS_Store +0 -0
- package/src/AttributesEditor.tsx +83 -16
- package/src/RenderPage.tsx +86 -4
- package/src/attributes-editor/Field.tsx +60 -165
- package/src/attributes-editor/SizeField.tsx +184 -0
- package/src/attributes-editor/SpecialCategorySection.tsx +12 -4
- package/src/build-components/BackgroundImage/BackgroundImage.tsx +77 -0
- package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +61 -0
- package/src/build-components/BackgroundImage/pattern.json +45 -0
- package/src/build-components/Button/Button.tsx +29 -4
- package/src/build-components/Button/ButtonProps.generated.ts +8 -0
- package/src/build-components/Carousel/Carousel.tsx +25 -3
- package/src/build-components/Carousel/CarouselProps.generated.ts +8 -0
- package/src/build-components/CarouselButtons/CarouselButtons.tsx +19 -4
- package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +8 -0
- package/src/build-components/CarouselDots/CarouselDots.tsx +13 -4
- package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +8 -0
- package/src/build-components/CarouselItem/CarouselItem.tsx +20 -4
- package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +8 -0
- package/src/build-components/CarouselProvider/CarouselProvider.tsx +14 -3
- package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +8 -0
- package/src/build-components/Image/Image.tsx +27 -9
- package/src/build-components/Image/ImageProps.generated.ts +8 -0
- package/src/build-components/Image/pattern.json +1 -9
- package/src/build-components/Onboard/Onboard.tsx +2 -2
- package/src/build-components/Onboard/OnboardProps.generated.ts +8 -0
- package/src/build-components/OnboardButton/OnboardButton.tsx +11 -7
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +8 -1
- package/src/build-components/OnboardButtons/OnboardButtons.tsx +17 -5
- package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +8 -0
- package/src/build-components/OnboardDot/OnboardDot.tsx +68 -39
- package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +9 -3
- package/src/build-components/OnboardDot/pattern.json +3 -19
- package/src/build-components/OnboardFooter/OnboardFooter.tsx +37 -14
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +8 -0
- package/src/build-components/OnboardImage/OnboardImage.tsx +28 -6
- package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +9 -1
- package/src/build-components/OnboardItem/OnboardItem.tsx +15 -14
- package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +8 -0
- package/src/build-components/OnboardProvider/OnboardProvider.tsx +35 -20
- package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +8 -1
- package/src/build-components/OnboardProvider/pattern.json +0 -8
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +8 -0
- package/src/build-components/OnboardSubtitle/pattern.json +1 -1
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +8 -0
- package/src/build-components/OnboardTitle/pattern.json +1 -1
- package/src/build-components/RenderNode.generated.tsx +3 -0
- package/src/build-components/Text/Text.tsx +28 -10
- package/src/build-components/Text/TextProps.generated.ts +8 -0
- package/src/build-components/View/View.tsx +25 -3
- package/src/build-components/View/ViewProps.generated.ts +8 -0
- package/src/build-components/View/pattern.json +67 -1
- package/src/build-components/index.ts +5 -0
- package/src/build-components/patterns.generated.ts +1620 -46
- package/src/components/AttributesEditorPanel.tsx +13 -6
- package/src/components/Builder.tsx +200 -56
- package/src/components/BuilderButton.tsx +127 -0
- package/src/components/DeviceNavigationBar.tsx +0 -1
- package/src/components/EditorHeader.tsx +11 -1
- package/src/components/JsonTextEditor.tsx +185 -0
- package/src/index.ts +2 -2
- package/src/mockOS/components/MockOSRouter.tsx +17 -3
- package/src/mockOS/context/MockOSContext.tsx +0 -5
- package/src/mockOS/managers/mockPermissionManager.ts +0 -4
- package/src/mockOS/managers/navigationManager.ts +1 -6
- package/src/modals/ColorModal.tsx +306 -71
- package/src/modals/LocalicationModal.tsx +4 -5
- package/src/modals/Modal.tsx +8 -1
- package/src/pages/ProjectPage.tsx +299 -55
- package/src/pages/tabs/{BuilderTab.tsx → BuilderPanel.tsx} +13 -9
- package/src/pages/tabs/SideTool.tsx +260 -0
- package/src/size-matters/index.ts +6 -0
- package/src/store.ts +18 -1
- package/src/styles/base/_global.scss +163 -7
- package/src/styles/components/_attributes-editor.scss +12 -0
- package/src/styles/components/_editor-shell.scss +25 -0
- package/src/styles/foundation/_sizes.scss +1 -1
- package/src/styles/layout/_builder.scss +66 -10
- package/src/styles/modals/_color-modal.scss +59 -1
- package/src/styles/utilities/_carousel.scss +9 -8
- package/src/types/Project.ts +14 -0
- package/src/utils/analyseNode.ts +98 -0
- package/src/utils/extractImageStyle.ts +3 -6
- package/src/utils/extractTextStyle.ts +19 -82
- package/src/utils/extractViewStyle.ts +41 -12
- package/src/utils/parseColor.ts +43 -0
- package/src/utils/selection.ts +24 -0
- package/src/utils/useMergedStyle.ts +16 -0
- package/dist/pages/tabs/BuilderTab.d.ts +0 -9
- package/dist/pages/tabs/DebugTab.d.ts +0 -7
- package/dist/pages/tabs/PreviewTab.d.ts +0 -3
- package/src/pages/tabs/DebugTab.tsx +0 -64
- package/src/pages/tabs/PreviewTab.tsx +0 -206
|
@@ -1,35 +1,82 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Node, RenderPage, Project } from '..';
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { Node, NodeData, RenderPage, Project, ProjectColors } from '..';
|
|
3
3
|
import { EditorHeader } from '../components/EditorHeader';
|
|
4
4
|
import { AttributesEditorPanel } from '../components/AttributesEditorPanel';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { DebugTab } from './tabs/DebugTab';
|
|
5
|
+
import { BuilderPanel } from './tabs/BuilderPanel';
|
|
6
|
+
import { SideTool } from './tabs/SideTool';
|
|
8
7
|
import { AppConfig, defaultAppConfig } from '../types/PreviewConfig';
|
|
9
8
|
import { useRenderStore } from '../store';
|
|
10
9
|
import { logger } from '../utils/logger';
|
|
11
10
|
import { useLogRender } from '../utils/useLogRender';
|
|
12
11
|
import type { LogLevel } from '../types/Project';
|
|
12
|
+
import { analyseAndProccess } from '../utils/analyseNode';
|
|
13
13
|
import backgroundImage from '../assets/images/background.jpg';
|
|
14
|
-
export type Tab = 'builder' | 'preview';
|
|
15
|
-
|
|
16
14
|
export type ProjectPageProps = {
|
|
17
15
|
project: Project;
|
|
18
16
|
onSaveProject: (project: Project) => void;
|
|
19
17
|
appConfig?: AppConfig;
|
|
20
18
|
logLevel?: LogLevel;
|
|
19
|
+
projectColors?: ProjectColors;
|
|
21
20
|
};
|
|
22
21
|
|
|
22
|
+
const MOBILE_BREAKPOINT = 1000;
|
|
23
|
+
|
|
23
24
|
export function ProjectPage({
|
|
24
25
|
project,
|
|
25
26
|
appConfig = defaultAppConfig,
|
|
26
27
|
onSaveProject,
|
|
27
28
|
logLevel,
|
|
29
|
+
projectColors,
|
|
28
30
|
}: ProjectPageProps) {
|
|
29
31
|
useLogRender('ProjectPage');
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
32
|
+
const resolvedProjectColors = projectColors ?? project.projectColors;
|
|
33
|
+
const { current, setCurrent, setProjectColors } = useRenderStore((s) => ({
|
|
34
|
+
current: s.current,
|
|
35
|
+
setCurrent: s.setCurrent,
|
|
36
|
+
setProjectColors: s.setProjectColors,
|
|
37
|
+
}));
|
|
38
|
+
const [editorData, setEditorData] = useState<Node>(null);
|
|
39
|
+
const [mobilePanel, setMobilePanel] = useState<
|
|
40
|
+
'builder' | 'attributes' | null
|
|
41
|
+
>(null);
|
|
42
|
+
const [isMobile, setIsMobile] = useState<boolean>(() => {
|
|
43
|
+
if (typeof window === 'undefined') return false;
|
|
44
|
+
return window.innerWidth <= MOBILE_BREAKPOINT;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const handleDeleteNode = useCallback(
|
|
48
|
+
(nodeToDelete: Node) => {
|
|
49
|
+
// Extra warning for deleting the root node (editorData)
|
|
50
|
+
if (nodeToDelete === editorData) {
|
|
51
|
+
const shouldDeleteRoot = window.confirm(
|
|
52
|
+
'You are about to delete the root component. This will clear the entire screen. Continue?',
|
|
53
|
+
);
|
|
54
|
+
if (!shouldDeleteRoot) return;
|
|
55
|
+
setEditorData(null);
|
|
56
|
+
setCurrent(null);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const updated: Node = deleteNodeFromTree(editorData, nodeToDelete);
|
|
60
|
+
//@ts-ignore
|
|
61
|
+
setEditorData(updated);
|
|
62
|
+
|
|
63
|
+
if (current === nodeToDelete) {
|
|
64
|
+
setCurrent(updated);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isNodeRecord(current) && nodeHasChild(current, nodeToDelete)) {
|
|
69
|
+
const currentKey = current.key;
|
|
70
|
+
if (currentKey) {
|
|
71
|
+
const nextCurrent = findNodeByKey(updated, currentKey);
|
|
72
|
+
setCurrent(nextCurrent ?? updated);
|
|
73
|
+
} else {
|
|
74
|
+
setCurrent(updated);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[editorData, current],
|
|
79
|
+
);
|
|
33
80
|
|
|
34
81
|
useEffect(() => {
|
|
35
82
|
logger.info('ProjectPage', 'mount', { projectName: project.name });
|
|
@@ -40,11 +87,59 @@ export function ProjectPage({
|
|
|
40
87
|
};
|
|
41
88
|
}, [appConfig, project.name]);
|
|
42
89
|
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
setProjectColors(resolvedProjectColors);
|
|
92
|
+
return () => setProjectColors(undefined);
|
|
93
|
+
}, [resolvedProjectColors, setProjectColors]);
|
|
94
|
+
|
|
43
95
|
useEffect(() => {
|
|
44
96
|
if (!logLevel) return;
|
|
45
97
|
logger.setLevel(logLevel);
|
|
46
98
|
}, [logLevel]);
|
|
47
99
|
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
function handleResize() {
|
|
102
|
+
setIsMobile(window.innerWidth <= MOBILE_BREAKPOINT);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
handleResize();
|
|
106
|
+
window.addEventListener('resize', handleResize);
|
|
107
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
108
|
+
}, []);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
setMobilePanel(null);
|
|
112
|
+
}, [isMobile]);
|
|
113
|
+
|
|
114
|
+
const toggleMobilePanel = (panel: 'builder' | 'attributes') => {
|
|
115
|
+
setMobilePanel((prev) => (prev === panel ? null : panel));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const closeMobilePanels = () => {
|
|
119
|
+
setMobilePanel(null);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const leftPanelIsOpen = !isMobile || mobilePanel === 'builder';
|
|
123
|
+
const attributesPanelIsOpen = !isMobile || mobilePanel === 'attributes';
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
try {
|
|
127
|
+
const processed = analyseAndProccess(project.data);
|
|
128
|
+
if (!processed) {
|
|
129
|
+
setEditorData(null);
|
|
130
|
+
setCurrent(null);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
setEditorData(processed);
|
|
134
|
+
setCurrent(processed);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// eslint-disable-next-line no-alert
|
|
137
|
+
alert('Duplicate node key detected. Please check your project data.');
|
|
138
|
+
setEditorData(null);
|
|
139
|
+
setCurrent(null);
|
|
140
|
+
}
|
|
141
|
+
}, [project.data]);
|
|
142
|
+
|
|
48
143
|
return (
|
|
49
144
|
<div className="container-full">
|
|
50
145
|
<EditorHeader
|
|
@@ -64,53 +159,83 @@ export function ProjectPage({
|
|
|
64
159
|
editorData={editorData}
|
|
65
160
|
setEditorData={setEditorData}
|
|
66
161
|
/>
|
|
162
|
+
{isMobile && (
|
|
163
|
+
<div
|
|
164
|
+
className="mobile-panel-toggle"
|
|
165
|
+
role="group"
|
|
166
|
+
aria-label="Editor panels"
|
|
167
|
+
>
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
className={`mobile-panel-toggle__button${mobilePanel === 'builder' ? ' mobile-panel-toggle__button--active' : ''}`}
|
|
171
|
+
aria-label="Toggle builder panel"
|
|
172
|
+
aria-expanded={mobilePanel === 'builder'}
|
|
173
|
+
aria-controls="split-left-panel"
|
|
174
|
+
onClick={() => toggleMobilePanel('builder')}
|
|
175
|
+
>
|
|
176
|
+
<span className="mobile-panel-toggle__icon" aria-hidden="true">
|
|
177
|
+
<svg viewBox="0 0 16 12" role="presentation" focusable="false">
|
|
178
|
+
<path
|
|
179
|
+
d="M1 1h14M1 6h14M1 11h14"
|
|
180
|
+
stroke="currentColor"
|
|
181
|
+
strokeWidth="2"
|
|
182
|
+
strokeLinecap="round"
|
|
183
|
+
fill="none"
|
|
184
|
+
/>
|
|
185
|
+
</svg>
|
|
186
|
+
</span>
|
|
187
|
+
<span className="mobile-panel-toggle__label">Builder</span>
|
|
188
|
+
</button>
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
className={`mobile-panel-toggle__button${mobilePanel === 'attributes' ? ' mobile-panel-toggle__button--active' : ''}`}
|
|
192
|
+
aria-label="Toggle attributes panel"
|
|
193
|
+
aria-expanded={mobilePanel === 'attributes'}
|
|
194
|
+
aria-controls="split-attributes-panel"
|
|
195
|
+
onClick={() => toggleMobilePanel('attributes')}
|
|
196
|
+
>
|
|
197
|
+
<span className="mobile-panel-toggle__icon" aria-hidden="true">
|
|
198
|
+
<svg viewBox="0 0 16 12" role="presentation" focusable="false">
|
|
199
|
+
<path
|
|
200
|
+
d="M1 1h14M1 6h14M1 11h14"
|
|
201
|
+
stroke="currentColor"
|
|
202
|
+
strokeWidth="2"
|
|
203
|
+
strokeLinecap="round"
|
|
204
|
+
fill="none"
|
|
205
|
+
/>
|
|
206
|
+
</svg>
|
|
207
|
+
</span>
|
|
208
|
+
<span className="mobile-panel-toggle__label">Attributes</span>
|
|
209
|
+
</button>
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
67
212
|
<div className="editor-container">
|
|
68
|
-
<div
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
213
|
+
<div
|
|
214
|
+
id="split-left-panel"
|
|
215
|
+
className={`split-left${leftPanelIsOpen ? ' is-open' : ''}`}
|
|
216
|
+
aria-hidden={isMobile && !leftPanelIsOpen}
|
|
217
|
+
>
|
|
218
|
+
{isMobile && (
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
className="split-panel__close"
|
|
222
|
+
aria-label="Close builder panel"
|
|
223
|
+
onClick={closeMobilePanels}
|
|
74
224
|
>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<span className="editor-tab__label">Builder</span>
|
|
85
|
-
</button>
|
|
86
|
-
<button
|
|
87
|
-
className={`editor-tab ${tab === 'preview' ? 'editor-tab--active' : ''}`}
|
|
88
|
-
role="tab"
|
|
89
|
-
aria-selected={tab === 'preview'}
|
|
90
|
-
onClick={() => {
|
|
91
|
-
setTab('preview');
|
|
92
|
-
logger.info('ProjectPage', 'tab change', { to: 'preview' });
|
|
93
|
-
}}
|
|
94
|
-
>
|
|
95
|
-
<span className="editor-tab__label">Preview Config</span>
|
|
96
|
-
</button>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
{tab === 'builder' && (
|
|
100
|
-
<BuilderTab
|
|
101
|
-
data={editorData}
|
|
102
|
-
setData={setEditorData}
|
|
103
|
-
current={current}
|
|
104
|
-
setCurrent={setCurrent}
|
|
105
|
-
/>
|
|
106
|
-
)}
|
|
107
|
-
|
|
108
|
-
{tab === 'preview' && <PreviewTab />}
|
|
225
|
+
Close
|
|
226
|
+
</button>
|
|
227
|
+
)}
|
|
228
|
+
<div>
|
|
229
|
+
<BuilderPanel
|
|
230
|
+
data={editorData}
|
|
231
|
+
setData={setEditorData}
|
|
232
|
+
onDeleteNode={handleDeleteNode}
|
|
233
|
+
/>
|
|
109
234
|
</div>
|
|
110
235
|
</div>
|
|
111
236
|
<div className="split-right">
|
|
112
237
|
<div className="split-right__controls">
|
|
113
|
-
<
|
|
238
|
+
<SideTool data={editorData} setData={setEditorData} />
|
|
114
239
|
</div>
|
|
115
240
|
<div
|
|
116
241
|
className="split-right-background"
|
|
@@ -118,12 +243,26 @@ export function ProjectPage({
|
|
|
118
243
|
backgroundImage: `url(${backgroundImage})`,
|
|
119
244
|
}}
|
|
120
245
|
/>
|
|
121
|
-
<RenderPage data={editorData} name={project.name} />
|
|
246
|
+
{editorData && <RenderPage data={editorData} name={project.name} />}
|
|
122
247
|
</div>
|
|
123
|
-
<div
|
|
248
|
+
<div
|
|
249
|
+
id="split-attributes-panel"
|
|
250
|
+
className={`split-third${attributesPanelIsOpen ? ' is-open' : ''}`}
|
|
251
|
+
aria-hidden={isMobile && !attributesPanelIsOpen}
|
|
252
|
+
>
|
|
253
|
+
{isMobile && (
|
|
254
|
+
<button
|
|
255
|
+
type="button"
|
|
256
|
+
className="split-panel__close"
|
|
257
|
+
aria-label="Close attributes panel"
|
|
258
|
+
onClick={closeMobilePanels}
|
|
259
|
+
>
|
|
260
|
+
Close
|
|
261
|
+
</button>
|
|
262
|
+
)}
|
|
124
263
|
<AttributesEditorPanel
|
|
125
|
-
current={current}
|
|
126
264
|
attributes={editorData}
|
|
265
|
+
projectColors={resolvedProjectColors}
|
|
127
266
|
onChange={(data) => {
|
|
128
267
|
setEditorData(data);
|
|
129
268
|
let nodeKey: string | undefined = undefined;
|
|
@@ -141,10 +280,115 @@ export function ProjectPage({
|
|
|
141
280
|
nodeKey ? { nodeKey } : undefined,
|
|
142
281
|
);
|
|
143
282
|
}}
|
|
144
|
-
setCurrent={setCurrent}
|
|
145
283
|
/>
|
|
146
284
|
</div>
|
|
285
|
+
{isMobile && mobilePanel && (
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
className="editor-container__overlay"
|
|
289
|
+
aria-label="Close active panel"
|
|
290
|
+
onClick={closeMobilePanels}
|
|
291
|
+
/>
|
|
292
|
+
)}
|
|
147
293
|
</div>
|
|
148
294
|
</div>
|
|
149
295
|
);
|
|
150
296
|
}
|
|
297
|
+
|
|
298
|
+
function deleteNodeFromTree(root: Node, target: Node): Node {
|
|
299
|
+
if (root === null || root === undefined) return root;
|
|
300
|
+
if (typeof root === 'string') return root;
|
|
301
|
+
|
|
302
|
+
if (Array.isArray(root)) {
|
|
303
|
+
let changed = false;
|
|
304
|
+
const nextChildren: Node[] = [];
|
|
305
|
+
for (const child of root) {
|
|
306
|
+
if (child === target) {
|
|
307
|
+
changed = true;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const nextChild = deleteNodeFromTree(child, target);
|
|
311
|
+
if (nextChild !== child) changed = true;
|
|
312
|
+
nextChildren.push(nextChild);
|
|
313
|
+
}
|
|
314
|
+
return changed ? nextChildren : root;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const data = root as any;
|
|
318
|
+
if ('children' in data) {
|
|
319
|
+
const prev = data.children as Node;
|
|
320
|
+
if (!prev) return root;
|
|
321
|
+
|
|
322
|
+
if (Array.isArray(prev)) {
|
|
323
|
+
let changed = false;
|
|
324
|
+
const nextChildren: Node[] = [];
|
|
325
|
+
for (const child of prev) {
|
|
326
|
+
if (child === target) {
|
|
327
|
+
changed = true;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const nextChild = deleteNodeFromTree(child, target);
|
|
331
|
+
if (nextChild !== child) changed = true;
|
|
332
|
+
nextChildren.push(nextChild);
|
|
333
|
+
}
|
|
334
|
+
if (changed) {
|
|
335
|
+
return { ...data, children: nextChildren } as Node;
|
|
336
|
+
}
|
|
337
|
+
return root;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (prev === target) {
|
|
341
|
+
return { ...data, children: '' } as Node;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const nextChild = deleteNodeFromTree(prev, target);
|
|
345
|
+
if (nextChild !== prev) {
|
|
346
|
+
return { ...data, children: nextChild } as Node;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return root;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function isNodeRecord(node: Node): node is NodeData {
|
|
354
|
+
return (
|
|
355
|
+
node !== null &&
|
|
356
|
+
node !== undefined &&
|
|
357
|
+
typeof node === 'object' &&
|
|
358
|
+
!Array.isArray(node)
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function nodeHasChild(parent: NodeData, potentialChild: Node): boolean {
|
|
363
|
+
const { children } = parent;
|
|
364
|
+
if (!children) return false;
|
|
365
|
+
if (Array.isArray(children)) {
|
|
366
|
+
return children.some((child) => child === potentialChild);
|
|
367
|
+
}
|
|
368
|
+
return children === potentialChild;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function findNodeByKey(root: Node, key?: string): Node | null {
|
|
372
|
+
if (!key) return null;
|
|
373
|
+
if (root === null || root === undefined) return null;
|
|
374
|
+
if (typeof root === 'string') return null;
|
|
375
|
+
|
|
376
|
+
if (Array.isArray(root)) {
|
|
377
|
+
for (const child of root) {
|
|
378
|
+
const found = findNodeByKey(child, key);
|
|
379
|
+
if (found) return found;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const nodeData = root as NodeData;
|
|
385
|
+
if (nodeData.key === key) {
|
|
386
|
+
return nodeData;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (nodeData.children) {
|
|
390
|
+
return findNodeByKey(nodeData.children as Node, key);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { Node } from '../..';
|
|
2
2
|
import { useLogRender } from '../../utils/useLogRender';
|
|
3
3
|
import { Builder } from '../../components/Builder';
|
|
4
|
+
import { useRenderStore } from '../../store';
|
|
4
5
|
|
|
5
|
-
type
|
|
6
|
+
type BuilderPanelProps = {
|
|
6
7
|
data: Node;
|
|
7
8
|
setData: (data: Node) => void;
|
|
8
|
-
|
|
9
|
-
setCurrent: (current: Node) => void;
|
|
9
|
+
onDeleteNode: (node: Node) => void;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export function
|
|
12
|
+
export function BuilderPanel({
|
|
13
13
|
data,
|
|
14
14
|
setData,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
onDeleteNode,
|
|
16
|
+
}: BuilderPanelProps) {
|
|
17
|
+
useLogRender('BuilderPanel');
|
|
18
|
+
const { current, setCurrent } = useRenderStore((s) => ({
|
|
19
|
+
current: s.current,
|
|
20
|
+
setCurrent: s.setCurrent,
|
|
21
|
+
}));
|
|
19
22
|
return (
|
|
20
23
|
<div
|
|
21
|
-
role="
|
|
24
|
+
role="region"
|
|
22
25
|
className="editor-panel-builder editor-panel editor-panel--active"
|
|
23
26
|
aria-hidden={false}
|
|
24
27
|
>
|
|
@@ -27,6 +30,7 @@ export function BuilderTab({
|
|
|
27
30
|
setData={setData}
|
|
28
31
|
current={current}
|
|
29
32
|
setCurrent={setCurrent}
|
|
33
|
+
onDeleteNode={onDeleteNode}
|
|
30
34
|
/>
|
|
31
35
|
</div>
|
|
32
36
|
);
|