@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
@@ -0,0 +1,259 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { JsonEditor } from 'json-edit-react';
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
+
10
+ const screenStyleDefaults = {
11
+ light: { backgroundColor: '#FDFDFD', color: '#161827' },
12
+ dark: { backgroundColor: '#12131A', color: '#E9EBF9' },
13
+ } as const;
14
+
15
+ type ScreenMode = keyof typeof screenStyleDefaults;
16
+ type ScreenColorKey = keyof (typeof screenStyleDefaults)['light'];
17
+
18
+ type SideToolProps = {
19
+ data: Node;
20
+ setData: (data: Node) => void;
21
+ };
22
+
23
+ const colorFields = [
24
+ {
25
+ id: 'light-bg',
26
+ label: 'Light Background Color',
27
+ mode: 'light' as ScreenMode,
28
+ key: 'backgroundColor' as ScreenColorKey,
29
+ },
30
+ {
31
+ id: 'light-color',
32
+ label: 'Light Color',
33
+ mode: 'light' as ScreenMode,
34
+ key: 'color' as ScreenColorKey,
35
+ },
36
+ {
37
+ id: 'dark-bg',
38
+ label: 'Dark Background Color',
39
+ mode: 'dark' as ScreenMode,
40
+ key: 'backgroundColor' as ScreenColorKey,
41
+ },
42
+ {
43
+ id: 'dark-color',
44
+ label: 'Dark Color',
45
+ mode: 'dark' as ScreenMode,
46
+ key: 'color' as ScreenColorKey,
47
+ },
48
+ ];
49
+
50
+ export function SideTool({ data, setData }: SideToolProps) {
51
+ useLogRender('SideTool');
52
+ const [isDebugModalOpen, setIsDebugModalOpen] = useState(false);
53
+ const [isLocalicationModalOpen, setIsLocalicationModalOpen] = useState(false);
54
+ const [isCompactMode, setIsCompactMode] = useState(() => {
55
+ if (typeof window === 'undefined') {
56
+ return false;
57
+ }
58
+ return window.innerWidth < 1000;
59
+ });
60
+ const [isCompactPanelVisible, setIsCompactPanelVisible] = useState(false);
61
+ const { appConfig, setAppConfig, previewMode, setPreviewMode } =
62
+ useRenderStore((s) => ({
63
+ appConfig: s.appConfig,
64
+ setAppConfig: s.setAppConfig,
65
+ previewMode: s.previewMode,
66
+ setPreviewMode: s.setPreviewMode,
67
+ }));
68
+
69
+ useEffect(() => {
70
+ if (typeof window === 'undefined') {
71
+ return;
72
+ }
73
+
74
+ const handleResize = () => {
75
+ const compact = window.innerWidth < 1000;
76
+ setIsCompactMode(compact);
77
+ };
78
+
79
+ handleResize();
80
+ window.addEventListener('resize', handleResize);
81
+ return () => window.removeEventListener('resize', handleResize);
82
+ }, []);
83
+
84
+ const getScreenColorValue = (mode: ScreenMode, key: ScreenColorKey) =>
85
+ appConfig.screenStyle?.[mode]?.[key] ?? screenStyleDefaults[mode][key];
86
+
87
+ const handleScreenStyleChange = (
88
+ mode: ScreenMode,
89
+ key: ScreenColorKey,
90
+ value: string,
91
+ ) => {
92
+ setAppConfig({
93
+ ...appConfig,
94
+ screenStyle: {
95
+ ...screenStyleDefaults,
96
+ ...appConfig.screenStyle,
97
+ [mode]: {
98
+ ...screenStyleDefaults[mode],
99
+ ...appConfig.screenStyle?.[mode],
100
+ [key]: value,
101
+ },
102
+ },
103
+ });
104
+ };
105
+
106
+ const handleLocalicationChange = (data: Localication) => {
107
+ setAppConfig({ ...appConfig, localication: data });
108
+ };
109
+
110
+ return (
111
+ <div
112
+ style={{
113
+ height: '100%',
114
+ display: 'flex',
115
+ flexDirection: 'column',
116
+ gap: 8,
117
+ backgroundColor: isCompactMode ? '#fff' : undefined,
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" style={{ display: 'flex', height: '100%' }}>
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
+ <JsonEditor
237
+ rootName="debug"
238
+ data={data as any}
239
+ setData={setData as any}
240
+ className="localication-modal__json-editor"
241
+ maxWidth="100%"
242
+ />
243
+ </div>
244
+ </div>
245
+ </Modal>
246
+ )}
247
+
248
+ {isLocalicationModalOpen && (
249
+ <LocalicationModal
250
+ data={appConfig.localication ?? {}}
251
+ onChange={handleLocalicationChange}
252
+ onClose={() => setIsLocalicationModalOpen(false)}
253
+ />
254
+ )}
255
+ </div>
256
+ )}
257
+ </div>
258
+ );
259
+ }
@@ -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,21 @@ 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;
18
20
  device: Device;
19
21
  setDevice: (device: Device) => void;
20
22
  appConfig: AppConfig;
21
23
  setAppConfig: (appConfig: AppConfig) => void;
24
+ projectColors?: ProjectColors;
25
+ setProjectColors: (projectColors?: ProjectColors) => void;
26
+ previewMode: boolean;
27
+ setPreviewMode: (previewMode: boolean) => void;
22
28
  // Logging
23
29
  logs: LogEntry[];
24
30
  logLevel: LogLevel;
@@ -36,10 +42,16 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
36
42
  (set) => ({
37
43
  copiedNode: null,
38
44
  setCopiedNode: (node) => set({ copiedNode: node }),
45
+ current: null,
46
+ setCurrent: (node) => set({ current: node }),
39
47
  device: getDefaultDevice(),
40
48
  setDevice: (device) => set({ device }),
41
49
  appConfig: defaultAppConfig,
42
50
  setAppConfig: (appConfig) => set({ appConfig }),
51
+ projectColors: undefined,
52
+ setProjectColors: (projectColors) => set({ projectColors }),
53
+ previewMode: false,
54
+ setPreviewMode: (previewMode) => set({ previewMode }),
43
55
  // Logging defaults
44
56
  logs: [],
45
57
  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);
@@ -251,3 +248,157 @@ body,
251
248
  position: relative;
252
249
  z-index: 100;
253
250
  }
251
+
252
+ .mobile-panel-toggle {
253
+ display: none;
254
+ padding: 0 sizes.$spaceComfy sizes.$spaceCozy;
255
+ gap: sizes.$spaceCompact;
256
+ }
257
+
258
+ .mobile-panel-toggle__button {
259
+ flex: 1;
260
+ display: inline-flex;
261
+ align-items: center;
262
+ justify-content: center;
263
+ gap: sizes.$spaceCompact;
264
+ padding: sizes.$spaceCozy;
265
+ border-radius: sizes.$radiusRounded;
266
+ border: 1px solid colors.$borderColor;
267
+ background: #fff;
268
+ color: colors.$textColor;
269
+ font-weight: 600;
270
+ cursor: pointer;
271
+ transition:
272
+ background 0.2s ease,
273
+ color 0.2s ease,
274
+ border-color 0.2s ease,
275
+ box-shadow 0.2s ease;
276
+
277
+ &:focus-visible {
278
+ outline: sizes.$outlineWidthFocus solid colors.$accentColor;
279
+ outline-offset: 2px;
280
+ }
281
+ }
282
+
283
+ .mobile-panel-toggle__button--active {
284
+ background: colors.$textColor;
285
+ color: #fff;
286
+ border-color: colors.$textColor;
287
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
288
+ }
289
+
290
+ .mobile-panel-toggle__icon {
291
+ width: 18px;
292
+ height: 12px;
293
+ display: inline-flex;
294
+ }
295
+
296
+ .mobile-panel-toggle__icon svg {
297
+ width: 100%;
298
+ height: 100%;
299
+ }
300
+
301
+ .mobile-panel-toggle__label {
302
+ line-height: 1;
303
+ }
304
+
305
+ .split-panel__close {
306
+ display: none;
307
+ position: absolute;
308
+ top: sizes.$spaceCozy;
309
+ right: sizes.$spaceCozy;
310
+ border: none;
311
+ border-radius: sizes.$radiusRounded;
312
+ background: rgba(0, 0, 0, 0.7);
313
+ color: #fff;
314
+ padding: sizes.$spaceSnug sizes.$spaceComfy;
315
+ font-weight: 600;
316
+ cursor: pointer;
317
+ z-index: 5;
318
+ }
319
+
320
+ .editor-container__overlay {
321
+ display: none;
322
+ }
323
+
324
+ @media (max-width: 1000px) {
325
+ .editor-container {
326
+ position: relative;
327
+ min-height: calc(100vh - 60px);
328
+ flex-direction: column;
329
+ }
330
+
331
+ .split-left,
332
+ .split-third {
333
+ position: absolute;
334
+ top: 0;
335
+ left: 0;
336
+ right: 0;
337
+ bottom: 0;
338
+ width: 100%;
339
+ height: 100%;
340
+ background: colors.$canvasColor;
341
+ box-shadow: 0 25px 60px rgba(0, 0, 0, 0.15);
342
+ transition:
343
+ transform 0.3s ease,
344
+ opacity 0.3s ease;
345
+ opacity: 0;
346
+ pointer-events: none;
347
+ z-index: 4;
348
+ }
349
+
350
+ .split-left {
351
+ transform: translate3d(-100%, 0, 0);
352
+ }
353
+
354
+ .split-third {
355
+ transform: translate3d(100%, 0, 0);
356
+ }
357
+
358
+ .split-left.is-open,
359
+ .split-third.is-open {
360
+ opacity: 1;
361
+ pointer-events: auto;
362
+ transform: translate3d(0, 0, 0);
363
+ }
364
+
365
+ .split-right {
366
+ flex: 1 1 auto;
367
+ min-width: 0;
368
+ min-height: calc(100vh - 120px);
369
+ padding: sizes.$spaceComfy;
370
+ padding-bottom: 120px;
371
+ position: relative;
372
+ }
373
+
374
+ .mobile-panel-toggle {
375
+ display: flex;
376
+ }
377
+
378
+ .split-panel__close {
379
+ display: inline-flex;
380
+ }
381
+
382
+ .editor-container__overlay {
383
+ display: block;
384
+ position: absolute;
385
+ inset: 0;
386
+ background: rgba(17, 24, 39, 0.35);
387
+ border: none;
388
+ padding: 0;
389
+ margin: 0;
390
+ cursor: pointer;
391
+ z-index: 3;
392
+ }
393
+ }
394
+
395
+ @media (max-width: 900px) {
396
+ .split-right__controls {
397
+ top: sizes.$spaceCozy;
398
+ z-index: 5;
399
+ padding-bottom: sizes.$spaceCozy;
400
+ .side-tool {
401
+ width: 100%;
402
+ }
403
+ }
404
+ }
@@ -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,26 @@
187
187
  background: #eef2ff;
188
188
  color: #111827;
189
189
  }
190
+
191
+ .side-tool {
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: sizes.$spaceTight;
195
+ padding: sizes.$spaceTight;
196
+ border-radius: 4px;
197
+ border: 1px solid colors.$borderColor;
198
+ font-size: 11px;
199
+ max-width: 220px;
200
+ }
201
+
202
+ .side-tool select {
203
+ width: 100%;
204
+ font-size: 11px;
205
+ padding: 4px 6px;
206
+ }
207
+
208
+ .side-tool .debug-button {
209
+ border-radius: 0;
210
+ width: 100%;
211
+ justify-content: center;
212
+ }
@@ -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,
@@ -44,6 +44,35 @@
44
44
  font-weight: 600;
45
45
  }
46
46
 
47
+ .color-modal__toggle {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: sizes.$spaceSnug;
51
+ padding: 0 sizes.$spaceCompact;
52
+ }
53
+
54
+ .color-modal__toggle-label {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: sizes.$spaceCompact;
58
+ font-size: 13px;
59
+ font-weight: 600;
60
+ color: colors.$textColor;
61
+ cursor: pointer;
62
+ }
63
+
64
+ .color-modal__toggle-input {
65
+ width: 16px;
66
+ height: 16px;
67
+ accent-color: colors.$accentColor;
68
+ }
69
+
70
+ .color-modal__toggle-description {
71
+ margin: 0;
72
+ font-size: 12px;
73
+ color: colors.$mutedTextColor;
74
+ }
75
+
47
76
  .color-modal__link-button {
48
77
  border: none;
49
78
  background: none;