PickerIOS.ios.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow strict-local
  9. */
  10. // This is a controlled component version of RCTPickerIOS.
  11. 'use strict';
  12. const React = require('react');
  13. const StyleSheet = require('../../StyleSheet/StyleSheet');
  14. const View = require('../View/View');
  15. const invariant = require('invariant');
  16. const processColor = require('../../StyleSheet/processColor');
  17. import RCTPickerNativeComponent, {
  18. Commands as PickerCommands,
  19. } from './RCTPickerNativeComponent';
  20. import type {TextStyleProp} from '../../StyleSheet/StyleSheet';
  21. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  22. import type {ProcessedColorValue} from '../../StyleSheet/processColor';
  23. import type {SyntheticEvent} from '../../Types/CoreEventTypes';
  24. import type {ViewProps} from '../View/ViewPropTypes';
  25. type PickerIOSChangeEvent = SyntheticEvent<
  26. $ReadOnly<{|
  27. newValue: number | string,
  28. newIndex: number,
  29. |}>,
  30. >;
  31. type RCTPickerIOSItemType = $ReadOnly<{|
  32. label: ?Label,
  33. value: ?(number | string),
  34. textColor: ?ProcessedColorValue,
  35. |}>;
  36. type Label = Stringish | number;
  37. type Props = $ReadOnly<{|
  38. ...ViewProps,
  39. children: React.ChildrenArray<React.Element<typeof PickerIOSItem>>,
  40. itemStyle?: ?TextStyleProp,
  41. onChange?: ?(event: PickerIOSChangeEvent) => mixed,
  42. onValueChange?: ?(itemValue: string | number, itemIndex: number) => mixed,
  43. selectedValue: ?(number | string),
  44. accessibilityLabel?: ?string,
  45. |}>;
  46. type State = {|
  47. selectedIndex: number,
  48. items: $ReadOnlyArray<RCTPickerIOSItemType>,
  49. |};
  50. type ItemProps = $ReadOnly<{|
  51. label: ?Label,
  52. value?: ?(number | string),
  53. color?: ?ColorValue,
  54. |}>;
  55. const PickerIOSItem = (props: ItemProps): null => {
  56. return null;
  57. };
  58. class PickerIOS extends React.Component<Props, State> {
  59. _picker: ?React.ElementRef<typeof RCTPickerNativeComponent> = null;
  60. _lastNativeValue: ?number;
  61. state: State = {
  62. selectedIndex: 0,
  63. items: [],
  64. };
  65. static Item: (props: ItemProps) => null = PickerIOSItem;
  66. static getDerivedStateFromProps(props: Props): State {
  67. let selectedIndex = 0;
  68. const items = [];
  69. React.Children.toArray(props.children)
  70. .filter(child => child !== null)
  71. .forEach(function(child, index) {
  72. if (child.props.value === props.selectedValue) {
  73. selectedIndex = index;
  74. }
  75. const processedTextColor = processColor(child.props.color);
  76. invariant(
  77. processedTextColor == null || typeof processedTextColor === 'number',
  78. 'Unexpected color given for PickerIOSItem color',
  79. );
  80. items.push({
  81. value: child.props.value,
  82. label: child.props.label,
  83. textColor: processedTextColor,
  84. });
  85. });
  86. return {selectedIndex, items};
  87. }
  88. render(): React.Node {
  89. return (
  90. <View style={this.props.style}>
  91. <RCTPickerNativeComponent
  92. ref={picker => {
  93. this._picker = picker;
  94. }}
  95. testID={this.props.testID}
  96. style={[styles.pickerIOS, this.props.itemStyle]}
  97. items={this.state.items}
  98. selectedIndex={this.state.selectedIndex}
  99. onChange={this._onChange}
  100. accessibilityLabel={this.props.accessibilityLabel}
  101. />
  102. </View>
  103. );
  104. }
  105. componentDidUpdate() {
  106. // This is necessary in case native updates the picker and JS decides
  107. // that the update should be ignored and we should stick with the value
  108. // that we have in JS.
  109. if (
  110. this._picker &&
  111. this._lastNativeValue !== undefined &&
  112. this._lastNativeValue !== this.state.selectedIndex
  113. ) {
  114. PickerCommands.setNativeSelectedIndex(
  115. this._picker,
  116. this.state.selectedIndex,
  117. );
  118. }
  119. }
  120. _onChange = event => {
  121. if (this.props.onChange) {
  122. this.props.onChange(event);
  123. }
  124. if (this.props.onValueChange) {
  125. this.props.onValueChange(
  126. event.nativeEvent.newValue,
  127. event.nativeEvent.newIndex,
  128. );
  129. }
  130. this._lastNativeValue = event.nativeEvent.newIndex;
  131. this.forceUpdate();
  132. };
  133. }
  134. const styles = StyleSheet.create({
  135. pickerIOS: {
  136. // The picker will conform to whatever width is given, but we do
  137. // have to set the component's height explicitly on the
  138. // surrounding view to ensure it gets rendered.
  139. height: 216,
  140. },
  141. });
  142. module.exports = PickerIOS;