@developer_tribe/react-builder 1.0.5 → 1.0.6
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/build-components/index.d.ts +1 -2
- package/dist/build-components/patterns.generated.d.ts +56 -439
- package/dist/components/AttributesEditorPanel.d.ts +2 -2
- package/dist/components/BottomBar.d.ts +8 -0
- package/dist/components/Checkbox.d.ts +1 -1
- package/dist/components/LoadingComponent.d.ts +1 -0
- package/dist/components/MobilePanelToggleButton.d.ts +8 -0
- package/dist/hooks/useMinimumDelay.d.ts +7 -0
- package/dist/hooks/useMobileEditorPanels.d.ts +12 -0
- package/dist/hooks/useSyncProjectPageStore.d.ts +15 -0
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.cjs.js +1 -1
- package/dist/index.native.cjs.js.map +1 -1
- package/dist/index.native.esm.js +4 -4
- package/dist/index.native.esm.js.map +1 -1
- package/dist/modals/ScreenColorsModal.d.ts +8 -0
- package/dist/modals/index.d.ts +1 -0
- package/dist/pages/tabs/BuilderPanel.d.ts +2 -2
- package/dist/store.d.ts +6 -0
- package/dist/styles.css +1 -1
- package/dist/utils/nodeTree.d.ts +5 -0
- package/package.json +1 -1
- package/src/RenderPage.tsx +4 -1
- package/src/assets/samples/carousel-sample.json +99 -81
- package/src/assets/samples/simple-1.json +8 -2
- package/src/assets/samples/simple-2.json +36 -9
- package/src/assets/samples/vpn-onboard-1.json +27 -23
- package/src/assets/samples/vpn-onboard-2.json +279 -275
- package/src/assets/samples/vpn-onboard-3.json +247 -246
- package/src/assets/samples/vpn-onboard-4.json +247 -246
- package/src/assets/samples/vpn-onboard-5.json +375 -369
- package/src/assets/samples/vpn-onboard-6.json +252 -248
- package/src/build-components/RenderNode.generated.tsx +0 -7
- package/src/build-components/View/pattern.json +2 -2
- package/src/build-components/index.ts +0 -5
- package/src/build-components/patterns.generated.ts +56 -455
- package/src/components/AttributesEditorPanel.tsx +12 -8
- package/src/components/BottomBar.tsx +236 -0
- package/src/components/EditorHeader.tsx +11 -4
- package/src/components/LoadingComponent.tsx +10 -0
- package/src/components/MobilePanelToggleButton.tsx +39 -0
- package/src/hooks/useMinimumDelay.ts +20 -0
- package/src/hooks/useMobileEditorPanels.ts +56 -0
- package/src/hooks/useSyncProjectPageStore.ts +40 -0
- package/src/modals/ScreenColorsModal.tsx +115 -0
- package/src/modals/index.ts +1 -0
- package/src/pages/ProjectPage.tsx +53 -243
- package/src/pages/tabs/BuilderPanel.tsx +14 -8
- package/src/store.ts +10 -6
- package/src/styles/base/_global.scss +12 -4
- package/src/styles/components/_attributes-editor.scss +9 -1
- package/src/styles/components/_bottom-bar.scss +113 -0
- package/src/styles/components/_editor-shell.scss +0 -19
- package/src/styles/index.scss +1 -0
- package/src/utils/analyseNodeByPatterns.ts +15 -0
- package/src/utils/nodeTree.ts +99 -0
- package/dist/build-components/PaywallSubscriButton/PaywallSubscriButton.d.ts +0 -5
- package/dist/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.d.ts +0 -50
- package/dist/pages/tabs/SideTool.d.ts +0 -8
- package/src/build-components/PaywallSubscriButton/PaywallSubscriButton.tsx +0 -10
- package/src/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.ts +0 -77
- package/src/build-components/PaywallSubscriButton/pattern.json +0 -27
- package/src/pages/tabs/SideTool.tsx +0 -253
|
@@ -187,22 +187,3 @@
|
|
|
187
187
|
background: #eef2ff;
|
|
188
188
|
color: #111827;
|
|
189
189
|
}
|
|
190
|
-
|
|
191
|
-
.side-tool-container {
|
|
192
|
-
position: absolute;
|
|
193
|
-
top: 0;
|
|
194
|
-
left: 0;
|
|
195
|
-
z-index: 5;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.side-tool select {
|
|
199
|
-
width: 100%;
|
|
200
|
-
font-size: 11px;
|
|
201
|
-
padding: 4px 6px;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.side-tool .debug-button {
|
|
205
|
-
border-radius: 0;
|
|
206
|
-
width: 100%;
|
|
207
|
-
justify-content: center;
|
|
208
|
-
}
|
package/src/styles/index.scss
CHANGED
|
@@ -334,11 +334,26 @@ function validateAttributesByPattern(
|
|
|
334
334
|
if (attrs == null) return ok();
|
|
335
335
|
if (!isPlainObject(attrs)) return fail(`attributes must be an object`, path);
|
|
336
336
|
|
|
337
|
+
const componentType = normalizeTypeOrFallback(pattern.pattern.type);
|
|
338
|
+
|
|
337
339
|
const schema: AttributeSchema = (getAttributeSchema(pattern.pattern.type) ??
|
|
338
340
|
pattern.pattern.attributes ??
|
|
339
341
|
{}) as AttributeSchema;
|
|
340
342
|
|
|
341
343
|
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
|
344
|
+
// Legacy compatibility: older onboard samples/projects stored theme on the provider.
|
|
345
|
+
// Modern projects store theme under `appConfig.theme`, but we still accept this
|
|
346
|
+
// attribute to avoid breaking existing JSON.
|
|
347
|
+
if (componentType === 'OnboardProvider' && attrName === 'theme') {
|
|
348
|
+
if (typeof attrValue !== 'string') {
|
|
349
|
+
return fail(`Expected one of: light, dark`, joinPath(path, attrName));
|
|
350
|
+
}
|
|
351
|
+
if (attrValue !== 'light' && attrValue !== 'dark') {
|
|
352
|
+
return fail(`Expected one of: light, dark`, joinPath(path, attrName));
|
|
353
|
+
}
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
|
|
342
357
|
const attrSpec = schema?.[attrName];
|
|
343
358
|
if (!attrSpec) {
|
|
344
359
|
if (pattern.allowUnknownAttributes) continue;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Node, NodeData } from '../types/Node';
|
|
2
|
+
|
|
3
|
+
export function deleteNodeFromTree(root: Node, target: Node): Node {
|
|
4
|
+
if (root === null || root === undefined) return root;
|
|
5
|
+
if (typeof root === 'string') return root;
|
|
6
|
+
|
|
7
|
+
if (Array.isArray(root)) {
|
|
8
|
+
let changed = false;
|
|
9
|
+
const nextChildren: Node[] = [];
|
|
10
|
+
for (const child of root) {
|
|
11
|
+
if (child === target) {
|
|
12
|
+
changed = true;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const nextChild = deleteNodeFromTree(child, target);
|
|
16
|
+
if (nextChild !== child) changed = true;
|
|
17
|
+
nextChildren.push(nextChild);
|
|
18
|
+
}
|
|
19
|
+
return changed ? nextChildren : root;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data = root as any;
|
|
23
|
+
if ('children' in data) {
|
|
24
|
+
const prev = data.children as Node;
|
|
25
|
+
if (!prev) return root;
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(prev)) {
|
|
28
|
+
let changed = false;
|
|
29
|
+
const nextChildren: Node[] = [];
|
|
30
|
+
for (const child of prev) {
|
|
31
|
+
if (child === target) {
|
|
32
|
+
changed = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const nextChild = deleteNodeFromTree(child, target);
|
|
36
|
+
if (nextChild !== child) changed = true;
|
|
37
|
+
nextChildren.push(nextChild);
|
|
38
|
+
}
|
|
39
|
+
if (changed) {
|
|
40
|
+
return { ...data, children: nextChildren } as Node;
|
|
41
|
+
}
|
|
42
|
+
return root;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (prev === target) {
|
|
46
|
+
return { ...data, children: '' } as Node;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const nextChild = deleteNodeFromTree(prev, target);
|
|
50
|
+
if (nextChild !== prev) {
|
|
51
|
+
return { ...data, children: nextChild } as Node;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return root;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isNodeRecord(node: Node): node is NodeData {
|
|
59
|
+
return (
|
|
60
|
+
node !== null &&
|
|
61
|
+
node !== undefined &&
|
|
62
|
+
typeof node === 'object' &&
|
|
63
|
+
!Array.isArray(node)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function nodeHasChild(parent: NodeData, potentialChild: Node): boolean {
|
|
68
|
+
const { children } = parent;
|
|
69
|
+
if (!children) return false;
|
|
70
|
+
if (Array.isArray(children)) {
|
|
71
|
+
return children.some((child) => child === potentialChild);
|
|
72
|
+
}
|
|
73
|
+
return children === potentialChild;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function findNodeByKey(root: Node, key?: string): Node | null {
|
|
77
|
+
if (!key) return null;
|
|
78
|
+
if (root === null || root === undefined) return null;
|
|
79
|
+
if (typeof root === 'string') return null;
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(root)) {
|
|
82
|
+
for (const child of root) {
|
|
83
|
+
const found = findNodeByKey(child, key);
|
|
84
|
+
if (found) return found;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const nodeData = root as NodeData;
|
|
90
|
+
if (nodeData.key === key) {
|
|
91
|
+
return nodeData;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (nodeData.children) {
|
|
95
|
+
return findNodeByKey(nodeData.children as Node, key);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { PaywallSubscriButtonComponentProps } from './PaywallSubscriButtonProps.generated';
|
|
3
|
-
declare function PaywallSubscriButton({ node }: PaywallSubscriButtonComponentProps): null;
|
|
4
|
-
declare const _default: React.MemoExoticComponent<typeof PaywallSubscriButton>;
|
|
5
|
-
export default _default;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { NodeData } from '../../types/Node';
|
|
2
|
-
export type FontWeightOptionType = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
|
3
|
-
export type FlexDirectionOptionType = 'row' | 'column';
|
|
4
|
-
export type AlignItemsOptionType = 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
|
|
5
|
-
export type JustifyContentOptionType = 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
|
|
6
|
-
export type PositionOptionType = 'relative' | 'absolute';
|
|
7
|
-
export interface PaywallSubscriButtonPropsGenerated {
|
|
8
|
-
child: string;
|
|
9
|
-
attributes: {
|
|
10
|
-
color?: string;
|
|
11
|
-
fontSize?: string;
|
|
12
|
-
fontWeight?: FontWeightOptionType;
|
|
13
|
-
scrollable?: boolean;
|
|
14
|
-
flexDirection?: FlexDirectionOptionType;
|
|
15
|
-
alignItems?: AlignItemsOptionType;
|
|
16
|
-
justifyContent?: JustifyContentOptionType;
|
|
17
|
-
gap?: string;
|
|
18
|
-
padding?: string;
|
|
19
|
-
paddingHorizontal?: string;
|
|
20
|
-
paddingVertical?: string;
|
|
21
|
-
paddingTop?: string;
|
|
22
|
-
paddingBottom?: string;
|
|
23
|
-
paddingLeft?: string;
|
|
24
|
-
paddingRight?: string;
|
|
25
|
-
margin?: string;
|
|
26
|
-
marginVertical?: string;
|
|
27
|
-
marginTop?: string;
|
|
28
|
-
marginBottom?: string;
|
|
29
|
-
marginLeft?: string;
|
|
30
|
-
marginRight?: string;
|
|
31
|
-
backgroundColor?: string;
|
|
32
|
-
borderRadius?: string;
|
|
33
|
-
width?: string;
|
|
34
|
-
minWidth?: string;
|
|
35
|
-
maxWidth?: string;
|
|
36
|
-
height?: string;
|
|
37
|
-
minHeight?: string;
|
|
38
|
-
maxHeight?: string;
|
|
39
|
-
flex?: number;
|
|
40
|
-
position?: PositionOptionType;
|
|
41
|
-
top?: string;
|
|
42
|
-
bottom?: string;
|
|
43
|
-
left?: string;
|
|
44
|
-
right?: string;
|
|
45
|
-
zIndex?: number;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export interface PaywallSubscriButtonComponentProps {
|
|
49
|
-
node: NodeData<PaywallSubscriButtonPropsGenerated['attributes']>;
|
|
50
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Node } from '../../types/Node';
|
|
3
|
-
type SideToolProps = {
|
|
4
|
-
data: Node;
|
|
5
|
-
setData: React.Dispatch<React.SetStateAction<Node>>;
|
|
6
|
-
};
|
|
7
|
-
export declare function SideTool({ data, setData }: SideToolProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export {};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { PaywallSubscriButtonComponentProps } from './PaywallSubscriButtonProps.generated';
|
|
3
|
-
import useNode from '../useNode';
|
|
4
|
-
|
|
5
|
-
function PaywallSubscriButton({ node }: PaywallSubscriButtonComponentProps) {
|
|
6
|
-
node = useNode(node);
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export default React.memo(PaywallSubscriButton);
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/* AUTO-GENERATED FILE - DO NOT EDIT */
|
|
2
|
-
|
|
3
|
-
import type { NodeData } from '../../types/Node';
|
|
4
|
-
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
|
-
export type FlexDirectionOptionType = 'row' | 'column';
|
|
18
|
-
export type AlignItemsOptionType =
|
|
19
|
-
| 'flex-start'
|
|
20
|
-
| 'center'
|
|
21
|
-
| 'flex-end'
|
|
22
|
-
| 'stretch'
|
|
23
|
-
| 'baseline';
|
|
24
|
-
export type JustifyContentOptionType =
|
|
25
|
-
| 'flex-start'
|
|
26
|
-
| 'center'
|
|
27
|
-
| 'flex-end'
|
|
28
|
-
| 'space-between'
|
|
29
|
-
| 'space-around'
|
|
30
|
-
| 'space-evenly';
|
|
31
|
-
export type PositionOptionType = 'relative' | 'absolute';
|
|
32
|
-
|
|
33
|
-
export interface PaywallSubscriButtonPropsGenerated {
|
|
34
|
-
child: string;
|
|
35
|
-
attributes: {
|
|
36
|
-
color?: string;
|
|
37
|
-
fontSize?: string;
|
|
38
|
-
fontWeight?: FontWeightOptionType;
|
|
39
|
-
scrollable?: boolean;
|
|
40
|
-
flexDirection?: FlexDirectionOptionType;
|
|
41
|
-
alignItems?: AlignItemsOptionType;
|
|
42
|
-
justifyContent?: JustifyContentOptionType;
|
|
43
|
-
gap?: string;
|
|
44
|
-
padding?: string;
|
|
45
|
-
paddingHorizontal?: string;
|
|
46
|
-
paddingVertical?: string;
|
|
47
|
-
paddingTop?: string;
|
|
48
|
-
paddingBottom?: string;
|
|
49
|
-
paddingLeft?: string;
|
|
50
|
-
paddingRight?: string;
|
|
51
|
-
margin?: string;
|
|
52
|
-
marginVertical?: string;
|
|
53
|
-
marginTop?: string;
|
|
54
|
-
marginBottom?: string;
|
|
55
|
-
marginLeft?: string;
|
|
56
|
-
marginRight?: string;
|
|
57
|
-
backgroundColor?: string;
|
|
58
|
-
borderRadius?: string;
|
|
59
|
-
width?: string;
|
|
60
|
-
minWidth?: string;
|
|
61
|
-
maxWidth?: string;
|
|
62
|
-
height?: string;
|
|
63
|
-
minHeight?: string;
|
|
64
|
-
maxHeight?: string;
|
|
65
|
-
flex?: number;
|
|
66
|
-
position?: PositionOptionType;
|
|
67
|
-
top?: string;
|
|
68
|
-
bottom?: string;
|
|
69
|
-
left?: string;
|
|
70
|
-
right?: string;
|
|
71
|
-
zIndex?: number;
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface PaywallSubscriButtonComponentProps {
|
|
76
|
-
node: NodeData<PaywallSubscriButtonPropsGenerated['attributes']>;
|
|
77
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"allowUnknownAttributes": false,
|
|
4
|
-
"pattern": {
|
|
5
|
-
"type": "PaywallSubscriButton",
|
|
6
|
-
"children": "string",
|
|
7
|
-
"extends": "Button",
|
|
8
|
-
"attributes": {}
|
|
9
|
-
},
|
|
10
|
-
"defaults": {
|
|
11
|
-
"paddingHorizontal": "20@s",
|
|
12
|
-
"paddingVertical": "12@vs",
|
|
13
|
-
"borderRadius": "12@s",
|
|
14
|
-
"backgroundColor": "#1C1C1E",
|
|
15
|
-
"color": "cornflowerblue",
|
|
16
|
-
"fontSize": "16@fs",
|
|
17
|
-
"fontWeight": "700",
|
|
18
|
-
"justifyContent": "center",
|
|
19
|
-
"alignItems": "center"
|
|
20
|
-
},
|
|
21
|
-
"meta": {
|
|
22
|
-
"desiredParent": [">PaywallProvider"],
|
|
23
|
-
"label": "Paywall Subscribe Button",
|
|
24
|
-
"description": "Paywall subscribe call-to-action button. Extends Button.",
|
|
25
|
-
"attributes": {}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { JsonTextEditor } from '../../components/JsonTextEditor';
|
|
3
|
-
import { Modal } from '../../modals';
|
|
4
|
-
import type { Node } from '../../types/Node';
|
|
5
|
-
import type { Localication } from '../../types/PreviewConfig';
|
|
6
|
-
import { useLogRender } from '../../utils/useLogRender';
|
|
7
|
-
import { useRenderStore } from '../../store';
|
|
8
|
-
import { Checkbox } from '../../components/Checkbox';
|
|
9
|
-
import { LocalicationModal } from '../../modals/LocalicationModal';
|
|
10
|
-
import { analyseAndProccess } from '../../utils/analyseNode';
|
|
11
|
-
|
|
12
|
-
const screenStyleDefaults = {
|
|
13
|
-
light: { backgroundColor: '#FDFDFD', color: '#161827' },
|
|
14
|
-
dark: { backgroundColor: '#12131A', color: '#E9EBF9' },
|
|
15
|
-
} as const;
|
|
16
|
-
|
|
17
|
-
type ScreenMode = keyof typeof screenStyleDefaults;
|
|
18
|
-
type ScreenColorKey = keyof (typeof screenStyleDefaults)['light'];
|
|
19
|
-
|
|
20
|
-
type SideToolProps = {
|
|
21
|
-
data: Node;
|
|
22
|
-
setData: React.Dispatch<React.SetStateAction<Node>>;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const colorFields = [
|
|
26
|
-
{
|
|
27
|
-
id: 'light-bg',
|
|
28
|
-
label: 'Light Background Color',
|
|
29
|
-
mode: 'light' as ScreenMode,
|
|
30
|
-
key: 'backgroundColor' as ScreenColorKey,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
id: 'light-color',
|
|
34
|
-
label: 'Light Color',
|
|
35
|
-
mode: 'light' as ScreenMode,
|
|
36
|
-
key: 'color' as ScreenColorKey,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: 'dark-bg',
|
|
40
|
-
label: 'Dark Background Color',
|
|
41
|
-
mode: 'dark' as ScreenMode,
|
|
42
|
-
key: 'backgroundColor' as ScreenColorKey,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: 'dark-color',
|
|
46
|
-
label: 'Dark Color',
|
|
47
|
-
mode: 'dark' as ScreenMode,
|
|
48
|
-
key: 'color' as ScreenColorKey,
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
export function SideTool({ data, setData }: SideToolProps) {
|
|
53
|
-
useLogRender('SideTool');
|
|
54
|
-
const [isDebugModalOpen, setIsDebugModalOpen] = useState(false);
|
|
55
|
-
const [isLocalicationModalOpen, setIsLocalicationModalOpen] = useState(false);
|
|
56
|
-
const [isCompactMode, setIsCompactMode] = useState(() => {
|
|
57
|
-
if (typeof window === 'undefined') {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
return window.innerWidth < 1000;
|
|
61
|
-
});
|
|
62
|
-
const [isCompactPanelVisible, setIsCompactPanelVisible] = useState(false);
|
|
63
|
-
const { appConfig, setAppConfig, previewMode, setPreviewMode } =
|
|
64
|
-
useRenderStore((s) => ({
|
|
65
|
-
appConfig: s.appConfig,
|
|
66
|
-
setAppConfig: s.setAppConfig,
|
|
67
|
-
previewMode: s.previewMode,
|
|
68
|
-
setPreviewMode: s.setPreviewMode,
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (typeof window === 'undefined') {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const handleResize = () => {
|
|
77
|
-
const compact = window.innerWidth < 1000;
|
|
78
|
-
setIsCompactMode(compact);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
handleResize();
|
|
82
|
-
window.addEventListener('resize', handleResize);
|
|
83
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
84
|
-
}, []);
|
|
85
|
-
|
|
86
|
-
const getScreenColorValue = (mode: ScreenMode, key: ScreenColorKey) =>
|
|
87
|
-
appConfig.screenStyle?.[mode]?.[key] ?? screenStyleDefaults[mode][key];
|
|
88
|
-
|
|
89
|
-
const handleScreenStyleChange = (
|
|
90
|
-
mode: ScreenMode,
|
|
91
|
-
key: ScreenColorKey,
|
|
92
|
-
value: string,
|
|
93
|
-
) => {
|
|
94
|
-
setAppConfig({
|
|
95
|
-
...appConfig,
|
|
96
|
-
screenStyle: {
|
|
97
|
-
...screenStyleDefaults,
|
|
98
|
-
...appConfig.screenStyle,
|
|
99
|
-
[mode]: {
|
|
100
|
-
...screenStyleDefaults[mode],
|
|
101
|
-
...appConfig.screenStyle?.[mode],
|
|
102
|
-
[key]: value,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const handleLocalicationChange = (data: Localication) => {
|
|
109
|
-
setAppConfig({ ...appConfig, localication: data });
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<div className="side-tool-container">
|
|
114
|
-
<button
|
|
115
|
-
type="button"
|
|
116
|
-
className="editor-button"
|
|
117
|
-
onClick={() => setIsCompactPanelVisible((prev) => !prev)}
|
|
118
|
-
aria-pressed={isCompactPanelVisible}
|
|
119
|
-
>
|
|
120
|
-
{isCompactPanelVisible ? 'Hide tools' : 'Show tools'}
|
|
121
|
-
</button>
|
|
122
|
-
|
|
123
|
-
{isCompactPanelVisible && (
|
|
124
|
-
<div className="side-tool">
|
|
125
|
-
<select
|
|
126
|
-
value={appConfig.defaultLanguage ?? 'en'}
|
|
127
|
-
onChange={(e) =>
|
|
128
|
-
setAppConfig({ ...appConfig, defaultLanguage: e.target.value })
|
|
129
|
-
}
|
|
130
|
-
>
|
|
131
|
-
{Object.keys(appConfig.localication ?? {}).map((language) => (
|
|
132
|
-
<option key={language} value={language}>
|
|
133
|
-
{language}
|
|
134
|
-
</option>
|
|
135
|
-
))}
|
|
136
|
-
</select>
|
|
137
|
-
|
|
138
|
-
<Checkbox
|
|
139
|
-
label="Dark Mode"
|
|
140
|
-
checked={appConfig.theme === 'dark'}
|
|
141
|
-
onChange={(checked) =>
|
|
142
|
-
setAppConfig({ ...appConfig, theme: checked ? 'dark' : 'light' })
|
|
143
|
-
}
|
|
144
|
-
/>
|
|
145
|
-
|
|
146
|
-
<Checkbox
|
|
147
|
-
label="Is RTL"
|
|
148
|
-
checked={appConfig.isRtl ?? false}
|
|
149
|
-
onChange={(checked) =>
|
|
150
|
-
setAppConfig({ ...appConfig, isRtl: checked })
|
|
151
|
-
}
|
|
152
|
-
/>
|
|
153
|
-
|
|
154
|
-
<Checkbox
|
|
155
|
-
label="Preview mode"
|
|
156
|
-
checked={previewMode}
|
|
157
|
-
onChange={setPreviewMode}
|
|
158
|
-
/>
|
|
159
|
-
|
|
160
|
-
<div>
|
|
161
|
-
<div
|
|
162
|
-
style={{
|
|
163
|
-
display: 'grid',
|
|
164
|
-
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
|
|
165
|
-
gap: 12,
|
|
166
|
-
}}
|
|
167
|
-
>
|
|
168
|
-
{colorFields.map(({ id, label, mode, key }) => (
|
|
169
|
-
<React.Fragment key={id}>
|
|
170
|
-
<div>{label}</div>
|
|
171
|
-
<input
|
|
172
|
-
id={id}
|
|
173
|
-
type="color"
|
|
174
|
-
className="input input--color"
|
|
175
|
-
value={getScreenColorValue(mode, key)}
|
|
176
|
-
onChange={(e) =>
|
|
177
|
-
handleScreenStyleChange(mode, key, e.target.value)
|
|
178
|
-
}
|
|
179
|
-
/>
|
|
180
|
-
</React.Fragment>
|
|
181
|
-
))}
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
|
|
185
|
-
<div
|
|
186
|
-
style={{
|
|
187
|
-
marginTop: 'auto',
|
|
188
|
-
paddingTop: 16,
|
|
189
|
-
display: 'flex',
|
|
190
|
-
flexDirection: 'column',
|
|
191
|
-
gap: 8,
|
|
192
|
-
}}
|
|
193
|
-
>
|
|
194
|
-
<button
|
|
195
|
-
type="button"
|
|
196
|
-
className="editor-button"
|
|
197
|
-
onClick={() => setIsLocalicationModalOpen(true)}
|
|
198
|
-
>
|
|
199
|
-
Open localization editor
|
|
200
|
-
</button>
|
|
201
|
-
<button
|
|
202
|
-
type="button"
|
|
203
|
-
className="editor-button debug-button"
|
|
204
|
-
title="Inspect raw JSON data"
|
|
205
|
-
onClick={() => setIsDebugModalOpen(true)}
|
|
206
|
-
>
|
|
207
|
-
Debug JSON
|
|
208
|
-
</button>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
{isDebugModalOpen && (
|
|
212
|
-
<Modal
|
|
213
|
-
onClose={() => setIsDebugModalOpen(false)}
|
|
214
|
-
ariaLabelledBy="debug-json-editor-title"
|
|
215
|
-
className="modal--large modal--scrollable"
|
|
216
|
-
contentClassName="localication-modal__content"
|
|
217
|
-
>
|
|
218
|
-
<div className="modal__header localication-modal__header">
|
|
219
|
-
<button
|
|
220
|
-
type="button"
|
|
221
|
-
className="editor-button"
|
|
222
|
-
onClick={() => setIsDebugModalOpen(false)}
|
|
223
|
-
>
|
|
224
|
-
Close
|
|
225
|
-
</button>
|
|
226
|
-
</div>
|
|
227
|
-
<div className="localication-modal__body">
|
|
228
|
-
<div className="localication-modal__editor">
|
|
229
|
-
<JsonTextEditor
|
|
230
|
-
rootName="node"
|
|
231
|
-
value={data ?? {}}
|
|
232
|
-
onChange={(next) =>
|
|
233
|
-
setData(analyseAndProccess(next as Node) as Node)
|
|
234
|
-
}
|
|
235
|
-
className="localication-modal__json-editor"
|
|
236
|
-
/>
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
</Modal>
|
|
240
|
-
)}
|
|
241
|
-
|
|
242
|
-
{isLocalicationModalOpen && (
|
|
243
|
-
<LocalicationModal
|
|
244
|
-
data={appConfig.localication ?? {}}
|
|
245
|
-
onChange={handleLocalicationChange}
|
|
246
|
-
onClose={() => setIsLocalicationModalOpen(false)}
|
|
247
|
-
/>
|
|
248
|
-
)}
|
|
249
|
-
</div>
|
|
250
|
-
)}
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
}
|