react-native-platform-components 0.6.1 → 0.7.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 (40) hide show
  1. package/README.md +153 -44
  2. package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +241 -0
  3. package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +105 -0
  4. package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +1 -0
  5. package/ios/PCDatePickerView.swift +16 -13
  6. package/ios/PCSegmentedControl.h +10 -0
  7. package/ios/PCSegmentedControl.mm +194 -0
  8. package/ios/PCSegmentedControl.swift +200 -0
  9. package/lib/commonjs/SegmentedControl.js +93 -0
  10. package/lib/commonjs/SegmentedControl.js.map +1 -0
  11. package/lib/commonjs/SegmentedControlNativeComponent.ts +79 -0
  12. package/lib/commonjs/index.js +11 -0
  13. package/lib/commonjs/index.js.map +1 -1
  14. package/lib/module/SegmentedControl.js +87 -0
  15. package/lib/module/SegmentedControl.js.map +1 -0
  16. package/lib/module/SegmentedControlNativeComponent.ts +79 -0
  17. package/lib/module/index.js +1 -0
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/typescript/commonjs/src/SegmentedControl.d.ts +62 -0
  20. package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -0
  21. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +63 -0
  22. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -0
  23. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  24. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  25. package/lib/typescript/module/src/SegmentedControl.d.ts +62 -0
  26. package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -0
  27. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +63 -0
  28. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -0
  29. package/lib/typescript/module/src/index.d.ts +1 -0
  30. package/lib/typescript/module/src/index.d.ts.map +1 -1
  31. package/package.json +4 -3
  32. package/react-native.config.js +1 -0
  33. package/shared/PCSegmentedControlComponentDescriptors-custom.h +22 -0
  34. package/shared/PCSegmentedControlShadowNode-custom.cpp +54 -0
  35. package/shared/PCSegmentedControlShadowNode-custom.h +56 -0
  36. package/shared/PCSegmentedControlState-custom.h +62 -0
  37. package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +1 -0
  38. package/src/SegmentedControl.tsx +178 -0
  39. package/src/SegmentedControlNativeComponent.ts +79 -0
  40. package/src/index.tsx +1 -0
@@ -0,0 +1,194 @@
1
+ // PCSegmentedControl.mm
2
+
3
+ #import "PCSegmentedControl.h"
4
+
5
+ #import <React/RCTComponentViewFactory.h>
6
+ #import <React/RCTConversions.h>
7
+ #import <React/RCTFabricComponentsPlugins.h>
8
+
9
+ #import <react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h>
10
+ #import <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
11
+ #import <react/renderer/components/PlatformComponentsViewSpec/Props.h>
12
+ #import <react/renderer/core/LayoutPrimitives.h>
13
+
14
+ #if __has_include(<PlatformComponents/PlatformComponents-Swift.h>)
15
+ #import <PlatformComponents/PlatformComponents-Swift.h>
16
+ #else
17
+ #import "PlatformComponents-Swift.h"
18
+ #endif
19
+
20
+ #import "PCSegmentedControlComponentDescriptors-custom.h"
21
+ #import "PCSegmentedControlShadowNode-custom.h"
22
+ #import "PCSegmentedControlState-custom.h"
23
+
24
+ using namespace facebook::react;
25
+
26
+ namespace {
27
+ static inline bool SegmentsEqual(
28
+ const std::vector<facebook::react::PCSegmentedControlSegmentsStruct> &a,
29
+ const std::vector<facebook::react::PCSegmentedControlSegmentsStruct> &b) {
30
+ if (a.size() != b.size()) return false;
31
+ for (size_t i = 0; i < a.size(); i++) {
32
+ if (a[i].label != b[i].label) return false;
33
+ if (a[i].value != b[i].value) return false;
34
+ if (a[i].disabled != b[i].disabled) return false;
35
+ if (a[i].icon != b[i].icon) return false;
36
+ }
37
+ return true;
38
+ }
39
+ } // namespace
40
+
41
+ @interface PCSegmentedControl ()
42
+
43
+ - (void)updateMeasurements;
44
+
45
+ @end
46
+
47
+ @implementation PCSegmentedControl {
48
+ PCSegmentedControlView *_view;
49
+ MeasuringPCSegmentedControlShadowNode::ConcreteState::Shared _state;
50
+ }
51
+
52
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
53
+ return concreteComponentDescriptorProvider<
54
+ MeasuringPCSegmentedControlComponentDescriptor>();
55
+ }
56
+
57
+ - (instancetype)initWithFrame:(CGRect)frame {
58
+ if (self = [super initWithFrame:frame]) {
59
+ _view = [PCSegmentedControlView new];
60
+ self.contentView = _view;
61
+
62
+ __weak __typeof(self) weakSelf = self;
63
+
64
+ _view.onSelect = ^(NSInteger index, NSString *value) {
65
+ __typeof(self) strongSelf = weakSelf;
66
+ if (!strongSelf) return;
67
+
68
+ auto eventEmitter =
69
+ std::static_pointer_cast<const PCSegmentedControlEventEmitter>(
70
+ strongSelf->_eventEmitter);
71
+ if (!eventEmitter) return;
72
+
73
+ PCSegmentedControlEventEmitter::OnSelect payload = {
74
+ .index = (int)index,
75
+ .value = value.UTF8String,
76
+ };
77
+
78
+ eventEmitter->onSelect(payload);
79
+ };
80
+ }
81
+ return self;
82
+ }
83
+
84
+ - (void)updateProps:(Props::Shared const &)props
85
+ oldProps:(Props::Shared const &)oldProps {
86
+ const auto &newProps =
87
+ *std::static_pointer_cast<const PCSegmentedControlProps>(props);
88
+ const auto prevProps =
89
+ std::static_pointer_cast<const PCSegmentedControlProps>(oldProps);
90
+
91
+ // segments: [{label, value, disabled, icon}]
92
+ if (!prevProps || !SegmentsEqual(newProps.segments, prevProps->segments)) {
93
+ NSMutableArray *arr = [NSMutableArray new];
94
+ 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
+ [arr addObject:@{
108
+ @"label": label,
109
+ @"value": value,
110
+ @"disabled": disabled,
111
+ @"icon": icon
112
+ }];
113
+ }
114
+ _view.segments = arr;
115
+ }
116
+
117
+ // selectedValue (default "")
118
+ if (!prevProps || newProps.selectedValue != prevProps->selectedValue) {
119
+ if (!newProps.selectedValue.empty()) {
120
+ _view.selectedValue =
121
+ [NSString stringWithUTF8String:newProps.selectedValue.c_str()];
122
+ } else {
123
+ _view.selectedValue = @""; // sentinel for no selection
124
+ }
125
+ }
126
+
127
+ // interactivity: "enabled" | "disabled"
128
+ if (!prevProps || newProps.interactivity != prevProps->interactivity) {
129
+ if (!newProps.interactivity.empty()) {
130
+ _view.interactivity =
131
+ [NSString stringWithUTF8String:newProps.interactivity.c_str()];
132
+ } else {
133
+ _view.interactivity = @"enabled";
134
+ }
135
+ }
136
+
137
+ // iOS-specific props
138
+ const auto &newIos = newProps.ios;
139
+ const auto &oldIos =
140
+ prevProps ? prevProps->ios : PCSegmentedControlIosStruct{};
141
+
142
+ if (!prevProps || newIos.momentary != oldIos.momentary) {
143
+ _view.momentary = (newIos.momentary == "true");
144
+ }
145
+
146
+ if (!prevProps || newIos.apportionsSegmentWidthsByContent != oldIos.apportionsSegmentWidthsByContent) {
147
+ _view.apportionsSegmentWidthsByContent = (newIos.apportionsSegmentWidthsByContent == "true");
148
+ }
149
+
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
+ }
157
+ }
158
+
159
+ [super updateProps:props oldProps:oldProps];
160
+
161
+ // Update measurements when props change that affect layout
162
+ [self updateMeasurements];
163
+ }
164
+
165
+ #pragma mark - State (Measuring)
166
+
167
+ - (void)updateState:(const State::Shared &)state
168
+ oldState:(const State::Shared &)oldState {
169
+ _state = std::static_pointer_cast<
170
+ const MeasuringPCSegmentedControlShadowNode::ConcreteState>(state);
171
+
172
+ if (oldState == nullptr) {
173
+ // First time: compute initial size.
174
+ [self updateMeasurements];
175
+ }
176
+
177
+ [super updateState:state oldState:oldState];
178
+ }
179
+
180
+ - (void)updateMeasurements {
181
+ if (_state == nullptr)
182
+ return;
183
+
184
+ // Use the real width Yoga gave us
185
+ const CGFloat w = self.bounds.size.width > 1 ? self.bounds.size.width : 320;
186
+
187
+ CGSize size = [_view sizeForLayoutWithConstrainedTo:CGSizeMake(w, 0)];
188
+
189
+ PCSegmentedControlStateFrameSize next;
190
+ next.frameSize = {(Float)size.width, (Float)size.height};
191
+ _state->updateState(std::move(next));
192
+ }
193
+
194
+ @end
@@ -0,0 +1,200 @@
1
+ import UIKit
2
+
3
+ // MARK: - Segment model (bridged from ObjC++ as dictionaries)
4
+
5
+ struct PCSegmentedControlSegment {
6
+ let label: String
7
+ let value: String
8
+ let disabled: Bool
9
+ let icon: String
10
+ }
11
+
12
+ @objcMembers
13
+ public final class PCSegmentedControlView: UIControl {
14
+ // MARK: - Props (set from ObjC++)
15
+
16
+ /// ObjC++ sets this as an array of dictionaries: [{label, value, disabled, icon}]
17
+ public var segments: [Any] = [] { didSet { rebuildControl() } }
18
+
19
+ /// Controlled selection by value. "" = no selection.
20
+ public var selectedValue: String = "" { didSet { updateSelection() } }
21
+
22
+ /// "enabled" | "disabled"
23
+ public var interactivity: String = "enabled" { didSet { updateEnabled() } }
24
+
25
+ /// iOS-specific: momentary mode (segment springs back after touch)
26
+ public var momentary: Bool = false { didSet { control.isMomentary = momentary } }
27
+
28
+ /// iOS-specific: segment widths proportional to content
29
+ public var apportionsSegmentWidthsByContent: Bool = false {
30
+ didSet { control.apportionsSegmentWidthsByContent = apportionsSegmentWidthsByContent }
31
+ }
32
+
33
+ /// iOS-specific: selected segment tint color (hex string)
34
+ public var selectedSegmentTintColor: String? { didSet { updateTintColor() } }
35
+
36
+ // MARK: - Events back to ObjC++
37
+
38
+ public var onSelect: ((Int, String) -> Void)? // (index, value)
39
+
40
+ // MARK: - Internal
41
+
42
+ private let control = UISegmentedControl()
43
+ private var parsedSegments: [PCSegmentedControlSegment] = []
44
+
45
+ // MARK: - Init
46
+
47
+ public override init(frame: CGRect) {
48
+ super.init(frame: frame)
49
+ setup()
50
+ }
51
+
52
+ public required init?(coder: NSCoder) {
53
+ super.init(coder: coder)
54
+ setup()
55
+ }
56
+
57
+ private func setup() {
58
+ control.translatesAutoresizingMaskIntoConstraints = false
59
+ addSubview(control)
60
+
61
+ NSLayoutConstraint.activate([
62
+ control.topAnchor.constraint(equalTo: topAnchor),
63
+ control.bottomAnchor.constraint(equalTo: bottomAnchor),
64
+ control.leadingAnchor.constraint(equalTo: leadingAnchor),
65
+ control.trailingAnchor.constraint(equalTo: trailingAnchor),
66
+ ])
67
+
68
+ control.addTarget(self, action: #selector(valueChanged), for: .valueChanged)
69
+ }
70
+
71
+ @objc private func valueChanged() {
72
+ let index = control.selectedSegmentIndex
73
+ guard index != UISegmentedControl.noSegment,
74
+ index >= 0, index < parsedSegments.count else { return }
75
+
76
+ let segment = parsedSegments[index]
77
+ onSelect?(index, segment.value)
78
+ }
79
+
80
+ // MARK: - Props handling
81
+
82
+ private func rebuildControl() {
83
+ parsedSegments = segments.compactMap { any in
84
+ guard let dict = any as? [String: Any] else { return nil }
85
+ let label = (dict["label"] as? String) ?? ""
86
+ let value = (dict["value"] as? String) ?? ""
87
+ let disabled = (dict["disabled"] as? String) == "disabled"
88
+ let icon = (dict["icon"] as? String) ?? ""
89
+ return PCSegmentedControlSegment(
90
+ label: label,
91
+ value: value,
92
+ disabled: disabled,
93
+ icon: icon
94
+ )
95
+ }
96
+
97
+ control.removeAllSegments()
98
+
99
+ for (index, segment) in parsedSegments.enumerated() {
100
+ // Try to use SF Symbol icon if available
101
+ if !segment.icon.isEmpty,
102
+ let sfImage = UIImage(systemName: segment.icon) {
103
+ control.insertSegment(with: sfImage, at: index, animated: false)
104
+ } else {
105
+ control.insertSegment(withTitle: segment.label, at: index, animated: false)
106
+ }
107
+
108
+ // Set enabled state for this segment
109
+ control.setEnabled(!segment.disabled, forSegmentAt: index)
110
+ }
111
+
112
+ updateSelection()
113
+ invalidateIntrinsicContentSize()
114
+ }
115
+
116
+ private func updateSelection() {
117
+ if selectedValue.isEmpty {
118
+ control.selectedSegmentIndex = UISegmentedControl.noSegment
119
+ } else if let index = parsedSegments.firstIndex(where: { $0.value == selectedValue }) {
120
+ control.selectedSegmentIndex = index
121
+ } else {
122
+ control.selectedSegmentIndex = UISegmentedControl.noSegment
123
+ }
124
+ }
125
+
126
+ private func updateEnabled() {
127
+ let enabled = interactivity != "disabled"
128
+ control.isEnabled = enabled
129
+ alpha = enabled ? 1.0 : 0.5
130
+ }
131
+
132
+ private func updateTintColor() {
133
+ if let colorString = selectedSegmentTintColor, !colorString.isEmpty {
134
+ control.selectedSegmentTintColor = UIColor(hex: colorString)
135
+ } else {
136
+ control.selectedSegmentTintColor = nil
137
+ }
138
+ }
139
+
140
+ // MARK: - Sizing
141
+
142
+ public override func sizeThatFits(_ size: CGSize) -> CGSize {
143
+ let fitted = control.sizeThatFits(CGSize(width: size.width, height: .greatestFiniteMagnitude))
144
+ return CGSize(
145
+ width: size.width > 0 ? size.width : fitted.width,
146
+ height: max(PCConstants.minTouchTargetHeight, fitted.height)
147
+ )
148
+ }
149
+
150
+ public override var intrinsicContentSize: CGSize {
151
+ let fitted = control.intrinsicContentSize
152
+ return CGSize(
153
+ width: fitted.width,
154
+ height: max(PCConstants.minTouchTargetHeight, fitted.height)
155
+ )
156
+ }
157
+
158
+ /// Called by the measuring pipeline to get the size for Yoga layout.
159
+ @objc public func sizeForLayout(withConstrainedTo constrainedSize: CGSize) -> CGSize {
160
+ let fitted = control.sizeThatFits(
161
+ CGSize(width: constrainedSize.width > 0 ? constrainedSize.width : .greatestFiniteMagnitude,
162
+ height: .greatestFiniteMagnitude)
163
+ )
164
+ return CGSize(
165
+ width: constrainedSize.width > 0 ? constrainedSize.width : fitted.width,
166
+ height: max(PCConstants.minTouchTargetHeight, fitted.height)
167
+ )
168
+ }
169
+ }
170
+
171
+ // MARK: - UIColor hex extension
172
+
173
+ private extension UIColor {
174
+ convenience init?(hex: String) {
175
+ var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
176
+ hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
177
+
178
+ var rgb: UInt64 = 0
179
+ guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
180
+
181
+ let length = hexSanitized.count
182
+ if length == 6 {
183
+ self.init(
184
+ red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
185
+ green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
186
+ blue: CGFloat(rgb & 0x0000FF) / 255.0,
187
+ alpha: 1.0
188
+ )
189
+ } else if length == 8 {
190
+ self.init(
191
+ red: CGFloat((rgb & 0xFF000000) >> 24) / 255.0,
192
+ green: CGFloat((rgb & 0x00FF0000) >> 16) / 255.0,
193
+ blue: CGFloat((rgb & 0x0000FF00) >> 8) / 255.0,
194
+ alpha: CGFloat(rgb & 0x000000FF) / 255.0
195
+ )
196
+ } else {
197
+ return nil
198
+ }
199
+ }
200
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SegmentedControl = SegmentedControl;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _SegmentedControlNativeComponent = _interopRequireDefault(require("./SegmentedControlNativeComponent"));
10
+ var _jsxRuntime = require("react/jsx-runtime");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ // SegmentedControl.tsx
14
+
15
+ // Android: Minimum height to ensure visibility.
16
+ // Fabric's shadow node measurement isn't being called on initial render,
17
+ // so we apply a minHeight that matches Material design touch target guidelines.
18
+ const ANDROID_MIN_HEIGHT = 48;
19
+ function normalizeSelectedValue(selected) {
20
+ return selected ?? '';
21
+ }
22
+ function SegmentedControl(props) {
23
+ const {
24
+ style,
25
+ segments,
26
+ selectedValue,
27
+ disabled,
28
+ onSelect,
29
+ ios,
30
+ android,
31
+ ...viewProps
32
+ } = props;
33
+
34
+ // Normalize segments for native
35
+ const nativeSegments = (0, _react.useMemo)(() => {
36
+ return segments.map(seg => ({
37
+ label: seg.label,
38
+ value: seg.value,
39
+ disabled: seg.disabled ? 'disabled' : 'enabled',
40
+ icon: seg.icon ?? ''
41
+ }));
42
+ }, [segments]);
43
+ const selectedData = (0, _react.useMemo)(() => normalizeSelectedValue(selectedValue), [selectedValue]);
44
+ const handleSelect = (0, _react.useCallback)(e => {
45
+ const {
46
+ index,
47
+ value
48
+ } = e.nativeEvent;
49
+ onSelect?.(value, index);
50
+ }, [onSelect]);
51
+
52
+ // Normalize iOS props to native string format
53
+ const nativeIos = (0, _react.useMemo)(() => {
54
+ if (!ios) return undefined;
55
+ return {
56
+ momentary: ios.momentary ? 'true' : 'false',
57
+ apportionsSegmentWidthsByContent: ios.apportionsSegmentWidthsByContent ? 'true' : 'false',
58
+ selectedSegmentTintColor: ios.selectedSegmentTintColor ?? ''
59
+ };
60
+ }, [ios]);
61
+
62
+ // Normalize Android props
63
+ const nativeAndroid = (0, _react.useMemo)(() => {
64
+ if (!android) return undefined;
65
+ return {
66
+ selectionRequired: android.selectionRequired ? 'true' : 'false'
67
+ };
68
+ }, [android]);
69
+
70
+ // Merge user style with Android minHeight default
71
+ const mergedStyle = (0, _react.useMemo)(() => {
72
+ if (_reactNative.Platform.OS === 'android') {
73
+ return [styles.androidDefault, style];
74
+ }
75
+ return style;
76
+ }, [style]);
77
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_SegmentedControlNativeComponent.default, {
78
+ style: mergedStyle,
79
+ segments: nativeSegments,
80
+ selectedValue: selectedData,
81
+ interactivity: disabled ? 'disabled' : 'enabled',
82
+ onSelect: onSelect ? handleSelect : undefined,
83
+ ios: nativeIos,
84
+ android: nativeAndroid,
85
+ ...viewProps
86
+ });
87
+ }
88
+ const styles = _reactNative.StyleSheet.create({
89
+ androidDefault: {
90
+ minHeight: ANDROID_MIN_HEIGHT
91
+ }
92
+ });
93
+ //# sourceMappingURL=SegmentedControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_SegmentedControlNativeComponent","_interopRequireDefault","_jsxRuntime","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ANDROID_MIN_HEIGHT","normalizeSelectedValue","selected","SegmentedControl","props","style","segments","selectedValue","disabled","onSelect","ios","android","viewProps","nativeSegments","useMemo","map","seg","label","value","icon","selectedData","handleSelect","useCallback","index","nativeEvent","nativeIos","undefined","momentary","apportionsSegmentWidthsByContent","selectedSegmentTintColor","nativeAndroid","selectionRequired","mergedStyle","Platform","OS","styles","androidDefault","jsx","interactivity","StyleSheet","create","minHeight"],"sourceRoot":"../../src","sources":["SegmentedControl.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,79 @@
1
+ // SegmentedControlNativeComponent.ts
2
+ import type { CodegenTypes, ViewProps } from 'react-native';
3
+ import { codegenNativeComponent } from 'react-native';
4
+
5
+ /**
6
+ * A single segment in the control.
7
+ */
8
+ export type SegmentedControlSegment = Readonly<{
9
+ label: string;
10
+ value: string;
11
+ disabled: string; // 'enabled' | 'disabled'
12
+ icon: string; // SF Symbol (iOS) or drawable name (Android), empty = none
13
+ }>;
14
+
15
+ /**
16
+ * Event emitted when the user selects a segment.
17
+ */
18
+ export type SegmentedControlSelectEvent = Readonly<{
19
+ /** Selected segment index */
20
+ index: CodegenTypes.Int32;
21
+
22
+ /** Selected segment value */
23
+ value: string;
24
+ }>;
25
+
26
+ /** Interactivity state (no booleans). */
27
+ export type SegmentedControlInteractivity = 'enabled' | 'disabled';
28
+
29
+ /**
30
+ * iOS-specific configuration.
31
+ */
32
+ export type IOSProps = Readonly<{
33
+ /** Momentary mode: segment springs back after touch */
34
+ momentary?: string; // 'true' | 'false'
35
+
36
+ /** Whether segment widths are proportional to content */
37
+ apportionsSegmentWidthsByContent?: string; // 'true' | 'false'
38
+
39
+ /** Selected segment tint color (hex string) */
40
+ selectedSegmentTintColor?: string;
41
+ }>;
42
+
43
+ /**
44
+ * Android-specific configuration.
45
+ */
46
+ export type AndroidProps = Readonly<{
47
+ /** Whether one segment must always be selected */
48
+ selectionRequired?: string; // 'true' | 'false'
49
+ }>;
50
+
51
+ export interface SegmentedControlProps extends ViewProps {
52
+ /**
53
+ * Segments to display.
54
+ */
55
+ segments: ReadonlyArray<SegmentedControlSegment>;
56
+
57
+ /**
58
+ * Controlled selection by `value`.
59
+ * Empty string means "no selection".
60
+ */
61
+ selectedValue?: CodegenTypes.WithDefault<string, ''>;
62
+
63
+ /**
64
+ * Enabled / disabled state.
65
+ */
66
+ interactivity?: string; // SegmentedControlInteractivity
67
+
68
+ /**
69
+ * Fired when the user selects a segment.
70
+ */
71
+ onSelect?: CodegenTypes.BubblingEventHandler<SegmentedControlSelectEvent>;
72
+
73
+ ios?: IOSProps;
74
+ android?: AndroidProps;
75
+ }
76
+
77
+ export default codegenNativeComponent<SegmentedControlProps>(
78
+ 'PCSegmentedControl'
79
+ );
@@ -36,6 +36,17 @@ Object.keys(_ContextMenu).forEach(function (key) {
36
36
  }
37
37
  });
38
38
  });
39
+ var _SegmentedControl = require("./SegmentedControl");
40
+ Object.keys(_SegmentedControl).forEach(function (key) {
41
+ if (key === "default" || key === "__esModule") return;
42
+ if (key in exports && exports[key] === _SegmentedControl[key]) return;
43
+ Object.defineProperty(exports, key, {
44
+ enumerable: true,
45
+ get: function () {
46
+ return _SegmentedControl[key];
47
+ }
48
+ });
49
+ });
39
50
  var _sharedTypes = require("./sharedTypes");
40
51
  Object.keys(_sharedTypes).forEach(function (key) {
41
52
  if (key === "default" || key === "__esModule") return;
@@ -1 +1 @@
1
- {"version":3,"names":["_DatePicker","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_SelectionMenu","_ContextMenu","_sharedTypes"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,WAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,WAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,WAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,cAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,cAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,cAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,cAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,YAAA,GAAAV,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAQ,YAAA,EAAAP,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,YAAA,CAAAN,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,YAAA,CAAAN,GAAA;IAAA;EAAA;AAAA;AACA,IAAAO,YAAA,GAAAX,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAS,YAAA,EAAAR,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,YAAA,CAAAP,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,YAAA,CAAAP,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
1
+ {"version":3,"names":["_DatePicker","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_SelectionMenu","_ContextMenu","_SegmentedControl","_sharedTypes"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,WAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,WAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,WAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,cAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,cAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,cAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,cAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,YAAA,GAAAV,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAQ,YAAA,EAAAP,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,YAAA,CAAAN,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,YAAA,CAAAN,GAAA;IAAA;EAAA;AAAA;AACA,IAAAO,iBAAA,GAAAX,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAS,iBAAA,EAAAR,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,iBAAA,CAAAP,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,iBAAA,CAAAP,GAAA;IAAA;EAAA;AAAA;AACA,IAAAQ,YAAA,GAAAZ,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAU,YAAA,EAAAT,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAQ,YAAA,CAAAR,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAI,YAAA,CAAAR,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ // SegmentedControl.tsx
4
+ import React, { useCallback, useMemo } from 'react';
5
+ import { Platform, StyleSheet } from 'react-native';
6
+ import NativeSegmentedControl from './SegmentedControlNativeComponent';
7
+
8
+ // Android: Minimum height to ensure visibility.
9
+ // Fabric's shadow node measurement isn't being called on initial render,
10
+ // so we apply a minHeight that matches Material design touch target guidelines.
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ const ANDROID_MIN_HEIGHT = 48;
13
+ function normalizeSelectedValue(selected) {
14
+ return selected ?? '';
15
+ }
16
+ export function SegmentedControl(props) {
17
+ const {
18
+ style,
19
+ segments,
20
+ selectedValue,
21
+ disabled,
22
+ onSelect,
23
+ ios,
24
+ android,
25
+ ...viewProps
26
+ } = props;
27
+
28
+ // Normalize segments for native
29
+ const nativeSegments = useMemo(() => {
30
+ return segments.map(seg => ({
31
+ label: seg.label,
32
+ value: seg.value,
33
+ disabled: seg.disabled ? 'disabled' : 'enabled',
34
+ icon: seg.icon ?? ''
35
+ }));
36
+ }, [segments]);
37
+ const selectedData = useMemo(() => normalizeSelectedValue(selectedValue), [selectedValue]);
38
+ const handleSelect = useCallback(e => {
39
+ const {
40
+ index,
41
+ value
42
+ } = e.nativeEvent;
43
+ onSelect?.(value, index);
44
+ }, [onSelect]);
45
+
46
+ // Normalize iOS props to native string format
47
+ const nativeIos = useMemo(() => {
48
+ if (!ios) return undefined;
49
+ return {
50
+ momentary: ios.momentary ? 'true' : 'false',
51
+ apportionsSegmentWidthsByContent: ios.apportionsSegmentWidthsByContent ? 'true' : 'false',
52
+ selectedSegmentTintColor: ios.selectedSegmentTintColor ?? ''
53
+ };
54
+ }, [ios]);
55
+
56
+ // Normalize Android props
57
+ const nativeAndroid = useMemo(() => {
58
+ if (!android) return undefined;
59
+ return {
60
+ selectionRequired: android.selectionRequired ? 'true' : 'false'
61
+ };
62
+ }, [android]);
63
+
64
+ // Merge user style with Android minHeight default
65
+ const mergedStyle = useMemo(() => {
66
+ if (Platform.OS === 'android') {
67
+ return [styles.androidDefault, style];
68
+ }
69
+ return style;
70
+ }, [style]);
71
+ return /*#__PURE__*/_jsx(NativeSegmentedControl, {
72
+ style: mergedStyle,
73
+ segments: nativeSegments,
74
+ selectedValue: selectedData,
75
+ interactivity: disabled ? 'disabled' : 'enabled',
76
+ onSelect: onSelect ? handleSelect : undefined,
77
+ ios: nativeIos,
78
+ android: nativeAndroid,
79
+ ...viewProps
80
+ });
81
+ }
82
+ const styles = StyleSheet.create({
83
+ androidDefault: {
84
+ minHeight: ANDROID_MIN_HEIGHT
85
+ }
86
+ });
87
+ //# sourceMappingURL=SegmentedControl.js.map