1#import "RNCSafeAreaShadowView.h"
2
3#import <React/RCTAssert.h>
4#include <math.h>
5
6#import "RNCSafeAreaViewEdgeMode.h"
7#import "RNCSafeAreaViewEdges.h"
8#import "RNCSafeAreaViewLocalData.h"
9#import "RNCSafeAreaViewMode.h"
10
11// From RCTShadowView.m
12typedef NS_ENUM(unsigned int, meta_prop_t) {
13  META_PROP_LEFT,
14  META_PROP_TOP,
15  META_PROP_RIGHT,
16  META_PROP_BOTTOM,
17  META_PROP_HORIZONTAL,
18  META_PROP_VERTICAL,
19  META_PROP_ALL,
20  META_PROP_COUNT,
21};
22
23@implementation RNCSafeAreaShadowView {
24  RNCSafeAreaViewLocalData *_localData;
25  bool _needsUpdate;
26  YGValue _paddingMetaProps[META_PROP_COUNT];
27  YGValue _marginMetaProps[META_PROP_COUNT];
28}
29
30- (instancetype)init
31{
32  self = [super init];
33  if (self) {
34    _needsUpdate = false;
35    for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
36      _paddingMetaProps[ii] = YGValueUndefined;
37      _marginMetaProps[ii] = YGValueUndefined;
38    }
39  }
40  return self;
41}
42
43- (void)extractEdges:(YGValue[])_metaProps
44                 top:(CGFloat *)top
45               right:(CGFloat *)right
46              bottom:(CGFloat *)bottom
47                left:(CGFloat *)left
48{
49  if (_metaProps[META_PROP_ALL].unit == YGUnitPoint) {
50    *top = _metaProps[META_PROP_ALL].value;
51    *right = _metaProps[META_PROP_ALL].value;
52    *bottom = _metaProps[META_PROP_ALL].value;
53    *left = _metaProps[META_PROP_ALL].value;
54  }
55
56  if (_metaProps[META_PROP_HORIZONTAL].unit == YGUnitPoint) {
57    *right = _metaProps[META_PROP_HORIZONTAL].value;
58    *left = _metaProps[META_PROP_HORIZONTAL].value;
59  }
60
61  if (_metaProps[META_PROP_VERTICAL].unit == YGUnitPoint) {
62    *top = _metaProps[META_PROP_VERTICAL].value;
63    *bottom = _metaProps[META_PROP_VERTICAL].value;
64  }
65
66  if (_metaProps[META_PROP_TOP].unit == YGUnitPoint) {
67    *top = _metaProps[META_PROP_TOP].value;
68  }
69
70  if (_metaProps[META_PROP_RIGHT].unit == YGUnitPoint) {
71    *right = _metaProps[META_PROP_RIGHT].value;
72  }
73
74  if (_metaProps[META_PROP_BOTTOM].unit == YGUnitPoint) {
75    *bottom = _metaProps[META_PROP_BOTTOM].value;
76  }
77
78  if (_metaProps[META_PROP_LEFT].unit == YGUnitPoint) {
79    *left = _metaProps[META_PROP_LEFT].value;
80  }
81}
82
83- (void)resetInsetsForMode:(RNCSafeAreaViewMode)mode
84{
85  if (mode == RNCSafeAreaViewModePadding) {
86    super.paddingTop = _paddingMetaProps[META_PROP_TOP];
87    super.paddingRight = _paddingMetaProps[META_PROP_RIGHT];
88    super.paddingBottom = _paddingMetaProps[META_PROP_BOTTOM];
89    super.paddingLeft = _paddingMetaProps[META_PROP_LEFT];
90  } else if (mode == RNCSafeAreaViewModeMargin) {
91    super.marginTop = _marginMetaProps[META_PROP_TOP];
92    super.marginRight = _marginMetaProps[META_PROP_RIGHT];
93    super.marginBottom = _marginMetaProps[META_PROP_BOTTOM];
94    super.marginLeft = _marginMetaProps[META_PROP_LEFT];
95  }
96}
97
98- (void)updateInsets
99{
100  if (_localData == nil) {
101    return;
102  }
103
104  UIEdgeInsets insets = _localData.insets;
105  RNCSafeAreaViewMode mode = _localData.mode;
106  RNCSafeAreaViewEdges edges = _localData.edges;
107
108  CGFloat top = 0;
109  CGFloat right = 0;
110  CGFloat bottom = 0;
111  CGFloat left = 0;
112
113  if (mode == RNCSafeAreaViewModePadding) {
114    [self extractEdges:_paddingMetaProps top:&top right:&right bottom:&bottom left:&left];
115    super.paddingTop = (YGValue){[self getEdgeValue:edges.top insetValue:insets.top edgeValue:top], YGUnitPoint};
116    super.paddingRight =
117        (YGValue){[self getEdgeValue:edges.right insetValue:insets.right edgeValue:right], YGUnitPoint};
118    super.paddingBottom =
119        (YGValue){[self getEdgeValue:edges.bottom insetValue:insets.bottom edgeValue:bottom], YGUnitPoint};
120    super.paddingLeft = (YGValue){[self getEdgeValue:edges.left insetValue:insets.left edgeValue:left], YGUnitPoint};
121  } else if (mode == RNCSafeAreaViewModeMargin) {
122    [self extractEdges:_marginMetaProps top:&top right:&right bottom:&bottom left:&left];
123    super.marginTop = (YGValue){[self getEdgeValue:edges.top insetValue:insets.top edgeValue:top], YGUnitPoint};
124    super.marginRight = (YGValue){[self getEdgeValue:edges.right insetValue:insets.right edgeValue:right], YGUnitPoint};
125    super.marginBottom =
126        (YGValue){[self getEdgeValue:edges.bottom insetValue:insets.bottom edgeValue:bottom], YGUnitPoint};
127    super.marginLeft = (YGValue){[self getEdgeValue:edges.left insetValue:insets.left edgeValue:left], YGUnitPoint};
128  }
129}
130
131- (CGFloat)getEdgeValue:(RNCSafeAreaViewEdgeMode)edgeMode insetValue:(CGFloat)insetValue edgeValue:(CGFloat)edgeValue
132{
133  if (edgeMode == RNCSafeAreaViewEdgeModeOff) {
134    return edgeValue;
135  } else if (edgeMode == RNCSafeAreaViewEdgeModeMaximum) {
136    return MAX(insetValue, edgeValue);
137  } else {
138    return insetValue + edgeValue;
139  }
140}
141
142- (void)didSetProps:(NSArray<NSString *> *)changedProps
143{
144  if (_needsUpdate) {
145    _needsUpdate = false;
146    [self updateInsets];
147  }
148  [super didSetProps:changedProps];
149}
150
151- (void)setLocalData:(RNCSafeAreaViewLocalData *)localData
152{
153  RCTAssert(
154      [localData isKindOfClass:[RNCSafeAreaViewLocalData class]],
155      @"Local data object for `RCTRNCSafeAreaShadowView` must be `RCTRNCSafeAreaViewLocalData` instance.");
156
157  if (_localData != nil && _localData.mode != localData.mode) {
158    [self resetInsetsForMode:_localData.mode];
159  }
160
161  _localData = localData;
162  _needsUpdate = false;
163  [self updateInsets];
164
165  if (_localData.mode == RNCSafeAreaViewModePadding) {
166    [super didSetProps:@[ @"paddingTop", @"paddingRight", @"paddingBottom", @"paddingLeft" ]];
167  } else {
168    [super didSetProps:@[ @"marginTop", @"marginRight", @"marginBottom", @"marginLeft" ]];
169  }
170}
171
172#define SHADOW_VIEW_MARGIN_PADDING_PROP(edge, metaProp) \
173  -(void)setPadding##edge : (YGValue)value              \
174  {                                                     \
175    [super setPadding##edge:value];                     \
176    _needsUpdate = true;                                \
177    _paddingMetaProps[META_PROP_##metaProp] = value;    \
178  }                                                     \
179  -(void)setMargin##edge : (YGValue)value               \
180  {                                                     \
181    [super setMargin##edge:value];                      \
182    _needsUpdate = true;                                \
183    _marginMetaProps[META_PROP_##metaProp] = value;     \
184  }
185
186SHADOW_VIEW_MARGIN_PADDING_PROP(, ALL);
187SHADOW_VIEW_MARGIN_PADDING_PROP(Vertical, VERTICAL);
188SHADOW_VIEW_MARGIN_PADDING_PROP(Horizontal, HORIZONTAL);
189SHADOW_VIEW_MARGIN_PADDING_PROP(Top, TOP);
190SHADOW_VIEW_MARGIN_PADDING_PROP(Right, RIGHT);
191SHADOW_VIEW_MARGIN_PADDING_PROP(Bottom, BOTTOM);
192SHADOW_VIEW_MARGIN_PADDING_PROP(Left, LEFT);
193
194@end
195