@developer_tribe/react-builder 1.2.39 → 1.2.41

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 (93) hide show
  1. package/dist/attributes-editor/FallbackLocalizationField.d.ts +6 -0
  2. package/dist/build-components/NavigationBarColor/NavigationBarColorProps.generated.d.ts +1 -40
  3. package/dist/build-components/StatusBarColor/StatusBarColorProps.generated.d.ts +1 -1
  4. package/dist/build-components/patterns.generated.d.ts +21 -344
  5. package/dist/components/BuilderProvider.d.ts +1 -0
  6. package/dist/components/DeviceButton.d.ts +4 -1
  7. package/dist/index.cjs.js +1 -1
  8. package/dist/index.cjs.js.map +1 -1
  9. package/dist/index.esm.js +1 -1
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.web.cjs.js +4 -4
  12. package/dist/index.web.cjs.js.map +1 -1
  13. package/dist/index.web.d.ts +8 -0
  14. package/dist/index.web.esm.js +4 -4
  15. package/dist/index.web.esm.js.map +1 -1
  16. package/dist/mockOS/context/MockOSContext.d.ts +3 -1
  17. package/dist/product-base/types.d.ts +3 -0
  18. package/dist/size-matters/index.d.ts +1 -1
  19. package/dist/store.d.ts +31 -0
  20. package/dist/styles.css +1 -1
  21. package/dist/types/Device.d.ts +5 -0
  22. package/dist/types/PreviewConfig.d.ts +1 -1
  23. package/dist/utils/extractTextStyle/extractTextStyle.d.ts +1 -0
  24. package/dist/utils/extractViewStyle/extractViewStyle.d.ts +1 -0
  25. package/package.json +1 -1
  26. package/scripts/prebuild/assets/prompt_scheme.md +7 -0
  27. package/scripts/public/bin.js +0 -0
  28. package/src/DeviceMockFrame.tsx +8 -0
  29. package/src/RenderPage.tsx +3 -0
  30. package/src/assets/devices.json +747 -183
  31. package/src/assets/meta.json +1 -1
  32. package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
  33. package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
  34. package/src/assets/samples/carousel-sample.json +30 -26
  35. package/src/assets/samples/paywall-1.json +31 -31
  36. package/src/assets/samples/paywall-2.json +28 -28
  37. package/src/assets/samples/paywall-app-delete-offer.json +29 -29
  38. package/src/assets/samples/paywall-app-open-offer.json +29 -29
  39. package/src/assets/samples/paywall-back-offer.json +28 -28
  40. package/src/assets/samples/paywall-notification-offer.json +28 -28
  41. package/src/assets/samples/simple-1.json +4 -4
  42. package/src/assets/samples/simple-2.json +25 -25
  43. package/src/assets/samples/unmigrated-builder-1.1.1.json +7 -7
  44. package/src/assets/samples/unmigrated-builder1.json +4 -4
  45. package/src/assets/samples/unvalidated-builder1.json +4 -4
  46. package/src/assets/samples/unvalidated-crash1.json +2 -2
  47. package/src/assets/samples/unvalidated-crashcomponent1.json +2 -2
  48. package/src/assets/samples/vpn-onboard-1.json +30 -30
  49. package/src/assets/samples/vpn-onboard-2.json +30 -30
  50. package/src/assets/samples/vpn-onboard-3.json +27 -27
  51. package/src/assets/samples/vpn-onboard-4.json +27 -27
  52. package/src/assets/samples/vpn-onboard-5.json +40 -40
  53. package/src/assets/samples/vpn-onboard-6.json +30 -30
  54. package/src/assets/samples/vpn-onboard-7.json +29 -29
  55. package/src/attribute-analyser/style/web/useExtractImageStyle.ts +8 -3
  56. package/src/attribute-analyser/style/web/useExtractViewStyle.ts +8 -3
  57. package/src/attributes-editor/AttributesEditorView.tsx +17 -6
  58. package/src/attributes-editor/FallbackLocalizationField.tsx +384 -0
  59. package/src/build-components/CarouselDots/CarouselDots.tsx +8 -3
  60. package/src/build-components/Main/Main.tsx +3 -1
  61. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +15 -1
  62. package/src/build-components/NavigationBarColor/NavigationBarColorProps.generated.ts +1 -52
  63. package/src/build-components/NavigationBarColor/pattern.json +11 -2
  64. package/src/build-components/OnboardDot/OnboardDot.tsx +3 -2
  65. package/src/build-components/PaywallCloseButton/pattern.json +1 -0
  66. package/src/build-components/StatusBarColor/StatusBarColor.tsx +15 -1
  67. package/src/build-components/StatusBarColor/StatusBarColorProps.generated.ts +1 -1
  68. package/src/build-components/StatusBarColor/pattern.json +10 -1
  69. package/src/build-components/patterns.generated.ts +25 -364
  70. package/src/components/BottomBar.tsx +135 -31
  71. package/src/components/BuilderProvider.tsx +1 -0
  72. package/src/components/DeviceButton.tsx +35 -0
  73. package/src/components/EditorHeader.tsx +16 -1
  74. package/src/hooks/useLocalize.ts +3 -1
  75. package/src/hooks/useSafeAreaViewStyle.ts +24 -4
  76. package/src/index.web.ts +19 -0
  77. package/src/mockOS/context/MockOSContext.tsx +41 -13
  78. package/src/modals/DeviceSelectorModal.tsx +94 -10
  79. package/src/modals/InspectModal.tsx +112 -4
  80. package/src/product-base/buildPaywallLocalizationParams.ts +3 -0
  81. package/src/product-base/extractAndroidParams.ts +38 -8
  82. package/src/product-base/types.ts +3 -0
  83. package/src/size-matters/index.ts +15 -9
  84. package/src/store.ts +66 -0
  85. package/src/styles/modals/_product-edit-modal.scss +2 -2
  86. package/src/types/Device.ts +5 -0
  87. package/src/types/PreviewConfig.ts +6 -0
  88. package/src/utils/analyseNodeByPatterns.ts +6 -2
  89. package/src/utils/extractTextStyle/extractTextStyle.ts +3 -1
  90. package/src/utils/extractTextStyle/extractTextStyleNative.ts +1 -1
  91. package/src/utils/extractViewStyle/extractViewStyle.ts +19 -5
  92. package/src/utils/extractViewStyle/extractViewStyleNative.ts +5 -1
  93. package/src/utils/replaceLocalizationParams.ts +5 -7
@@ -0,0 +1,384 @@
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
2
+ import { useRenderStore } from '../store';
3
+
4
+ /**
5
+ * Self-contained fallback localization field for react-builder.
6
+ *
7
+ * Uses the localizationApiConfig from the store to:
8
+ * 1. Pre-load ALL keys on first open (per_page=100, auto-paginate)
9
+ * 2. Filter locally — no subsequent API calls for search
10
+ * 3. Works independently of the host app
11
+ *
12
+ * API: GET {forgeUrl}/api/manage/localization/{platform}?per_page=100&page=N&app_id=X
13
+ * Header: X-Authorization: {forgeToken}
14
+ */
15
+
16
+ type LocalizationRecord = {
17
+ key: string;
18
+ translations: Record<string, { value: string | null }>;
19
+ };
20
+
21
+ type FallbackLocalizationFieldProps = {
22
+ value: string;
23
+ onChange: (v: string) => void;
24
+ };
25
+
26
+ export function FallbackLocalizationField({
27
+ value,
28
+ onChange,
29
+ }: FallbackLocalizationFieldProps) {
30
+ const config = useRenderStore((s) => s.localizationApiConfig);
31
+ const [open, setOpen] = useState(false);
32
+ const [query, setQuery] = useState('');
33
+ const [allRecords, setAllRecords] = useState<LocalizationRecord[]>([]);
34
+ const [filteredRecords, setFilteredRecords] = useState<LocalizationRecord[]>(
35
+ [],
36
+ );
37
+ const [preloadDone, setPreloadDone] = useState(false);
38
+ const [loading, setLoading] = useState(false);
39
+ const preloadVersionRef = useRef(0);
40
+
41
+ const canSearch = !!config?.forgeUrl && !!config?.forgeToken;
42
+ const platform = 'ios'; // default platform for fallback
43
+
44
+ // --- Forge API helpers ---
45
+ const buildUrl = useCallback(
46
+ (page: number) => {
47
+ if (!config) return '';
48
+ const base = `${config.forgeUrl.replace(/\/$/, '')}/api/manage/localization/${platform}`;
49
+ const params = new URLSearchParams({
50
+ per_page: '1000',
51
+ page: String(page),
52
+ });
53
+ if (config.forgeAppId) {
54
+ params.append('app_id', String(config.forgeAppId));
55
+ }
56
+ return `${base}?${params.toString()}`;
57
+ },
58
+ [config],
59
+ );
60
+
61
+ const fetchPage = useCallback(
62
+ async (page: number) => {
63
+ const url = buildUrl(page);
64
+ const res = await fetch(url, {
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ Accept: 'application/json',
68
+ 'X-Authorization': config!.forgeToken,
69
+ },
70
+ });
71
+ if (!res.ok) return { data: [] as LocalizationRecord[], hasMore: false };
72
+ const json = await res.json();
73
+ const records: LocalizationRecord[] = json.data ?? [];
74
+ const pagination = json.pagination;
75
+ const hasMore =
76
+ pagination && pagination.current_page < pagination.last_page;
77
+ return { data: records, hasMore };
78
+ },
79
+ [buildUrl, config],
80
+ );
81
+
82
+ // --- Pre-load all records on first dialog open ---
83
+ useEffect(() => {
84
+ if (!open || !canSearch || preloadDone) return;
85
+
86
+ const version = ++preloadVersionRef.current;
87
+ setLoading(true);
88
+
89
+ const loadAll = async () => {
90
+ const all: LocalizationRecord[] = [];
91
+ let page = 1;
92
+ let hasMore = true;
93
+ while (hasMore) {
94
+ const result = await fetchPage(page);
95
+ if (version !== preloadVersionRef.current) return;
96
+ all.push(...result.data);
97
+ hasMore = result.hasMore;
98
+ page++;
99
+ }
100
+ if (version === preloadVersionRef.current) {
101
+ setAllRecords(all);
102
+ setFilteredRecords(all);
103
+ setPreloadDone(true);
104
+ setLoading(false);
105
+ }
106
+ };
107
+
108
+ void loadAll();
109
+ }, [open, canSearch, preloadDone, fetchPage]);
110
+
111
+ // --- Local filter ---
112
+ useEffect(() => {
113
+ if (!preloadDone) return;
114
+ const q = query.trim().toLowerCase();
115
+ if (!q) {
116
+ setFilteredRecords(allRecords);
117
+ return;
118
+ }
119
+ setFilteredRecords(
120
+ allRecords.filter((r) => {
121
+ if (r.key.toLowerCase().includes(q)) return true;
122
+ return Object.values(r.translations).some(
123
+ (t) => t?.value && t.value.toLowerCase().includes(q),
124
+ );
125
+ }),
126
+ );
127
+ }, [query, allRecords, preloadDone]);
128
+
129
+ const invalidate = () => {
130
+ setPreloadDone(false);
131
+ setAllRecords([]);
132
+ setFilteredRecords([]);
133
+ };
134
+
135
+ return (
136
+ <>
137
+ <div style={styles.row}>
138
+ <input
139
+ type="text"
140
+ className="attributes-editor__text-input"
141
+ value={value ?? ''}
142
+ onChange={(e) => onChange(e.target.value)}
143
+ style={{ flex: 1 }}
144
+ />
145
+ {canSearch ? (
146
+ <button
147
+ type="button"
148
+ onClick={() => setOpen(true)}
149
+ title="Browse localization keys"
150
+ style={styles.browseBtn}
151
+ >
152
+ 🔍
153
+ </button>
154
+ ) : (
155
+ <span title="Localization API yapılandırılmadı" style={styles.dot} />
156
+ )}
157
+ </div>
158
+ {open && (
159
+ <div onClick={() => setOpen(false)} style={styles.overlay}>
160
+ <div onClick={(e) => e.stopPropagation()} style={styles.modal}>
161
+ {/* Header */}
162
+ <div style={styles.header}>
163
+ <span style={{ fontWeight: 600, fontSize: 14 }}>
164
+ Localization Anahtarı Seç
165
+ </span>
166
+ <button
167
+ type="button"
168
+ onClick={() => setOpen(false)}
169
+ style={styles.closeBtn}
170
+ >
171
+
172
+ </button>
173
+ </div>
174
+ {/* Search */}
175
+ <div style={{ padding: '10px 16px 6px' }}>
176
+ <input
177
+ autoFocus
178
+ type="text"
179
+ value={query}
180
+ onChange={(e) => setQuery(e.target.value)}
181
+ placeholder="Anahtar veya çeviri ara…"
182
+ style={styles.searchInput}
183
+ />
184
+ </div>
185
+ {/* Info */}
186
+ {preloadDone && (
187
+ <div style={styles.infoBar}>
188
+ {filteredRecords.length} / {allRecords.length} anahtar
189
+ </div>
190
+ )}
191
+ {/* Results */}
192
+ <div style={styles.resultsList}>
193
+ {loading && <div style={styles.emptyMsg}>Yükleniyor…</div>}
194
+ {!loading && preloadDone && filteredRecords.length === 0 && (
195
+ <div style={styles.emptyMsg}>Sonuç bulunamadı</div>
196
+ )}
197
+ {filteredRecords.map((entry) => {
198
+ const isSelected = entry.key === value;
199
+ const translations = Object.entries(entry.translations);
200
+ return (
201
+ <button
202
+ key={entry.key}
203
+ type="button"
204
+ onClick={() => {
205
+ onChange(entry.key);
206
+ setOpen(false);
207
+ }}
208
+ style={{
209
+ ...styles.resultItem,
210
+ background: isSelected ? '#333' : 'transparent',
211
+ border: isSelected
212
+ ? '1px solid #6366f1'
213
+ : '1px solid #333',
214
+ }}
215
+ onMouseEnter={(e) => {
216
+ if (!isSelected)
217
+ e.currentTarget.style.background = '#2a2a2a';
218
+ }}
219
+ onMouseLeave={(e) => {
220
+ if (!isSelected)
221
+ e.currentTarget.style.background = 'transparent';
222
+ }}
223
+ >
224
+ <div
225
+ style={{
226
+ fontWeight: 600,
227
+ fontSize: 12,
228
+ color: '#e0e0e0',
229
+ }}
230
+ >
231
+ {entry.key}
232
+ </div>
233
+ {translations.slice(0, 3).map(([lang, cell]) => (
234
+ <div key={lang} style={styles.translationRow}>
235
+ <span style={styles.langBadge}>{lang}</span>
236
+ <span style={styles.translationText}>
237
+ {cell?.value && cell.value.length > 80
238
+ ? cell.value.slice(0, 80) + '…'
239
+ : cell?.value || (
240
+ <span style={{ color: '#ef4444' }}>Eksik</span>
241
+ )}
242
+ </span>
243
+ </div>
244
+ ))}
245
+ </button>
246
+ );
247
+ })}
248
+ </div>
249
+ </div>
250
+ </div>
251
+ )}
252
+ </>
253
+ );
254
+ }
255
+
256
+ const styles: Record<string, React.CSSProperties> = {
257
+ row: {
258
+ display: 'flex',
259
+ alignItems: 'center',
260
+ gap: 4,
261
+ },
262
+ browseBtn: {
263
+ width: 28,
264
+ height: 28,
265
+ display: 'flex',
266
+ alignItems: 'center',
267
+ justifyContent: 'center',
268
+ border: '1px solid hsl(0 0% 40%)',
269
+ borderRadius: 4,
270
+ background: '#2a2a2a',
271
+ color: '#fff',
272
+ cursor: 'pointer',
273
+ fontSize: 13,
274
+ flexShrink: 0,
275
+ },
276
+ dot: {
277
+ display: 'inline-block',
278
+ width: 8,
279
+ height: 8,
280
+ borderRadius: '50%',
281
+ backgroundColor: '#ef4444',
282
+ flexShrink: 0,
283
+ cursor: 'help',
284
+ },
285
+ overlay: {
286
+ position: 'fixed',
287
+ inset: 0,
288
+ background: 'rgba(0,0,0,0.6)',
289
+ zIndex: 9999,
290
+ display: 'flex',
291
+ alignItems: 'center',
292
+ justifyContent: 'center',
293
+ },
294
+ modal: {
295
+ background: '#1e1e1e',
296
+ borderRadius: 10,
297
+ width: 460,
298
+ maxHeight: '75vh',
299
+ display: 'flex',
300
+ flexDirection: 'column',
301
+ boxShadow: '0 12px 40px rgba(0,0,0,0.5)',
302
+ color: '#e0e0e0',
303
+ border: '1px solid #333',
304
+ overflow: 'hidden',
305
+ },
306
+ header: {
307
+ display: 'flex',
308
+ justifyContent: 'space-between',
309
+ alignItems: 'center',
310
+ padding: '14px 18px',
311
+ borderBottom: '1px solid #333',
312
+ },
313
+ closeBtn: {
314
+ background: 'none',
315
+ border: 'none',
316
+ cursor: 'pointer',
317
+ fontSize: 18,
318
+ color: '#999',
319
+ padding: 4,
320
+ },
321
+ searchInput: {
322
+ width: '100%',
323
+ padding: '8px 10px',
324
+ border: '1px solid #444',
325
+ borderRadius: 6,
326
+ background: '#2a2a2a',
327
+ color: '#e0e0e0',
328
+ fontSize: 13,
329
+ outline: 'none',
330
+ boxSizing: 'border-box' as const,
331
+ },
332
+ infoBar: {
333
+ padding: '2px 18px 4px',
334
+ fontSize: 11,
335
+ color: '#666',
336
+ textAlign: 'right' as const,
337
+ },
338
+ resultsList: {
339
+ overflowY: 'auto' as const,
340
+ flex: 1,
341
+ padding: '6px 10px 10px',
342
+ },
343
+ emptyMsg: {
344
+ padding: 20,
345
+ textAlign: 'center' as const,
346
+ color: '#888',
347
+ fontSize: 13,
348
+ },
349
+ resultItem: {
350
+ display: 'block',
351
+ width: '100%',
352
+ textAlign: 'left' as const,
353
+ borderRadius: 6,
354
+ padding: '8px 10px',
355
+ cursor: 'pointer',
356
+ marginBottom: 3,
357
+ transition: 'background 0.15s',
358
+ },
359
+ translationRow: {
360
+ display: 'flex',
361
+ alignItems: 'center',
362
+ gap: 6,
363
+ marginTop: 3,
364
+ fontSize: 11,
365
+ },
366
+ langBadge: {
367
+ display: 'inline-block',
368
+ background: '#333',
369
+ border: '1px solid #444',
370
+ borderRadius: 3,
371
+ padding: '1px 5px',
372
+ fontSize: 10,
373
+ fontWeight: 600,
374
+ color: '#aaa',
375
+ flexShrink: 0,
376
+ },
377
+ translationText: {
378
+ color: '#999',
379
+ overflow: 'hidden',
380
+ whiteSpace: 'nowrap' as const,
381
+ textOverflow: 'ellipsis',
382
+ flex: 1,
383
+ },
384
+ };
@@ -34,7 +34,12 @@ function CarouselDots({ node }: CarouselDotsComponentProps) {
34
34
  const onboardApi = useContext(onboardContext);
35
35
  const emblaApi = isOnboard ? onboardApi?.emblaApi : carouselApi;
36
36
 
37
- const { selectedTheme: theme, projectColors, baseSize } = useBuilderParams();
37
+ const {
38
+ selectedTheme: theme,
39
+ projectColors,
40
+ baseSize,
41
+ device,
42
+ } = useBuilderParams();
38
43
  const inactiveDotOpacity =
39
44
  (stylesBag?.inactive_dot_opacity as number | undefined) ??
40
45
  (attrRecord.inactive_dot_opacity as number | undefined) ??
@@ -59,12 +64,12 @@ function CarouselDots({ node }: CarouselDotsComponentProps) {
59
64
  );
60
65
 
61
66
  const dotSizeCss = useMemo(() => {
62
- const parsed = parseSize(dotThicknessRaw, baseSize);
67
+ const parsed = parseSize(dotThicknessRaw, baseSize, device!);
63
68
  if (parsed === undefined) return '10px';
64
69
  if (typeof parsed === 'number') return `${parsed}px`;
65
70
  if (typeof parsed === 'string' && parsed.trim()) return parsed;
66
71
  return '10px';
67
- }, [dotThicknessRaw, baseSize]);
72
+ }, [dotThicknessRaw, baseSize, device]);
68
73
 
69
74
  const dotGapCss = useMemo((): string => {
70
75
  const px =
@@ -4,6 +4,7 @@ import RenderNode from '../RenderNode.generated';
4
4
  import type { Node } from '../../types/Node';
5
5
  import useNode from '../useNode';
6
6
  import { useBuilderParams } from '../../components/BuilderProvider';
7
+ import { useRenderStore } from '../../store';
7
8
  import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
8
9
  import { useLogRender } from '../../utils/useLogRender';
9
10
  import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
@@ -19,11 +20,12 @@ function Main({ node }: MainComponentProps) {
19
20
  const attributeKey = node.key ?? generatedId;
20
21
 
21
22
  const { previewMode, selectedKey } = useBuilderParams();
23
+ const device = useRenderStore((s) => s.device);
22
24
 
23
25
  const baseStyle = useExtractViewStyle(node);
24
26
 
25
27
  const useSafeAreaView = node.attributes?.useSafeAreaView ?? true;
26
- const layoutStyle = useSafeAreaViewStyle(baseStyle, useSafeAreaView);
28
+ const layoutStyle = useSafeAreaViewStyle(baseStyle, useSafeAreaView, device);
27
29
 
28
30
  const isSelected = isNodeSelected({
29
31
  previewMode: !!previewMode,
@@ -15,6 +15,9 @@ function NavigationBarColor({ node }: NavigationBarColorComponentProps) {
15
15
  const setNavBarOverrideColor = useRenderStore(
16
16
  (s) => s.setNavBarOverrideColor,
17
17
  );
18
+ const setNavBarOverrideTranslucent = useRenderStore(
19
+ (s) => s.setNavBarOverrideTranslucent,
20
+ );
18
21
 
19
22
  const rawBg = getStyleBag(node.attributes)?.backgroundColor as
20
23
  | string
@@ -24,14 +27,25 @@ function NavigationBarColor({ node }: NavigationBarColorComponentProps) {
24
27
  theme,
25
28
  });
26
29
 
30
+ const translucent = node.attributes?.translucent;
31
+
27
32
  useEffect(() => {
28
33
  if (resolvedColor) {
29
34
  setNavBarOverrideColor(resolvedColor);
30
35
  }
36
+ if (translucent !== undefined) {
37
+ setNavBarOverrideTranslucent(translucent);
38
+ }
31
39
  return () => {
32
40
  setNavBarOverrideColor(null);
41
+ setNavBarOverrideTranslucent(null);
33
42
  };
34
- }, [resolvedColor, setNavBarOverrideColor]);
43
+ }, [
44
+ resolvedColor,
45
+ setNavBarOverrideColor,
46
+ translucent,
47
+ setNavBarOverrideTranslucent,
48
+ ]);
35
49
 
36
50
  return null;
37
51
  }
@@ -2,66 +2,15 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
- export type FlexDirectionOptionType = 'row' | 'column';
6
- export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
7
- export type AlignItemsOptionType =
8
- | 'flex-start'
9
- | 'center'
10
- | 'flex-end'
11
- | 'stretch'
12
- | 'baseline';
13
- export type JustifyContentOptionType =
14
- | 'flex-start'
15
- | 'center'
16
- | 'flex-end'
17
- | 'space-between'
18
- | 'space-around'
19
- | 'space-evenly';
20
- export type PositionOptionType = 'relative' | 'absolute';
21
-
22
5
  export interface NavigationBarColorStyleGenerated {
23
- flexDirection?: FlexDirectionOptionType;
24
- flexWrap?: FlexWrapOptionType;
25
- alignItems?: AlignItemsOptionType;
26
- justifyContent?: JustifyContentOptionType;
27
- gap?: string;
28
- padding?: string;
29
- paddingHorizontal?: string;
30
- paddingVertical?: string;
31
- paddingTop?: string;
32
- paddingBottom?: string;
33
- paddingLeft?: string;
34
- paddingRight?: string;
35
- margin?: string;
36
- marginHorizontal?: string;
37
- marginVertical?: string;
38
- marginTop?: string;
39
- marginBottom?: string;
40
- marginLeft?: string;
41
- marginRight?: string;
42
6
  backgroundColor?: string;
43
- borderRadius?: string;
44
- width?: string;
45
- minWidth?: string;
46
- maxWidth?: string;
47
- height?: string;
48
- minHeight?: string;
49
- maxHeight?: string;
50
- flex?: number;
51
- position?: PositionOptionType;
52
- top?: string;
53
- bottom?: string;
54
- left?: string;
55
- right?: string;
56
- zIndex?: number;
57
7
  }
58
8
 
59
9
  export interface NavigationBarColorPropsGenerated {
60
10
  child: string;
61
11
  attributes: {
62
12
  styles?: NavigationBarColorStyleGenerated;
63
- scrollable?: boolean;
64
- testID?: string;
13
+ translucent?: boolean;
65
14
  };
66
15
  }
67
16
 
@@ -5,11 +5,11 @@
5
5
  "title": "title",
6
6
  "description": "description",
7
7
  "children": "never",
8
- "extends": "View",
9
8
  "attributes": {
10
9
  "styles": {
11
10
  "backgroundColor": "color"
12
- }
11
+ },
12
+ "translucent": "boolean"
13
13
  }
14
14
  },
15
15
  "meta": {
@@ -24,6 +24,15 @@
24
24
  "specialCategory": null,
25
25
  "sort": 1
26
26
  }
27
+ },
28
+ "attributes": {
29
+ "translucent": {
30
+ "label": "Translucent",
31
+ "description": "Sets the navigation bar to translucent.",
32
+ "category": "style",
33
+ "specialCategory": null,
34
+ "sort": 2
35
+ }
27
36
  }
28
37
  },
29
38
  "defaults": {
@@ -34,6 +34,7 @@ export function OnboardDot({ node }: OnboardDotComponentProps) {
34
34
  selectedTheme: theme,
35
35
  projectColors,
36
36
  baseSize,
37
+ device,
37
38
  } = useBuilderParams();
38
39
  // OnboardDot specific attributes
39
40
  const inactiveDotOpacity =
@@ -74,12 +75,12 @@ export function OnboardDot({ node }: OnboardDotComponentProps) {
74
75
  (attrRecord.dot_thickness as string | number | undefined) ??
75
76
  (stylesBag?.dot_thickness as string | number | undefined);
76
77
  const dotSizeCss = useMemo((): string => {
77
- const parsed = parseSize(dotThicknessRaw, baseSize);
78
+ const parsed = parseSize(dotThicknessRaw, baseSize, device!);
78
79
  if (parsed === undefined) return '10px';
79
80
  if (typeof parsed === 'number') return `${parsed}px`;
80
81
  if (typeof parsed === 'string' && parsed.trim()) return parsed;
81
82
  return '10px';
82
- }, [dotThicknessRaw, baseSize]);
83
+ }, [dotThicknessRaw, baseSize, device]);
83
84
  const dotGapCss = useMemo((): string => {
84
85
  // Prefer px math when possible; otherwise fall back to 10px/3.
85
86
  const px =
@@ -14,6 +14,7 @@
14
14
  "styles": {}
15
15
  },
16
16
  "defaults": {
17
+ "testID": "paywall-close-button",
17
18
  "iconType": "close",
18
19
  "size": 24,
19
20
  "styles": {
@@ -15,6 +15,9 @@ function StatusBarColor({ node }: StatusBarColorComponentProps) {
15
15
  const setStatusBarOverrideColor = useRenderStore(
16
16
  (s) => s.setStatusBarOverrideColor,
17
17
  );
18
+ const setStatusBarOverrideTranslucent = useRenderStore(
19
+ (s) => s.setStatusBarOverrideTranslucent,
20
+ );
18
21
 
19
22
  const rawBg = getStyleBag(node.attributes)?.backgroundColor as
20
23
  | string
@@ -24,14 +27,25 @@ function StatusBarColor({ node }: StatusBarColorComponentProps) {
24
27
  theme,
25
28
  });
26
29
 
30
+ const translucent = node.attributes?.translucent;
31
+
27
32
  useEffect(() => {
28
33
  if (resolvedColor) {
29
34
  setStatusBarOverrideColor(resolvedColor);
30
35
  }
36
+ if (translucent !== undefined) {
37
+ setStatusBarOverrideTranslucent(translucent);
38
+ }
31
39
  return () => {
32
40
  setStatusBarOverrideColor(null);
41
+ setStatusBarOverrideTranslucent(null);
33
42
  };
34
- }, [resolvedColor, setStatusBarOverrideColor]);
43
+ }, [
44
+ resolvedColor,
45
+ setStatusBarOverrideColor,
46
+ translucent,
47
+ setStatusBarOverrideTranslucent,
48
+ ]);
35
49
 
36
50
  return null;
37
51
  }
@@ -10,7 +10,7 @@ export interface StatusBarColorPropsGenerated {
10
10
  child: string;
11
11
  attributes: {
12
12
  styles?: StatusBarColorStyleGenerated;
13
- testID?: string;
13
+ translucent?: boolean;
14
14
  };
15
15
  }
16
16
 
@@ -9,7 +9,7 @@
9
9
  "styles": {
10
10
  "backgroundColor": "color"
11
11
  },
12
- "testID": "string"
12
+ "translucent": "boolean"
13
13
  }
14
14
  },
15
15
  "meta": {
@@ -24,6 +24,15 @@
24
24
  "specialCategory": null,
25
25
  "sort": 1
26
26
  }
27
+ },
28
+ "attributes": {
29
+ "translucent": {
30
+ "label": "Translucent",
31
+ "description": "Sets the status bar to translucent.",
32
+ "category": "style",
33
+ "specialCategory": null,
34
+ "sort": 2
35
+ }
27
36
  }
28
37
  },
29
38
  "defaults": {