react-native-platform-components 0.8.10 → 1.0.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 (67) hide show
  1. package/README.md +68 -891
  2. package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +17 -3
  3. package/android/src/main/java/com/platformcomponents/PCImageLoader.kt +88 -0
  4. package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +292 -31
  5. package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +74 -8
  6. package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +2 -1
  7. package/android/src/main/java/com/platformcomponents/PCThemeSupport.kt +94 -0
  8. package/android/src/main/res/values/styles.xml +40 -0
  9. package/ios/PCImageLoader.swift +58 -0
  10. package/ios/PCSegmentedControl.mm +85 -25
  11. package/ios/PCSegmentedControl.swift +231 -308
  12. package/lib/commonjs/DatePickerNativeComponent.ts +4 -2
  13. package/lib/commonjs/SegmentedControl.js +138 -6
  14. package/lib/commonjs/SegmentedControl.js.map +1 -1
  15. package/lib/commonjs/SegmentedControlNativeComponent.ts +62 -8
  16. package/lib/commonjs/SelectionMenuNativeComponent.ts +4 -2
  17. package/lib/commonjs/index.web.js.map +1 -1
  18. package/lib/module/DatePickerNativeComponent.ts +4 -2
  19. package/lib/module/SegmentedControl.js +138 -7
  20. package/lib/module/SegmentedControl.js.map +1 -1
  21. package/lib/module/SegmentedControlNativeComponent.ts +62 -8
  22. package/lib/module/SelectionMenuNativeComponent.ts +4 -2
  23. package/lib/module/index.web.js.map +1 -1
  24. package/lib/typescript/commonjs/docs/docusaurus.config.d.ts +4 -0
  25. package/lib/typescript/commonjs/docs/docusaurus.config.d.ts.map +1 -0
  26. package/lib/typescript/commonjs/docs/sidebars.d.ts +4 -0
  27. package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -0
  28. package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts +2 -2
  29. package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/src/SegmentedControl.d.ts +111 -6
  31. package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +46 -7
  33. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts +2 -2
  35. package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/src/index.web.d.ts +36 -1
  37. package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
  38. package/lib/typescript/module/docs/docusaurus.config.d.ts +4 -0
  39. package/lib/typescript/module/docs/docusaurus.config.d.ts.map +1 -0
  40. package/lib/typescript/module/docs/sidebars.d.ts +4 -0
  41. package/lib/typescript/module/docs/sidebars.d.ts.map +1 -0
  42. package/lib/typescript/module/src/DatePicker.d.ts +1 -1
  43. package/lib/typescript/module/src/DatePicker.d.ts.map +1 -1
  44. package/lib/typescript/module/src/DatePickerNativeComponent.d.ts +2 -2
  45. package/lib/typescript/module/src/DatePickerNativeComponent.d.ts.map +1 -1
  46. package/lib/typescript/module/src/SegmentedControl.d.ts +111 -6
  47. package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
  48. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +46 -7
  49. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  50. package/lib/typescript/module/src/SelectionMenu.d.ts +1 -1
  51. package/lib/typescript/module/src/SelectionMenu.d.ts.map +1 -1
  52. package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts +2 -2
  53. package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts.map +1 -1
  54. package/lib/typescript/module/src/index.d.ts +6 -6
  55. package/lib/typescript/module/src/index.d.ts.map +1 -1
  56. package/lib/typescript/module/src/index.web.d.ts +36 -1
  57. package/lib/typescript/module/src/index.web.d.ts.map +1 -1
  58. package/package.json +62 -37
  59. package/plugin/build/index.cjs +58 -17
  60. package/plugin/build/index.d.ts +32 -15
  61. package/plugin/build/index.d.ts.map +1 -1
  62. package/plugin/tsconfig.json +9 -4
  63. package/src/DatePickerNativeComponent.ts +4 -2
  64. package/src/SegmentedControl.tsx +238 -11
  65. package/src/SegmentedControlNativeComponent.ts +62 -8
  66. package/src/SelectionMenuNativeComponent.ts +4 -2
  67. package/src/index.web.tsx +34 -1
@@ -356,7 +356,8 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context), ReactScrollV
356
356
  if (mode == MaterialMode.M3) {
357
357
  // M3 exposed dropdown menu - the standard Material 3 way
358
358
  // Must set box background mode BEFORE setting endIconMode to avoid IllegalStateException
359
- val til = TextInputLayout(context).apply {
359
+ // Material widgets need a Material theme; fall back to Material 3 defaults instead of crashing.
360
+ val til = TextInputLayout(PCThemeSupport.materialContext(context, "SelectionMenu")).apply {
360
361
  layoutParams = FrameLayout.LayoutParams(
361
362
  FrameLayout.LayoutParams.WRAP_CONTENT,
362
363
  FrameLayout.LayoutParams.WRAP_CONTENT
@@ -0,0 +1,94 @@
1
+ package com.platformcomponents
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import android.util.TypedValue
6
+ import android.view.ContextThemeWrapper
7
+ import androidx.annotation.AttrRes
8
+ import androidx.appcompat.R as AppCompatR
9
+ import com.google.android.material.R as MaterialR
10
+
11
+ /**
12
+ * Theme guards for the Material and AppCompat widgets this library creates.
13
+ *
14
+ * React Native and Expo templates ship an AppCompat app theme. Material widgets
15
+ * (MaterialButtonToggleGroup, TextInputLayout, MaterialDatePicker, ...) throw during
16
+ * inflation when the host theme is not a Material Components / Material 3 theme.
17
+ * Rather than crash the app, wrap the context in a Material 3 theme (widgets) or
18
+ * hand the picker dialogs a full Material 3 dialog theme, and warn once so the
19
+ * developer knows how to get their own colors applied.
20
+ */
21
+ internal object PCThemeSupport {
22
+ private const val TAG = "PlatformComponents"
23
+
24
+ @Volatile private var warnedMaterial = false
25
+ @Volatile private var warnedAppCompat = false
26
+
27
+ /** True for Theme.MaterialComponents.* and Theme.Material3.* (and descendants). */
28
+ fun hasMaterialTheme(context: Context): Boolean =
29
+ resolves(context, MaterialR.attr.materialButtonOutlinedStyle) &&
30
+ resolves(context, MaterialR.attr.colorSurface)
31
+
32
+ /** True for Theme.AppCompat.* and descendants, which includes every Material theme. */
33
+ fun hasAppCompatTheme(context: Context): Boolean =
34
+ resolves(context, AppCompatR.attr.alertDialogTheme)
35
+
36
+ /**
37
+ * Context to build Material widgets from. The host context when its theme is a
38
+ * Material theme; otherwise a Theme.Material3.DayNight wrapper, so the widget
39
+ * renders with Material 3 defaults instead of throwing.
40
+ */
41
+ fun materialContext(context: Context, component: String): Context {
42
+ if (hasMaterialTheme(context)) return context
43
+ warnMaterial(component)
44
+ return ContextThemeWrapper(context, MaterialR.style.Theme_Material3_DayNight_NoActionBar)
45
+ }
46
+
47
+ /**
48
+ * Context to build AppCompat dialogs from. The host context when its theme is an
49
+ * AppCompat theme; otherwise a Theme.AppCompat.DayNight.Dialog.Alert wrapper.
50
+ */
51
+ fun appCompatDialogContext(context: Context, component: String): Context {
52
+ if (hasAppCompatTheme(context)) return context
53
+ warnAppCompat(component)
54
+ return ContextThemeWrapper(context, AppCompatR.style.Theme_AppCompat_DayNight_Dialog_Alert)
55
+ }
56
+
57
+ /**
58
+ * Theme override for MaterialDatePicker / MaterialTimePicker when the activity
59
+ * theme is not a Material theme. Returns 0 (no override) when it is.
60
+ */
61
+ fun materialDialogThemeOverride(context: Context, component: String, fallbackTheme: Int): Int {
62
+ if (hasMaterialTheme(context)) return 0
63
+ warnMaterial(component)
64
+ return fallbackTheme
65
+ }
66
+
67
+ private fun resolves(context: Context, @AttrRes attr: Int): Boolean =
68
+ context.theme.resolveAttribute(attr, TypedValue(), true)
69
+
70
+ private fun warnMaterial(component: String) {
71
+ if (warnedMaterial) return
72
+ warnedMaterial = true
73
+ Log.w(
74
+ TAG,
75
+ "$component: the app theme is not a Material 3 theme, so Material components " +
76
+ "fall back to Material 3 default colors. To use your app's colors, make " +
77
+ "AppTheme inherit from Theme.Material3.DayNight.NoActionBar in " +
78
+ "android/app/src/main/res/values/styles.xml (Expo: pass " +
79
+ "{ android: { theme: 'material3' } } to the react-native-platform-components " +
80
+ "config plugin). See the README section \"Android Theme Configuration\"."
81
+ )
82
+ }
83
+
84
+ private fun warnAppCompat(component: String) {
85
+ if (warnedAppCompat) return
86
+ warnedAppCompat = true
87
+ Log.w(
88
+ TAG,
89
+ "$component: the app theme is not an AppCompat theme, so system dialogs fall " +
90
+ "back to Theme.AppCompat.DayNight.Dialog.Alert. Make AppTheme inherit from " +
91
+ "Theme.AppCompat.* or Theme.Material3.* to style them with your theme."
92
+ )
93
+ }
94
+ }
@@ -0,0 +1,40 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <!--
4
+ Fallback dialog themes for MaterialDatePicker / MaterialTimePicker when the host
5
+ app theme is not a Material theme (see PCThemeSupport). Each is a full
6
+ Theme.Material3 dialog theme plus the items from ThemeOverlay.Material3.MaterialCalendar
7
+ and ThemeOverlay.Material3.MaterialTimePicker (material 1.12.0), which the pickers
8
+ skip resolving when a theme override is supplied.
9
+ -->
10
+ <style name="PCMaterial3CalendarDialogTheme" parent="Theme.Material3.DayNight.Dialog">
11
+ <item name="buttonBarButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog.Flush</item>
12
+ <item name="materialCalendarStyle">@style/Widget.Material3.MaterialCalendar</item>
13
+ <item name="materialCalendarHeaderLayout">@style/Widget.Material3.MaterialCalendar.HeaderLayout</item>
14
+ <item name="materialCalendarHeaderDivider">@style/Widget.Material3.MaterialCalendar.HeaderDivider</item>
15
+ <item name="materialCalendarHeaderTitle">@style/Widget.Material3.MaterialCalendar.HeaderTitle</item>
16
+ <item name="materialCalendarHeaderSelection">@style/Widget.Material3.MaterialCalendar.HeaderSelection</item>
17
+ <item name="materialCalendarHeaderCancelButton">@style/Widget.Material3.MaterialCalendar.HeaderCancelButton</item>
18
+ <item name="materialCalendarHeaderConfirmButton">@style/Widget.Material3.Button.TextButton</item>
19
+ <item name="materialCalendarHeaderToggleButton">@style/Widget.Material3.MaterialCalendar.HeaderToggleButton</item>
20
+ <item name="materialCalendarYearNavigationButton">@style/Widget.Material3.MaterialCalendar.YearNavigationButton</item>
21
+ <item name="materialCalendarMonthNavigationButton">@style/Widget.Material3.MaterialCalendar.MonthNavigationButton</item>
22
+ <item name="materialCalendarDayOfWeekLabel">@style/Widget.Material3.MaterialCalendar.DayOfWeekLabel</item>
23
+ <item name="materialCalendarDay">@style/Widget.Material3.MaterialCalendar.DayTextView</item>
24
+ <item name="materialCalendarMonth">@style/Widget.Material3.MaterialCalendar.MonthTextView</item>
25
+ <item name="buttonBarPositiveButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog</item>
26
+ <item name="buttonBarNegativeButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog</item>
27
+ </style>
28
+
29
+ <style name="PCMaterial3TimePickerDialogTheme" parent="Theme.Material3.DayNight.Dialog">
30
+ <item name="buttonBarButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog.Flush</item>
31
+ <item name="chipStyle">@style/Widget.Material3.MaterialTimePicker.Display</item>
32
+ <item name="textInputStyle">@style/Widget.Material3.MaterialTimePicker.Display.TextInputLayout</item>
33
+ <item name="materialDisplayDividerStyle">@style/Widget.Material3.MaterialTimePicker.Display.Divider</item>
34
+ <item name="materialButtonOutlinedStyle">@style/Widget.Material3.MaterialTimePicker.Button</item>
35
+ <item name="materialClockStyle">@style/Widget.Material3.MaterialTimePicker.Clock</item>
36
+ <item name="materialTimePickerStyle">@style/Widget.Material3.MaterialTimePicker</item>
37
+ <item name="imageButtonStyle">@style/Widget.Material3.MaterialTimePicker.ImageButton</item>
38
+ <item name="materialTimePickerTitleStyle">@style/TextAppearance.Material3.MaterialTimePicker.Title</item>
39
+ </style>
40
+ </resources>
@@ -0,0 +1,58 @@
1
+ import UIKit
2
+
3
+ /// Loads images referenced by React Native asset URIs for native controls that
4
+ /// take a `UIImage` (segmented control icons).
5
+ ///
6
+ /// Release builds bundle local assets next to the JS bundle, so those arrive as
7
+ /// `file://` URLs and load synchronously. Debug builds and remote sources arrive
8
+ /// as `http(s)://` (Metro) or `data:` URLs and load asynchronously.
9
+ ///
10
+ /// Must be used from the main thread.
11
+ final class PCImageLoader {
12
+ static let shared = PCImageLoader()
13
+
14
+ private var cache: [String: UIImage] = [:]
15
+ private var pending: [String: [(UIImage?) -> Void]] = [:]
16
+
17
+ /// Returns the image immediately when it is cached or readable from disk.
18
+ /// Otherwise starts a load and calls `completion` on the main thread with
19
+ /// the result; the return value is `nil` in that case.
20
+ @discardableResult
21
+ func image(
22
+ uri: String,
23
+ scale: CGFloat,
24
+ completion: @escaping (UIImage?) -> Void
25
+ ) -> UIImage? {
26
+ let key = "\(uri)@\(scale)x"
27
+ if let cached = cache[key] {
28
+ return cached
29
+ }
30
+
31
+ guard let url = URL(string: uri) else { return nil }
32
+
33
+ if url.isFileURL {
34
+ guard let data = try? Data(contentsOf: url),
35
+ let image = UIImage(data: data, scale: scale) else { return nil }
36
+ cache[key] = image
37
+ return image
38
+ }
39
+
40
+ if pending[key] != nil {
41
+ pending[key]?.append(completion)
42
+ return nil
43
+ }
44
+ pending[key] = [completion]
45
+
46
+ URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
47
+ let image = data.flatMap { UIImage(data: $0, scale: scale) }
48
+ DispatchQueue.main.async {
49
+ guard let self else { return }
50
+ if let image { self.cache[key] = image }
51
+ let callbacks = self.pending.removeValue(forKey: key) ?? []
52
+ callbacks.forEach { $0(image) }
53
+ }
54
+ }.resume()
55
+
56
+ return nil
57
+ }
58
+ }
@@ -5,6 +5,7 @@
5
5
  #import <React/RCTComponentViewFactory.h>
6
6
  #import <React/RCTConversions.h>
7
7
  #import <React/RCTFabricComponentsPlugins.h>
8
+ #import <React/RCTFont.h>
8
9
 
9
10
  #import <react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h>
10
11
  #import <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
@@ -32,10 +33,46 @@ static inline bool SegmentsEqual(
32
33
  if (a[i].label != b[i].label) return false;
33
34
  if (a[i].value != b[i].value) return false;
34
35
  if (a[i].disabled != b[i].disabled) return false;
35
- if (a[i].icon != b[i].icon) return false;
36
+ if (a[i].iconType != b[i].iconType) return false;
37
+ if (a[i].iconName != b[i].iconName) return false;
38
+ if (a[i].iconUri != b[i].iconUri) return false;
39
+ if (a[i].iconScale != b[i].iconScale) return false;
40
+ if (a[i].iconTinted != b[i].iconTinted) return false;
41
+ if (a[i].badge != b[i].badge) return false;
42
+ if (a[i].accessibilityLabel != b[i].accessibilityLabel) return false;
36
43
  }
37
44
  return true;
38
45
  }
46
+
47
+ static inline NSString *NSStringFromStd(const std::string &s, NSString *fallback) {
48
+ return s.empty() ? fallback : [NSString stringWithUTF8String:s.c_str()];
49
+ }
50
+
51
+ static inline bool LabelStyleEqual(
52
+ const facebook::react::PCSegmentedControlLabelStyleStruct &a,
53
+ const facebook::react::PCSegmentedControlLabelStyleStruct &b) {
54
+ return a.fontFamily == b.fontFamily && a.fontSize == b.fontSize &&
55
+ a.fontWeight == b.fontWeight && a.fontStyle == b.fontStyle;
56
+ }
57
+
58
+ /// Builds the label font from RN-style font props, or nil when every field is
59
+ /// unset so the control keeps the system font.
60
+ static UIFont *FontFromLabelStyle(
61
+ const facebook::react::PCSegmentedControlLabelStyleStruct &style) {
62
+ if (style.fontFamily.empty() && style.fontSize <= 0 &&
63
+ style.fontWeight.empty() && style.fontStyle.empty()) {
64
+ return nil;
65
+ }
66
+ // UISegmentedControl titles default to 13pt; start there so a lone
67
+ // fontWeight or fontStyle doesn't change the size.
68
+ return [RCTFont updateFont:[UIFont systemFontOfSize:13]
69
+ withFamily:NSStringFromStd(style.fontFamily, nil)
70
+ size:style.fontSize > 0 ? @(style.fontSize) : nil
71
+ weight:NSStringFromStd(style.fontWeight, nil)
72
+ style:NSStringFromStd(style.fontStyle, nil)
73
+ variant:nil
74
+ scaleMultiplier:1.0];
75
+ }
39
76
  } // namespace
40
77
 
41
78
  @interface PCSegmentedControl ()
@@ -77,6 +114,12 @@ static inline bool SegmentsEqual(
77
114
 
78
115
  eventEmitter->onSelect(payload);
79
116
  };
117
+
118
+ _view.onNeedsRemeasure = ^{
119
+ __typeof(self) strongSelf = weakSelf;
120
+ if (!strongSelf) return;
121
+ [strongSelf updateMeasurements];
122
+ };
80
123
  }
81
124
  return self;
82
125
  }
@@ -88,32 +131,32 @@ static inline bool SegmentsEqual(
88
131
  const auto prevProps =
89
132
  std::static_pointer_cast<const PCSegmentedControlProps>(oldProps);
90
133
 
91
- // segments: [{label, value, disabled, icon}]
134
+ // segments: [{label, value, disabled, iconType, iconName, iconUri, iconScale,
135
+ // iconTinted, accessibilityLabel}]
92
136
  if (!prevProps || !SegmentsEqual(newProps.segments, prevProps->segments)) {
93
137
  NSMutableArray *arr = [NSMutableArray new];
94
138
  for (const auto &seg : newProps.segments) {
95
- NSString *label = seg.label.empty()
96
- ? @""
97
- : [NSString stringWithUTF8String:seg.label.c_str()];
98
- NSString *value = seg.value.empty()
99
- ? @""
100
- : [NSString stringWithUTF8String:seg.value.c_str()];
101
- NSString *disabled = seg.disabled.empty()
102
- ? @"enabled"
103
- : [NSString stringWithUTF8String:seg.disabled.c_str()];
104
- NSString *icon = seg.icon.empty()
105
- ? @""
106
- : [NSString stringWithUTF8String:seg.icon.c_str()];
107
139
  [arr addObject:@{
108
- @"label": label,
109
- @"value": value,
110
- @"disabled": disabled,
111
- @"icon": icon
140
+ @"label": NSStringFromStd(seg.label, @""),
141
+ @"value": NSStringFromStd(seg.value, @""),
142
+ @"disabled": NSStringFromStd(seg.disabled, @"enabled"),
143
+ @"iconType": NSStringFromStd(seg.iconType, @""),
144
+ @"iconName": NSStringFromStd(seg.iconName, @""),
145
+ @"iconUri": NSStringFromStd(seg.iconUri, @""),
146
+ @"iconScale": @(seg.iconScale),
147
+ @"iconTinted": NSStringFromStd(seg.iconTinted, @"true"),
148
+ @"badge": NSStringFromStd(seg.badge, @""),
149
+ @"accessibilityLabel": NSStringFromStd(seg.accessibilityLabel, @""),
112
150
  }];
113
151
  }
114
152
  _view.segments = arr;
115
153
  }
116
154
 
155
+ // labelVisibility: "auto" | "labeled" | "unlabeled"
156
+ if (!prevProps || newProps.labelVisibility != prevProps->labelVisibility) {
157
+ _view.labelVisibility = NSStringFromStd(newProps.labelVisibility, @"auto");
158
+ }
159
+
117
160
  // selectedValue (default "")
118
161
  if (!prevProps || newProps.selectedValue != prevProps->selectedValue) {
119
162
  if (!newProps.selectedValue.empty()) {
@@ -147,13 +190,30 @@ static inline bool SegmentsEqual(
147
190
  _view.apportionsSegmentWidthsByContent = (newIos.apportionsSegmentWidthsByContent == "true");
148
191
  }
149
192
 
150
- if (!prevProps || newIos.selectedSegmentTintColor != oldIos.selectedSegmentTintColor) {
151
- if (!newIos.selectedSegmentTintColor.empty()) {
152
- _view.selectedSegmentTintColor =
153
- [NSString stringWithUTF8String:newIos.selectedSegmentTintColor.c_str()];
154
- } else {
155
- _view.selectedSegmentTintColor = nil;
156
- }
193
+ // Colors arrive as SharedColor (already processed by React Native)
194
+ if (!prevProps || newProps.selectedSegmentColor != prevProps->selectedSegmentColor) {
195
+ _view.selectedSegmentColor = RCTUIColorFromSharedColor(newProps.selectedSegmentColor);
196
+ }
197
+
198
+ if (!prevProps || newProps.activeTintColor != prevProps->activeTintColor) {
199
+ _view.activeTintColor = RCTUIColorFromSharedColor(newProps.activeTintColor);
200
+ }
201
+
202
+ if (!prevProps || newProps.inactiveTintColor != prevProps->inactiveTintColor) {
203
+ _view.inactiveTintColor = RCTUIColorFromSharedColor(newProps.inactiveTintColor);
204
+ }
205
+
206
+ // labelStyle: {fontFamily, fontSize, fontWeight, fontStyle}
207
+ if (!prevProps || !LabelStyleEqual(newProps.labelStyle, prevProps->labelStyle)) {
208
+ _view.labelFont = FontFromLabelStyle(newProps.labelStyle);
209
+ }
210
+
211
+ if (!prevProps || newProps.badgeBackgroundColor != prevProps->badgeBackgroundColor) {
212
+ _view.badgeBackgroundColor = RCTUIColorFromSharedColor(newProps.badgeBackgroundColor);
213
+ }
214
+
215
+ if (!prevProps || newProps.badgeTextColor != prevProps->badgeTextColor) {
216
+ _view.badgeTextColor = RCTUIColorFromSharedColor(newProps.badgeTextColor);
157
217
  }
158
218
 
159
219
  [super updateProps:props oldProps:oldProps];