@developer_tribe/react-builder 1.2.39 → 1.2.40

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 (79) hide show
  1. package/dist/build-components/NavigationBarColor/NavigationBarColorProps.generated.d.ts +1 -40
  2. package/dist/build-components/StatusBarColor/StatusBarColorProps.generated.d.ts +1 -1
  3. package/dist/build-components/patterns.generated.d.ts +21 -344
  4. package/dist/components/BuilderProvider.d.ts +1 -0
  5. package/dist/components/DeviceButton.d.ts +4 -1
  6. package/dist/index.cjs.js +1 -1
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.esm.js +1 -1
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.web.cjs.js +4 -4
  11. package/dist/index.web.cjs.js.map +1 -1
  12. package/dist/index.web.esm.js +4 -4
  13. package/dist/index.web.esm.js.map +1 -1
  14. package/dist/mockOS/context/MockOSContext.d.ts +3 -1
  15. package/dist/size-matters/index.d.ts +1 -1
  16. package/dist/store.d.ts +6 -0
  17. package/dist/styles.css +1 -1
  18. package/dist/types/Device.d.ts +5 -0
  19. package/dist/utils/extractTextStyle/extractTextStyle.d.ts +1 -0
  20. package/dist/utils/extractViewStyle/extractViewStyle.d.ts +1 -0
  21. package/package.json +1 -1
  22. package/scripts/prebuild/assets/prompt_scheme.md +7 -0
  23. package/src/DeviceMockFrame.tsx +8 -0
  24. package/src/RenderPage.tsx +3 -0
  25. package/src/assets/devices.json +747 -183
  26. package/src/assets/meta.json +1 -1
  27. package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
  28. package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
  29. package/src/assets/samples/carousel-sample.json +30 -26
  30. package/src/assets/samples/paywall-1.json +30 -30
  31. package/src/assets/samples/paywall-2.json +26 -26
  32. package/src/assets/samples/paywall-app-delete-offer.json +27 -27
  33. package/src/assets/samples/paywall-app-open-offer.json +27 -27
  34. package/src/assets/samples/paywall-back-offer.json +27 -27
  35. package/src/assets/samples/paywall-notification-offer.json +27 -27
  36. package/src/assets/samples/simple-1.json +4 -4
  37. package/src/assets/samples/simple-2.json +25 -25
  38. package/src/assets/samples/unmigrated-builder-1.1.1.json +7 -7
  39. package/src/assets/samples/unmigrated-builder1.json +4 -4
  40. package/src/assets/samples/unvalidated-builder1.json +4 -4
  41. package/src/assets/samples/unvalidated-crash1.json +2 -2
  42. package/src/assets/samples/unvalidated-crashcomponent1.json +2 -2
  43. package/src/assets/samples/vpn-onboard-1.json +30 -30
  44. package/src/assets/samples/vpn-onboard-2.json +30 -30
  45. package/src/assets/samples/vpn-onboard-3.json +27 -27
  46. package/src/assets/samples/vpn-onboard-4.json +27 -27
  47. package/src/assets/samples/vpn-onboard-5.json +40 -40
  48. package/src/assets/samples/vpn-onboard-6.json +30 -30
  49. package/src/assets/samples/vpn-onboard-7.json +29 -29
  50. package/src/attribute-analyser/style/web/useExtractImageStyle.ts +8 -3
  51. package/src/attribute-analyser/style/web/useExtractViewStyle.ts +8 -3
  52. package/src/build-components/CarouselDots/CarouselDots.tsx +8 -3
  53. package/src/build-components/Main/Main.tsx +3 -1
  54. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +15 -1
  55. package/src/build-components/NavigationBarColor/NavigationBarColorProps.generated.ts +1 -52
  56. package/src/build-components/NavigationBarColor/pattern.json +11 -2
  57. package/src/build-components/OnboardDot/OnboardDot.tsx +3 -2
  58. package/src/build-components/PaywallCloseButton/pattern.json +1 -0
  59. package/src/build-components/StatusBarColor/StatusBarColor.tsx +15 -1
  60. package/src/build-components/StatusBarColor/StatusBarColorProps.generated.ts +1 -1
  61. package/src/build-components/StatusBarColor/pattern.json +10 -1
  62. package/src/build-components/patterns.generated.ts +25 -364
  63. package/src/components/BuilderProvider.tsx +1 -0
  64. package/src/components/DeviceButton.tsx +35 -0
  65. package/src/components/EditorHeader.tsx +16 -1
  66. package/src/hooks/useSafeAreaViewStyle.ts +24 -4
  67. package/src/mockOS/context/MockOSContext.tsx +41 -13
  68. package/src/modals/DeviceSelectorModal.tsx +94 -10
  69. package/src/product-base/extractAndroidParams.ts +38 -8
  70. package/src/size-matters/index.ts +15 -9
  71. package/src/store.ts +27 -0
  72. package/src/styles/modals/_product-edit-modal.scss +2 -2
  73. package/src/types/Device.ts +5 -0
  74. package/src/utils/analyseNodeByPatterns.ts +6 -2
  75. package/src/utils/extractTextStyle/extractTextStyle.ts +3 -1
  76. package/src/utils/extractTextStyle/extractTextStyleNative.ts +1 -1
  77. package/src/utils/extractViewStyle/extractViewStyle.ts +19 -5
  78. package/src/utils/extractViewStyle/extractViewStyleNative.ts +5 -1
  79. package/src/utils/replaceLocalizationParams.ts +5 -7
@@ -4507,364 +4507,14 @@ export const patterns = [
4507
4507
  description: 'description',
4508
4508
  children: 'never',
4509
4509
  attributes: {
4510
- scrollable: 'boolean',
4511
- styles: {
4512
- flexDirection: ['row', 'column'],
4513
- flexWrap: ['nowrap', 'wrap', 'wrap-reverse'],
4514
- alignItems: [
4515
- 'flex-start',
4516
- 'center',
4517
- 'flex-end',
4518
- 'stretch',
4519
- 'baseline',
4520
- ],
4521
- justifyContent: [
4522
- 'flex-start',
4523
- 'center',
4524
- 'flex-end',
4525
- 'space-between',
4526
- 'space-around',
4527
- 'space-evenly',
4528
- ],
4529
- gap: 'size',
4530
- padding: 'size',
4531
- paddingHorizontal: 'size',
4532
- paddingVertical: 'size',
4533
- paddingTop: 'size',
4534
- paddingBottom: 'size',
4535
- paddingLeft: 'size',
4536
- paddingRight: 'size',
4537
- margin: 'size',
4538
- marginHorizontal: 'size',
4539
- marginVertical: 'size',
4540
- marginTop: 'size',
4541
- marginBottom: 'size',
4542
- marginLeft: 'size',
4543
- marginRight: 'size',
4544
- backgroundColor: 'color',
4545
- borderRadius: 'size',
4546
- width: 'size',
4547
- minWidth: 'size',
4548
- maxWidth: 'size',
4549
- height: 'size',
4550
- minHeight: 'size',
4551
- maxHeight: 'size',
4552
- flex: 'number',
4553
- position: ['relative', 'absolute'],
4554
- top: 'size',
4555
- bottom: 'size',
4556
- left: 'size',
4557
- right: 'size',
4558
- zIndex: 'number',
4559
- },
4560
- testID: 'string',
4510
+ styles: { backgroundColor: 'color' },
4511
+ translucent: 'boolean',
4561
4512
  },
4562
4513
  },
4563
4514
  meta: {
4564
4515
  desiredParent: ['all'],
4565
4516
  label: 'Navigation Bar Color',
4566
4517
  description: 'Sets the OS navigation bar background color.',
4567
- specialCategories: {
4568
- padding: {
4569
- label: 'Padding',
4570
- description: 'Uniform padding on all sides.',
4571
- category: 'container',
4572
- sort: 1,
4573
- },
4574
- margin: {
4575
- label: 'Margin',
4576
- description: 'Uniform margin on all sides.',
4577
- category: 'container',
4578
- sort: 2,
4579
- },
4580
- size: {
4581
- label: 'Size',
4582
- description: 'Fixed dimensions.',
4583
- category: 'container',
4584
- sort: 3,
4585
- },
4586
- offset: {
4587
- label: 'Offset',
4588
- description: 'Absolute positioning offsets.',
4589
- category: 'container',
4590
- sort: 4,
4591
- },
4592
- },
4593
- attributes: {
4594
- scrollable: {
4595
- label: 'Scrollable',
4596
- description: 'Turns scroll interaction on.',
4597
- category: 'container',
4598
- specialCategory: null,
4599
- sort: -1,
4600
- },
4601
- styles: {
4602
- backgroundColor: {
4603
- label: 'Background Color',
4604
- description: 'Background fill color.',
4605
- category: 'style',
4606
- specialCategory: null,
4607
- sort: 20,
4608
- },
4609
- borderRadius: {
4610
- label: 'Border Radius',
4611
- description: 'Corner rounding amount.',
4612
- category: 'style',
4613
- specialCategory: null,
4614
- sort: 21,
4615
- preferredScale: 's',
4616
- },
4617
- flexDirection: {
4618
- label: 'Flex Direction',
4619
- description: 'Sets row or column layout.',
4620
- category: 'container',
4621
- specialCategory: null,
4622
- sort: 4,
4623
- },
4624
- flexWrap: {
4625
- label: 'Flex Wrap',
4626
- description: 'Controls whether flex items wrap to multiple lines.',
4627
- category: 'container',
4628
- specialCategory: null,
4629
- sort: 4.5,
4630
- },
4631
- alignItems: {
4632
- label: 'Align Items',
4633
- description: 'Controls cross-axis alignment.',
4634
- category: 'container',
4635
- specialCategory: null,
4636
- sort: 3,
4637
- },
4638
- justifyContent: {
4639
- label: 'Justify Content',
4640
- description: 'Controls main-axis alignment.',
4641
- category: 'container',
4642
- specialCategory: null,
4643
- sort: 5,
4644
- },
4645
- gap: {
4646
- label: 'Gap',
4647
- description: 'Space between children.',
4648
- category: 'container',
4649
- specialCategory: null,
4650
- sort: 10,
4651
- preferredScale: 's',
4652
- },
4653
- padding: {
4654
- label: 'Padding',
4655
- description: 'Uniform padding on all sides.',
4656
- category: 'container',
4657
- specialCategory: 'padding',
4658
- sort: 6,
4659
- preferredScale: 's',
4660
- },
4661
- paddingHorizontal: {
4662
- label: 'Padding Horizontal',
4663
- description: 'Left and right padding.',
4664
- category: 'container',
4665
- specialCategory: 'padding',
4666
- sort: 7,
4667
- preferredScale: 's',
4668
- },
4669
- paddingVertical: {
4670
- label: 'Padding Vertical',
4671
- description: 'Top and bottom padding.',
4672
- category: 'container',
4673
- specialCategory: 'padding',
4674
- sort: 8,
4675
- preferredScale: 'vs',
4676
- },
4677
- paddingTop: {
4678
- label: 'Padding Top',
4679
- description: 'Top padding only.',
4680
- category: 'container',
4681
- specialCategory: 'padding',
4682
- sort: 9,
4683
- preferredScale: 'vs',
4684
- },
4685
- paddingBottom: {
4686
- label: 'Padding Bottom',
4687
- description: 'Bottom padding only.',
4688
- category: 'container',
4689
- specialCategory: 'padding',
4690
- sort: 10,
4691
- preferredScale: 'vs',
4692
- },
4693
- paddingLeft: {
4694
- label: 'Padding Left',
4695
- description: 'Left padding only.',
4696
- category: 'container',
4697
- specialCategory: 'padding',
4698
- sort: 11,
4699
- preferredScale: 's',
4700
- },
4701
- paddingRight: {
4702
- label: 'Padding Right',
4703
- description: 'Right padding only.',
4704
- category: 'container',
4705
- specialCategory: 'padding',
4706
- sort: 12,
4707
- preferredScale: 's',
4708
- },
4709
- margin: {
4710
- label: 'Margin',
4711
- description: 'Uniform margin on all sides.',
4712
- category: 'container',
4713
- specialCategory: 'margin',
4714
- sort: 13,
4715
- preferredScale: 's',
4716
- },
4717
- marginHorizontal: {
4718
- label: 'Margin Horizontal',
4719
- description: 'Left and right margin.',
4720
- category: 'container',
4721
- specialCategory: 'margin',
4722
- sort: 14,
4723
- preferredScale: 's',
4724
- },
4725
- marginVertical: {
4726
- label: 'Margin Vertical',
4727
- description: 'Top and bottom margin.',
4728
- category: 'container',
4729
- specialCategory: 'margin',
4730
- sort: 15,
4731
- preferredScale: 'vs',
4732
- },
4733
- marginTop: {
4734
- label: 'Margin Top',
4735
- description: 'Top margin only.',
4736
- category: 'container',
4737
- specialCategory: 'margin',
4738
- sort: 16,
4739
- preferredScale: 'vs',
4740
- },
4741
- marginBottom: {
4742
- label: 'Margin Bottom',
4743
- description: 'Bottom margin only.',
4744
- category: 'container',
4745
- specialCategory: 'margin',
4746
- sort: 17,
4747
- preferredScale: 'vs',
4748
- },
4749
- marginLeft: {
4750
- label: 'Margin Left',
4751
- description: 'Left margin only.',
4752
- category: 'container',
4753
- specialCategory: 'margin',
4754
- sort: 18,
4755
- preferredScale: 's',
4756
- },
4757
- marginRight: {
4758
- label: 'Margin Right',
4759
- description: 'Right margin only.',
4760
- category: 'container',
4761
- specialCategory: 'margin',
4762
- sort: 19,
4763
- preferredScale: 's',
4764
- },
4765
- width: {
4766
- label: 'Width',
4767
- description: 'Fixed width value.',
4768
- category: 'container',
4769
- specialCategory: 'size',
4770
- sort: 0,
4771
- preferredScale: 's',
4772
- },
4773
- minWidth: {
4774
- label: 'Min Width',
4775
- description: 'Minimum width constraint.',
4776
- category: 'container',
4777
- specialCategory: 'size',
4778
- sort: 1,
4779
- preferredScale: 's',
4780
- },
4781
- maxWidth: {
4782
- label: 'Max Width',
4783
- description: 'Maximum width constraint.',
4784
- category: 'container',
4785
- specialCategory: 'size',
4786
- sort: 2,
4787
- preferredScale: 's',
4788
- },
4789
- height: {
4790
- label: 'Height',
4791
- description: 'Fixed height value.',
4792
- category: 'container',
4793
- specialCategory: 'size',
4794
- sort: 3,
4795
- preferredScale: 'vs',
4796
- },
4797
- minHeight: {
4798
- label: 'Min Height',
4799
- description: 'Minimum height constraint.',
4800
- category: 'container',
4801
- specialCategory: 'size',
4802
- sort: 4,
4803
- preferredScale: 'vs',
4804
- },
4805
- maxHeight: {
4806
- label: 'Max Height',
4807
- description: 'Maximum height constraint.',
4808
- category: 'container',
4809
- specialCategory: 'size',
4810
- sort: 5,
4811
- preferredScale: 'vs',
4812
- },
4813
- flex: {
4814
- label: 'Flex',
4815
- description: 'Flex grow factor (e.g. 1 fills available space).',
4816
- category: 'container',
4817
- specialCategory: 'size',
4818
- sort: 6,
4819
- },
4820
- position: {
4821
- label: 'Position',
4822
- description: 'Sets layout positioning mode.',
4823
- category: 'container',
4824
- specialCategory: null,
4825
- sort: 3,
4826
- },
4827
- top: {
4828
- label: 'Top',
4829
- description: 'Offset from the top edge.',
4830
- category: 'container',
4831
- specialCategory: 'offset',
4832
- sort: 22,
4833
- preferredScale: 'vs',
4834
- },
4835
- bottom: {
4836
- label: 'Bottom',
4837
- description: 'Offset from the bottom edge.',
4838
- category: 'container',
4839
- specialCategory: 'offset',
4840
- sort: 23,
4841
- preferredScale: 'vs',
4842
- },
4843
- left: {
4844
- label: 'Left',
4845
- description: 'Offset from the left edge.',
4846
- category: 'container',
4847
- specialCategory: 'offset',
4848
- sort: 24,
4849
- preferredScale: 's',
4850
- },
4851
- right: {
4852
- label: 'Right',
4853
- description: 'Offset from the right edge.',
4854
- category: 'container',
4855
- specialCategory: 'offset',
4856
- sort: 25,
4857
- preferredScale: 's',
4858
- },
4859
- zIndex: {
4860
- label: 'Z-Index',
4861
- description: 'Controls stacking order.',
4862
- category: 'container',
4863
- specialCategory: null,
4864
- sort: 26,
4865
- },
4866
- },
4867
- },
4868
4518
  styles: {
4869
4519
  backgroundColor: {
4870
4520
  label: 'Background Color',
@@ -4874,19 +4524,17 @@ export const patterns = [
4874
4524
  sort: 1,
4875
4525
  },
4876
4526
  },
4877
- },
4878
- defaults: {
4879
- styles: {
4880
- flexDirection: 'column',
4881
- position: 'relative',
4882
- zIndex: 1,
4883
- alignSelf: 'flex-start',
4884
- flexGrow: 0,
4885
- flexShrink: 0,
4886
- backgroundColor: 'THEME_COLORS.BACKGROUND',
4527
+ attributes: {
4528
+ translucent: {
4529
+ label: 'Translucent',
4530
+ description: 'Sets the navigation bar to translucent.',
4531
+ category: 'style',
4532
+ specialCategory: null,
4533
+ sort: 2,
4534
+ },
4887
4535
  },
4888
4536
  },
4889
- types: {},
4537
+ defaults: { styles: { backgroundColor: 'THEME_COLORS.BACKGROUND' } },
4890
4538
  },
4891
4539
  {
4892
4540
  schemaVersion: 2,
@@ -10058,6 +9706,7 @@ export const patterns = [
10058
9706
  alignItems: 'center',
10059
9707
  },
10060
9708
  translateCounter: 1,
9709
+ testID: 'paywall-close-button',
10061
9710
  size: 24,
10062
9711
  },
10063
9712
  types: {},
@@ -13096,7 +12745,10 @@ export const patterns = [
13096
12745
  title: 'title',
13097
12746
  description: 'description',
13098
12747
  children: 'never',
13099
- attributes: { styles: { backgroundColor: 'color' }, testID: 'string' },
12748
+ attributes: {
12749
+ styles: { backgroundColor: 'color' },
12750
+ translucent: 'boolean',
12751
+ },
13100
12752
  },
13101
12753
  meta: {
13102
12754
  desiredParent: ['all'],
@@ -13111,6 +12763,15 @@ export const patterns = [
13111
12763
  sort: 1,
13112
12764
  },
13113
12765
  },
12766
+ attributes: {
12767
+ translucent: {
12768
+ label: 'Translucent',
12769
+ description: 'Sets the status bar to translucent.',
12770
+ category: 'style',
12771
+ specialCategory: null,
12772
+ sort: 2,
12773
+ },
12774
+ },
13114
12775
  },
13115
12776
  defaults: { styles: { backgroundColor: 'THEME_COLORS.BACKGROUND' } },
13116
12777
  },
@@ -44,6 +44,7 @@ export type BuilderProviderParams = {
44
44
  fonts?: Fonts;
45
45
  previewMode?: boolean;
46
46
  selectedKey?: string;
47
+ device?: import('../types/Device').Device;
47
48
  };
48
49
 
49
50
  type BuilderProviderProps = {
@@ -11,12 +11,16 @@ type DeviceButtonProps = {
11
11
  device: Device;
12
12
  selectedDevice: Device | null;
13
13
  onSelect: (device: Device) => void;
14
+ isFavorite?: boolean;
15
+ onToggleFavorite?: (device: Device, e: React.MouseEvent) => void;
14
16
  };
15
17
 
16
18
  export function DeviceButton({
17
19
  device,
18
20
  selectedDevice,
19
21
  onSelect,
22
+ isFavorite,
23
+ onToggleFavorite,
20
24
  }: DeviceButtonProps) {
21
25
  const platformIcon = platformIcons[device.platform];
22
26
  const aspect =
@@ -36,6 +40,37 @@ export function DeviceButton({
36
40
  }`}
37
41
  onClick={() => onSelect(device)}
38
42
  >
43
+ {onToggleFavorite && (
44
+ <span
45
+ className="editor-device-button__favorite"
46
+ onClick={(e) => {
47
+ e.stopPropagation();
48
+ onToggleFavorite(device, e);
49
+ }}
50
+ title={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
51
+ style={{
52
+ position: 'absolute',
53
+ top: 4,
54
+ right: 4,
55
+ cursor: 'pointer',
56
+ opacity: isFavorite ? 1 : 0.3,
57
+ color: isFavorite ? '#ecc538' : 'currentColor',
58
+ }}
59
+ >
60
+ <svg
61
+ width="14"
62
+ height="14"
63
+ viewBox="0 0 24 24"
64
+ fill={isFavorite ? 'currentColor' : 'none'}
65
+ stroke="currentColor"
66
+ strokeWidth="2"
67
+ strokeLinecap="round"
68
+ strokeLinejoin="round"
69
+ >
70
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
71
+ </svg>
72
+ </span>
73
+ )}
39
74
  {device.name} <br />
40
75
  {device.width}×{device.height} ({aspect})
41
76
  {platformIcon && (
@@ -40,6 +40,8 @@ export function EditorHeader({
40
40
  setLocalization,
41
41
  setBaseSize,
42
42
  setProjectColors,
43
+ favoriteDevices,
44
+ toggleFavoriteDevice,
43
45
  } = useRenderStore((s) => ({
44
46
  device: s.device,
45
47
  setDevice: s.setDevice,
@@ -47,8 +49,19 @@ export function EditorHeader({
47
49
  setLocalization: s.setLocalization,
48
50
  setBaseSize: s.setBaseSize,
49
51
  setProjectColors: s.setProjectColors,
52
+ favoriteDevices: s.favoriteDevices || [],
53
+ toggleFavoriteDevice: s.toggleFavoriteDevice,
50
54
  }));
51
55
 
56
+ const headerDevices = useMemo(() => {
57
+ const favs = favoriteDevices
58
+ .map((name) => devices.find((d) => d.name === name))
59
+ .filter((d): d is Device => d !== undefined);
60
+
61
+ const others = devices.filter((d) => !favoriteDevices.includes(d.name));
62
+ return [...favs, ...others].slice(0, 5);
63
+ }, [favoriteDevices]);
64
+
52
65
  const sortedSamples = useMemo(() => {
53
66
  const weight = (t?: Project['type']) => {
54
67
  if (t === 'paywall') return 0;
@@ -249,12 +262,14 @@ export function EditorHeader({
249
262
  aria-label="Editor utility header"
250
263
  >
251
264
  <div className="editor-header__devices">
252
- {devices.slice(0, 5).map((deviceOption: Device) => (
265
+ {headerDevices.map((deviceOption: Device) => (
253
266
  <DeviceButton
254
267
  key={deviceOption.name}
255
268
  selectedDevice={selectedDevice}
256
269
  onSelect={setDevice}
257
270
  device={deviceOption}
271
+ isFavorite={favoriteDevices.includes(deviceOption.name)}
272
+ onToggleFavorite={(d) => toggleFavoriteDevice(d.name)}
258
273
  />
259
274
  ))}
260
275
  <button
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import type { Device } from '../types/Device';
3
+ import { useRenderStore } from '../store';
3
4
 
4
5
  function addInset(
5
6
  base: React.CSSProperties['paddingTop'],
@@ -30,17 +31,30 @@ export function useSafeAreaViewStyle(
30
31
  enabled: boolean,
31
32
  device?: Device,
32
33
  ) {
34
+ const statusBarOverrideTranslucent = useRenderStore(
35
+ (s) => s.statusBarOverrideTranslucent,
36
+ );
37
+ const navBarOverrideTranslucent = useRenderStore(
38
+ (s) => s.navBarOverrideTranslucent,
39
+ );
40
+
33
41
  return useMemo(() => {
34
42
  if (!enabled) return baseStyle;
35
43
 
36
44
  const [insetTop, insetRight, , insetLeft] = device?.insets ?? [0, 0, 0, 0];
37
45
 
38
46
  // Match DeviceMockFrame fallbacks: status bar overlays content, so we treat it as top safe area.
39
- const top =
40
- insetTop || (device?.platform === 'ios' ? 20 : device?.platform ? 24 : 0);
47
+ // If translucent is true, we do not apply safe area to top.
48
+ const top = statusBarOverrideTranslucent
49
+ ? 0
50
+ : insetTop ||
51
+ (device?.platform === 'ios' ? 20 : device?.platform ? 24 : 0);
41
52
 
42
53
  // Bottom safe area is handled visually by the mock navigation bar area, which takes layout space.
43
- // So we intentionally don't add bottom padding here to avoid double-spacing.
54
+ // However, if we need to support insets for bottom when not rendered by the mock navigation bar,
55
+ // or if the navigation bar is translucent, we apply the inset here. But the mock frame handles it for now.
56
+ // Since Main acts as the safe area provider, if navigation bar is translucent it shouldn't have bottom inset,
57
+ // but right now it is 0. If it were relying on bottom inset, we would check `navBarOverrideTranslucent ? 0 : bottom`.
44
58
  const right = insetRight ?? 0;
45
59
  const left = insetLeft ?? 0;
46
60
  const bottom = 0;
@@ -60,5 +74,11 @@ export function useSafeAreaViewStyle(
60
74
  width: subtractInset(baseStyle.width ?? '100%', horizontal),
61
75
  height: subtractInset(baseStyle.height ?? '100%', vertical),
62
76
  };
63
- }, [enabled, baseStyle, device]);
77
+ }, [
78
+ enabled,
79
+ baseStyle,
80
+ device,
81
+ statusBarOverrideTranslucent,
82
+ navBarOverrideTranslucent,
83
+ ]);
64
84
  }
@@ -32,12 +32,14 @@ interface MockOSProviderProps {
32
32
  statusBarBackgroundColor: string;
33
33
  statusBarPlatform: Device['platform'];
34
34
  statusBarIsDark: boolean;
35
+ statusBarTranslucent?: boolean;
35
36
  // Navigation Bar props
36
37
  navBarHeight: number;
37
38
  navBarBackgroundColor: string;
38
39
  navBarPlatform: Device['platform'];
39
40
  navBarNavigationBarType: Device['navigationBarType'];
40
41
  navBarIsDark: boolean;
42
+ navBarTranslucent?: boolean;
41
43
  // Insets
42
44
  insetLeft: number;
43
45
  insetRight: number;
@@ -51,11 +53,13 @@ export function MockOSProvider({
51
53
  statusBarBackgroundColor,
52
54
  statusBarPlatform,
53
55
  statusBarIsDark,
56
+ statusBarTranslucent = false,
54
57
  navBarHeight,
55
58
  navBarBackgroundColor,
56
59
  navBarPlatform,
57
60
  navBarNavigationBarType,
58
61
  navBarIsDark,
62
+ navBarTranslucent = false,
59
63
  }: MockOSProviderProps) {
60
64
  const [permission, setPermission] = useState<PermissionType | string | null>(
61
65
  null,
@@ -217,31 +221,55 @@ export function MockOSProvider({
217
221
  onCancel={() => resolveSubscriptionPurchase(false)}
218
222
  />
219
223
  )}
220
- <DeviceStatusBar
221
- height={statusBarHeight}
222
- backgroundColor={statusBarBackgroundColor}
223
- platform={statusBarPlatform}
224
- isDark={statusBarIsDark}
225
- />
224
+ <div
225
+ className="device-status-bar-container"
226
+ style={{
227
+ position: statusBarTranslucent ? 'absolute' : 'relative',
228
+ top: 0,
229
+ left: 0,
230
+ right: 0,
231
+ zIndex: 10,
232
+ }}
233
+ >
234
+ <DeviceStatusBar
235
+ height={statusBarHeight}
236
+ backgroundColor={statusBarBackgroundColor}
237
+ platform={statusBarPlatform}
238
+ isDark={statusBarIsDark}
239
+ />
240
+ </div>
226
241
  <div
227
242
  className="device-content"
228
243
  style={{
229
244
  flex: 1,
230
245
  overflow: 'hidden',
231
246
  position: 'relative',
247
+ // If nav bar is translucent, let the content area stretch under it
248
+ marginBottom: navBarTranslucent ? -navBarHeight : 0,
232
249
  }}
233
250
  >
234
251
  <MockOSRouter childrenBelongTo="app" appName={appName}>
235
252
  {children}
236
253
  </MockOSRouter>
237
254
  </div>
238
- <DeviceNavigationBar
239
- height={navBarHeight}
240
- backgroundColor={navBarBackgroundColor}
241
- platform={navBarPlatform}
242
- navigationBarType={navBarNavigationBarType}
243
- isDark={navBarIsDark}
244
- />
255
+ <div
256
+ className="device-navigation-bar-container"
257
+ style={{
258
+ position: navBarTranslucent ? 'absolute' : 'relative',
259
+ bottom: 0,
260
+ left: 0,
261
+ right: 0,
262
+ zIndex: 10,
263
+ }}
264
+ >
265
+ <DeviceNavigationBar
266
+ height={navBarHeight}
267
+ backgroundColor={navBarBackgroundColor}
268
+ platform={navBarPlatform}
269
+ navigationBarType={navBarNavigationBarType}
270
+ isDark={navBarIsDark}
271
+ />
272
+ </div>
245
273
  </MockOSContext.Provider>
246
274
  );
247
275
  }