PickerAndroid.android.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. 'use strict';
  11. import AndroidDropdownPickerNativeComponent, {
  12. Commands as AndroidDropdownPickerCommands,
  13. } from './AndroidDropdownPickerNativeComponent';
  14. import AndroidDialogPickerNativeComponent, {
  15. Commands as AndroidDialogPickerCommands,
  16. } from './AndroidDialogPickerNativeComponent';
  17. import * as React from 'react';
  18. import StyleSheet from '../../StyleSheet/StyleSheet';
  19. import invariant from 'invariant';
  20. import processColor from '../../StyleSheet/processColor';
  21. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  22. import type {SyntheticEvent} from '../../Types/CoreEventTypes';
  23. import type {TextStyleProp} from '../../StyleSheet/StyleSheet';
  24. type PickerItemSelectSyntheticEvent = SyntheticEvent<
  25. $ReadOnly<{|
  26. position: number,
  27. |}>,
  28. >;
  29. type PickerItemValue = number | string;
  30. type Props = $ReadOnly<{|
  31. accessibilityLabel?: ?Stringish,
  32. children?: React.Node,
  33. style?: ?TextStyleProp,
  34. backgroundColor?: ?ColorValue,
  35. selectedValue?: ?PickerItemValue,
  36. enabled?: ?boolean,
  37. mode?: ?('dialog' | 'dropdown'),
  38. onValueChange?: ?(itemValue: ?PickerItemValue, itemIndex: number) => mixed,
  39. prompt?: ?string,
  40. testID?: string,
  41. |}>;
  42. /**
  43. * Not exposed as a public API - use <Picker> instead.
  44. */
  45. function PickerAndroid(props: Props): React.Node {
  46. const pickerRef = React.useRef(null);
  47. const [items, selected] = React.useMemo(() => {
  48. // eslint-disable-next-line no-shadow
  49. let selected = 0;
  50. // eslint-disable-next-line no-shadow
  51. const items = React.Children.map(props.children, (child, index) => {
  52. if (child === null) {
  53. return null;
  54. }
  55. if (child.props.value === props.selectedValue) {
  56. selected = index;
  57. }
  58. const {color, label} = child.props;
  59. const processedColor = processColor(color);
  60. invariant(
  61. processedColor == null || typeof processedColor === 'number',
  62. 'Unexpected color given for PickerAndroid color prop',
  63. );
  64. return {
  65. color: color == null ? null : processedColor,
  66. label,
  67. };
  68. });
  69. return [items, selected];
  70. }, [props.children, props.selectedValue]);
  71. const onSelect = React.useCallback(
  72. ({nativeEvent}: PickerItemSelectSyntheticEvent) => {
  73. const {position} = nativeEvent;
  74. const onValueChange = props.onValueChange;
  75. if (onValueChange != null) {
  76. if (position >= 0) {
  77. const children = React.Children.toArray(props.children).filter(
  78. item => item != null,
  79. );
  80. const value = children[position].props.value;
  81. if (props.selectedValue !== value) {
  82. onValueChange(value, position);
  83. }
  84. } else {
  85. onValueChange(null, position);
  86. }
  87. }
  88. const {current} = pickerRef;
  89. if (current != null && position !== selected) {
  90. const Commands =
  91. props.mode === 'dropdown'
  92. ? AndroidDropdownPickerCommands
  93. : AndroidDialogPickerCommands;
  94. Commands.setNativeSelectedPosition(current, selected);
  95. }
  96. },
  97. [
  98. props.children,
  99. props.onValueChange,
  100. props.selectedValue,
  101. props.mode,
  102. selected,
  103. ],
  104. );
  105. const rootProps = {
  106. accessibilityLabel: props.accessibilityLabel,
  107. enabled: props.enabled,
  108. items,
  109. onSelect,
  110. prompt: props.prompt,
  111. ref: pickerRef,
  112. selected,
  113. style: StyleSheet.compose(
  114. styles.pickerAndroid,
  115. props.style,
  116. ),
  117. backgroundColor: props.backgroundColor,
  118. testID: props.testID,
  119. };
  120. return props.mode === 'dropdown' ? (
  121. <AndroidDropdownPickerNativeComponent {...rootProps} />
  122. ) : (
  123. <AndroidDialogPickerNativeComponent {...rootProps} />
  124. );
  125. }
  126. const styles = StyleSheet.create({
  127. pickerAndroid: {
  128. // The picker will conform to whatever width is given, but we do
  129. // have to set the component's height explicitly on the
  130. // surrounding view to ensure it gets rendered.
  131. // TODO would be better to export a native constant for this,
  132. // like in iOS the RCTDatePickerManager.m
  133. height: 50,
  134. },
  135. });
  136. module.exports = PickerAndroid;