CheckBox.android.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. * @flow strict-local
  8. * @format
  9. */
  10. 'use strict';
  11. const React = require('react');
  12. const StyleSheet = require('../../StyleSheet/StyleSheet');
  13. const invariant = require('invariant');
  14. const processColor = require('../../StyleSheet/processColor');
  15. const nullthrows = require('nullthrows');
  16. const setAndForwardRef = require('../../Utilities/setAndForwardRef');
  17. import AndroidCheckBoxNativeComponent, {
  18. Commands as AndroidCheckBoxCommands,
  19. } from './AndroidCheckBoxNativeComponent';
  20. import type {ViewProps} from '../View/ViewPropTypes';
  21. import type {SyntheticEvent} from '../../Types/CoreEventTypes';
  22. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  23. type CheckBoxEvent = SyntheticEvent<
  24. $ReadOnly<{|
  25. target: number,
  26. value: boolean,
  27. |}>,
  28. >;
  29. type CommonProps = $ReadOnly<{|
  30. ...ViewProps,
  31. /**
  32. * Used in case the props change removes the component.
  33. */
  34. onChange?: ?(event: CheckBoxEvent) => mixed,
  35. /**
  36. * Invoked with the new value when the value changes.
  37. */
  38. onValueChange?: ?(value: boolean) => mixed,
  39. /**
  40. * Used to locate this view in end-to-end tests.
  41. */
  42. testID?: ?string,
  43. |}>;
  44. type Props = $ReadOnly<{|
  45. ...CommonProps,
  46. /**
  47. * The value of the checkbox. If true the checkbox will be turned on.
  48. * Default value is false.
  49. */
  50. value?: ?boolean,
  51. /**
  52. * If true the user won't be able to toggle the checkbox.
  53. * Default value is false.
  54. */
  55. disabled?: ?boolean,
  56. /**
  57. * Used to get the ref for the native checkbox
  58. */
  59. forwardedRef?: ?React.Ref<typeof AndroidCheckBoxNativeComponent>,
  60. /**
  61. * Controls the colors the checkbox has in checked and unchecked states.
  62. */
  63. tintColors?: {|true?: ?ColorValue, false?: ?ColorValue|},
  64. |}>;
  65. /**
  66. * Renders a boolean input (Android only).
  67. *
  68. * This is a controlled component that requires an `onValueChange` callback that
  69. * updates the `value` prop in order for the component to reflect user actions.
  70. * If the `value` prop is not updated, the component will continue to render
  71. * the supplied `value` prop instead of the expected result of any user actions.
  72. *
  73. * ```
  74. * import React from 'react';
  75. * import { AppRegistry, StyleSheet, Text, View, CheckBox } from 'react-native';
  76. *
  77. * export default class App extends React.Component {
  78. * constructor(props) {
  79. * super(props);
  80. * this.state = {
  81. * checked: false
  82. * }
  83. * }
  84. *
  85. * toggle() {
  86. * this.setState(({checked}) => {
  87. * return {
  88. * checked: !checked
  89. * };
  90. * });
  91. * }
  92. *
  93. * render() {
  94. * const {checked} = this.state;
  95. * return (
  96. * <View style={styles.container}>
  97. * <Text>Checked</Text>
  98. * <CheckBox value={checked} onChange={this.toggle.bind(this)} />
  99. * </View>
  100. * );
  101. * }
  102. * }
  103. *
  104. * const styles = StyleSheet.create({
  105. * container: {
  106. * flex: 1,
  107. * flexDirection: 'row',
  108. * alignItems: 'center',
  109. * justifyContent: 'center',
  110. * },
  111. * });
  112. *
  113. * // skip this line if using Create React Native App
  114. * AppRegistry.registerComponent('App', () => App);
  115. * ```
  116. *
  117. * @keyword checkbox
  118. * @keyword toggle
  119. */
  120. class CheckBox extends React.Component<Props> {
  121. _nativeRef: ?React.ElementRef<typeof AndroidCheckBoxNativeComponent> = null;
  122. _setNativeRef = setAndForwardRef({
  123. getForwardedRef: () => this.props.forwardedRef,
  124. setLocalRef: ref => {
  125. this._nativeRef = ref;
  126. },
  127. });
  128. _onChange = (event: CheckBoxEvent) => {
  129. const value = this.props.value ?? false;
  130. AndroidCheckBoxCommands.setNativeValue(nullthrows(this._nativeRef), value);
  131. // Change the props after the native props are set in case the props
  132. // change removes the component
  133. this.props.onChange && this.props.onChange(event);
  134. this.props.onValueChange &&
  135. this.props.onValueChange(event.nativeEvent.value);
  136. };
  137. _getTintColors(tintColors) {
  138. if (tintColors) {
  139. const processedTextColorTrue = processColor(tintColors.true);
  140. invariant(
  141. processedTextColorTrue == null ||
  142. typeof processedTextColorTrue === 'number',
  143. 'Unexpected color given for tintColors.true',
  144. );
  145. const processedTextColorFalse = processColor(tintColors.true);
  146. invariant(
  147. processedTextColorFalse == null ||
  148. typeof processedTextColorFalse === 'number',
  149. 'Unexpected color given for tintColors.false',
  150. );
  151. return {
  152. true: processedTextColorTrue,
  153. false: processedTextColorFalse,
  154. };
  155. } else {
  156. return undefined;
  157. }
  158. }
  159. render() {
  160. const {
  161. disabled: _,
  162. value: __,
  163. tintColors,
  164. style,
  165. forwardedRef,
  166. ...props
  167. } = this.props;
  168. const disabled = this.props.disabled ?? false;
  169. const value = this.props.value ?? false;
  170. const nativeProps = {
  171. ...props,
  172. onStartShouldSetResponder: () => true,
  173. onResponderTerminationRequest: () => false,
  174. enabled: !disabled,
  175. on: value,
  176. tintColors: this._getTintColors(tintColors),
  177. style: [styles.rctCheckBox, style],
  178. };
  179. return (
  180. <AndroidCheckBoxNativeComponent
  181. {...nativeProps}
  182. ref={this._setNativeRef}
  183. onChange={this._onChange}
  184. />
  185. );
  186. }
  187. }
  188. const styles = StyleSheet.create({
  189. rctCheckBox: {
  190. height: 32,
  191. width: 32,
  192. },
  193. });
  194. type CheckBoxType = React.AbstractComponent<
  195. Props,
  196. React.ElementRef<typeof AndroidCheckBoxNativeComponent>,
  197. >;
  198. const CheckBoxWithRef = React.forwardRef<
  199. Props,
  200. React.ElementRef<typeof AndroidCheckBoxNativeComponent>,
  201. >(function CheckBoxWithRef(props, ref) {
  202. return <CheckBox {...props} forwardedRef={ref} />;
  203. });
  204. module.exports = (CheckBoxWithRef: CheckBoxType);