@developer_tribe/react-builder 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/AttributesEditor.d.ts +3 -1
  2. package/dist/RenderPage.d.ts +2 -1
  3. package/dist/attributes-editor/Field.d.ts +2 -1
  4. package/dist/attributes-editor/SpecialCategorySection.d.ts +2 -1
  5. package/dist/build-components/BackgroundImage/BackgroundImage.d.ts +5 -0
  6. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +44 -0
  7. package/dist/build-components/Button/ButtonProps.generated.d.ts +7 -0
  8. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +7 -0
  9. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +7 -0
  10. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +7 -0
  11. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +7 -0
  12. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +7 -0
  13. package/dist/build-components/Image/ImageProps.generated.d.ts +7 -0
  14. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +7 -0
  15. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +7 -0
  16. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +7 -0
  17. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +7 -0
  18. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +7 -0
  19. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +7 -0
  20. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +7 -0
  21. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +7 -1
  22. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +7 -0
  23. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +7 -0
  24. package/dist/build-components/Text/TextProps.generated.d.ts +7 -0
  25. package/dist/build-components/View/ViewProps.generated.d.ts +7 -0
  26. package/dist/build-components/index.d.ts +2 -1
  27. package/dist/build-components/patterns.generated.d.ts +1444 -15
  28. package/dist/components/AttributesEditorPanel.d.ts +3 -4
  29. package/dist/components/Builder.d.ts +2 -1
  30. package/dist/components/BuilderButton.d.ts +9 -0
  31. package/dist/index.cjs.js +5 -5
  32. package/dist/index.cjs.js.map +1 -1
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.esm.js +5 -5
  35. package/dist/index.esm.js.map +1 -1
  36. package/dist/modals/ColorModal.d.ts +3 -1
  37. package/dist/pages/ProjectPage.d.ts +3 -3
  38. package/dist/pages/tabs/BuilderPanel.d.ts +8 -0
  39. package/dist/pages/tabs/{DebugTab.d.ts → SideTool.d.ts} +2 -2
  40. package/dist/store.d.ts +7 -1
  41. package/dist/styles.css +1 -1
  42. package/dist/types/Project.d.ts +11 -0
  43. package/dist/utils/analyseNode.d.ts +1 -0
  44. package/dist/utils/extractTextStyle.d.ts +8 -1
  45. package/dist/utils/extractViewStyle.d.ts +8 -1
  46. package/dist/utils/parseColor.d.ts +7 -0
  47. package/package.json +1 -1
  48. package/src/AttributesEditor.tsx +76 -14
  49. package/src/RenderPage.tsx +82 -4
  50. package/src/attributes-editor/Field.tsx +12 -5
  51. package/src/attributes-editor/SpecialCategorySection.tsx +2 -1
  52. package/src/build-components/BackgroundImage/BackgroundImage.tsx +87 -0
  53. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +60 -0
  54. package/src/build-components/BackgroundImage/pattern.json +45 -0
  55. package/src/build-components/Button/Button.tsx +31 -4
  56. package/src/build-components/Button/ButtonProps.generated.ts +7 -0
  57. package/src/build-components/Carousel/Carousel.tsx +27 -3
  58. package/src/build-components/Carousel/CarouselProps.generated.ts +7 -0
  59. package/src/build-components/CarouselButtons/CarouselButtons.tsx +19 -4
  60. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +7 -0
  61. package/src/build-components/CarouselDots/CarouselDots.tsx +13 -4
  62. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +7 -0
  63. package/src/build-components/CarouselItem/CarouselItem.tsx +20 -4
  64. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +7 -0
  65. package/src/build-components/CarouselProvider/CarouselProvider.tsx +14 -3
  66. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +7 -0
  67. package/src/build-components/Image/Image.tsx +29 -4
  68. package/src/build-components/Image/ImageProps.generated.ts +7 -0
  69. package/src/build-components/Onboard/Onboard.tsx +2 -2
  70. package/src/build-components/Onboard/OnboardProps.generated.ts +7 -0
  71. package/src/build-components/OnboardButton/OnboardButton.tsx +11 -4
  72. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +7 -0
  73. package/src/build-components/OnboardButtons/OnboardButtons.tsx +17 -5
  74. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +7 -0
  75. package/src/build-components/OnboardDot/OnboardDot.tsx +15 -6
  76. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +7 -0
  77. package/src/build-components/OnboardDot/pattern.json +1 -1
  78. package/src/build-components/OnboardFooter/OnboardFooter.tsx +15 -5
  79. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +7 -0
  80. package/src/build-components/OnboardImage/OnboardImage.tsx +28 -6
  81. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +7 -0
  82. package/src/build-components/OnboardItem/OnboardItem.tsx +14 -3
  83. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +7 -0
  84. package/src/build-components/OnboardProvider/OnboardProvider.tsx +34 -12
  85. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +7 -1
  86. package/src/build-components/OnboardProvider/pattern.json +0 -8
  87. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +7 -0
  88. package/src/build-components/OnboardSubtitle/pattern.json +1 -1
  89. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +7 -0
  90. package/src/build-components/OnboardTitle/pattern.json +1 -1
  91. package/src/build-components/RenderNode.generated.tsx +3 -0
  92. package/src/build-components/Text/Text.tsx +33 -9
  93. package/src/build-components/Text/TextProps.generated.ts +7 -0
  94. package/src/build-components/View/View.tsx +27 -3
  95. package/src/build-components/View/ViewProps.generated.ts +7 -0
  96. package/src/build-components/View/pattern.json +59 -1
  97. package/src/build-components/index.ts +5 -0
  98. package/src/build-components/patterns.generated.ts +1452 -15
  99. package/src/components/AttributesEditorPanel.tsx +13 -6
  100. package/src/components/Builder.tsx +140 -40
  101. package/src/components/BuilderButton.tsx +127 -0
  102. package/src/index.ts +2 -2
  103. package/src/mockOS/components/MockOSRouter.tsx +11 -3
  104. package/src/modals/ColorModal.tsx +212 -55
  105. package/src/pages/ProjectPage.tsx +293 -55
  106. package/src/pages/tabs/{BuilderTab.tsx → BuilderPanel.tsx} +13 -9
  107. package/src/pages/tabs/SideTool.tsx +259 -0
  108. package/src/size-matters/index.ts +6 -0
  109. package/src/store.ts +13 -1
  110. package/src/styles/base/_global.scss +158 -7
  111. package/src/styles/components/_attributes-editor.scss +12 -0
  112. package/src/styles/components/_editor-shell.scss +23 -0
  113. package/src/styles/foundation/_sizes.scss +1 -1
  114. package/src/styles/layout/_builder.scss +66 -10
  115. package/src/styles/modals/_color-modal.scss +29 -0
  116. package/src/types/Project.ts +14 -0
  117. package/src/utils/analyseNode.ts +98 -0
  118. package/src/utils/extractTextStyle.ts +24 -8
  119. package/src/utils/extractViewStyle.ts +27 -3
  120. package/src/utils/parseColor.ts +43 -0
  121. package/dist/pages/tabs/BuilderTab.d.ts +0 -9
  122. package/dist/pages/tabs/PreviewTab.d.ts +0 -3
  123. package/src/pages/tabs/DebugTab.tsx +0 -64
  124. package/src/pages/tabs/PreviewTab.tsx +0 -206
@@ -1,11 +1,25 @@
1
1
  import { Node } from '../types/Node';
2
2
  import { AppConfig } from './PreviewConfig';
3
3
 
4
+ export type ProjectColorTokenMap = Record<string, string>;
5
+
6
+ export type ProjectThemeColors = {
7
+ light?: ProjectColorTokenMap;
8
+ dark?: ProjectColorTokenMap;
9
+ [themeName: string]: ProjectColorTokenMap | undefined;
10
+ };
11
+
12
+ export interface ProjectColors {
13
+ STATIC_COLORS?: ProjectColorTokenMap;
14
+ THEME_COLORS?: ProjectThemeColors;
15
+ }
16
+
4
17
  export interface ProjectBase<T> {
5
18
  name: string;
6
19
  version: string;
7
20
  data: T;
8
21
  appConfig?: AppConfig;
22
+ projectColors?: ProjectColors;
9
23
  }
10
24
 
11
25
  export interface Project extends ProjectBase<Node> {}
@@ -1,4 +1,5 @@
1
1
  import { Node, NodeData, NodeDefaultAttribute } from '../types/Node';
2
+ import { generateRandomKeyForNode } from './generateRandomKeyForNode';
2
3
 
3
4
  export function isNodeNullOrUndefined<T = NodeDefaultAttribute>(
4
5
  node: Node<T>,
@@ -17,6 +18,82 @@ export function isEmptyObject<T = NodeDefaultAttribute>(
17
18
  return Object.keys(node as Object).length === 0;
18
19
  }
19
20
 
21
+ function collectDuplicateKey(
22
+ node: Node<NodeDefaultAttribute>,
23
+ usedKeys: Set<string>,
24
+ ): string | null {
25
+ if (isNodeNullOrUndefined(node) || isNodeString(node)) {
26
+ return null;
27
+ }
28
+
29
+ if (isNodeArray(node)) {
30
+ const nodeArray = node as unknown as Node<NodeDefaultAttribute>[];
31
+ for (const value of nodeArray) {
32
+ const duplicate = collectDuplicateKey(value, usedKeys);
33
+ if (duplicate) {
34
+ return duplicate;
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+
40
+ const recordData = node as NodeData<NodeDefaultAttribute>;
41
+ if (recordData.key) {
42
+ if (usedKeys.has(recordData.key)) {
43
+ return recordData.key;
44
+ }
45
+ usedKeys.add(recordData.key);
46
+ }
47
+
48
+ if (recordData.children) {
49
+ return collectDuplicateKey(
50
+ recordData.children as Node<NodeDefaultAttribute>,
51
+ usedKeys,
52
+ );
53
+ }
54
+
55
+ return null;
56
+ }
57
+
58
+ function assignMissingKeys(
59
+ node: Node<NodeDefaultAttribute>,
60
+ usedKeys: Set<string>,
61
+ ): Node<NodeDefaultAttribute> {
62
+ if (isNodeNullOrUndefined(node) || isNodeString(node)) {
63
+ return node;
64
+ }
65
+
66
+ if (isNodeArray(node)) {
67
+ const nodeArray = node as unknown as Node<NodeDefaultAttribute>[];
68
+ return nodeArray.map((child) =>
69
+ assignMissingKeys(child, usedKeys),
70
+ ) as unknown as Node<NodeDefaultAttribute>;
71
+ }
72
+
73
+ const recordData = node as NodeData<NodeDefaultAttribute>;
74
+
75
+ let key = recordData.key;
76
+ if (!key) {
77
+ do {
78
+ key = generateRandomKeyForNode(recordData.type);
79
+ } while (usedKeys.has(key));
80
+ usedKeys.add(key);
81
+ }
82
+
83
+ const children = recordData.children
84
+ ? (assignMissingKeys(
85
+ recordData.children as Node<NodeDefaultAttribute>,
86
+ usedKeys,
87
+ ) as Node<NodeDefaultAttribute>)
88
+ : recordData.children;
89
+
90
+ return {
91
+ ...recordData,
92
+ key,
93
+ children,
94
+ };
95
+ }
96
+
20
97
  export function analyseNode(node: Node<NodeDefaultAttribute>): {
21
98
  valid: boolean;
22
99
  message?: string;
@@ -75,3 +152,24 @@ export function analyseNode(node: Node<NodeDefaultAttribute>): {
75
152
  };
76
153
  }
77
154
  }
155
+
156
+ export function analyseAndProccess(
157
+ node: Node<NodeDefaultAttribute>,
158
+ ): Node<NodeDefaultAttribute> | null {
159
+ if (isNodeNullOrUndefined(node)) {
160
+ return null;
161
+ }
162
+
163
+ const { valid, message } = analyseNode(node);
164
+ if (!valid) {
165
+ throw new Error(message ?? 'Node is not valid');
166
+ }
167
+
168
+ const usedKeys = new Set<string>();
169
+ const duplicateKey = collectDuplicateKey(node, usedKeys);
170
+ if (duplicateKey) {
171
+ throw new Error(`Duplicate node key detected: ${duplicateKey}`);
172
+ }
173
+
174
+ return assignMissingKeys(node, usedKeys);
175
+ }
@@ -1,16 +1,23 @@
1
1
  import type { NodeData } from '../types/Node';
2
2
  import type { TextPropsGenerated } from '../build-components/Text/TextProps.generated';
3
+ import type { AppConfig } from '../types/PreviewConfig';
4
+ import { defaultAppConfig } from '../types/PreviewConfig';
5
+ import type { ProjectColors } from '../types/Project';
3
6
  import { fs, parseSize } from '../size-matters';
4
- import { useRenderStore } from '../store';
7
+ import { parseColor } from './parseColor';
8
+
9
+ type ExtractTextStyleOptions = {
10
+ appConfig?: AppConfig;
11
+ projectColors?: ProjectColors;
12
+ };
5
13
 
6
14
  export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
7
15
  node: NodeData<T>,
16
+ options: ExtractTextStyleOptions = {},
8
17
  ) {
9
18
  const attributes = node.attributes;
10
- //TODO: it should be passed as a prop to the function (need a state)
11
- const {
12
- appConfig: { screenStyle, theme },
13
- } = useRenderStore.getState();
19
+ const resolvedAppConfig = options.appConfig ?? defaultAppConfig;
20
+ const { screenStyle, theme } = resolvedAppConfig;
14
21
  const fallbackColor =
15
22
  theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
16
23
 
@@ -30,7 +37,11 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
30
37
  style.fontSize = fs(14);
31
38
  }
32
39
  if (attributes.fontWeight) style.fontWeight = attributes.fontWeight;
33
- style.color = attributes.color ?? fallbackColor;
40
+ const resolvedTextColor = parseColor(attributes.color, {
41
+ projectColors: options.projectColors,
42
+ appConfig: resolvedAppConfig,
43
+ });
44
+ style.color = resolvedTextColor ?? fallbackColor;
34
45
  if (attributes.textAlign)
35
46
  style.textAlign = attributes.textAlign as React.CSSProperties['textAlign'];
36
47
 
@@ -99,8 +110,13 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
99
110
  ) as React.CSSProperties['marginRight'];
100
111
 
101
112
  // Decor
102
- if (attributes.backgroundColor)
103
- style.backgroundColor = attributes.backgroundColor;
113
+ if (attributes.backgroundColor) {
114
+ style.backgroundColor =
115
+ parseColor(attributes.backgroundColor, {
116
+ projectColors: options.projectColors,
117
+ appConfig: resolvedAppConfig,
118
+ }) ?? attributes.backgroundColor;
119
+ }
104
120
  if (attributes.borderRadius !== undefined)
105
121
  style.borderRadius =
106
122
  attributes.borderRadius as React.CSSProperties['borderRadius'];
@@ -1,9 +1,18 @@
1
1
  import { ViewPropsGenerated } from '../build-components/View/ViewProps.generated';
2
2
  import type { NodeData } from '../types/Node';
3
+ import type { AppConfig } from '../types/PreviewConfig';
4
+ import type { ProjectColors } from '../types/Project';
3
5
  import { parseSize } from '../size-matters';
6
+ import { parseColor } from './parseColor';
7
+
8
+ type ExtractViewStyleOptions = {
9
+ appConfig?: AppConfig;
10
+ projectColors?: ProjectColors;
11
+ };
4
12
 
5
13
  export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
6
14
  node: NodeData<T>,
15
+ options: ExtractViewStyleOptions = {},
7
16
  ) {
8
17
  const attributes = node.attributes;
9
18
  const scrollable = attributes?.scrollable ?? false;
@@ -78,11 +87,26 @@ export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
78
87
  setParsedSize('marginBottom', attributes.marginBottom);
79
88
  setParsedSize('marginLeft', attributes.marginLeft);
80
89
  setParsedSize('marginRight', attributes.marginRight);
81
-
82
- if (attributes.backgroundColor)
83
- style.backgroundColor = attributes.backgroundColor;
90
+ console.log('attributes.backgroundColor', attributes.backgroundColor);
91
+ console.log(
92
+ 'parseColor(attributes.backgroundColor)',
93
+ parseColor(attributes.backgroundColor),
94
+ );
95
+ if (attributes.backgroundColor) {
96
+ style.backgroundColor =
97
+ parseColor(attributes.backgroundColor, {
98
+ projectColors: options.projectColors,
99
+ appConfig: options.appConfig,
100
+ }) ?? attributes.backgroundColor;
101
+ }
84
102
  setParsedSize('borderRadius', attributes.borderRadius);
85
103
  setParsedSize('width', attributes.width);
86
104
  setParsedSize('height', attributes.height);
105
+ if (attributes.position)
106
+ style.position = attributes.position as React.CSSProperties['position'];
107
+ setParsedSize('top', attributes.top);
108
+ setParsedSize('bottom', attributes.bottom);
109
+ setParsedSize('left', attributes.left);
110
+ setParsedSize('right', attributes.right);
87
111
  return style;
88
112
  }
@@ -0,0 +1,43 @@
1
+ import type { AppConfig } from '../types/PreviewConfig';
2
+ import type { ProjectColors } from '../types/Project';
3
+
4
+ const STATIC_PREFIX = 'STATIC_COLORS.';
5
+ const THEME_PREFIX = 'THEME_COLORS.';
6
+
7
+ export type ParseColorOptions = {
8
+ projectColors?: ProjectColors;
9
+ appConfig?: AppConfig;
10
+ };
11
+
12
+ export function parseColor(value?: string, options: ParseColorOptions = {}) {
13
+ if (typeof value !== 'string') return value;
14
+ const trimmed = value.trim();
15
+ if (!trimmed) return undefined;
16
+
17
+ const { projectColors, appConfig } = options;
18
+ if (!projectColors) return trimmed;
19
+
20
+ if (trimmed.startsWith(STATIC_PREFIX)) {
21
+ const token = trimmed.slice(STATIC_PREFIX.length);
22
+ const resolved = projectColors.STATIC_COLORS?.[token];
23
+ return typeof resolved === 'string' && resolved.trim()
24
+ ? resolved.trim()
25
+ : trimmed;
26
+ }
27
+
28
+ if (trimmed.startsWith(THEME_PREFIX)) {
29
+ const token = trimmed.slice(THEME_PREFIX.length);
30
+ if (!token) return trimmed;
31
+
32
+ const theme = appConfig?.theme ?? 'light';
33
+ const themeTokens = projectColors.THEME_COLORS?.[theme];
34
+ const resolved = themeTokens?.[token];
35
+ if (typeof resolved === 'string' && resolved.trim()) {
36
+ return resolved.trim();
37
+ }
38
+
39
+ return trimmed;
40
+ }
41
+
42
+ return trimmed;
43
+ }
@@ -1,9 +0,0 @@
1
- import { Node } from '../..';
2
- type BuilderTabProps = {
3
- data: Node;
4
- setData: (data: Node) => void;
5
- current: Node;
6
- setCurrent: (current: Node) => void;
7
- };
8
- export declare function BuilderTab({ data, setData, current, setCurrent, }: BuilderTabProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
@@ -1,3 +0,0 @@
1
- type PreviewTabProps = {};
2
- export declare function PreviewTab({}: PreviewTabProps): import("react/jsx-runtime").JSX.Element;
3
- export {};
@@ -1,64 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { JsonEditor } from 'json-edit-react';
3
- import { Modal } from '../../modals';
4
- import { Node } from '../..';
5
- import { useLogRender } from '../../utils/useLogRender';
6
-
7
- type DebugTabProps = {
8
- data: Node;
9
- setData: (data: Node) => void;
10
- };
11
-
12
- export function DebugTab({ data, setData }: DebugTabProps) {
13
- useLogRender('DebugTab');
14
- const [isOpen, setIsOpen] = useState(false);
15
- return (
16
- <>
17
- <button
18
- type="button"
19
- className="editor-button debug-button"
20
- title="Inspect raw JSON data"
21
- onClick={() => setIsOpen(true)}
22
- >
23
- Debug JSON
24
- </button>
25
- {isOpen ? (
26
- <Modal
27
- onClose={() => setIsOpen(false)}
28
- ariaLabelledBy="debug-json-editor-title"
29
- className="modal--large modal--scrollable"
30
- contentClassName="localication-modal__content"
31
- >
32
- <div className="modal__header localication-modal__header">
33
- <div className="localication-modal__header-main">
34
- <h3 id="debug-json-editor-title" className="modal__title">
35
- Debug data
36
- </h3>
37
- <p className="localication-modal__description">
38
- Inspect and edit the current node tree using the JSON editor.
39
- </p>
40
- </div>
41
- <button
42
- type="button"
43
- className="editor-button"
44
- onClick={() => setIsOpen(false)}
45
- >
46
- Close
47
- </button>
48
- </div>
49
- <div className="localication-modal__body">
50
- <div className="localication-modal__editor">
51
- <JsonEditor
52
- rootName="debug"
53
- data={data as any}
54
- setData={setData as any}
55
- className="localication-modal__json-editor"
56
- maxWidth={'100%'}
57
- />
58
- </div>
59
- </div>
60
- </Modal>
61
- ) : null}
62
- </>
63
- );
64
- }
@@ -1,206 +0,0 @@
1
- import React, { useCallback, useState } from 'react';
2
- import { Localication } from '../..';
3
- import { useLogRender } from '../../utils/useLogRender';
4
- import { useRenderStore } from '../../store';
5
- import { LocalicationModal } from '../../modals/LocalicationModal';
6
- import { Checkbox } from '../../components/Checkbox';
7
-
8
- type PreviewTabProps = {};
9
-
10
- export function PreviewTab({}: PreviewTabProps) {
11
- useLogRender('PreviewTab');
12
- const { appConfig, setAppConfig } = useRenderStore((s) => ({
13
- appConfig: s.appConfig,
14
- setAppConfig: s.setAppConfig,
15
- }));
16
- const [isLocalicationModalOpen, setIsLocalicationModalOpen] = useState(false);
17
-
18
- const handleLocalicationChange = useCallback(
19
- (data: Localication) => {
20
- setAppConfig({
21
- ...appConfig,
22
- localication: data,
23
- });
24
- },
25
- [appConfig, setAppConfig],
26
- );
27
-
28
- return (
29
- <div
30
- role="tabpanel"
31
- className="editor-panel editor-panel--active"
32
- aria-hidden={false}
33
- >
34
- <div style={{ padding: 12 }}>
35
- <div
36
- style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}
37
- >
38
- <div>Default Language</div>
39
- <select
40
- value={appConfig.defaultLanguage ?? 'en'}
41
- onChange={(e) => {
42
- setAppConfig({
43
- ...appConfig,
44
- defaultLanguage: e.target.value,
45
- });
46
- }}
47
- >
48
- {Object.keys(appConfig.localication ?? {}).map((language) => (
49
- <option key={language} value={language}>
50
- {language}
51
- </option>
52
- ))}
53
- </select>
54
- </div>
55
- <div
56
- style={{
57
- display: 'grid',
58
- gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
59
- gap: 12,
60
- marginTop: 8,
61
- }}
62
- >
63
- <div>Light Background Color</div>
64
- <input
65
- type="color"
66
- value={appConfig.screenStyle?.light.backgroundColor ?? '#FDFDFD'}
67
- onChange={(e) => {
68
- const next = {
69
- ...appConfig.screenStyle,
70
- light: {
71
- ...(appConfig.screenStyle?.light ?? {
72
- color: '#161827',
73
- }),
74
- backgroundColor: e.target.value,
75
- },
76
- };
77
-
78
- setAppConfig({
79
- ...appConfig,
80
- screenStyle: next,
81
- });
82
- }}
83
- className="input input--color"
84
- />
85
- <div>Light Color</div>
86
- <input
87
- type="color"
88
- value={appConfig.screenStyle?.light.color ?? '#161827'}
89
- onChange={(e) => {
90
- const next = {
91
- ...appConfig.screenStyle,
92
- light: {
93
- ...(appConfig.screenStyle?.light ?? {
94
- backgroundColor: '#FDFDFD',
95
- }),
96
- color: e.target.value,
97
- },
98
- };
99
-
100
- setAppConfig({
101
- ...appConfig,
102
- screenStyle: next,
103
- });
104
- }}
105
- className="input input--color"
106
- />
107
- <div>Dark Background Color</div>
108
- <input
109
- type="color"
110
- value={appConfig.screenStyle?.dark.backgroundColor ?? '#12131A'}
111
- onChange={(e) => {
112
- const next = {
113
- ...appConfig.screenStyle,
114
- dark: {
115
- ...(appConfig.screenStyle?.dark ?? {
116
- color: '#E9EBF9',
117
- }),
118
- backgroundColor: e.target.value,
119
- },
120
- };
121
-
122
- setAppConfig({
123
- ...appConfig,
124
- screenStyle: next,
125
- });
126
- }}
127
- className="input input--color"
128
- />
129
- <div>Dark Color</div>
130
- <input
131
- type="color"
132
- value={appConfig.screenStyle?.dark.color ?? '#E9EBF9'}
133
- onChange={(e) => {
134
- const next = {
135
- ...appConfig.screenStyle,
136
- dark: {
137
- ...(appConfig.screenStyle?.dark ?? {
138
- backgroundColor: '#12131A',
139
- }),
140
- color: e.target.value,
141
- },
142
- };
143
- setAppConfig({
144
- ...appConfig,
145
- screenStyle: next,
146
- });
147
- }}
148
- className="input input--color"
149
- />
150
- </div>
151
- <div style={{ marginTop: 8 }}>
152
- <Checkbox
153
- label="Dark Mode"
154
- checked={appConfig.theme === 'dark'}
155
- onChange={(checked) => {
156
- const nextTheme = checked ? 'dark' : 'light';
157
- setAppConfig({
158
- ...appConfig,
159
- theme: nextTheme,
160
- });
161
- }}
162
- />
163
- </div>
164
- <div style={{ marginTop: 8 }}>
165
- <Checkbox
166
- label="Is RTL"
167
- checked={appConfig.isRtl ?? false}
168
- onChange={(checked) => {
169
- setAppConfig({
170
- ...appConfig,
171
- isRtl: checked,
172
- });
173
- }}
174
- />
175
- </div>
176
- <div
177
- style={{
178
- marginTop: 16,
179
- display: 'flex',
180
- flexDirection: 'column',
181
- gap: 8,
182
- }}
183
- >
184
- <div style={{ fontWeight: 600 }}>Localization</div>
185
- <p style={{ margin: 0, fontSize: 13, color: '#4b5563' }}>
186
- Manage translations for each language configured in your preview.
187
- </p>
188
- <button
189
- type="button"
190
- className="editor-button"
191
- onClick={() => setIsLocalicationModalOpen(true)}
192
- >
193
- Open localization editor
194
- </button>
195
- </div>
196
- </div>
197
- {isLocalicationModalOpen ? (
198
- <LocalicationModal
199
- data={appConfig.localication ?? {}}
200
- onChange={handleLocalicationChange}
201
- onClose={() => setIsLocalicationModalOpen(false)}
202
- />
203
- ) : null}
204
- </div>
205
- );
206
- }