@developer_tribe/react-builder 0.1.31 → 1.1.0

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 (102) hide show
  1. package/dist/DeviceMockFrame.d.ts +1 -17
  2. package/dist/RenderPage.d.ts +1 -9
  3. package/dist/build-components/Button/ButtonProps.generated.d.ts +2 -1
  4. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +2 -1
  5. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +2 -1
  6. package/dist/build-components/Image/ImageProps.generated.d.ts +2 -1
  7. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -4
  8. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +6 -3
  9. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +2 -1
  10. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +10 -5
  11. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +4 -1
  12. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +4 -2
  13. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +10 -5
  14. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +10 -5
  15. package/dist/build-components/Text/TextProps.generated.d.ts +10 -5
  16. package/dist/build-components/View/ViewProps.generated.d.ts +6 -3
  17. package/dist/build-components/index.d.ts +1 -0
  18. package/dist/build-components/patterns.generated.d.ts +7 -2
  19. package/dist/components/AttributesEditorPanel.d.ts +9 -0
  20. package/dist/components/Breadcrumb.d.ts +13 -0
  21. package/dist/components/Builder.d.ts +9 -0
  22. package/dist/components/EditorHeader.d.ts +15 -0
  23. package/dist/index.cjs.js +7 -4
  24. package/dist/index.cjs.js.map +1 -0
  25. package/dist/index.d.ts +5 -4
  26. package/dist/index.esm.js +7 -4
  27. package/dist/index.esm.js.map +1 -0
  28. package/dist/pages/ProjectPage.d.ts +9 -0
  29. package/dist/pages/tabs/BuilderTab.d.ts +9 -0
  30. package/dist/pages/tabs/DebugTab.d.ts +7 -0
  31. package/dist/pages/tabs/PreviewTab.d.ts +3 -0
  32. package/dist/store.d.ts +8 -18
  33. package/dist/styles.css +1 -1
  34. package/dist/types/PreviewConfig.d.ts +6 -3
  35. package/dist/types/Project.d.ts +2 -2
  36. package/dist/utils/copyNode.d.ts +2 -0
  37. package/package.json +17 -9
  38. package/scripts/prebuild/utils/createBuildComponentsIndex.js +15 -1
  39. package/scripts/prebuild/utils/createGeneratedProps.js +64 -5
  40. package/src/DeviceMockFrame.tsx +20 -31
  41. package/src/RenderPage.tsx +3 -38
  42. package/src/assets/images/android.svg +43 -0
  43. package/src/assets/images/apple.svg +16 -0
  44. package/src/assets/images/background.jpg +0 -0
  45. package/src/assets/samples/carousel-sample.json +2 -3
  46. package/src/assets/samples/getSamples.ts +51 -8
  47. package/src/assets/samples/simple-1.json +1 -2
  48. package/src/assets/samples/simple-2.json +1 -2
  49. package/src/assets/samples/vpn-onboard-1.json +1 -2
  50. package/src/assets/samples/vpn-onboard-2.json +1 -2
  51. package/src/assets/samples/vpn-onboard-3.json +1 -2
  52. package/src/assets/samples/vpn-onboard-4.json +1 -2
  53. package/src/assets/samples/vpn-onboard-5.json +1024 -0
  54. package/src/assets/samples/vpn-onboard-6.json +708 -0
  55. package/src/build-components/Button/ButtonProps.generated.ts +14 -12
  56. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +6 -1
  57. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +9 -7
  58. package/src/build-components/Image/ImageProps.generated.ts +3 -1
  59. package/src/build-components/OnboardButton/OnboardButton.tsx +5 -4
  60. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +14 -9
  61. package/src/build-components/OnboardButton/pattern.json +3 -2
  62. package/src/build-components/OnboardButtons/OnboardButtons.tsx +5 -7
  63. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +10 -3
  64. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +9 -7
  65. package/src/build-components/OnboardFooter/OnboardFooter.tsx +3 -3
  66. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +33 -22
  67. package/src/build-components/OnboardImage/OnboardImage.tsx +24 -1
  68. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +5 -1
  69. package/src/build-components/OnboardImage/pattern.json +3 -5
  70. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +5 -2
  71. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +33 -22
  72. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +33 -22
  73. package/src/build-components/Text/Text.tsx +3 -3
  74. package/src/build-components/Text/TextProps.generated.ts +33 -22
  75. package/src/build-components/View/ViewProps.generated.ts +18 -9
  76. package/src/build-components/index.ts +22 -0
  77. package/src/build-components/patterns.generated.ts +7 -2
  78. package/src/components/AttributesEditorPanel.tsx +110 -0
  79. package/src/components/Breadcrumb.tsx +46 -0
  80. package/src/components/Builder.tsx +270 -0
  81. package/src/components/EditorHeader.tsx +184 -0
  82. package/src/index.ts +5 -4
  83. package/src/pages/ProjectPage.tsx +112 -0
  84. package/src/pages/tabs/BuilderTab.tsx +31 -0
  85. package/src/pages/tabs/DebugTab.tsx +21 -0
  86. package/src/pages/tabs/PreviewTab.tsx +192 -0
  87. package/src/size-matters/index.ts +5 -1
  88. package/src/store.ts +26 -38
  89. package/src/styles/_mixins.scss +21 -0
  90. package/src/styles/_variables.scss +27 -0
  91. package/src/styles/builder.scss +60 -0
  92. package/src/styles/components.scss +88 -0
  93. package/src/styles/editor.scss +174 -0
  94. package/src/styles/global.scss +200 -0
  95. package/src/styles/index.scss +7 -0
  96. package/src/styles/pages.scss +2 -0
  97. package/src/types/PreviewConfig.ts +14 -5
  98. package/src/types/Project.ts +2 -2
  99. package/src/utils/copyNode.ts +7 -0
  100. package/src/utils/extractTextStyle.ts +4 -2
  101. package/src/utils/getDevices.ts +1 -0
  102. package/src/utils/novaToJson.ts +5 -0
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import './styles/index.scss';
2
2
  import AttributesEditor from './AttributesEditor';
3
3
 
4
- export { TargetedScreenSize } from './types/TargetedScreenSize';
5
- export { Node, NodeData, NodeDefaultAttribute } from './types/Node';
6
- export { Project } from './types/Project';
4
+ export type { TargetedScreenSize } from './types/TargetedScreenSize';
5
+ export type { Node, NodeData, NodeDefaultAttribute } from './types/Node';
6
+ export type { Project } from './types/Project';
7
7
  export {
8
8
  isNodeNullOrUndefined,
9
9
  isNodeString,
@@ -14,7 +14,6 @@ export {
14
14
  export { getSamples } from './assets/samples/getSamples';
15
15
  export { getBasicSamples } from './assets/samples/getSamples';
16
16
  export { getOnboardSamples } from './assets/samples/getSamples';
17
- export { PreviewConfig } from './types/PreviewConfig';
18
17
  export { RenderPage } from './RenderPage';
19
18
  export { DeviceMockFrame } from './DeviceMockFrame';
20
19
  export { novaToJson } from './utils/novaToJson';
@@ -28,3 +27,5 @@ export { querySelector } from './utils/querySelector';
28
27
  export { extractViewStyle } from './utils/extractViewStyle';
29
28
  export { extractImageStyle } from './utils/extractImageStyle';
30
29
  export { extractTextStyle } from './utils/extractTextStyle';
30
+ export { ProjectPage } from './pages/ProjectPage';
31
+ export { copyNode } from './utils/copyNode';
@@ -0,0 +1,112 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Node, RenderPage, Project } from '..';
3
+ import { EditorHeader } from '../components/EditorHeader';
4
+ import { AttributesEditorPanel } from '../components/AttributesEditorPanel';
5
+ import { BuilderTab } from './tabs/BuilderTab';
6
+ import { PreviewTab } from './tabs/PreviewTab';
7
+ import { DebugTab } from './tabs/DebugTab';
8
+ import { AppConfig, defaultAppConfig } from '../types/PreviewConfig';
9
+ import { useRenderStore } from '../store';
10
+
11
+ type Tab = 'builder' | 'preview' | 'debug';
12
+
13
+ type ProjectPageProps = {
14
+ project: Project;
15
+ onSaveProject: (project: Project) => void;
16
+ appConfig?: AppConfig;
17
+ };
18
+
19
+ export function ProjectPage({
20
+ project,
21
+ appConfig = defaultAppConfig,
22
+ onSaveProject,
23
+ }: ProjectPageProps) {
24
+ const [current, setCurrent] = useState<Node>(project.data);
25
+ const [editorData, setEditorData] = useState<Node>(project.data);
26
+ const [tab, setTab] = useState<Tab>('builder');
27
+
28
+ useEffect(() => {
29
+ useRenderStore.getState().setAppConfig(appConfig);
30
+ }, [appConfig]);
31
+
32
+ return (
33
+ <div className="container-full">
34
+ <EditorHeader
35
+ onSaveProject={() => {
36
+ onSaveProject({
37
+ ...project,
38
+ data: editorData,
39
+ });
40
+ }}
41
+ current={current}
42
+ editorData={editorData}
43
+ setEditorData={setEditorData}
44
+ />
45
+ <div className="editor-container">
46
+ <div className="split-left">
47
+ <div>
48
+ <div
49
+ className="editor-tabs"
50
+ role="tablist"
51
+ aria-label="Editor tabs"
52
+ >
53
+ <button
54
+ className={`editor-tab ${tab === 'builder' ? 'editor-tab--active' : ''}`}
55
+ role="tab"
56
+ aria-selected={tab === 'builder'}
57
+ onClick={() => setTab('builder')}
58
+ >
59
+ Builder
60
+ </button>
61
+ <button
62
+ className={`editor-tab ${tab === 'preview' ? 'editor-tab--active' : ''}`}
63
+ role="tab"
64
+ aria-selected={tab === 'preview'}
65
+ onClick={() => setTab('preview')}
66
+ >
67
+ Preview Config
68
+ </button>
69
+ <button
70
+ className={`editor-tab ${tab === 'debug' ? 'editor-tab--active' : ''}`}
71
+ role="tab"
72
+ aria-selected={tab === 'debug'}
73
+ onClick={() => setTab('debug')}
74
+ >
75
+ Debug
76
+ </button>
77
+ </div>
78
+
79
+ {tab === 'builder' && (
80
+ <BuilderTab
81
+ data={editorData}
82
+ setData={setEditorData}
83
+ current={current}
84
+ setCurrent={setCurrent}
85
+ />
86
+ )}
87
+
88
+ {tab === 'preview' && <PreviewTab />}
89
+
90
+ {tab === 'debug' && (
91
+ <DebugTab data={editorData} setData={setEditorData} />
92
+ )}
93
+ </div>
94
+ </div>
95
+ <div className="split-right">
96
+ <div className="split-right-background" />
97
+ <RenderPage data={editorData} />
98
+ </div>
99
+ <div className="split-third">
100
+ <AttributesEditorPanel
101
+ current={current}
102
+ attributes={editorData}
103
+ onChange={(data) => {
104
+ setEditorData(data);
105
+ }}
106
+ setCurrent={setCurrent}
107
+ />
108
+ </div>
109
+ </div>
110
+ </div>
111
+ );
112
+ }
@@ -0,0 +1,31 @@
1
+ import { Node } from '../..';
2
+ import { Builder } from '../../components/Builder';
3
+
4
+ type BuilderTabProps = {
5
+ data: Node;
6
+ setData: (data: Node) => void;
7
+ current: Node;
8
+ setCurrent: (current: Node) => void;
9
+ };
10
+
11
+ export function BuilderTab({
12
+ data,
13
+ setData,
14
+ current,
15
+ setCurrent,
16
+ }: BuilderTabProps) {
17
+ return (
18
+ <div
19
+ role="tabpanel"
20
+ className="editor-panel-builder editor-panel editor-panel--active"
21
+ aria-hidden={false}
22
+ >
23
+ <Builder
24
+ data={data}
25
+ setData={setData}
26
+ current={current}
27
+ setCurrent={setCurrent}
28
+ />
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,21 @@
1
+ import { JsonEditor } from 'json-edit-react';
2
+ import { Node } from '../..';
3
+
4
+ type DebugTabProps = {
5
+ data: Node;
6
+ setData: (data: Node) => void;
7
+ };
8
+
9
+ export function DebugTab({ data, setData }: DebugTabProps) {
10
+ return (
11
+ <div
12
+ role="tabpanel"
13
+ className="editor-panel editor-panel--active editor-panels-debug"
14
+ aria-hidden={false}
15
+ >
16
+ <div style={{ flex: '1 1 auto', minHeight: 0, overflow: 'auto' }}>
17
+ <JsonEditor data={data as any} setData={setData as any} />
18
+ </div>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,192 @@
1
+ import { JsonEditor } from 'json-edit-react';
2
+ import { Localication, TargetedScreenSize } from '../..';
3
+ import { useRenderStore } from '../../store';
4
+
5
+ type PreviewTabProps = {};
6
+
7
+ export function PreviewTab({}: PreviewTabProps) {
8
+ const { appConfig, setAppConfig } = useRenderStore((s) => ({
9
+ appConfig: s.appConfig,
10
+ setAppConfig: s.setAppConfig,
11
+ }));
12
+ return (
13
+ <div
14
+ role="tabpanel"
15
+ className="editor-panel editor-panel--active"
16
+ aria-hidden={false}
17
+ >
18
+ <div style={{ padding: 12 }}>
19
+ <div
20
+ style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}
21
+ >
22
+ <div>Default Language</div>
23
+ <select
24
+ value={appConfig.defaultLanguage ?? 'en'}
25
+ onChange={(e) => {
26
+ setAppConfig({
27
+ ...appConfig,
28
+ defaultLanguage: e.target.value,
29
+ });
30
+ }}
31
+ >
32
+ {Object.keys(appConfig.localication ?? {}).map((language) => (
33
+ <option key={language} value={language}>
34
+ {language}
35
+ </option>
36
+ ))}
37
+ </select>
38
+ </div>
39
+ <div
40
+ style={{
41
+ display: 'grid',
42
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
43
+ gap: 12,
44
+ marginTop: 8,
45
+ }}
46
+ >
47
+ <div>Light Background Color</div>
48
+ <input
49
+ type="color"
50
+ value={appConfig.screenStyle?.light.backgroundColor ?? '#FDFDFD'}
51
+ onChange={(e) => {
52
+ const next = {
53
+ ...appConfig.screenStyle,
54
+ light: {
55
+ ...(appConfig.screenStyle?.light ?? {
56
+ color: '#161827',
57
+ }),
58
+ backgroundColor: e.target.value,
59
+ },
60
+ };
61
+
62
+ setAppConfig({
63
+ ...appConfig,
64
+ screenStyle: next,
65
+ });
66
+ }}
67
+ className="input input--color"
68
+ />
69
+ <div>Light Color</div>
70
+ <input
71
+ type="color"
72
+ value={appConfig.screenStyle?.light.color ?? '#161827'}
73
+ onChange={(e) => {
74
+ const next = {
75
+ ...appConfig.screenStyle,
76
+ light: {
77
+ ...(appConfig.screenStyle?.light ?? {
78
+ backgroundColor: '#FDFDFD',
79
+ }),
80
+ color: e.target.value,
81
+ },
82
+ };
83
+
84
+ setAppConfig({
85
+ ...appConfig,
86
+ screenStyle: next,
87
+ });
88
+ }}
89
+ className="input input--color"
90
+ />
91
+ <div>Dark Background Color</div>
92
+ <input
93
+ type="color"
94
+ value={appConfig.screenStyle?.dark.backgroundColor ?? '#12131A'}
95
+ onChange={(e) => {
96
+ const next = {
97
+ ...appConfig.screenStyle,
98
+ dark: {
99
+ ...(appConfig.screenStyle?.dark ?? {
100
+ color: '#E9EBF9',
101
+ }),
102
+ backgroundColor: e.target.value,
103
+ },
104
+ };
105
+
106
+ setAppConfig({
107
+ ...appConfig,
108
+ screenStyle: next,
109
+ });
110
+ }}
111
+ className="input input--color"
112
+ />
113
+ <div>Dark Color</div>
114
+ <input
115
+ type="color"
116
+ value={appConfig.screenStyle?.dark.color ?? '#E9EBF9'}
117
+ onChange={(e) => {
118
+ const next = {
119
+ ...appConfig.screenStyle,
120
+ dark: {
121
+ ...(appConfig.screenStyle?.dark ?? {
122
+ backgroundColor: '#12131A',
123
+ }),
124
+ color: e.target.value,
125
+ },
126
+ };
127
+ setAppConfig({
128
+ ...appConfig,
129
+ screenStyle: next,
130
+ });
131
+ }}
132
+ className="input input--color"
133
+ />
134
+ </div>
135
+ <div
136
+ style={{
137
+ display: 'flex',
138
+ alignItems: 'center',
139
+ gap: 8,
140
+ marginTop: 8,
141
+ }}
142
+ >
143
+ <div>Dark Mode</div>
144
+ <input
145
+ type="checkbox"
146
+ checked={appConfig.theme === 'dark'}
147
+ onChange={(e) => {
148
+ const nextTheme = e.target.checked ? 'dark' : 'light';
149
+
150
+ setAppConfig({
151
+ ...appConfig,
152
+ theme: nextTheme,
153
+ });
154
+ }}
155
+ />
156
+ </div>
157
+ <div
158
+ style={{
159
+ display: 'flex',
160
+ alignItems: 'center',
161
+ gap: 8,
162
+ marginTop: 8,
163
+ }}
164
+ >
165
+ <div>Is RTL</div>
166
+ <input
167
+ type="checkbox"
168
+ checked={appConfig.isRtl ?? false}
169
+ onChange={(e) => {
170
+ setAppConfig({
171
+ ...appConfig,
172
+ isRtl: e.target.checked,
173
+ });
174
+ }}
175
+ />
176
+ </div>
177
+ <div style={{ marginTop: 8 }}>
178
+ <JsonEditor
179
+ rootName="localication"
180
+ data={appConfig.localication ?? {}}
181
+ setData={(data) => {
182
+ setAppConfig({
183
+ ...appConfig,
184
+ localication: data as Localication,
185
+ });
186
+ }}
187
+ />
188
+ </div>
189
+ </div>
190
+ </div>
191
+ );
192
+ }
@@ -8,7 +8,11 @@ function getBaseDimensions() {
8
8
  ? [device.width, device.height]
9
9
  : [device.height, device.width];
10
10
 
11
- return { baseSize: currentState.baseSize, shortDimension, longDimension };
11
+ return {
12
+ baseSize: currentState.appConfig.baseSize,
13
+ shortDimension,
14
+ longDimension,
15
+ };
12
16
  }
13
17
  export function scale(size: number) {
14
18
  const { baseSize, shortDimension } = getBaseDimensions();
package/src/store.ts CHANGED
@@ -1,56 +1,44 @@
1
1
  import { createWithEqualityFn } from 'zustand/traditional';
2
2
  import { shallow } from 'zustand/shallow';
3
3
  import type { Device } from './types/Device';
4
- import type { Localication } from './types/PreviewConfig';
4
+ import {
5
+ defaultAppConfig,
6
+ type AppConfig,
7
+ type Localication,
8
+ } from './types/PreviewConfig';
5
9
  import { getDefaultDevice } from './utils/getDevices';
6
10
  import { ScreenStyle } from './RenderPage';
11
+ import { createJSONStorage } from 'zustand/middleware';
12
+ import { Node } from './types/Node';
7
13
 
8
14
  type RenderStore = {
15
+ copiedNode: Node | null;
16
+ setCopiedNode: (node: Node | null) => void;
9
17
  device: Device;
10
- localication: Localication | null;
11
- defaultLanguage?: string;
12
- baseSize: {
13
- width: number;
14
- height: number;
15
- };
16
- theme: 'dark' | 'light';
17
- screenStyle: ScreenStyle;
18
- setBaseSize: (baseSize: { width: number; height: number }) => void;
19
18
  setDevice: (device: Device) => void;
20
- setLocalication: (localication: Localication | null) => void;
21
- setDefaultLanguage: (defaultLanguage?: string) => void;
22
- setTheme: (theme: 'dark' | 'light') => void;
23
- setScreenStyle: (screenStyle: ScreenStyle) => void;
19
+ appConfig: AppConfig;
20
+ setAppConfig: (appConfig: AppConfig) => void;
21
+ renderCount: number;
22
+ forceRender: () => void;
24
23
  };
25
24
 
26
25
  export const useRenderStore = createWithEqualityFn<RenderStore>()(
27
26
  (set) => ({
27
+ copiedNode: null,
28
+ setCopiedNode: (node) => set({ copiedNode: node }),
29
+ renderCount: 0,
30
+ forceRender: () => set((state) => ({ renderCount: state.renderCount + 1 })),
28
31
  device: getDefaultDevice(),
29
- localication: null,
30
- defaultLanguage: undefined,
31
- baseSize: {
32
- width: 390,
33
- height: 844,
34
- },
35
- theme: 'light',
36
- screenStyle: {
37
- light: {
38
- backgroundColor: '#FDFDFD',
39
- color: '#161827',
40
- seperatorColor: '#E5E7EB',
41
- },
42
- dark: {
43
- backgroundColor: '#12131A',
44
- color: '#E9EBF9',
45
- seperatorColor: '#E5E7EB',
46
- },
47
- },
48
- setBaseSize: (baseSize) => set({ baseSize }),
49
32
  setDevice: (device) => set({ device }),
50
- setLocalication: (localication) => set({ localication }),
51
- setDefaultLanguage: (defaultLanguage) => set({ defaultLanguage }),
52
- setTheme: (theme) => set({ theme }),
53
- setScreenStyle: (screenStyle) => set({ screenStyle }),
33
+ appConfig: defaultAppConfig,
34
+ setAppConfig: (appConfig) => set({ appConfig }),
35
+ persist: {
36
+ name: 'render-store',
37
+ partialize: (state: RenderStore) => ({
38
+ copiedNode: state.copiedNode ?? null,
39
+ }),
40
+ storage: createJSONStorage(() => localStorage),
41
+ },
54
42
  }),
55
43
  shallow,
56
44
  );
@@ -0,0 +1,21 @@
1
+ @use './variables' as *;
2
+ @mixin center-content {
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ }
7
+
8
+ @mixin card {
9
+ background: #fff;
10
+ border: 1px solid $color-border;
11
+ border-radius: $radius-xs;
12
+ }
13
+
14
+ @mixin thin-scrollbar {
15
+ scrollbar-width: thin;
16
+ scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
17
+ &::-webkit-scrollbar {
18
+ width: 8px;
19
+ height: 4px;
20
+ }
21
+ }
@@ -0,0 +1,27 @@
1
+ // Color palette
2
+ $color-background: #f3f4f6;
3
+ $color-border: #e5e7eb;
4
+ $color-text: #111827;
5
+ $color-muted: #9ca3af;
6
+ $color-error: #ef4444;
7
+ $color-button: cornflowerblue;
8
+
9
+ // Spacing
10
+ $space-1: 4px;
11
+ $space-2: 8px;
12
+ $space-3: 12px;
13
+ $space-4: 16px;
14
+ $space-5: 20px;
15
+ $space-6: 24px;
16
+
17
+ // Radius & shadow
18
+ $radius-xs: 4px;
19
+ $radius-sm: 8px;
20
+ $radius-md: 12px;
21
+ $radius-lg: 24px;
22
+ $shadow-card: 0 10px 30px rgba(0, 0, 0, 0.12);
23
+
24
+ // Fonts
25
+ $font-sans:
26
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
27
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif;
@@ -0,0 +1,60 @@
1
+ @use './variables' as *;
2
+ @use './mixins' as *;
3
+
4
+ .builder {
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: $space-3;
8
+ }
9
+
10
+ .builder__breadcrumbs {
11
+ display: flex;
12
+ flex-direction: row;
13
+ gap: $space-2;
14
+ }
15
+
16
+ .builder__breadcrumb {
17
+ color: $color-muted;
18
+ font-size: 12px;
19
+ }
20
+
21
+ .builder__current {
22
+ font-weight: 600;
23
+ }
24
+
25
+ .builder__list {
26
+ display: flex;
27
+ flex-wrap: wrap;
28
+ gap: $space-2;
29
+ }
30
+
31
+ .builder__button {
32
+ @include card;
33
+ padding: $space-3 $space-3;
34
+ font-size: 14px;
35
+ background: $color-button;
36
+ color: #fff;
37
+ cursor: pointer;
38
+ }
39
+
40
+ .builder__node {
41
+ @include card;
42
+ padding: $space-3;
43
+ }
44
+
45
+ .builder__node-type {
46
+ margin: 0 0 $space-2 0;
47
+ font-weight: 600;
48
+ }
49
+
50
+ .builder__children {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: $space-2;
54
+ }
55
+
56
+ .builder__text,
57
+ .builder__placeholder {
58
+ color: $color-muted;
59
+ font-size: 12px;
60
+ }
@@ -0,0 +1,88 @@
1
+ @use './variables' as *;
2
+ @use './mixins' as *;
3
+
4
+ .editor-controls {
5
+ display: grid;
6
+ grid-template-columns: auto 1fr;
7
+ gap: $space-3;
8
+ padding: $space-4;
9
+ }
10
+
11
+ .editor-section {
12
+ background: #fff;
13
+ border: 1px solid $color-border;
14
+ border-radius: $radius-md;
15
+ padding: $space-4;
16
+ }
17
+
18
+ .form-row {
19
+ display: grid;
20
+ grid-template-columns: 160px 1fr;
21
+ align-items: center;
22
+ gap: $space-3;
23
+ margin-bottom: $space-3;
24
+ }
25
+
26
+ .form-actions {
27
+ display: flex;
28
+ gap: $space-3;
29
+ margin-top: $space-4;
30
+ }
31
+
32
+ .btn {
33
+ padding: 8px 12px;
34
+ border-radius: 8px;
35
+ border: 1px solid $color-border;
36
+ background: #fff;
37
+ cursor: pointer;
38
+ transition:
39
+ background 0.2s ease,
40
+ box-shadow 0.2s ease;
41
+
42
+ &:hover {
43
+ background: #f9fafb;
44
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
45
+ }
46
+ }
47
+
48
+ /* Inputs */
49
+ .input {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ height: 32px;
53
+ padding: 0 8px;
54
+ border: 1px solid $color-border;
55
+ border-radius: $radius-md;
56
+ background: #fff;
57
+ color: $color-text;
58
+ font: inherit;
59
+ outline: none;
60
+ transition:
61
+ border-color 0.15s ease,
62
+ box-shadow 0.15s ease;
63
+
64
+ &:focus {
65
+ border-color: darken($color-border, 8%);
66
+ box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.04);
67
+ }
68
+ }
69
+
70
+ .input--color {
71
+ padding: 0;
72
+ width: 40px;
73
+ height: 28px;
74
+ border-radius: 6px;
75
+ cursor: pointer;
76
+
77
+ &::-webkit-color-swatch-wrapper {
78
+ padding: 0;
79
+ }
80
+ &::-webkit-color-swatch {
81
+ border: none;
82
+ border-radius: 4px;
83
+ }
84
+ &::-moz-color-swatch {
85
+ border: none;
86
+ border-radius: 4px;
87
+ }
88
+ }