229 lines
6.1 KiB
JavaScript
229 lines
6.1 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow strict-local
|
|
* @format
|
|
* @generate-docs
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import Platform from '../../Utilities/Platform';
|
|
import * as React from 'react';
|
|
import StyleSheet from '../../StyleSheet/StyleSheet';
|
|
|
|
import AndroidSwitchNativeComponent, {
|
|
Commands as AndroidSwitchCommands,
|
|
} from './AndroidSwitchNativeComponent';
|
|
import SwitchNativeComponent, {
|
|
Commands as SwitchCommands,
|
|
} from './SwitchNativeComponent';
|
|
|
|
import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
|
|
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
|
|
import type {ViewProps} from '../View/ViewPropTypes';
|
|
|
|
type SwitchChangeEvent = SyntheticEvent<
|
|
$ReadOnly<{|
|
|
value: boolean,
|
|
|}>,
|
|
>;
|
|
|
|
export type Props = $ReadOnly<{|
|
|
...ViewProps,
|
|
|
|
/**
|
|
* Whether the switch is disabled. Defaults to false.
|
|
*/
|
|
disabled?: ?boolean,
|
|
|
|
/**
|
|
* Boolean value of the switch. Defaults to false.
|
|
*/
|
|
value?: ?boolean,
|
|
|
|
/**
|
|
* Custom color for the switch thumb.
|
|
*/
|
|
thumbColor?: ?ColorValue,
|
|
|
|
/**
|
|
* Custom colors for the switch track.
|
|
*
|
|
* NOTE: On iOS when the switch value is false, the track shrinks into the
|
|
* border. If you want to change the color of the background exposed by the
|
|
* shrunken track, use `ios_backgroundColor`.
|
|
*/
|
|
trackColor?: ?$ReadOnly<{|
|
|
false?: ?ColorValue,
|
|
true?: ?ColorValue,
|
|
|}>,
|
|
|
|
/**
|
|
* On iOS, custom color for the background. This background color can be seen
|
|
* either when the switch value is false or when the switch is disabled (and
|
|
* the switch is translucent).
|
|
*/
|
|
ios_backgroundColor?: ?ColorValue,
|
|
|
|
/**
|
|
* Called when the user tries to change the value of the switch.
|
|
*
|
|
* Receives the change event as an argument. If you want to only receive the
|
|
* new value, use `onValueChange` instead.
|
|
*/
|
|
onChange?: ?(event: SwitchChangeEvent) => Promise<void> | void,
|
|
|
|
/**
|
|
* Called when the user tries to change the value of the switch.
|
|
*
|
|
* Receives the new value as an argument. If you want to instead receive an
|
|
* event, use `onChange`.
|
|
*/
|
|
onValueChange?: ?(value: boolean) => Promise<void> | void,
|
|
|}>;
|
|
|
|
/**
|
|
* A visual toggle between two mutually exclusive states.
|
|
*
|
|
* This is a controlled component that requires an `onValueChange` callback that
|
|
* updates the `value` prop in order for the component to reflect user actions.
|
|
* If the `value` prop is not updated, the component will continue to render the
|
|
* supplied `value` prop instead of the expected result of any user actions.
|
|
*/
|
|
class Switch extends React.Component<Props> {
|
|
_nativeSwitchRef: ?React.ElementRef<
|
|
typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
|
|
>;
|
|
_lastNativeValue: ?boolean;
|
|
|
|
render(): React.Node {
|
|
const {
|
|
disabled,
|
|
ios_backgroundColor,
|
|
onChange,
|
|
onValueChange,
|
|
style,
|
|
thumbColor,
|
|
trackColor,
|
|
value,
|
|
...props
|
|
} = this.props;
|
|
|
|
const trackColorForFalse = trackColor?.false;
|
|
const trackColorForTrue = trackColor?.true;
|
|
|
|
if (Platform.OS === 'android') {
|
|
const platformProps = {
|
|
enabled: disabled !== true,
|
|
on: value === true,
|
|
style,
|
|
thumbTintColor: thumbColor,
|
|
trackColorForFalse: trackColorForFalse,
|
|
trackColorForTrue: trackColorForTrue,
|
|
trackTintColor: value === true ? trackColorForTrue : trackColorForFalse,
|
|
};
|
|
|
|
return (
|
|
<AndroidSwitchNativeComponent
|
|
{...props}
|
|
{...platformProps}
|
|
accessibilityRole={props.accessibilityRole ?? 'switch'}
|
|
onChange={this._handleChange}
|
|
onResponderTerminationRequest={returnsFalse}
|
|
onStartShouldSetResponder={returnsTrue}
|
|
ref={this._handleSwitchNativeComponentRef}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const platformProps = {
|
|
disabled,
|
|
onTintColor: trackColorForTrue,
|
|
style: StyleSheet.compose(
|
|
{height: 31, width: 51},
|
|
StyleSheet.compose(
|
|
style,
|
|
ios_backgroundColor == null
|
|
? null
|
|
: {
|
|
backgroundColor: ios_backgroundColor,
|
|
borderRadius: 16,
|
|
},
|
|
),
|
|
),
|
|
thumbTintColor: thumbColor,
|
|
tintColor: trackColorForFalse,
|
|
value: value === true,
|
|
};
|
|
|
|
return (
|
|
<SwitchNativeComponent
|
|
{...props}
|
|
{...platformProps}
|
|
accessibilityRole={props.accessibilityRole ?? 'switch'}
|
|
onChange={this._handleChange}
|
|
onResponderTerminationRequest={returnsFalse}
|
|
onStartShouldSetResponder={returnsTrue}
|
|
ref={this._handleSwitchNativeComponentRef}
|
|
/>
|
|
);
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
// This is necessary in case native updates the switch and JS decides
|
|
// that the update should be ignored and we should stick with the value
|
|
// that we have in JS.
|
|
const nativeProps = {};
|
|
const value = this.props.value === true;
|
|
|
|
if (this._lastNativeValue !== value) {
|
|
nativeProps.value = value;
|
|
}
|
|
|
|
if (
|
|
Object.keys(nativeProps).length > 0 &&
|
|
this._nativeSwitchRef &&
|
|
this._nativeSwitchRef.setNativeProps
|
|
) {
|
|
if (Platform.OS === 'android') {
|
|
AndroidSwitchCommands.setNativeValue(
|
|
this._nativeSwitchRef,
|
|
nativeProps.value,
|
|
);
|
|
} else {
|
|
SwitchCommands.setValue(this._nativeSwitchRef, nativeProps.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
_handleChange = (event: SwitchChangeEvent) => {
|
|
if (this.props.onChange != null) {
|
|
this.props.onChange(event);
|
|
}
|
|
|
|
if (this.props.onValueChange != null) {
|
|
this.props.onValueChange(event.nativeEvent.value);
|
|
}
|
|
|
|
this._lastNativeValue = event.nativeEvent.value;
|
|
this.forceUpdate();
|
|
};
|
|
|
|
_handleSwitchNativeComponentRef = (
|
|
ref: ?React.ElementRef<
|
|
typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
|
|
>,
|
|
) => {
|
|
this._nativeSwitchRef = ref;
|
|
};
|
|
}
|
|
|
|
const returnsFalse = () => false;
|
|
const returnsTrue = () => true;
|
|
|
|
module.exports = Switch;
|