@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.
Files changed (147) hide show
  1. package/dist/AttributesEditor.d.ts +3 -1
  2. package/dist/RenderPage.d.ts +2 -1
  3. package/dist/android.svg +43 -0
  4. package/dist/apple.svg +16 -0
  5. package/dist/attributes-editor/Field.d.ts +4 -2
  6. package/dist/attributes-editor/SizeField.d.ts +9 -0
  7. package/dist/attributes-editor/SpecialCategorySection.d.ts +2 -1
  8. package/dist/build-components/BackgroundImage/BackgroundImage.d.ts +5 -0
  9. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +45 -0
  10. package/dist/build-components/Button/ButtonProps.generated.d.ts +8 -0
  11. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +8 -0
  12. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +8 -0
  13. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +8 -0
  14. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +8 -0
  15. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +8 -0
  16. package/dist/build-components/Image/ImageProps.generated.d.ts +8 -0
  17. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +8 -0
  18. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -1
  19. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +8 -0
  20. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +9 -3
  21. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +8 -0
  22. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +9 -1
  23. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +8 -0
  24. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +8 -1
  25. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +8 -0
  26. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +8 -0
  27. package/dist/build-components/Text/TextProps.generated.d.ts +8 -0
  28. package/dist/build-components/View/ViewProps.generated.d.ts +8 -0
  29. package/dist/build-components/index.d.ts +2 -1
  30. package/dist/build-components/patterns.generated.d.ts +1612 -46
  31. package/dist/components/AttributesEditorPanel.d.ts +3 -4
  32. package/dist/components/Builder.d.ts +2 -1
  33. package/dist/components/BuilderButton.d.ts +9 -0
  34. package/dist/components/JsonTextEditor.d.ts +9 -0
  35. package/dist/index.cjs.js +5 -5
  36. package/dist/index.cjs.js.map +1 -1
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.esm.js +5 -5
  39. package/dist/index.esm.js.map +1 -1
  40. package/dist/modals/ColorModal.d.ts +3 -1
  41. package/dist/pages/ProjectPage.d.ts +3 -3
  42. package/dist/pages/tabs/BuilderPanel.d.ts +8 -0
  43. package/dist/pages/tabs/SideTool.d.ts +8 -0
  44. package/dist/store.d.ts +9 -1
  45. package/dist/styles.css +1 -1
  46. package/dist/types/Project.d.ts +11 -0
  47. package/dist/utils/analyseNode.d.ts +1 -0
  48. package/dist/utils/extractImageStyle.d.ts +2 -1
  49. package/dist/utils/extractTextStyle.d.ts +8 -1
  50. package/dist/utils/extractViewStyle.d.ts +7 -1
  51. package/dist/utils/parseColor.d.ts +7 -0
  52. package/dist/utils/selection.d.ts +7 -0
  53. package/dist/utils/useMergedStyle.d.ts +2 -0
  54. package/package.json +2 -5
  55. package/src/.DS_Store +0 -0
  56. package/src/AttributesEditor.tsx +83 -16
  57. package/src/RenderPage.tsx +86 -4
  58. package/src/attributes-editor/Field.tsx +60 -165
  59. package/src/attributes-editor/SizeField.tsx +184 -0
  60. package/src/attributes-editor/SpecialCategorySection.tsx +12 -4
  61. package/src/build-components/BackgroundImage/BackgroundImage.tsx +77 -0
  62. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +61 -0
  63. package/src/build-components/BackgroundImage/pattern.json +45 -0
  64. package/src/build-components/Button/Button.tsx +29 -4
  65. package/src/build-components/Button/ButtonProps.generated.ts +8 -0
  66. package/src/build-components/Carousel/Carousel.tsx +25 -3
  67. package/src/build-components/Carousel/CarouselProps.generated.ts +8 -0
  68. package/src/build-components/CarouselButtons/CarouselButtons.tsx +19 -4
  69. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +8 -0
  70. package/src/build-components/CarouselDots/CarouselDots.tsx +13 -4
  71. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +8 -0
  72. package/src/build-components/CarouselItem/CarouselItem.tsx +20 -4
  73. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +8 -0
  74. package/src/build-components/CarouselProvider/CarouselProvider.tsx +14 -3
  75. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +8 -0
  76. package/src/build-components/Image/Image.tsx +27 -9
  77. package/src/build-components/Image/ImageProps.generated.ts +8 -0
  78. package/src/build-components/Image/pattern.json +1 -9
  79. package/src/build-components/Onboard/Onboard.tsx +2 -2
  80. package/src/build-components/Onboard/OnboardProps.generated.ts +8 -0
  81. package/src/build-components/OnboardButton/OnboardButton.tsx +11 -7
  82. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +8 -1
  83. package/src/build-components/OnboardButtons/OnboardButtons.tsx +17 -5
  84. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +8 -0
  85. package/src/build-components/OnboardDot/OnboardDot.tsx +68 -39
  86. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +9 -3
  87. package/src/build-components/OnboardDot/pattern.json +3 -19
  88. package/src/build-components/OnboardFooter/OnboardFooter.tsx +37 -14
  89. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +8 -0
  90. package/src/build-components/OnboardImage/OnboardImage.tsx +28 -6
  91. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +9 -1
  92. package/src/build-components/OnboardItem/OnboardItem.tsx +15 -14
  93. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +8 -0
  94. package/src/build-components/OnboardProvider/OnboardProvider.tsx +35 -20
  95. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +8 -1
  96. package/src/build-components/OnboardProvider/pattern.json +0 -8
  97. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +8 -0
  98. package/src/build-components/OnboardSubtitle/pattern.json +1 -1
  99. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +8 -0
  100. package/src/build-components/OnboardTitle/pattern.json +1 -1
  101. package/src/build-components/RenderNode.generated.tsx +3 -0
  102. package/src/build-components/Text/Text.tsx +28 -10
  103. package/src/build-components/Text/TextProps.generated.ts +8 -0
  104. package/src/build-components/View/View.tsx +25 -3
  105. package/src/build-components/View/ViewProps.generated.ts +8 -0
  106. package/src/build-components/View/pattern.json +67 -1
  107. package/src/build-components/index.ts +5 -0
  108. package/src/build-components/patterns.generated.ts +1620 -46
  109. package/src/components/AttributesEditorPanel.tsx +13 -6
  110. package/src/components/Builder.tsx +200 -56
  111. package/src/components/BuilderButton.tsx +127 -0
  112. package/src/components/DeviceNavigationBar.tsx +0 -1
  113. package/src/components/EditorHeader.tsx +11 -1
  114. package/src/components/JsonTextEditor.tsx +185 -0
  115. package/src/index.ts +2 -2
  116. package/src/mockOS/components/MockOSRouter.tsx +17 -3
  117. package/src/mockOS/context/MockOSContext.tsx +0 -5
  118. package/src/mockOS/managers/mockPermissionManager.ts +0 -4
  119. package/src/mockOS/managers/navigationManager.ts +1 -6
  120. package/src/modals/ColorModal.tsx +306 -71
  121. package/src/modals/LocalicationModal.tsx +4 -5
  122. package/src/modals/Modal.tsx +8 -1
  123. package/src/pages/ProjectPage.tsx +299 -55
  124. package/src/pages/tabs/{BuilderTab.tsx → BuilderPanel.tsx} +13 -9
  125. package/src/pages/tabs/SideTool.tsx +260 -0
  126. package/src/size-matters/index.ts +6 -0
  127. package/src/store.ts +18 -1
  128. package/src/styles/base/_global.scss +163 -7
  129. package/src/styles/components/_attributes-editor.scss +12 -0
  130. package/src/styles/components/_editor-shell.scss +25 -0
  131. package/src/styles/foundation/_sizes.scss +1 -1
  132. package/src/styles/layout/_builder.scss +66 -10
  133. package/src/styles/modals/_color-modal.scss +59 -1
  134. package/src/styles/utilities/_carousel.scss +9 -8
  135. package/src/types/Project.ts +14 -0
  136. package/src/utils/analyseNode.ts +98 -0
  137. package/src/utils/extractImageStyle.ts +3 -6
  138. package/src/utils/extractTextStyle.ts +19 -82
  139. package/src/utils/extractViewStyle.ts +41 -12
  140. package/src/utils/parseColor.ts +43 -0
  141. package/src/utils/selection.ts +24 -0
  142. package/src/utils/useMergedStyle.ts +16 -0
  143. package/dist/pages/tabs/BuilderTab.d.ts +0 -9
  144. package/dist/pages/tabs/DebugTab.d.ts +0 -7
  145. package/dist/pages/tabs/PreviewTab.d.ts +0 -3
  146. package/src/pages/tabs/DebugTab.tsx +0 -64
  147. package/src/pages/tabs/PreviewTab.tsx +0 -206
@@ -0,0 +1,260 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { JsonTextEditor } from '../../components/JsonTextEditor';
3
+ import { Modal } from '../../modals';
4
+ import { Localication, Node } from '../..';
5
+ import { useLogRender } from '../../utils/useLogRender';
6
+ import { useRenderStore } from '../../store';
7
+ import { Checkbox } from '../../components/Checkbox';
8
+ import { LocalicationModal } from '../../modals/LocalicationModal';
9
+ import { analyseAndProccess } from '../../utils/analyseNode';
10
+
11
+ const screenStyleDefaults = {
12
+ light: { backgroundColor: '#FDFDFD', color: '#161827' },
13
+ dark: { backgroundColor: '#12131A', color: '#E9EBF9' },
14
+ } as const;
15
+
16
+ type ScreenMode = keyof typeof screenStyleDefaults;
17
+ type ScreenColorKey = keyof (typeof screenStyleDefaults)['light'];
18
+
19
+ type SideToolProps = {
20
+ data: Node;
21
+ setData: React.Dispatch<React.SetStateAction<Node>>;
22
+ };
23
+
24
+ const colorFields = [
25
+ {
26
+ id: 'light-bg',
27
+ label: 'Light Background Color',
28
+ mode: 'light' as ScreenMode,
29
+ key: 'backgroundColor' as ScreenColorKey,
30
+ },
31
+ {
32
+ id: 'light-color',
33
+ label: 'Light Color',
34
+ mode: 'light' as ScreenMode,
35
+ key: 'color' as ScreenColorKey,
36
+ },
37
+ {
38
+ id: 'dark-bg',
39
+ label: 'Dark Background Color',
40
+ mode: 'dark' as ScreenMode,
41
+ key: 'backgroundColor' as ScreenColorKey,
42
+ },
43
+ {
44
+ id: 'dark-color',
45
+ label: 'Dark Color',
46
+ mode: 'dark' as ScreenMode,
47
+ key: 'color' as ScreenColorKey,
48
+ },
49
+ ];
50
+
51
+ export function SideTool({ data, setData }: SideToolProps) {
52
+ useLogRender('SideTool');
53
+ const [isDebugModalOpen, setIsDebugModalOpen] = useState(false);
54
+ const [isLocalicationModalOpen, setIsLocalicationModalOpen] = useState(false);
55
+ const [isCompactMode, setIsCompactMode] = useState(() => {
56
+ if (typeof window === 'undefined') {
57
+ return false;
58
+ }
59
+ return window.innerWidth < 1000;
60
+ });
61
+ const [isCompactPanelVisible, setIsCompactPanelVisible] = useState(false);
62
+ const { appConfig, setAppConfig, previewMode, setPreviewMode } =
63
+ useRenderStore((s) => ({
64
+ appConfig: s.appConfig,
65
+ setAppConfig: s.setAppConfig,
66
+ previewMode: s.previewMode,
67
+ setPreviewMode: s.setPreviewMode,
68
+ }));
69
+
70
+ useEffect(() => {
71
+ if (typeof window === 'undefined') {
72
+ return;
73
+ }
74
+
75
+ const handleResize = () => {
76
+ const compact = window.innerWidth < 1000;
77
+ setIsCompactMode(compact);
78
+ };
79
+
80
+ handleResize();
81
+ window.addEventListener('resize', handleResize);
82
+ return () => window.removeEventListener('resize', handleResize);
83
+ }, []);
84
+
85
+ const getScreenColorValue = (mode: ScreenMode, key: ScreenColorKey) =>
86
+ appConfig.screenStyle?.[mode]?.[key] ?? screenStyleDefaults[mode][key];
87
+
88
+ const handleScreenStyleChange = (
89
+ mode: ScreenMode,
90
+ key: ScreenColorKey,
91
+ value: string,
92
+ ) => {
93
+ setAppConfig({
94
+ ...appConfig,
95
+ screenStyle: {
96
+ ...screenStyleDefaults,
97
+ ...appConfig.screenStyle,
98
+ [mode]: {
99
+ ...screenStyleDefaults[mode],
100
+ ...appConfig.screenStyle?.[mode],
101
+ [key]: value,
102
+ },
103
+ },
104
+ });
105
+ };
106
+
107
+ const handleLocalicationChange = (data: Localication) => {
108
+ setAppConfig({ ...appConfig, localication: data });
109
+ };
110
+
111
+ return (
112
+ <div
113
+ style={{
114
+ height: '100%',
115
+ display: 'flex',
116
+ flexDirection: 'column',
117
+ gap: 8,
118
+ }}
119
+ >
120
+ <button
121
+ type="button"
122
+ className="editor-button"
123
+ onClick={() => setIsCompactPanelVisible((prev) => !prev)}
124
+ aria-pressed={isCompactPanelVisible}
125
+ style={{ alignSelf: 'flex-start' }}
126
+ >
127
+ {isCompactPanelVisible ? 'Hide tools' : 'Show tools'}
128
+ </button>
129
+
130
+ {isCompactPanelVisible && (
131
+ <div className="side-tool">
132
+ <select
133
+ value={appConfig.defaultLanguage ?? 'en'}
134
+ onChange={(e) =>
135
+ setAppConfig({ ...appConfig, defaultLanguage: e.target.value })
136
+ }
137
+ >
138
+ {Object.keys(appConfig.localication ?? {}).map((language) => (
139
+ <option key={language} value={language}>
140
+ {language}
141
+ </option>
142
+ ))}
143
+ </select>
144
+
145
+ <Checkbox
146
+ label="Dark Mode"
147
+ checked={appConfig.theme === 'dark'}
148
+ onChange={(checked) =>
149
+ setAppConfig({ ...appConfig, theme: checked ? 'dark' : 'light' })
150
+ }
151
+ />
152
+
153
+ <Checkbox
154
+ label="Is RTL"
155
+ checked={appConfig.isRtl ?? false}
156
+ onChange={(checked) =>
157
+ setAppConfig({ ...appConfig, isRtl: checked })
158
+ }
159
+ />
160
+
161
+ <Checkbox
162
+ label="Preview mode"
163
+ checked={previewMode}
164
+ onChange={setPreviewMode}
165
+ />
166
+
167
+ <div>
168
+ <div
169
+ style={{
170
+ display: 'grid',
171
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
172
+ gap: 12,
173
+ }}
174
+ >
175
+ {colorFields.map(({ id, label, mode, key }) => (
176
+ <React.Fragment key={id}>
177
+ <div>{label}</div>
178
+ <input
179
+ id={id}
180
+ type="color"
181
+ className="input input--color"
182
+ value={getScreenColorValue(mode, key)}
183
+ onChange={(e) =>
184
+ handleScreenStyleChange(mode, key, e.target.value)
185
+ }
186
+ />
187
+ </React.Fragment>
188
+ ))}
189
+ </div>
190
+ </div>
191
+
192
+ <div
193
+ style={{
194
+ marginTop: 'auto',
195
+ paddingTop: 16,
196
+ display: 'flex',
197
+ flexDirection: 'column',
198
+ gap: 8,
199
+ }}
200
+ >
201
+ <button
202
+ type="button"
203
+ className="editor-button"
204
+ onClick={() => setIsLocalicationModalOpen(true)}
205
+ >
206
+ Open localization editor
207
+ </button>
208
+ <button
209
+ type="button"
210
+ className="editor-button debug-button"
211
+ title="Inspect raw JSON data"
212
+ onClick={() => setIsDebugModalOpen(true)}
213
+ >
214
+ Debug JSON
215
+ </button>
216
+ </div>
217
+
218
+ {isDebugModalOpen && (
219
+ <Modal
220
+ onClose={() => setIsDebugModalOpen(false)}
221
+ ariaLabelledBy="debug-json-editor-title"
222
+ className="modal--large modal--scrollable"
223
+ contentClassName="localication-modal__content"
224
+ >
225
+ <div className="modal__header localication-modal__header">
226
+ <button
227
+ type="button"
228
+ className="editor-button"
229
+ onClick={() => setIsDebugModalOpen(false)}
230
+ >
231
+ Close
232
+ </button>
233
+ </div>
234
+ <div className="localication-modal__body">
235
+ <div className="localication-modal__editor">
236
+ <JsonTextEditor
237
+ rootName="node"
238
+ value={data ?? {}}
239
+ onChange={(next) =>
240
+ setData(analyseAndProccess(next as Node) as Node)
241
+ }
242
+ className="localication-modal__json-editor"
243
+ />
244
+ </div>
245
+ </div>
246
+ </Modal>
247
+ )}
248
+
249
+ {isLocalicationModalOpen && (
250
+ <LocalicationModal
251
+ data={appConfig.localication ?? {}}
252
+ onChange={handleLocalicationChange}
253
+ onClose={() => setIsLocalicationModalOpen(false)}
254
+ />
255
+ )}
256
+ </div>
257
+ )}
258
+ </div>
259
+ );
260
+ }
@@ -67,6 +67,12 @@ export function parseSize(value?: string | number) {
67
67
  return Number.isFinite(n) ? fs(n) : raw;
68
68
  }
69
69
 
70
+ // Preserve percentage values as-is
71
+ if (lower.endsWith('%')) {
72
+ const numericPortion = parseFloat(lower.slice(0, -1));
73
+ return Number.isFinite(numericPortion) ? `${numericPortion}%` : raw;
74
+ }
75
+
70
76
  // Handle px explicitly (treat as absolute, not scaled)
71
77
  if (lower.endsWith('px')) {
72
78
  const n = parseFloat(lower.replace('px', ''));
package/src/store.ts CHANGED
@@ -10,15 +10,23 @@ import { getDefaultDevice } from './utils/getDevices';
10
10
  import { ScreenStyle } from './RenderPage';
11
11
  import { createJSONStorage } from 'zustand/middleware';
12
12
  import { Node } from './types/Node';
13
- import type { LogEntry, LogLevel } from './types/Project';
13
+ import type { LogEntry, LogLevel, ProjectColors } from './types/Project';
14
14
 
15
15
  type RenderStore = {
16
16
  copiedNode: Node | null;
17
17
  setCopiedNode: (node: Node | null) => void;
18
+ current: Node | null;
19
+ setCurrent: (node: Node | null) => void;
20
+ forceRender: number;
21
+ incForceRender: () => void;
18
22
  device: Device;
19
23
  setDevice: (device: Device) => void;
20
24
  appConfig: AppConfig;
21
25
  setAppConfig: (appConfig: AppConfig) => void;
26
+ projectColors?: ProjectColors;
27
+ setProjectColors: (projectColors?: ProjectColors) => void;
28
+ previewMode: boolean;
29
+ setPreviewMode: (previewMode: boolean) => void;
22
30
  // Logging
23
31
  logs: LogEntry[];
24
32
  logLevel: LogLevel;
@@ -36,10 +44,19 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
36
44
  (set) => ({
37
45
  copiedNode: null,
38
46
  setCopiedNode: (node) => set({ copiedNode: node }),
47
+ current: null,
48
+ setCurrent: (node) => set({ current: node }),
49
+ forceRender: 0,
50
+ incForceRender: () =>
51
+ set((state) => ({ forceRender: state.forceRender + 1 })),
39
52
  device: getDefaultDevice(),
40
53
  setDevice: (device) => set({ device }),
41
54
  appConfig: defaultAppConfig,
42
55
  setAppConfig: (appConfig) => set({ appConfig }),
56
+ projectColors: undefined,
57
+ setProjectColors: (projectColors) => set({ projectColors }),
58
+ previewMode: false,
59
+ setPreviewMode: (previewMode) => set({ previewMode }),
43
60
  // Logging defaults
44
61
  logs: [],
45
62
  logLevel: 'INFO',
@@ -12,13 +12,10 @@ body,
12
12
 
13
13
  .editor-container {
14
14
  height: 100vh;
15
- width: 100vw;
15
+ width: 100%;
16
16
  display: flex;
17
17
  overflow: hidden; /* Split panels manage their own scroll */
18
18
  }
19
- .editor-panel-builder {
20
- padding: 4px 8px;
21
- }
22
19
 
23
20
  .app-main {
24
21
  flex: 1 1 auto;
@@ -59,7 +56,7 @@ body,
59
56
  .split-left {
60
57
  @include thin-scrollbar;
61
58
  flex: 1 1 10%;
62
- min-width: 0;
59
+ min-width: 200px;
63
60
  border-right: 1px solid colors.$borderColor;
64
61
  overflow: auto;
65
62
  }
@@ -67,6 +64,7 @@ body,
67
64
  .split-right {
68
65
  position: relative;
69
66
  flex: 1 1 45%;
67
+ min-width: 450px;
70
68
  max-height: calc(100vh + 60px);
71
69
  //NOTE: Found by trial and error. We want it to make it full height of the screen with padding
72
70
  padding: sizes.$spaceComfy;
@@ -75,7 +73,7 @@ body,
75
73
  }
76
74
 
77
75
  .split-right__controls {
78
- position: sticky;
76
+ position: absolute;
79
77
  top: sizes.$spaceCozy;
80
78
  z-index: 2;
81
79
  display: flex;
@@ -84,7 +82,6 @@ body,
84
82
 
85
83
  .split-third {
86
84
  flex: 1 1 25%;
87
- min-width: 450px;
88
85
  border-right: 1px solid colors.$borderColor;
89
86
  overflow: auto;
90
87
  max-height: calc(100vh - 120px);
@@ -142,6 +139,11 @@ body,
142
139
  }
143
140
  }
144
141
 
142
+ /* Builder preview selection helpers */
143
+ .rb-node-selected {
144
+ border: 1px solid #2684ff;
145
+ }
146
+
145
147
  /* Header */
146
148
  .app-header {
147
149
  position: sticky;
@@ -251,3 +253,157 @@ body,
251
253
  position: relative;
252
254
  z-index: 100;
253
255
  }
256
+
257
+ .mobile-panel-toggle {
258
+ display: none;
259
+ padding: 0 sizes.$spaceComfy sizes.$spaceCozy;
260
+ gap: sizes.$spaceCompact;
261
+ }
262
+
263
+ .mobile-panel-toggle__button {
264
+ flex: 1;
265
+ display: inline-flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ gap: sizes.$spaceCompact;
269
+ padding: sizes.$spaceCozy;
270
+ border-radius: sizes.$radiusRounded;
271
+ border: 1px solid colors.$borderColor;
272
+ background: #fff;
273
+ color: colors.$textColor;
274
+ font-weight: 600;
275
+ cursor: pointer;
276
+ transition:
277
+ background 0.2s ease,
278
+ color 0.2s ease,
279
+ border-color 0.2s ease,
280
+ box-shadow 0.2s ease;
281
+
282
+ &:focus-visible {
283
+ outline: sizes.$outlineWidthFocus solid colors.$accentColor;
284
+ outline-offset: 2px;
285
+ }
286
+ }
287
+
288
+ .mobile-panel-toggle__button--active {
289
+ background: colors.$textColor;
290
+ color: #fff;
291
+ border-color: colors.$textColor;
292
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
293
+ }
294
+
295
+ .mobile-panel-toggle__icon {
296
+ width: 18px;
297
+ height: 12px;
298
+ display: inline-flex;
299
+ }
300
+
301
+ .mobile-panel-toggle__icon svg {
302
+ width: 100%;
303
+ height: 100%;
304
+ }
305
+
306
+ .mobile-panel-toggle__label {
307
+ line-height: 1;
308
+ }
309
+
310
+ .split-panel__close {
311
+ display: none;
312
+ position: absolute;
313
+ top: sizes.$spaceCozy;
314
+ right: sizes.$spaceCozy;
315
+ border: none;
316
+ border-radius: sizes.$radiusRounded;
317
+ background: rgba(0, 0, 0, 0.7);
318
+ color: #fff;
319
+ padding: sizes.$spaceSnug sizes.$spaceComfy;
320
+ font-weight: 600;
321
+ cursor: pointer;
322
+ z-index: 5;
323
+ }
324
+
325
+ .editor-container__overlay {
326
+ display: none;
327
+ }
328
+
329
+ @media (max-width: 1000px) {
330
+ .editor-container {
331
+ position: relative;
332
+ min-height: calc(100vh - 60px);
333
+ flex-direction: column;
334
+ }
335
+
336
+ .split-left,
337
+ .split-third {
338
+ position: absolute;
339
+ top: 0;
340
+ left: 0;
341
+ right: 0;
342
+ bottom: 0;
343
+ width: 100%;
344
+ height: 100%;
345
+ background: colors.$canvasColor;
346
+ box-shadow: 0 25px 60px rgba(0, 0, 0, 0.15);
347
+ transition:
348
+ transform 0.3s ease,
349
+ opacity 0.3s ease;
350
+ opacity: 0;
351
+ pointer-events: none;
352
+ z-index: 4;
353
+ }
354
+
355
+ .split-left {
356
+ transform: translate3d(-100%, 0, 0);
357
+ }
358
+
359
+ .split-third {
360
+ transform: translate3d(100%, 0, 0);
361
+ }
362
+
363
+ .split-left.is-open,
364
+ .split-third.is-open {
365
+ opacity: 1;
366
+ pointer-events: auto;
367
+ transform: translate3d(0, 0, 0);
368
+ }
369
+
370
+ .split-right {
371
+ flex: 1 1 auto;
372
+ min-width: 0;
373
+ min-height: calc(100vh - 120px);
374
+ padding: sizes.$spaceComfy;
375
+ padding-bottom: 120px;
376
+ position: relative;
377
+ }
378
+
379
+ .mobile-panel-toggle {
380
+ display: flex;
381
+ }
382
+
383
+ .split-panel__close {
384
+ display: inline-flex;
385
+ }
386
+
387
+ .editor-container__overlay {
388
+ display: block;
389
+ position: absolute;
390
+ inset: 0;
391
+ background: rgba(17, 24, 39, 0.35);
392
+ border: none;
393
+ padding: 0;
394
+ margin: 0;
395
+ cursor: pointer;
396
+ z-index: 3;
397
+ }
398
+ }
399
+
400
+ @media (max-width: 900px) {
401
+ .split-right__controls {
402
+ top: sizes.$spaceCozy;
403
+ z-index: 5;
404
+ padding-bottom: sizes.$spaceCozy;
405
+ .side-tool {
406
+ width: 100%;
407
+ }
408
+ }
409
+ }
@@ -247,6 +247,18 @@
247
247
  grid-area: bottom;
248
248
  }
249
249
  }
250
+
251
+ @media (max-width: 1400px) {
252
+ &--box {
253
+ /* Stack sections vertically on narrower viewports */
254
+ grid-template-columns: minmax(0, 1fr);
255
+ grid-template-areas:
256
+ 'top'
257
+ 'left'
258
+ 'right'
259
+ 'bottom';
260
+ }
261
+ }
250
262
  }
251
263
 
252
264
  &__field {
@@ -187,3 +187,28 @@
187
187
  background: #eef2ff;
188
188
  color: #111827;
189
189
  }
190
+
191
+ .side-tool {
192
+ display: flex;
193
+ flex-direction: column;
194
+ height: 100%;
195
+ max-width: 220px;
196
+ gap: sizes.$spaceTight;
197
+ padding: sizes.$spaceTight;
198
+ border-radius: 4px;
199
+ border: 1px solid colors.$borderColor;
200
+ font-size: 11xpx;
201
+ background: #fff;
202
+ }
203
+
204
+ .side-tool select {
205
+ width: 100%;
206
+ font-size: 11px;
207
+ padding: 4px 6px;
208
+ }
209
+
210
+ .side-tool .debug-button {
211
+ border-radius: 0;
212
+ width: 100%;
213
+ justify-content: center;
214
+ }
@@ -28,10 +28,10 @@ $fontSizeLg: 18px;
28
28
  $letterSpacingTight: 0.5px;
29
29
 
30
30
  $controlHeightMd: 36px;
31
+ $buttonHeight: 40px;
31
32
  $dimensionSelectMinWidth: 60px;
32
33
  $dimensionSelectWidth: 72px;
33
34
  $dimensionSizeGridMin: 140px;
34
35
  $dimensionSizeGridMax: 220px;
35
36
 
36
37
  $zIndexRaised: 20;
37
-
@@ -29,25 +29,68 @@
29
29
  gap: sizes.$spaceCompact;
30
30
  }
31
31
 
32
+ .builder__list-item {
33
+ flex: 1 1 220px;
34
+ min-width: 220px;
35
+ }
36
+
32
37
  .builder__button {
33
- @include card;
34
- padding: sizes.$spaceCozy sizes.$spaceCozy;
35
- background: colors.$accentColor;
36
- color: #fff;
38
+ position: relative;
39
+ display: flex;
40
+ align-items: stretch;
41
+ > .builder__button-link {
42
+ flex: 1;
43
+ min-width: 0; // allow text to shrink inside flex layout
44
+ display: flex;
45
+ align-items: center;
46
+ @include card;
47
+ height: sizes.$buttonHeight;
48
+ line-height: sizes.$buttonHeight - 3px; // simple adjustment to center the text
49
+ padding: 0 sizes.$spaceTight;
50
+ margin: 0;
51
+ background: colors.$accentColor;
52
+ color: #fff;
53
+ font-size: clamp(sizes.$fontSizeXs, 1.3vw, sizes.$fontSizeSmPlus);
54
+ white-space: nowrap;
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ cursor: pointer;
58
+ }
37
59
  }
38
60
 
39
- .builder__button-label {
40
- font-size: 13px;
41
- font-weight: 600;
61
+ .builder__sort-controls {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: sizes.$spaceTight;
65
+ }
66
+
67
+ .builder__sort-button {
68
+ width: 32px;
69
+ height: 18px;
70
+ border: none;
71
+ border-radius: sizes.$radiusSoft;
72
+ background: colors.$canvasColor;
73
+ color: colors.$mutedTextColor;
74
+ font-size: 14px;
75
+ cursor: pointer;
76
+ line-height: 1;
77
+ box-shadow: inset 0 0 0 1px colors.$borderColor;
78
+ padding: 0;
79
+ &:disabled {
80
+ opacity: 0.4;
81
+ cursor: not-allowed;
82
+ }
42
83
  }
43
84
 
44
85
  .builder__button-condition {
45
- font-size: 10px;
86
+ position: absolute;
87
+ bottom: -10px;
88
+ font-size: 8px;
46
89
  }
47
90
 
48
91
  .builder__node {
49
92
  @include card;
50
- padding: sizes.$spaceCozy;
93
+ padding: sizes.$spaceSnug;
51
94
  }
52
95
 
53
96
  .builder__node-type {
@@ -56,9 +99,22 @@
56
99
  }
57
100
 
58
101
  .builder__children {
102
+ position: relative;
59
103
  display: flex;
60
104
  flex-direction: column;
61
- gap: sizes.$spaceCompact;
105
+ gap: sizes.$spaceComfy;
106
+ > :not(:first-child) {
107
+ margin-left: sizes.$spaceComfy;
108
+ }
109
+ &::before {
110
+ content: '';
111
+ position: absolute;
112
+ left: 6px;
113
+ top: 2 * sizes.$spaceTight + sizes.$spaceComfy;
114
+ bottom: 0;
115
+ width: 1px;
116
+ background: colors.$borderColor;
117
+ }
62
118
  }
63
119
 
64
120
  .builder__text,