UNPKG

react-native-tvos

Version:
174 lines (150 loc) 4.76 kB
/** * Copyright (c) Meta Platforms, Inc. and 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 */ import type {HostInstance} from '../../../src/private/types/HostInstance'; import type {ViewProps} from './ViewPropTypes'; import TextAncestorContext from '../../Text/TextAncestorContext'; import Platform from '../../Utilities/Platform'; import useMergeRefs from '../../Utilities/useMergeRefs'; import ViewNativeComponent from './ViewNativeComponent'; import {Commands} from './ViewNativeComponent'; import * as React from 'react'; import {use} from 'react'; /** * The most fundamental component for building a UI, View is a container that * supports layout with flexbox, style, some touch handling, and accessibility * controls. * * @see https://reactnative.dev/docs/view */ component View( ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>, ...props: ViewProps ) { const hasTextAncestor = use(TextAncestorContext); const viewRef = React.useRef<?React.ElementRef<typeof View>>(null); const requestTVFocus = React.useCallback(() => { if (viewRef.current) { Commands.requestTVFocus(viewRef.current); } }, []); const setLocalRef = React.useCallback( (instance: HostInstance | null) => { // $FlowExpectedError[incompatible-type] viewRef.current = instance; if (instance != null) { // $FlowFixMe[prop-missing] // $FlowFixMe[unsafe-object-assign] Object.assign(instance, { requestTVFocus, }); } }, [requestTVFocus], ); const mergedRef = useMergeRefs(setLocalRef, ref); const { accessibilityState, accessibilityValue, isTVSelectable, focusable, 'aria-busy': ariaBusy, 'aria-checked': ariaChecked, 'aria-disabled': ariaDisabled, 'aria-expanded': ariaExpanded, 'aria-hidden': ariaHidden, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-live': ariaLive, 'aria-selected': ariaSelected, 'aria-valuemax': ariaValueMax, 'aria-valuemin': ariaValueMin, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText, id, tabIndex, ...otherProps } = props; // Since we destructured props, we can now treat it as mutable const processedProps = otherProps as {...ViewProps}; const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g); if (parsedAriaLabelledBy !== undefined) { processedProps.accessibilityLabelledBy = parsedAriaLabelledBy; } if (ariaLabel !== undefined) { processedProps.accessibilityLabel = ariaLabel; } if (ariaLive !== undefined) { processedProps.accessibilityLiveRegion = ariaLive === 'off' ? 'none' : ariaLive; } if (ariaHidden !== undefined) { processedProps.accessibilityElementsHidden = ariaHidden; if (ariaHidden === true) { processedProps.importantForAccessibility = 'no-hide-descendants'; } } if (id !== undefined) { processedProps.nativeID = id; } if (tabIndex !== undefined) { processedProps.focusable = !tabIndex; } if ( accessibilityState != null || ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null ) { processedProps.accessibilityState = { busy: ariaBusy ?? accessibilityState?.busy, checked: ariaChecked ?? accessibilityState?.checked, disabled: ariaDisabled ?? accessibilityState?.disabled, expanded: ariaExpanded ?? accessibilityState?.expanded, selected: ariaSelected ?? accessibilityState?.selected, }; } if ( accessibilityValue != null || ariaValueMax != null || ariaValueMin != null || ariaValueNow != null || ariaValueText != null ) { processedProps.accessibilityValue = { max: ariaValueMax ?? accessibilityValue?.max, min: ariaValueMin ?? accessibilityValue?.min, now: ariaValueNow ?? accessibilityValue?.now, text: ariaValueText ?? accessibilityValue?.text, }; } if (Platform.OS === 'ios') { processedProps.isTVSelectable = focusable ?? isTVSelectable ?? false; delete processedProps.focusable; } else { processedProps.focusable = focusable ?? false; delete processedProps.isTVSelectable; } const actualView = ref == null ? ( <ViewNativeComponent {...processedProps} /> ) : ( <ViewNativeComponent {...processedProps} ref={mergedRef} /> ); if (hasTextAncestor) { return ( <TextAncestorContext value={false}>{actualView}</TextAncestorContext> ); } return actualView; } View.displayName = 'View'; export default View;