@developer_tribe/react-builder 0.1.32 → 1.0.2

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 (218) hide show
  1. package/dist/DeviceMockFrame.d.ts +2 -17
  2. package/dist/RenderPage.d.ts +4 -11
  3. package/dist/attributes-editor/Field.d.ts +16 -0
  4. package/dist/attributes-editor/FieldInfoTooltip.d.ts +7 -0
  5. package/dist/attributes-editor/LayoutPreviewPicker.d.ts +12 -0
  6. package/dist/attributes-editor/SpecialCategorySection.d.ts +19 -0
  7. package/dist/attributes-editor/types.d.ts +14 -0
  8. package/dist/background.jpg +0 -0
  9. package/dist/build-components/Button/Button.d.ts +1 -1
  10. package/dist/build-components/Button/ButtonProps.generated.d.ts +26 -1
  11. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +27 -1
  12. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +25 -0
  13. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +25 -0
  14. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +27 -1
  15. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +27 -1
  16. package/dist/build-components/Image/ImageProps.generated.d.ts +25 -3
  17. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +27 -1
  18. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +25 -0
  19. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +25 -0
  20. package/dist/build-components/OnboardDot/OnboardDot.d.ts +1 -1
  21. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +22 -0
  22. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +4 -5
  23. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +25 -3
  24. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +24 -3
  25. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +25 -4
  26. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +4 -5
  27. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +4 -5
  28. package/dist/build-components/Text/TextProps.generated.d.ts +4 -5
  29. package/dist/build-components/View/ViewProps.generated.d.ts +3 -4
  30. package/dist/build-components/index.d.ts +1 -0
  31. package/dist/build-components/patterns.generated.d.ts +4855 -132
  32. package/dist/components/AttributesEditorPanel.d.ts +9 -0
  33. package/dist/components/Breadcrumb.d.ts +15 -0
  34. package/dist/components/Builder.d.ts +9 -0
  35. package/dist/components/Checkbox.d.ts +17 -0
  36. package/dist/components/DeviceButton.d.ts +8 -0
  37. package/dist/components/DeviceNavigationBar.d.ts +10 -0
  38. package/dist/components/DeviceStatusBar.d.ts +9 -0
  39. package/dist/components/EditorHeader.d.ts +10 -0
  40. package/dist/index.cjs.js +6 -5
  41. package/dist/index.cjs.js.map +1 -0
  42. package/dist/index.d.ts +8 -4
  43. package/dist/index.esm.js +6 -5
  44. package/dist/index.esm.js.map +1 -0
  45. package/dist/mockOS/components/MockLaunchScreenComponent.d.ts +6 -0
  46. package/dist/mockOS/components/MockOSRouter.d.ts +8 -0
  47. package/dist/mockOS/components/PermissionModal.d.ts +9 -0
  48. package/dist/mockOS/context/MockOSContext.d.ts +36 -0
  49. package/dist/mockOS/hooks/useMockNavigation.d.ts +3 -0
  50. package/dist/mockOS/hooks/useMockPermission.d.ts +3 -0
  51. package/dist/mockOS/index.d.ts +9 -0
  52. package/dist/mockOS/managers/mockPermissionManager.d.ts +10 -0
  53. package/dist/mockOS/managers/navigationManager.d.ts +17 -0
  54. package/dist/modals/AddComponentModal.d.ts +8 -0
  55. package/dist/modals/ColorModal.d.ts +9 -0
  56. package/dist/modals/DeviceSelectorModal.d.ts +9 -0
  57. package/dist/modals/LocalicationModal.d.ts +8 -0
  58. package/dist/modals/Modal.d.ts +12 -0
  59. package/dist/modals/index.d.ts +5 -0
  60. package/dist/pages/ProjectPage.d.ts +11 -0
  61. package/dist/pages/tabs/BuilderTab.d.ts +9 -0
  62. package/dist/pages/tabs/DebugTab.d.ts +7 -0
  63. package/dist/pages/tabs/PreviewTab.d.ts +3 -0
  64. package/dist/store.d.ts +15 -18
  65. package/dist/styles.css +1 -1
  66. package/dist/types/PreviewConfig.d.ts +6 -3
  67. package/dist/types/Project.d.ts +12 -2
  68. package/dist/utils/copyNode.d.ts +2 -0
  69. package/dist/utils/logger.d.ts +11 -0
  70. package/dist/utils/patterns.d.ts +24 -0
  71. package/dist/utils/useLogRender.d.ts +1 -0
  72. package/package.json +17 -9
  73. package/scripts/prebuild/utils/createBuildComponentsIndex.js +15 -1
  74. package/scripts/prebuild/utils/createGeneratedProps.js +11 -3
  75. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +45 -6
  76. package/scripts/prebuild/utils/validatePatternJson.js +13 -5
  77. package/src/AttributesEditor.tsx +434 -311
  78. package/src/DeviceMockFrame.tsx +42 -67
  79. package/src/RenderPage.tsx +8 -44
  80. package/src/assets/images/android.svg +43 -0
  81. package/src/assets/images/apple.svg +16 -0
  82. package/src/assets/images/background.jpg +0 -0
  83. package/src/assets/samples/carousel-sample.json +2 -3
  84. package/src/assets/samples/getSamples.ts +49 -12
  85. package/src/assets/samples/simple-1.json +1 -2
  86. package/src/assets/samples/simple-2.json +1 -2
  87. package/src/assets/samples/vpn-onboard-1.json +1 -2
  88. package/src/assets/samples/vpn-onboard-2.json +1 -2
  89. package/src/assets/samples/vpn-onboard-3.json +1 -2
  90. package/src/assets/samples/vpn-onboard-4.json +1 -2
  91. package/src/assets/samples/vpn-onboard-5.json +1 -2
  92. package/src/assets/samples/vpn-onboard-6.json +1 -2
  93. package/src/attributes-editor/Field.tsx +662 -0
  94. package/src/attributes-editor/FieldInfoTooltip.tsx +49 -0
  95. package/src/attributes-editor/LayoutPreviewPicker.tsx +199 -0
  96. package/src/attributes-editor/SpecialCategorySection.tsx +284 -0
  97. package/src/attributes-editor/types.ts +30 -0
  98. package/src/build-components/Button/Button.tsx +12 -2
  99. package/src/build-components/Button/ButtonProps.generated.ts +37 -1
  100. package/src/build-components/Button/pattern.json +31 -2
  101. package/src/build-components/Carousel/Carousel.tsx +17 -2
  102. package/src/build-components/Carousel/CarouselProps.generated.ts +39 -1
  103. package/src/build-components/Carousel/pattern.json +10 -0
  104. package/src/build-components/CarouselButtons/CarouselButtons.tsx +8 -2
  105. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +36 -0
  106. package/src/build-components/CarouselButtons/pattern.json +22 -0
  107. package/src/build-components/CarouselDots/CarouselDots.tsx +42 -8
  108. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +36 -0
  109. package/src/build-components/CarouselDots/pattern.json +15 -0
  110. package/src/build-components/CarouselItem/CarouselItem.tsx +7 -2
  111. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +39 -1
  112. package/src/build-components/CarouselItem/pattern.json +7 -0
  113. package/src/build-components/CarouselProvider/CarouselProvider.tsx +10 -2
  114. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +39 -1
  115. package/src/build-components/CarouselProvider/pattern.json +7 -0
  116. package/src/build-components/Image/Image.tsx +10 -2
  117. package/src/build-components/Image/ImageProps.generated.ts +36 -3
  118. package/src/build-components/Image/pattern.json +46 -3
  119. package/src/build-components/Onboard/Onboard.tsx +8 -1
  120. package/src/build-components/Onboard/OnboardProps.generated.ts +39 -1
  121. package/src/build-components/Onboard/pattern.json +11 -0
  122. package/src/build-components/OnboardButton/OnboardButton.tsx +53 -9
  123. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +36 -0
  124. package/src/build-components/OnboardButton/pattern.json +71 -5
  125. package/src/build-components/OnboardButtons/OnboardButtons.tsx +27 -17
  126. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +36 -0
  127. package/src/build-components/OnboardButtons/pattern.json +70 -4
  128. package/src/build-components/OnboardDot/OnboardDot.tsx +106 -4
  129. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +22 -0
  130. package/src/build-components/OnboardDot/pattern.json +54 -1
  131. package/src/build-components/OnboardFooter/OnboardFooter.tsx +14 -6
  132. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +4 -5
  133. package/src/build-components/OnboardFooter/pattern.json +58 -2
  134. package/src/build-components/OnboardImage/OnboardImage.tsx +29 -5
  135. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +36 -3
  136. package/src/build-components/OnboardImage/pattern.json +21 -0
  137. package/src/build-components/OnboardItem/OnboardItem.tsx +8 -1
  138. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +35 -3
  139. package/src/build-components/OnboardItem/pattern.json +38 -2
  140. package/src/build-components/OnboardProvider/OnboardProvider.tsx +22 -8
  141. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +37 -4
  142. package/src/build-components/OnboardProvider/pattern.json +51 -4
  143. package/src/build-components/OnboardSubtitle/OnboardSubtitle.tsx +2 -0
  144. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +4 -5
  145. package/src/build-components/OnboardSubtitle/pattern.json +6 -0
  146. package/src/build-components/OnboardTitle/OnboardTitle.tsx +2 -0
  147. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +4 -5
  148. package/src/build-components/OnboardTitle/pattern.json +6 -0
  149. package/src/build-components/Text/Text.tsx +12 -6
  150. package/src/build-components/Text/TextProps.generated.ts +4 -5
  151. package/src/build-components/Text/pattern.json +38 -2
  152. package/src/build-components/View/View.tsx +11 -6
  153. package/src/build-components/View/ViewProps.generated.ts +3 -4
  154. package/src/build-components/View/pattern.json +227 -19
  155. package/src/build-components/index.ts +22 -0
  156. package/src/build-components/patterns.generated.ts +4905 -139
  157. package/src/components/AttributesEditorPanel.tsx +58 -0
  158. package/src/components/Breadcrumb.tsx +80 -0
  159. package/src/components/Builder.tsx +375 -0
  160. package/src/components/Checkbox.tsx +81 -0
  161. package/src/components/DeviceButton.tsx +39 -0
  162. package/src/components/DeviceNavigationBar.tsx +201 -0
  163. package/src/components/DeviceStatusBar.tsx +85 -0
  164. package/src/components/EditorHeader.tsx +138 -0
  165. package/src/index.ts +8 -4
  166. package/src/mockOS/components/MockLaunchScreenComponent.tsx +43 -0
  167. package/src/mockOS/components/MockOSRouter.tsx +115 -0
  168. package/src/mockOS/components/PermissionModal.tsx +270 -0
  169. package/src/mockOS/context/MockOSContext.tsx +179 -0
  170. package/src/mockOS/hooks/useMockNavigation.ts +11 -0
  171. package/src/mockOS/hooks/useMockPermission.ts +11 -0
  172. package/src/mockOS/index.ts +26 -0
  173. package/src/mockOS/managers/mockPermissionManager.ts +54 -0
  174. package/src/mockOS/managers/navigationManager.ts +91 -0
  175. package/src/modals/AddComponentModal.tsx +313 -0
  176. package/src/modals/ColorModal.tsx +268 -0
  177. package/src/modals/DeviceSelectorModal.tsx +57 -0
  178. package/src/modals/LocalicationModal.tsx +54 -0
  179. package/src/modals/Modal.tsx +57 -0
  180. package/src/modals/index.ts +5 -0
  181. package/src/pages/ProjectPage.tsx +150 -0
  182. package/src/pages/tabs/BuilderTab.tsx +33 -0
  183. package/src/pages/tabs/DebugTab.tsx +64 -0
  184. package/src/pages/tabs/PreviewTab.tsx +206 -0
  185. package/src/size-matters/index.ts +25 -5
  186. package/src/store.ts +56 -38
  187. package/src/styles/base/_global.scss +253 -0
  188. package/src/styles/components/_attributes-editor.scss +261 -0
  189. package/src/styles/components/_editor-shell.scss +189 -0
  190. package/src/styles/components/_mockos-router.scss +140 -0
  191. package/src/styles/components/_ui-components.scss +183 -0
  192. package/src/styles/foundation/_colors.scss +8 -0
  193. package/src/styles/foundation/_mixins.scss +22 -0
  194. package/src/styles/{_reset.scss → foundation/_reset.scss} +5 -2
  195. package/src/styles/foundation/_sizes.scss +37 -0
  196. package/src/styles/foundation/_typography.scss +4 -0
  197. package/src/styles/foundation/_variables.scss +3 -0
  198. package/src/styles/index.scss +22 -129
  199. package/src/styles/layout/_builder.scss +68 -0
  200. package/src/styles/layout/_pages.scss +3 -0
  201. package/src/styles/modals/_add-component.scss +122 -0
  202. package/src/styles/modals/_color-modal.scss +130 -0
  203. package/src/styles/modals/_device-selector.scss +18 -0
  204. package/src/styles/modals/_localication-modal.scss +68 -0
  205. package/src/styles/modals/_modal-shell.scss +46 -0
  206. package/src/styles/utilities/_carousel.scss +125 -0
  207. package/src/types/PreviewConfig.ts +14 -5
  208. package/src/types/Project.ts +15 -2
  209. package/src/types/images.d.ts +8 -0
  210. package/src/utils/copyNode.ts +7 -0
  211. package/src/utils/extractTextStyle.ts +8 -4
  212. package/src/utils/extractViewStyle.ts +51 -7
  213. package/src/utils/getDevices.ts +1 -0
  214. package/src/utils/logger.ts +76 -0
  215. package/src/utils/patterns.ts +33 -0
  216. package/src/utils/useLogRender.ts +13 -0
  217. package/dist/build-components/OnboardDot/OnboardExpandingDotProps.generated.d.ts +0 -10
  218. package/src/build-components/OnboardDot/OnboardExpandingDotProps.generated.ts +0 -20
@@ -0,0 +1,201 @@
1
+ import React from 'react';
2
+ import { Device } from '../types/Device';
3
+ import { useMockOSContext } from '../mockOS';
4
+
5
+ type DeviceNavigationBarProps = {
6
+ height: number;
7
+ backgroundColor: string;
8
+ platform?: Device['platform'];
9
+ navigationBarType?: Device['navigationBarType'];
10
+ isDark: boolean;
11
+ };
12
+
13
+ const basePillStyle: React.CSSProperties = {
14
+ height: 5,
15
+ borderRadius: 999,
16
+ width: 120,
17
+ };
18
+
19
+ const tabItems = ['Today', 'Search', 'Profile'];
20
+
21
+ export function DeviceNavigationBar({
22
+ height,
23
+ backgroundColor,
24
+ platform = 'android',
25
+ navigationBarType = 'gesture',
26
+ isDark,
27
+ }: DeviceNavigationBarProps) {
28
+ const context = useMockOSContext();
29
+
30
+ if (!height) {
31
+ return null;
32
+ }
33
+
34
+ const barColor = isDark ? 'rgba(255, 255, 255, 0.85)' : 'rgba(0, 0, 0, 0.7)';
35
+ const mutedColor = isDark
36
+ ? 'rgba(255, 255, 255, 0.55)'
37
+ : 'rgba(0, 0, 0, 0.4)';
38
+
39
+ function handleBackButton() {
40
+ console.log('handleBackButton', context);
41
+ if (context) {
42
+ const canGoBack = context.goBack();
43
+ // If can't go back, go to launchscreen
44
+ if (!canGoBack) {
45
+ context.navigation('launchscreen');
46
+ }
47
+ } else {
48
+ alert('Navigation: Back\n(Mock OS context not available)');
49
+ }
50
+ }
51
+
52
+ function handleHomeButton() {
53
+ if (context) {
54
+ context.navigation('launchscreen');
55
+ } else {
56
+ alert('Navigation: Home\n(Mock OS context not available)');
57
+ }
58
+ }
59
+
60
+ const containerStyle: React.CSSProperties = {
61
+ height,
62
+ backgroundColor,
63
+ display: 'flex',
64
+ alignItems: 'center',
65
+ justifyContent: 'center',
66
+ padding: '0 16px',
67
+ };
68
+
69
+ if (platform === 'ios') {
70
+ switch (navigationBarType) {
71
+ case 'homeIndicator':
72
+ return (
73
+ <div className="device-navigation-bar" style={containerStyle}>
74
+ <div
75
+ style={{
76
+ ...basePillStyle,
77
+ backgroundColor: barColor,
78
+ }}
79
+ />
80
+ </div>
81
+ );
82
+ case 'tabBar':
83
+ return (
84
+ <div className="device-navigation-bar" style={containerStyle}>
85
+ <div
86
+ style={{
87
+ display: 'flex',
88
+ width: '100%',
89
+ maxWidth: 280,
90
+ justifyContent: 'space-between',
91
+ fontSize: 11,
92
+ textTransform: 'uppercase',
93
+ letterSpacing: 0.4,
94
+ gap: 20,
95
+ }}
96
+ >
97
+ {tabItems.map((item) => (
98
+ <div
99
+ key={item}
100
+ style={{
101
+ display: 'flex',
102
+ flexDirection: 'column',
103
+ alignItems: 'center',
104
+ gap: 4,
105
+ color: item === 'Today' ? barColor : mutedColor,
106
+ }}
107
+ >
108
+ <div
109
+ style={{
110
+ width: 20,
111
+ height: 20,
112
+ borderRadius: 6,
113
+ backgroundColor: item === 'Today' ? barColor : mutedColor,
114
+ opacity: 0.9,
115
+ }}
116
+ />
117
+ <span>{item}</span>
118
+ </div>
119
+ ))}
120
+ </div>
121
+ </div>
122
+ );
123
+ default:
124
+ return null;
125
+ }
126
+ }
127
+
128
+ // Android styles
129
+ if (navigationBarType === 'threeButtons') {
130
+ const buttonStyle: React.CSSProperties = {
131
+ position: 'relative',
132
+ width: 40,
133
+ height: 40,
134
+ display: 'flex',
135
+ alignItems: 'center',
136
+ justifyContent: 'center',
137
+ fontSize: 18,
138
+ color: barColor,
139
+ cursor: 'pointer',
140
+ userSelect: 'none',
141
+ };
142
+
143
+ return (
144
+ <div className="device-navigation-bar" style={containerStyle}>
145
+ <div
146
+ style={{
147
+ display: 'flex',
148
+ gap: 64,
149
+ }}
150
+ >
151
+ <div
152
+ style={{ ...buttonStyle, fontSize: 24 }}
153
+ onClick={handleBackButton}
154
+ role="button"
155
+ aria-label="Back"
156
+ >
157
+
158
+ </div>
159
+ <div
160
+ style={{ ...buttonStyle, fontSize: 32, top: -3 }}
161
+ onClick={handleHomeButton}
162
+ role="button"
163
+ aria-label="Home"
164
+ >
165
+
166
+ </div>
167
+ <div
168
+ style={{
169
+ ...buttonStyle,
170
+ fontSize: 20,
171
+ fontWeight: 1000,
172
+ }}
173
+ onClick={handleHomeButton}
174
+ role="button"
175
+ aria-label="Recent Apps"
176
+ >
177
+
178
+ </div>
179
+ </div>
180
+ </div>
181
+ );
182
+ }
183
+
184
+ if (navigationBarType === 'gesture') {
185
+ return (
186
+ <div className="device-navigation-bar" style={containerStyle}>
187
+ <div
188
+ style={{
189
+ ...basePillStyle,
190
+ width: 90,
191
+ backgroundColor: barColor,
192
+ }}
193
+ />
194
+ </div>
195
+ );
196
+ }
197
+
198
+ return null;
199
+ }
200
+
201
+ export default DeviceNavigationBar;
@@ -0,0 +1,85 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Device } from '../types/Device';
3
+
4
+ type DeviceStatusBarProps = {
5
+ height: number;
6
+ backgroundColor: string;
7
+ platform?: Device['platform'];
8
+ isDark: boolean;
9
+ };
10
+
11
+ const formatTime = () =>
12
+ new Date().toLocaleTimeString([], {
13
+ hour: '2-digit',
14
+ minute: '2-digit',
15
+ });
16
+
17
+ const iosIndicators = ['5G', 'Wi-Fi', '92%'];
18
+ const androidIndicators = ['Alarm', 'Wi-Fi', '84%'];
19
+
20
+ export function DeviceStatusBar({
21
+ height,
22
+ backgroundColor,
23
+ platform = 'android',
24
+ isDark,
25
+ }: DeviceStatusBarProps) {
26
+ const [time, setTime] = useState(() => formatTime());
27
+
28
+ useEffect(() => {
29
+ const intervalId = setInterval(() => setTime(formatTime()), 60_000);
30
+ return () => clearInterval(intervalId);
31
+ }, []);
32
+
33
+ const textColor = isDark ? 'rgb(255, 255, 255)' : 'rgb(0, 0, 0)';
34
+ const iconStyle: React.CSSProperties = {
35
+ fontSize: 10,
36
+ letterSpacing: 0.5,
37
+ textTransform: 'uppercase',
38
+ };
39
+
40
+ const containerStyle: React.CSSProperties = {
41
+ position: 'absolute',
42
+ top: 0,
43
+ left: 0,
44
+ right: 0,
45
+ height,
46
+ backgroundColor,
47
+ color: textColor,
48
+ display: 'flex',
49
+ alignItems: 'center',
50
+ fontSize: 12,
51
+ fontWeight: 500,
52
+ padding: '0 12px',
53
+ };
54
+
55
+ if (platform === 'ios') {
56
+ return (
57
+ <div className="device-status-bar" style={containerStyle}>
58
+ <span style={{ fontWeight: 600 }}>{time}</span>
59
+ <div style={{ flex: 1 }} />
60
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
61
+ {iosIndicators.map((indicator) => (
62
+ <span key={indicator} style={iconStyle}>
63
+ {indicator}
64
+ </span>
65
+ ))}
66
+ </div>
67
+ </div>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <div className="device-status-bar" style={containerStyle}>
73
+ <span style={{ flex: 1 }}>{time}</span>
74
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
75
+ {androidIndicators.map((indicator) => (
76
+ <span key={indicator} style={iconStyle}>
77
+ {indicator}
78
+ </span>
79
+ ))}
80
+ </div>
81
+ </div>
82
+ );
83
+ }
84
+
85
+ export default DeviceStatusBar;
@@ -0,0 +1,138 @@
1
+ import { useState } from 'react';
2
+ import { Device, getDevices, Node, copyNode } from '..';
3
+ import { useRenderStore } from '../store';
4
+ import { useLogRender } from '../utils/useLogRender';
5
+ import { DeviceButton } from './DeviceButton';
6
+ import { DeviceSelectorModal } from '../modals/DeviceSelectorModal';
7
+
8
+ const devices = getDevices();
9
+ interface EditorHeaderProps {
10
+ onSaveProject?: () => void;
11
+ onRestoreProject?: () => void;
12
+ current?: Node;
13
+ editorData?: Node;
14
+ setEditorData?: (data: Node) => void;
15
+ }
16
+
17
+ export function EditorHeader({
18
+ onSaveProject,
19
+ onRestoreProject,
20
+ current,
21
+ editorData,
22
+ setEditorData,
23
+ }: EditorHeaderProps) {
24
+ useLogRender('EditorHeader');
25
+ const [isDevicesModalOpen, setIsDevicesModalOpen] = useState(false);
26
+ const copiedNode = useRenderStore((s) => s.copiedNode);
27
+ const { device: selectedDevice, setDevice } = useRenderStore((s) => ({
28
+ device: s.device,
29
+ setDevice: s.setDevice,
30
+ }));
31
+
32
+ function replaceNode(root: Node, target: Node, next: Node): Node {
33
+ if (root === target) return next;
34
+ if (root === null || root === undefined) return root;
35
+ if (typeof root === 'string') return root;
36
+ if (Array.isArray(root)) {
37
+ let changed = false;
38
+ const arr = root.map((item) => {
39
+ const r = replaceNode(item, target, next);
40
+ if (r !== item) changed = true;
41
+ return r;
42
+ });
43
+ return changed ? arr : root;
44
+ }
45
+ const data = root as any;
46
+ if ('children' in data) {
47
+ const prev = data.children;
48
+ const replaced = Array.isArray(prev)
49
+ ? prev.map((c: Node) => replaceNode(c, target, next))
50
+ : replaceNode(prev as Node, target, next);
51
+ if (replaced !== prev) {
52
+ data.children = replaced;
53
+ return { ...data, children: replaced } as Node;
54
+ }
55
+ }
56
+ return root;
57
+ }
58
+ const handleCopy = () => {
59
+ if (current) copyNode(current);
60
+ };
61
+ const handlePaste = () => {
62
+ if (!current || !editorData || !setEditorData) return;
63
+ if (!copiedNode) return;
64
+ const cloned = JSON.parse(JSON.stringify(copiedNode)) as Node;
65
+ const updated = replaceNode(editorData, current, cloned);
66
+ useRenderStore.setState({
67
+ copiedNode: null,
68
+ });
69
+ setEditorData(updated);
70
+ };
71
+ return (
72
+ <div
73
+ className="editor-header"
74
+ role="region"
75
+ aria-label="Editor utility header"
76
+ >
77
+ <div className="editor-header__devices">
78
+ {devices.slice(0, 5).map((deviceOption: Device) => (
79
+ <DeviceButton
80
+ key={deviceOption.name}
81
+ selectedDevice={selectedDevice}
82
+ onSelect={setDevice}
83
+ device={deviceOption}
84
+ />
85
+ ))}
86
+ <button
87
+ className="editor-device-button"
88
+ aria-label="More devices"
89
+ onClick={() => setIsDevicesModalOpen(true)}
90
+ >
91
+ More devices
92
+ </button>
93
+ </div>
94
+ <div className="editor-header__actions">
95
+ <button
96
+ className="editor-button editor-save-button"
97
+ aria-label="Save project data"
98
+ onClick={() => onSaveProject && onSaveProject()}
99
+ >
100
+ Save
101
+ </button>
102
+ {onRestoreProject && (
103
+ <button
104
+ className="editor-button editor-save-previewconfig-button"
105
+ aria-label="Restore project data"
106
+ onClick={() => onRestoreProject()}
107
+ >
108
+ Restore
109
+ </button>
110
+ )}
111
+ <button
112
+ className="editor-button"
113
+ aria-label="Copy node"
114
+ onClick={handleCopy}
115
+ >
116
+ Copy
117
+ </button>
118
+ {copiedNode && (
119
+ <button
120
+ className="editor-button"
121
+ aria-label="Paste node"
122
+ onClick={handlePaste}
123
+ >
124
+ Paste
125
+ </button>
126
+ )}
127
+ </div>
128
+ {isDevicesModalOpen && (
129
+ <DeviceSelectorModal
130
+ devices={devices}
131
+ selectedDevice={selectedDevice}
132
+ onSelect={setDevice}
133
+ onClose={() => setIsDevicesModalOpen(false)}
134
+ />
135
+ )}
136
+ </div>
137
+ );
138
+ }
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,8 @@ 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 type { ProjectPageProps, Tab } from './pages/ProjectPage';
32
+ export { copyNode } from './utils/copyNode';
33
+ export { logger } from './utils/logger';
34
+ export type { LogLevel } from './types/Project';
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+
3
+ interface MockLaunchScreenComponentProps {
4
+ appName: string;
5
+ onAppClick: () => void;
6
+ }
7
+
8
+ export function MockLaunchScreenComponent({
9
+ appName,
10
+ onAppClick,
11
+ }: MockLaunchScreenComponentProps) {
12
+ return (
13
+ <div className="mockos-screen mockos-screen--launchscreen">
14
+ <div className="mockos-launcher">
15
+ <div className="mockos-launcher__grid">
16
+ <button
17
+ className="mockos-launcher__app"
18
+ onClick={onAppClick}
19
+ type="button"
20
+ >
21
+ <div className="mockos-launcher__app-icon">
22
+ <svg
23
+ viewBox="0 0 60 60"
24
+ fill="none"
25
+ xmlns="http://www.w3.org/2000/svg"
26
+ >
27
+ <rect width="60" height="60" rx="13" fill="#007AFF" />
28
+ <path
29
+ d="M30 15C21.716 15 15 21.716 15 30s6.716 15 15 15 15-6.716 15-15-6.716-15-15-15zm0 27c-6.617 0-12-5.383-12-12s5.383-12 12-12 12 5.383 12 12-5.383 12-12 12z"
30
+ fill="white"
31
+ />
32
+ <circle cx="30" cy="30" r="5" fill="white" />
33
+ </svg>
34
+ </div>
35
+ <div className="mockos-launcher__app-name">{appName}</div>
36
+ </button>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ export default MockLaunchScreenComponent;
@@ -0,0 +1,115 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { useMockOSContext } from '../context/MockOSContext';
3
+ import { MockLaunchScreenComponent } from './MockLaunchScreenComponent';
4
+ // Note: We might use react-router or similar library in the future for more complex routing
5
+
6
+ interface MockOSRouterProps {
7
+ children?: ReactNode;
8
+ childrenBelongTo?: 'launchscreen' | 'home' | 'onboard' | 'subscription';
9
+ appName?: string;
10
+ }
11
+
12
+ function HomeComponent() {
13
+ return (
14
+ <div className="mockos-screen mockos-screen--home">
15
+ <div className="mockos-screen__heading">Home</div>
16
+ <div className="mockos-screen__text">Welcome to the home screen</div>
17
+ </div>
18
+ );
19
+ }
20
+
21
+ function OnboardComponent() {
22
+ return (
23
+ <div className="mockos-screen mockos-screen--onboard">
24
+ <div className="mockos-screen__heading">Onboarding</div>
25
+ <div className="mockos-screen__text">Welcome! Let's get you started.</div>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ function SubscriptionComponent() {
31
+ return (
32
+ <div className="mockos-screen mockos-screen--subscription">
33
+ <div className="mockos-screen__heading">Subscription</div>
34
+ <div className="mockos-screen__text">Choose your plan</div>
35
+ <div className="mockos-screen__plan mockos-screen__plan--premium">
36
+ Premium - $9.99/month
37
+ </div>
38
+ <div className="mockos-screen__plan mockos-screen__plan--basic">
39
+ Basic - Free
40
+ </div>
41
+ </div>
42
+ );
43
+ }
44
+
45
+ function ScreenRenderer({
46
+ currentRoute,
47
+ children,
48
+ childrenBelongTo,
49
+ appName,
50
+ onLaunchApp,
51
+ }: {
52
+ currentRoute: string;
53
+ children?: ReactNode;
54
+ childrenBelongTo?: string;
55
+ appName: string;
56
+ onLaunchApp: () => void;
57
+ }) {
58
+ // If children provided and current route matches childrenBelongTo, render children
59
+ if (children && childrenBelongTo && currentRoute === childrenBelongTo) {
60
+ return <>{children}</>;
61
+ }
62
+
63
+ // Otherwise render default screens based on route
64
+ switch (currentRoute) {
65
+ case 'launchscreen':
66
+ return (
67
+ <MockLaunchScreenComponent appName={appName} onAppClick={onLaunchApp} />
68
+ );
69
+ case 'home':
70
+ return <HomeComponent />;
71
+ case 'onboard':
72
+ return <OnboardComponent />;
73
+ case 'subscription':
74
+ return <SubscriptionComponent />;
75
+ default:
76
+ return (
77
+ <MockLaunchScreenComponent appName={appName} onAppClick={onLaunchApp} />
78
+ );
79
+ }
80
+ }
81
+
82
+ export function MockOSRouter({
83
+ children,
84
+ childrenBelongTo,
85
+ appName = 'My App',
86
+ }: MockOSRouterProps) {
87
+ const context = useMockOSContext();
88
+
89
+ if (!context) {
90
+ throw new Error('MockOSRouter must be used within MockOSProvider');
91
+ }
92
+
93
+ const { currentRoute, navigation } = context;
94
+
95
+ function handleLaunchApp() {
96
+ // Navigate to the route specified in childrenBelongTo or default to 'onboard'
97
+ const targetRoute = childrenBelongTo || 'onboard';
98
+ navigation(targetRoute);
99
+ }
100
+
101
+ return (
102
+ <div className="mockos-router">
103
+ <ScreenRenderer
104
+ currentRoute={currentRoute}
105
+ childrenBelongTo={childrenBelongTo}
106
+ appName={appName}
107
+ onLaunchApp={handleLaunchApp}
108
+ >
109
+ {children}
110
+ </ScreenRenderer>
111
+ </div>
112
+ );
113
+ }
114
+
115
+ export default MockOSRouter;