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 @@
1
+ {"version":3,"names":["React","useCallback","useMemo","Platform","StyleSheet","NativeSegmentedControl","jsx","_jsx","ANDROID_MIN_HEIGHT","normalizeSelectedValue","selected","SegmentedControl","props","style","segments","selectedValue","disabled","onSelect","ios","android","viewProps","nativeSegments","map","seg","label","value","icon","selectedData","handleSelect","e","index","nativeEvent","nativeIos","undefined","momentary","apportionsSegmentWidthsByContent","selectedSegmentTintColor","nativeAndroid","selectionRequired","mergedStyle","OS","styles","androidDefault","interactivity","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
+ );
@@ -3,5 +3,6 @@
3
3
  export * from "./DatePicker.js";
4
4
  export * from "./SelectionMenu.js";
5
5
  export * from "./ContextMenu.js";
6
+ export * from "./SegmentedControl.js";
6
7
  export * from "./sharedTypes.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,cAAc,iBAAc;AAC5B,cAAc,oBAAiB;AAC/B,cAAc,kBAAe;AAC7B,cAAc,kBAAe","ignoreList":[]}
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,cAAc,iBAAc;AAC5B,cAAc,oBAAiB;AAC/B,cAAc,kBAAe;AAC7B,cAAc,uBAAoB;AAClC,cAAc,kBAAe","ignoreList":[]}
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { type ViewProps } from 'react-native';
3
+ export interface SegmentedControlSegmentProps {
4
+ /** Display label for the segment */
5
+ label: string;
6
+ /** Unique value identifier for the segment */
7
+ value: string;
8
+ /** Whether this specific segment is disabled */
9
+ disabled?: boolean;
10
+ /** Optional SF Symbol name (iOS) or drawable resource name (Android) */
11
+ icon?: string;
12
+ }
13
+ export interface SegmentedControlProps extends ViewProps {
14
+ /** Array of segments to display */
15
+ segments: readonly SegmentedControlSegmentProps[];
16
+ /**
17
+ * Currently selected segment value.
18
+ * Use `null` for no selection.
19
+ */
20
+ selectedValue: string | null;
21
+ /**
22
+ * Called when the user selects a segment.
23
+ * @param value - The selected segment's value
24
+ * @param index - The selected segment's index
25
+ */
26
+ onSelect?: (value: string, index: number) => void;
27
+ /** Whether the entire control is disabled */
28
+ disabled?: boolean;
29
+ /**
30
+ * iOS-specific configuration
31
+ */
32
+ ios?: {
33
+ /**
34
+ * Momentary mode: segment springs back after touch (no persistent selection)
35
+ * Default: false
36
+ */
37
+ momentary?: boolean;
38
+ /**
39
+ * Whether segment widths are proportional to content
40
+ * Default: false (equal widths)
41
+ */
42
+ apportionsSegmentWidthsByContent?: boolean;
43
+ /**
44
+ * Selected segment tint color (hex string, e.g., "#007AFF")
45
+ */
46
+ selectedSegmentTintColor?: string;
47
+ };
48
+ /**
49
+ * Android-specific configuration
50
+ */
51
+ android?: {
52
+ /**
53
+ * Whether one segment must always be selected.
54
+ * Default: false
55
+ */
56
+ selectionRequired?: boolean;
57
+ };
58
+ /** Test identifier */
59
+ testID?: string;
60
+ }
61
+ export declare function SegmentedControl(props: SegmentedControlProps): React.ReactElement;
62
+ //# sourceMappingURL=SegmentedControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../../src/SegmentedControl.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+B,MAAM,OAAO,CAAC;AACpD,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,cAAc,CAAC;AAWtB,MAAM,WAAW,4BAA4B;IAC3C,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IAEd,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,SAAS;IACtD,mCAAmC;IACnC,QAAQ,EAAE,SAAS,4BAA4B,EAAE,CAAC;IAElD;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElD,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE;QACJ;;;WAGG;QACH,SAAS,CAAC,EAAE,OAAO,CAAC;QAEpB;;;WAGG;QACH,gCAAgC,CAAC,EAAE,OAAO,CAAC;QAE3C;;WAEG;QACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;KACnC,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE;QACR;;;WAGG;QACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;IAEF,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,KAAK,CAAC,YAAY,CA2EpB"}
@@ -0,0 +1,63 @@
1
+ import type { CodegenTypes, ViewProps } from 'react-native';
2
+ /**
3
+ * A single segment in the control.
4
+ */
5
+ export type SegmentedControlSegment = Readonly<{
6
+ label: string;
7
+ value: string;
8
+ disabled: string;
9
+ icon: string;
10
+ }>;
11
+ /**
12
+ * Event emitted when the user selects a segment.
13
+ */
14
+ export type SegmentedControlSelectEvent = Readonly<{
15
+ /** Selected segment index */
16
+ index: CodegenTypes.Int32;
17
+ /** Selected segment value */
18
+ value: string;
19
+ }>;
20
+ /** Interactivity state (no booleans). */
21
+ export type SegmentedControlInteractivity = 'enabled' | 'disabled';
22
+ /**
23
+ * iOS-specific configuration.
24
+ */
25
+ export type IOSProps = Readonly<{
26
+ /** Momentary mode: segment springs back after touch */
27
+ momentary?: string;
28
+ /** Whether segment widths are proportional to content */
29
+ apportionsSegmentWidthsByContent?: string;
30
+ /** Selected segment tint color (hex string) */
31
+ selectedSegmentTintColor?: string;
32
+ }>;
33
+ /**
34
+ * Android-specific configuration.
35
+ */
36
+ export type AndroidProps = Readonly<{
37
+ /** Whether one segment must always be selected */
38
+ selectionRequired?: string;
39
+ }>;
40
+ export interface SegmentedControlProps extends ViewProps {
41
+ /**
42
+ * Segments to display.
43
+ */
44
+ segments: ReadonlyArray<SegmentedControlSegment>;
45
+ /**
46
+ * Controlled selection by `value`.
47
+ * Empty string means "no selection".
48
+ */
49
+ selectedValue?: CodegenTypes.WithDefault<string, ''>;
50
+ /**
51
+ * Enabled / disabled state.
52
+ */
53
+ interactivity?: string;
54
+ /**
55
+ * Fired when the user selects a segment.
56
+ */
57
+ onSelect?: CodegenTypes.BubblingEventHandler<SegmentedControlSelectEvent>;
58
+ ios?: IOSProps;
59
+ android?: AndroidProps;
60
+ }
61
+ declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<SegmentedControlProps>;
62
+ export default _default;
63
+ //# sourceMappingURL=SegmentedControlNativeComponent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentedControlNativeComponent.d.ts","sourceRoot":"","sources":["../../../../src/SegmentedControlNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG5D;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,6BAA6B;IAC7B,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;IAE1B,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,yCAAyC;AACzC,MAAM,MAAM,6BAA6B,GAAG,SAAS,GAAG,UAAU,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yDAAyD;IACzD,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAE1C,+CAA+C;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC,CAAC;AAEH,MAAM,WAAW,qBAAsB,SAAQ,SAAS;IACtD;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IAEjD;;;OAGG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAErD;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,CAAC;IAE1E,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;;AAED,wBAEE"}
@@ -1,5 +1,6 @@
1
1
  export * from './DatePicker';
2
2
  export * from './SelectionMenu';
3
3
  export * from './ContextMenu';
4
+ export * from './SegmentedControl';
4
5
  export * from './sharedTypes';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { type ViewProps } from 'react-native';
3
+ export interface SegmentedControlSegmentProps {
4
+ /** Display label for the segment */
5
+ label: string;
6
+ /** Unique value identifier for the segment */
7
+ value: string;
8
+ /** Whether this specific segment is disabled */
9
+ disabled?: boolean;
10
+ /** Optional SF Symbol name (iOS) or drawable resource name (Android) */
11
+ icon?: string;
12
+ }
13
+ export interface SegmentedControlProps extends ViewProps {
14
+ /** Array of segments to display */
15
+ segments: readonly SegmentedControlSegmentProps[];
16
+ /**
17
+ * Currently selected segment value.
18
+ * Use `null` for no selection.
19
+ */
20
+ selectedValue: string | null;
21
+ /**
22
+ * Called when the user selects a segment.
23
+ * @param value - The selected segment's value
24
+ * @param index - The selected segment's index
25
+ */
26
+ onSelect?: (value: string, index: number) => void;
27
+ /** Whether the entire control is disabled */
28
+ disabled?: boolean;
29
+ /**
30
+ * iOS-specific configuration
31
+ */
32
+ ios?: {
33
+ /**
34
+ * Momentary mode: segment springs back after touch (no persistent selection)
35
+ * Default: false
36
+ */
37
+ momentary?: boolean;
38
+ /**
39
+ * Whether segment widths are proportional to content
40
+ * Default: false (equal widths)
41
+ */
42
+ apportionsSegmentWidthsByContent?: boolean;
43
+ /**
44
+ * Selected segment tint color (hex string, e.g., "#007AFF")
45
+ */
46
+ selectedSegmentTintColor?: string;
47
+ };
48
+ /**
49
+ * Android-specific configuration
50
+ */
51
+ android?: {
52
+ /**
53
+ * Whether one segment must always be selected.
54
+ * Default: false
55
+ */
56
+ selectionRequired?: boolean;
57
+ };
58
+ /** Test identifier */
59
+ testID?: string;
60
+ }
61
+ export declare function SegmentedControl(props: SegmentedControlProps): React.ReactElement;
62
+ //# sourceMappingURL=SegmentedControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../../src/SegmentedControl.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+B,MAAM,OAAO,CAAC;AACpD,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,cAAc,CAAC;AAWtB,MAAM,WAAW,4BAA4B;IAC3C,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IAEd,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,SAAS;IACtD,mCAAmC;IACnC,QAAQ,EAAE,SAAS,4BAA4B,EAAE,CAAC;IAElD;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElD,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE;QACJ;;;WAGG;QACH,SAAS,CAAC,EAAE,OAAO,CAAC;QAEpB;;;WAGG;QACH,gCAAgC,CAAC,EAAE,OAAO,CAAC;QAE3C;;WAEG;QACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;KACnC,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE;QACR;;;WAGG;QACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;IAEF,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,KAAK,CAAC,YAAY,CA2EpB"}
@@ -0,0 +1,63 @@
1
+ import type { CodegenTypes, ViewProps } from 'react-native';
2
+ /**
3
+ * A single segment in the control.
4
+ */
5
+ export type SegmentedControlSegment = Readonly<{
6
+ label: string;
7
+ value: string;
8
+ disabled: string;
9
+ icon: string;
10
+ }>;
11
+ /**
12
+ * Event emitted when the user selects a segment.
13
+ */
14
+ export type SegmentedControlSelectEvent = Readonly<{
15
+ /** Selected segment index */
16
+ index: CodegenTypes.Int32;
17
+ /** Selected segment value */
18
+ value: string;
19
+ }>;
20
+ /** Interactivity state (no booleans). */
21
+ export type SegmentedControlInteractivity = 'enabled' | 'disabled';
22
+ /**
23
+ * iOS-specific configuration.
24
+ */
25
+ export type IOSProps = Readonly<{
26
+ /** Momentary mode: segment springs back after touch */
27
+ momentary?: string;
28
+ /** Whether segment widths are proportional to content */
29
+ apportionsSegmentWidthsByContent?: string;
30
+ /** Selected segment tint color (hex string) */
31
+ selectedSegmentTintColor?: string;
32
+ }>;
33
+ /**
34
+ * Android-specific configuration.
35
+ */
36
+ export type AndroidProps = Readonly<{
37
+ /** Whether one segment must always be selected */
38
+ selectionRequired?: string;
39
+ }>;
40
+ export interface SegmentedControlProps extends ViewProps {
41
+ /**
42
+ * Segments to display.
43
+ */
44
+ segments: ReadonlyArray<SegmentedControlSegment>;
45
+ /**
46
+ * Controlled selection by `value`.
47
+ * Empty string means "no selection".
48
+ */
49
+ selectedValue?: CodegenTypes.WithDefault<string, ''>;
50
+ /**
51
+ * Enabled / disabled state.
52
+ */
53
+ interactivity?: string;
54
+ /**
55
+ * Fired when the user selects a segment.
56
+ */
57
+ onSelect?: CodegenTypes.BubblingEventHandler<SegmentedControlSelectEvent>;
58
+ ios?: IOSProps;
59
+ android?: AndroidProps;
60
+ }
61
+ declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<SegmentedControlProps>;
62
+ export default _default;
63
+ //# sourceMappingURL=SegmentedControlNativeComponent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentedControlNativeComponent.d.ts","sourceRoot":"","sources":["../../../../src/SegmentedControlNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG5D;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,6BAA6B;IAC7B,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;IAE1B,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,yCAAyC;AACzC,MAAM,MAAM,6BAA6B,GAAG,SAAS,GAAG,UAAU,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yDAAyD;IACzD,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAE1C,+CAA+C;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC,CAAC;AAEH,MAAM,WAAW,qBAAsB,SAAQ,SAAS;IACtD;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IAEjD;;;OAGG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAErD;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,CAAC;IAE1E,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;;AAED,wBAEE"}
@@ -1,5 +1,6 @@
1
1
  export * from './DatePicker';
2
2
  export * from './SelectionMenu';
3
3
  export * from './ContextMenu';
4
+ export * from './SegmentedControl';
4
5
  export * from './sharedTypes';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-platform-components",
3
- "version": "0.6.1",
4
- "description": "A cross-platform toolkit of native UI components for React Native.",
3
+ "version": "0.7.0",
4
+ "description": "Native UI components for React Native: DatePicker, ContextMenu, SelectionMenu, SegmentedControl.",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
7
7
  "types": "./lib/typescript/commonjs/src/index.d.ts",
@@ -157,7 +157,8 @@
157
157
  "componentProvider": {
158
158
  "PCSelectionMenu": "PCSelectionMenu",
159
159
  "PCDatePicker": "PCDatePicker",
160
- "PCContextMenu": "PCContextMenu"
160
+ "PCContextMenu": "PCContextMenu",
161
+ "PCSegmentedControl": "PCSegmentedControl"
161
162
  }
162
163
  }
163
164
  },
@@ -5,6 +5,7 @@ module.exports = {
5
5
  componentDescriptors: [
6
6
  'MeasuringPCSelectionMenuComponentDescriptor',
7
7
  'MeasuringPCDatePickerComponentDescriptor',
8
+ 'MeasuringPCSegmentedControlComponentDescriptor',
8
9
  ],
9
10
  cmakeListsPath: 'src/main/jni/CMakeLists.txt',
10
11
  },
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
4
+
5
+ // Forward declaration to avoid circular includes
6
+ namespace facebook::react {
7
+ class MeasuringPCSegmentedControlShadowNode;
8
+ }
9
+
10
+ // Include the actual shadow node definition
11
+ #include "PCSegmentedControlShadowNode-custom.h"
12
+
13
+ namespace facebook::react {
14
+
15
+ /**
16
+ * Custom component descriptor that uses our measuring shadow node
17
+ * instead of the generated one.
18
+ */
19
+ using MeasuringPCSegmentedControlComponentDescriptor =
20
+ ConcreteComponentDescriptor<MeasuringPCSegmentedControlShadowNode>;
21
+
22
+ } // namespace facebook::react
@@ -0,0 +1,54 @@
1
+ #include "PCSegmentedControlShadowNode-custom.h"
2
+
3
+ #include <react/renderer/core/LayoutConstraints.h>
4
+ #include <algorithm>
5
+
6
+ namespace facebook::react {
7
+
8
+ Size MeasuringPCSegmentedControlShadowNode::measureContent(
9
+ const LayoutContext& /*layoutContext*/,
10
+ const LayoutConstraints& layoutConstraints) const {
11
+
12
+ // Get frame size from native state - native measures the actual control
13
+ const auto& stateData = this->getStateData();
14
+ Float measuredW = stateData.frameSize.width;
15
+ Float measuredH = stateData.frameSize.height;
16
+
17
+ // Platform-specific fallback heights
18
+ const Float fallbackHeight =
19
+ #ifdef __ANDROID__
20
+ static_cast<Float>(kFallbackHeightAndroid);
21
+ #else
22
+ static_cast<Float>(kFallbackHeightIOS);
23
+ #endif
24
+
25
+ // If height is 0, use fallback (state not yet set by native)
26
+ const bool usingFallback = (measuredH <= 0);
27
+ if (usingFallback) {
28
+ measuredH = fallbackHeight;
29
+ }
30
+
31
+ // If width is 0, use available width from constraints
32
+ const Float kHuge = static_cast<Float>(1.0e9);
33
+ if (measuredW <= 0) {
34
+ const Float maxW = layoutConstraints.maximumSize.width;
35
+ measuredW = (maxW > 0 && maxW < kHuge) ? maxW : 300;
36
+ }
37
+
38
+ // Respect layout constraints, but if using fallback height,
39
+ // don't let maximum constraint override our fallback
40
+ measuredW = std::max<Float>(measuredW, layoutConstraints.minimumSize.width);
41
+ if (layoutConstraints.maximumSize.width > 0 && layoutConstraints.maximumSize.width < kHuge) {
42
+ measuredW = std::min<Float>(measuredW, layoutConstraints.maximumSize.width);
43
+ }
44
+
45
+ measuredH = std::max<Float>(measuredH, layoutConstraints.minimumSize.height);
46
+ // Only clamp to max height if we have real measured data (not fallback)
47
+ if (!usingFallback && layoutConstraints.maximumSize.height > 0 && layoutConstraints.maximumSize.height < kHuge) {
48
+ measuredH = std::min<Float>(measuredH, layoutConstraints.maximumSize.height);
49
+ }
50
+
51
+ return Size{measuredW, measuredH};
52
+ }
53
+
54
+ } // namespace facebook::react
@@ -0,0 +1,56 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/components/view/ConcreteViewShadowNode.h>
4
+
5
+ // Only include what we need for the shadow node definition
6
+ // Do NOT include ComponentDescriptors.h here to avoid circular dependency
7
+ #include <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
8
+ #include <react/renderer/components/PlatformComponentsViewSpec/Props.h>
9
+
10
+ #include "PCSegmentedControlState-custom.h"
11
+
12
+ namespace facebook::react {
13
+
14
+ extern const char PCSegmentedControlComponentName[];
15
+
16
+ /**
17
+ * Custom ShadowNode for SegmentedControl that supports Yoga measurement.
18
+ *
19
+ * Key behavior:
20
+ * - Native side measures the actual segmented control and updates state with frameSize
21
+ * - measureContent() returns the size from state for proper Yoga layout
22
+ * - Falls back to platform-specific defaults if state hasn't been set yet
23
+ */
24
+ class MeasuringPCSegmentedControlShadowNode final : public ConcreteViewShadowNode<
25
+ PCSegmentedControlComponentName,
26
+ PCSegmentedControlProps,
27
+ PCSegmentedControlEventEmitter,
28
+ PCSegmentedControlStateFrameSize> {
29
+ public:
30
+ using ConcreteViewShadowNode::ConcreteViewShadowNode;
31
+
32
+ // Fallback heights used when native hasn't reported measurements yet
33
+ // iOS UISegmentedControl default height
34
+ static constexpr float kFallbackHeightIOS = 32.0f;
35
+
36
+ // Android MaterialButtonToggleGroup height
37
+ static constexpr float kFallbackHeightAndroid = 48.0f;
38
+
39
+ static ShadowNodeTraits BaseTraits() {
40
+ auto traits = ConcreteViewShadowNode::BaseTraits();
41
+ traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
42
+ traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
43
+ return traits;
44
+ }
45
+
46
+ /**
47
+ * Called by Yoga when it needs the intrinsic size of the component.
48
+ * Returns the size provided by native through state, with fallback to
49
+ * platform-specific defaults if state hasn't been set.
50
+ */
51
+ Size measureContent(
52
+ const LayoutContext& layoutContext,
53
+ const LayoutConstraints& layoutConstraints) const override;
54
+ };
55
+
56
+ } // namespace facebook::react
@@ -0,0 +1,62 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/core/LayoutPrimitives.h>
4
+ #include <memory>
5
+
6
+ #ifdef RN_SERIALIZABLE_STATE
7
+ #include <folly/dynamic.h>
8
+ #include <react/renderer/mapbuffer/MapBuffer.h>
9
+ #include <react/renderer/mapbuffer/MapBufferBuilder.h>
10
+ #endif
11
+
12
+ namespace facebook::react {
13
+
14
+ /**
15
+ * Custom state for SegmentedControl that holds the measured frame size from native.
16
+ * This allows the native side to measure the actual segmented control and communicate
17
+ * the size to the shadow node for proper Yoga layout.
18
+ */
19
+ struct PCSegmentedControlStateFrameSize {
20
+ using Shared = std::shared_ptr<const PCSegmentedControlStateFrameSize>;
21
+
22
+ Size frameSize{}; // {width, height} in points
23
+
24
+ PCSegmentedControlStateFrameSize() = default;
25
+
26
+ explicit PCSegmentedControlStateFrameSize(Size size) : frameSize(size) {}
27
+
28
+ bool operator==(const PCSegmentedControlStateFrameSize& other) const {
29
+ return frameSize.width == other.frameSize.width &&
30
+ frameSize.height == other.frameSize.height;
31
+ }
32
+
33
+ bool operator!=(const PCSegmentedControlStateFrameSize& other) const {
34
+ return !(*this == other);
35
+ }
36
+
37
+ #ifdef RN_SERIALIZABLE_STATE
38
+ // Required for Android state serialization
39
+ PCSegmentedControlStateFrameSize(
40
+ const PCSegmentedControlStateFrameSize& previousState,
41
+ folly::dynamic data)
42
+ : frameSize(previousState.frameSize) {
43
+ // Parse frame size from dynamic data if provided
44
+ if (data.isObject()) {
45
+ if (data.count("width") && data.count("height")) {
46
+ frameSize.width = static_cast<Float>(data["width"].asDouble());
47
+ frameSize.height = static_cast<Float>(data["height"].asDouble());
48
+ }
49
+ }
50
+ }
51
+
52
+ folly::dynamic getDynamic() const {
53
+ return folly::dynamic::object("width", frameSize.width)("height", frameSize.height);
54
+ }
55
+
56
+ MapBuffer getMapBuffer() const {
57
+ return MapBufferBuilder::EMPTY();
58
+ }
59
+ #endif
60
+ };
61
+
62
+ } // namespace facebook::react
@@ -7,3 +7,4 @@
7
7
  // Include our custom component descriptors which use measuring shadow nodes
8
8
  #include "PCSelectionMenuComponentDescriptors-custom.h"
9
9
  #include "PCDatePickerComponentDescriptors-custom.h"
10
+ #include "PCSegmentedControlComponentDescriptors-custom.h"