From c283cc4b321f19d3f12253954e740f044d56aab6 Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Wed, 22 Jan 2025 14:44:44 +0200 Subject: [PATCH 01/49] Remove old dialog --- .../componentScreens/ActionSheetScreen.tsx | 2 - .../componentScreens/DateTimePickerScreen.tsx | 5 - .../IncubatorExpandableOverlayScreen.tsx | 1 - src/components/actionSheet/index.tsx | 61 +--- .../dateTimePicker/__tests__/index.spec.tsx | 1 - src/components/dateTimePicker/index.tsx | 14 +- .../dialog/DialogDismissibleView.tsx | 262 --------------- .../dialog/OverlayFadingBackground.tsx | 63 ---- src/components/dialog/dialog.api.json | 60 ---- src/components/dialog/index.tsx | 301 ------------------ .../picker/__tests__/index.spec.tsx | 2 +- src/components/picker/index.tsx | 1 - src/incubator/dialog/index.tsx | 4 +- src/incubator/dialog/types.ts | 23 -- .../__tests__/index.spec.tsx | 2 +- src/incubator/expandableOverlay/index.tsx | 10 +- src/index.ts | 2 +- 17 files changed, 22 insertions(+), 792 deletions(-) delete mode 100644 src/components/dialog/DialogDismissibleView.tsx delete mode 100644 src/components/dialog/OverlayFadingBackground.tsx delete mode 100644 src/components/dialog/dialog.api.json delete mode 100644 src/components/dialog/index.tsx diff --git a/demo/src/screens/componentScreens/ActionSheetScreen.tsx b/demo/src/screens/componentScreens/ActionSheetScreen.tsx index 8ad4320d64..a9f2dedf8f 100644 --- a/demo/src/screens/componentScreens/ActionSheetScreen.tsx +++ b/demo/src/screens/componentScreens/ActionSheetScreen.tsx @@ -64,7 +64,6 @@ export default class ActionSheetScreen extends Component { cancelButtonIndex={3} destructiveButtonIndex={0} useNativeIOS={false} - migrateDialog options={[ {label: 'option 1', onPress: () => this.pickOption('option 1')}, {label: 'option 2', onPress: () => this.pickOption('option 2')}, @@ -80,7 +79,6 @@ export default class ActionSheetScreen extends Component { message={'Message of action sheet'} cancelButtonIndex={3} destructiveButtonIndex={0} - migrateDialog options={[ {label: 'option 1', onPress: () => this.pickOption('option 1'), iconSource: collectionsIcon}, {label: 'option 2', onPress: () => this.pickOption('option 2'), iconSource: shareIcon}, diff --git a/demo/src/screens/componentScreens/DateTimePickerScreen.tsx b/demo/src/screens/componentScreens/DateTimePickerScreen.tsx index 24d0b0d39e..eb2e12b0e3 100644 --- a/demo/src/screens/componentScreens/DateTimePickerScreen.tsx +++ b/demo/src/screens/componentScreens/DateTimePickerScreen.tsx @@ -62,7 +62,6 @@ export default class DateTimePickerScreen extends Component<{}, State> { Date Time Picker { // value={new Date('October 13, 2014')} /> { Disabled { console.warn('Dialog is dismissed')}} - migrateDialog > {this.renderColorRow(selectedColor)} diff --git a/src/components/actionSheet/index.tsx b/src/components/actionSheet/index.tsx index 155a132f90..dd5947a2e2 100644 --- a/src/components/actionSheet/index.tsx +++ b/src/components/actionSheet/index.tsx @@ -3,7 +3,6 @@ import React, {Component} from 'react'; import {ActionSheetIOS, StyleSheet, StyleProp, ViewStyle, ImageProps, ImageSourcePropType} from 'react-native'; import {Colors} from '../../style'; import {asBaseComponent, Constants} from '../../commons/new'; -import Dialog, {DialogProps} from '../dialog'; import View from '../view'; import Text from '../text'; import {ButtonProps} from '../button'; @@ -11,17 +10,12 @@ import Image from '../image'; //@ts-ignore import ListItem from '../listItem'; import PanningProvider from '../panningViews/panningProvider'; -import {Dialog as IncubatorDialog, DialogProps as IncubatorDialogProps} from '../../incubator'; -import {LogService} from '../../services'; +import {Dialog, DialogProps} from '../../incubator'; const VERTICAL_PADDING = 8; type ActionSheetOnOptionPress = (index: number) => void; type ActionSheetProps = { - /** - * Migrate to the Incubator.Dialog component - */ - migrateDialog?: boolean; /** * Whether to show the action sheet or not */ @@ -81,11 +75,6 @@ type ActionSheetProps = { * Note: you will need to call onOptionPress so the option's onPress will be called */ renderAction?: (option: ButtonProps, index: number, onOptionPress: ActionSheetOnOptionPress) => JSX.Element; - /** - * @deprecated - * Called once the modal has been dismissed completely - */ - onModalDismissed?: DialogProps['onDialogDismissed']; /** * Whether or not to handle SafeArea */ @@ -96,7 +85,7 @@ type ActionSheetProps = { dialogProps?: Omit< DialogProps, 'useSafeArea' | 'testID' | 'containerStyle' | 'visible' | 'onDismiss' | 'onDialogDismissed' - > | IncubatorDialogProps; + > | DialogProps; /** * testID for e2e tests */ @@ -220,42 +209,11 @@ class ActionSheet extends Component { ); } - renderOldDialog() { - const {useNativeIOS, visible, onDismiss, dialogStyle, onModalDismissed, testID, useSafeArea, dialogProps} = - this.props; - - if (Constants.isIOS && useNativeIOS) { - return null; - } + render() { + const {visible, onDismiss, dialogStyle, testID, useSafeArea, dialogProps} = this.props; return ( - {this.renderSheet()} - - ); - } - - renderNewDialog() { - const {visible, onDismiss, dialogStyle, onModalDismissed, testID, useSafeArea, dialogProps} = this.props; - - if (onModalDismissed) { - LogService.deprecationWarn({component: 'ActionSheet', oldProp: 'onModalDismissed', newProp: 'onDismiss'}); - } - - return ( - { onDismiss={onDismiss} > {this.renderSheet()} - + ); } - - render() { - const {migrateDialog} = this.props; - if (migrateDialog) { - return this.renderNewDialog(); - } else { - return this.renderOldDialog(); - } - } } export default asBaseComponent(ActionSheet); diff --git a/src/components/dateTimePicker/__tests__/index.spec.tsx b/src/components/dateTimePicker/__tests__/index.spec.tsx index a05ac7266b..51ab30d52c 100644 --- a/src/components/dateTimePicker/__tests__/index.spec.tsx +++ b/src/components/dateTimePicker/__tests__/index.spec.tsx @@ -10,7 +10,6 @@ const someDateNextHour = new Date(someDate.getTime() + 60 * 60 * 1000); const TestCase = (props: Partial>) => { const defaultProps: DateTimePickerProps = { value: someDate, - migrateDialog: true, testID }; return ; diff --git a/src/components/dateTimePicker/index.tsx b/src/components/dateTimePicker/index.tsx index 71bc5d83e9..582ea71c90 100644 --- a/src/components/dateTimePicker/index.tsx +++ b/src/components/dateTimePicker/index.tsx @@ -15,8 +15,7 @@ import {Colors} from '../../style'; import Assets from '../../assets'; import {Constants, asBaseComponent, BaseComponentInjectedProps} from '../../commons/new'; import TextField, {TextFieldProps, TextFieldMethods} from '../textField'; -import type {DialogMigrationProps} from '../../incubator/dialog'; -import {DialogProps} from '../dialog'; +import type {DialogProps} from '../../incubator/dialog'; import View from '../view'; import Button, {ButtonProps} from '../button'; import ExpandableOverlay, {ExpandableOverlayMethods, RenderCustomOverlayProps} from '../../incubator/expandableOverlay'; @@ -27,8 +26,7 @@ import {LogService} from 'services'; export type DateTimePickerMode = 'date' | 'time'; export type DateTimePickerProps = OldApiProps & - Omit & - DialogMigrationProps & { + Omit & { /** * The type of picker to display ('date' or 'time') */ @@ -37,6 +35,10 @@ export type DateTimePickerProps = OldApiProps & * The initial value to set the picker to. Defaults to device's date / time */ value?: Date; + /** + * The props to pass to the dialog expandable container + */ + dialogProps?: DialogProps; /** * The onChange callback */ @@ -145,7 +147,6 @@ const DateTimePicker = forwardRef((props: DateTimePickerPropsInternal, ref: Forw themeVariant = Colors.getScheme(), onChange, dialogProps, - migrateDialog, textColor = Colors.$textDefault, backgroundColor = Colors.$backgroundDefault, headerStyle, @@ -192,7 +193,7 @@ const DateTimePicker = forwardRef((props: DateTimePickerPropsInternal, ref: Forw 'landscape', 'landscape-left', 'landscape-right' - ] as DialogProps['supportedOrientations'], + ], ...dialogProps }; }, [dialogProps, testID]); @@ -339,7 +340,6 @@ const DateTimePicker = forwardRef((props: DateTimePickerPropsInternal, ref: Forw expandableContent={Constants.isIOS ? renderIOSExpandableOverlay() : undefined} useDialog dialogProps={_dialogProps} - migrateDialog={migrateDialog} disabled={editable === false} // NOTE: Android picker comes with its own overlay built-in therefor we're not using ExpandableOverlay for it renderCustomOverlay={Constants.isAndroid ? renderAndroidDateTimePicker : undefined} diff --git a/src/components/dialog/DialogDismissibleView.tsx b/src/components/dialog/DialogDismissibleView.tsx deleted file mode 100644 index a6db019395..0000000000 --- a/src/components/dialog/DialogDismissibleView.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import _ from 'lodash'; -import React, {useEffect, useRef, useCallback, useContext} from 'react'; -import {Animated, Easing, StyleSheet, StyleProp, ViewStyle, LayoutChangeEvent} from 'react-native'; -import {Constants} from '../../commons/new'; -import View from '../view'; -import PanningContext from '../panningViews/panningContext'; -import PanningProvider, { - PanningDirections, - PanAmountsProps, - PanDirectionsProps, - PanLocationProps -} from '../panningViews/panningProvider'; -import PanResponderView from '../panningViews/panResponderView'; - -const MAXIMUM_DRAGS_AFTER_SWIPE = 2; - -// TODO: move this to panningContext -interface PanContextProps { - isPanning: boolean; - dragDeltas: PanAmountsProps; - swipeDirections: PanDirectionsProps; -} - -interface DialogDismissibleProps { - /** - * Additional styling - */ - style?: StyleProp; - /** - * The direction of the allowed pan (default is DOWN) - * Types: UP, DOWN, LEFT and RIGHT (using PanningProvider.Directions.###) - */ - direction?: PanningDirections; - /** - * onDismiss callback - */ - onDismiss?: () => void; - /** - * The dialog`s container style - */ - containerStyle?: StyleProp; - /** - * Whether to show the dialog or not - */ - visible?: boolean; -} - -interface Props extends DialogDismissibleProps { - children?: React.ReactNode | React.ReactNode[]; -} - -interface LocationProps { - left: number; - top: number; -} - -const DEFAULT_DIRECTION = PanningProvider.Directions.DOWN; - -const DialogDismissibleView = (props: Props) => { - const { - direction = DEFAULT_DIRECTION, - visible: propsVisible, - containerStyle, - style, - children, - onDismiss = () => {} - } = props; - // @ts-expect-error - const {isPanning, dragDeltas, swipeDirections} = useContext(PanningContext); - - const width = useRef(Constants.screenWidth); - const height = useRef(Constants.screenHeight); - const TOP_INSET = useRef(Constants.isIphoneX ? Constants.getSafeAreaInsets().top : Constants.isIOS ? 20 : 0); - const BOTTOM_INSET = useRef(Constants.isIphoneX ? Constants.getSafeAreaInsets().bottom : Constants.isIOS ? 20 : 0); - const thresholdX = useRef(0); - const thresholdY = useRef(0); - const dragsCounter = useRef(0); - const containerRef = useRef(); - const animatedValue = useRef(new Animated.Value(0)); - const mutableSwipeDirections = useRef({}); - const prevDragDeltas = useRef(); - const prevSwipeDirections = useRef(); - const visible = useRef(Boolean(propsVisible)); - - const getHiddenLocation = useCallback((left: number, top: number) => { - const result = {left: 0, top: 0}; - switch (direction) { - case PanningProvider.Directions.LEFT: - result.left = -left - width.current; - break; - case PanningProvider.Directions.RIGHT: - result.left = Constants.screenWidth - left; - break; - case PanningProvider.Directions.UP: - result.top = -top - height.current - TOP_INSET.current; - break; - case PanningProvider.Directions.DOWN: - default: - result.top = Constants.screenHeight - top + BOTTOM_INSET.current; - break; - } - - return result; - }, - [direction]); - - const hiddenLocation = useRef(getHiddenLocation(0, 0)); - - const animateTo = useCallback((toValue: number, animationEndCallback?: Animated.EndCallback) => { - Animated.timing(animatedValue.current, { - toValue, - duration: 300, - easing: Easing.bezier(0.2, 0, 0.35, 1), - useNativeDriver: true - }).start(animationEndCallback); - }, []); - - const isSwiping = useCallback(() => { - return !_.isUndefined(mutableSwipeDirections.current.x) || !_.isUndefined(mutableSwipeDirections.current.y); - }, []); - - const resetSwipe = useCallback(() => { - dragsCounter.current = 0; - mutableSwipeDirections.current = {}; - }, []); - - const onDrag = useCallback(() => { - if (isSwiping()) { - if (dragsCounter.current < MAXIMUM_DRAGS_AFTER_SWIPE) { - dragsCounter.current += 1; - } else { - resetSwipe(); - } - } - }, [isSwiping, resetSwipe]); - - const hide = useCallback(() => { - // TODO: test we're not animating? - animateTo(0, () => { - visible.current = false; - onDismiss?.(); - }); - }, [animateTo, onDismiss]); - - useEffect(() => { - if ( - isPanning && - (dragDeltas.x || dragDeltas.y) && - (dragDeltas.x !== prevDragDeltas.current?.x || dragDeltas.y !== prevDragDeltas.current?.y) - ) { - onDrag(); - prevDragDeltas.current = dragDeltas; - } - }, [isPanning, dragDeltas, onDrag, hide]); - - useEffect(() => { - if ( - isPanning && - (swipeDirections.x || swipeDirections.y) && - (swipeDirections.x !== prevSwipeDirections.current?.x || swipeDirections.y !== prevSwipeDirections.current?.y) - ) { - mutableSwipeDirections.current = swipeDirections; - } - }, [isPanning, swipeDirections, hide]); - - useEffect(() => { - if (visible.current && !propsVisible) { - hide(); - } - }, [propsVisible, hide]); - - const onLayout = useCallback((event: LayoutChangeEvent) => { - // DO NOT move the width\height into the measureInWindow - it causes errors with orientation change - const layout = event.nativeEvent.layout; - width.current = layout.width; - height.current = layout.height; - thresholdX.current = width.current / 2; - thresholdY.current = height.current / 2; - if (containerRef.current) { - // @ts-ignore TODO: can we fix this on ViewProps \ View? - containerRef.current.measureInWindow((x: number, y: number) => { - hiddenLocation.current = getHiddenLocation(x, y); - animateTo(1); - }); - } - }, - [getHiddenLocation, animateTo]); - - const getAnimationStyle = useCallback(() => { - return { - transform: [ - { - translateX: animatedValue.current.interpolate({ - inputRange: [0, 1], - outputRange: [hiddenLocation.current.left, 0] - }) - }, - { - translateY: animatedValue.current.interpolate({ - inputRange: [0, 1], - outputRange: [hiddenLocation.current.top, 0] - }) - } - ] - }; - }, []); - - const resetToShown = useCallback((left: number, top: number, direction: PanningDirections) => { - const toValue = - //@ts-expect-error - [PanningProvider.Directions.LEFT, PanningProvider.Directions.RIGHT].includes(direction) - ? 1 + left / hiddenLocation.current.left - : 1 + top / hiddenLocation.current.top; - - animateTo(toValue); - }, - [animateTo]); - - const onPanLocationChanged = useCallback(({left = 0, top = 0}: PanLocationProps) => { - const endValue = {x: Math.round(left), y: Math.round(top)}; - if (isSwiping()) { - hide(); - } else { - resetSwipe(); - if ( - (direction === PanningProvider.Directions.LEFT && endValue.x <= -thresholdX.current) || - (direction === PanningProvider.Directions.RIGHT && endValue.x >= thresholdX.current) || - (direction === PanningProvider.Directions.UP && endValue.y <= -thresholdY.current) || - (direction === PanningProvider.Directions.DOWN && endValue.y >= thresholdY.current) - ) { - hide(); - } else { - resetToShown(left, top, direction); - } - } - }, - [isSwiping, hide, resetSwipe, direction, resetToShown]); - - return ( - // @ts-ignore - - - {children} - - - ); -}; - -DialogDismissibleView.displayName = 'IGNORE'; - -export default DialogDismissibleView; - -const styles = StyleSheet.create({ - hidden: { - opacity: 0 - } -}); diff --git a/src/components/dialog/OverlayFadingBackground.tsx b/src/components/dialog/OverlayFadingBackground.tsx deleted file mode 100644 index 162d194220..0000000000 --- a/src/components/dialog/OverlayFadingBackground.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {useRef, useEffect, useCallback, useMemo} from 'react'; -import View from '../view'; -import {Animated} from 'react-native'; - -interface Props { - testID?: string; - dialogVisibility?: boolean; - modalVisibility?: boolean; - overlayBackgroundColor?: string; - onFadeDone?: () => void; - fadeOut?: boolean; -} - -const OverlayFadingBackground = ({ - testID, - dialogVisibility, - modalVisibility, - overlayBackgroundColor, - onFadeDone: propsOnFadeDone, - fadeOut -}: Props) => { - const fadeAnimation = useRef(new Animated.Value(0)).current; - const isAnimating = useRef(false); - - const onFadeDone = useCallback(() => { - isAnimating.current = false; - propsOnFadeDone?.(); - }, [propsOnFadeDone]); - - const animateFading = useCallback((toValue: number) => { - isAnimating.current = true; - Animated.timing(fadeAnimation, { - toValue, - duration: 400, - useNativeDriver: true - }).start(onFadeDone); - }, [fadeAnimation, onFadeDone]); - - useEffect(() => { - if (!isAnimating.current && (!dialogVisibility || fadeOut)) { - animateFading(0); - } - }, [dialogVisibility, animateFading, fadeOut]); - - useEffect(() => { - if (modalVisibility) { - animateFading(1); - } - }, [modalVisibility, animateFading]); - - const style = useMemo(() => { - return { - opacity: fadeAnimation, - backgroundColor: overlayBackgroundColor - }; - }, [overlayBackgroundColor, fadeAnimation]); - - return ; -}; - -OverlayFadingBackground.displayName = 'IGNORE'; - -export default OverlayFadingBackground; diff --git a/src/components/dialog/dialog.api.json b/src/components/dialog/dialog.api.json deleted file mode 100644 index dc152e41fb..0000000000 --- a/src/components/dialog/dialog.api.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "Dialog", - "category": "overlays", - "description": "Component for displaying custom content inside a popup dialog", - "note": "Use alignment modifiers to control the dialog position (top, bottom, centerV, centerH, etc... by default the dialog is aligned to center)", - "modifiers": ["alignment"], - "example": "https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/DialogScreen.js", - "images": ["https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Dialog/Dialog.gif?raw=true"], - "props": [ - {"name": "visible", "type": "boolean", "description": "Control visibility of the component"}, - {"name": "ignoreBackgroundPress", "type": "boolean", "description": "Whether or not to ignore background press"}, - { - "name": "overlayBackgroundColor", - "type": "string", - "description": "The color of the overlay background", - "default": "Colors.rgba(Colors.grey10, 0.6)" - }, - {"name": "width", "type": "string | number", "description": "Width", "default": "90%"}, - {"name": "height", "type": "string | number", "description": "Height", "default": "undefined"}, - { - "name": "panDirection", - "type": "UP | DOWN | LEFT | RIGHT", - "description": "The direction of the allowed pan", - "default": "PanningProvider.Directions.DOWN" - }, - {"name": "onDismiss", "type": "(props?: any) => void", "description": "Called when clicking on the background"}, - { - "name": "onDialogDismissed", - "type": "(props: any) => void", - "description": "Called once the dialog has been dismissed completely" - }, - { - "name": "renderPannableHeader", - "type": "(props: any) => JSX.Element", - "description": "For scrollable content (the children of the dialog)", - "note": "If this is added only the header will be pannable. Props are transferred to the 'renderPannableHeader'" - }, - { - "name": "pannableHeaderProps", - "type": "any", - "description": "The props that will be passed to the pannable header" - }, - { - "name": "useSafeArea", - "type": "boolean", - "description": "In iOS, use safe area, in case component attached to the bottom" - }, - {"name": "containerStyle", "type": "ViewStyle", "description": "Component's container style"}, - {"name": "testID", "type": "string", "description": "The test id for e2e tests"} - ], - "snippet": [ - " console.log('dismissed')$2}", - " panDirection={PanningProvider.Directions.DOWN$3}", - ">", - " Content$4", - "" - ] -} diff --git a/src/components/dialog/index.tsx b/src/components/dialog/index.tsx deleted file mode 100644 index 0cfa421d8a..0000000000 --- a/src/components/dialog/index.tsx +++ /dev/null @@ -1,301 +0,0 @@ -import _ from 'lodash'; -import React, {Component} from 'react'; -import {StyleSheet, StyleProp, ViewStyle, ModalPropsIOS, AccessibilityProps, type DimensionValue} from 'react-native'; -import {Colors} from '../../style'; -import {AlignmentModifiers, extractAlignmentsValues} from '../../commons/modifiers'; -import {Constants, asBaseComponent} from '../../commons/new'; -import Modal, {ModalProps} from '../modal'; -import View from '../view'; -import PanListenerView from '../panningViews/panListenerView'; -import DialogDismissibleView from './DialogDismissibleView'; -import OverlayFadingBackground from './OverlayFadingBackground'; -import PanningProvider, {PanningDirections, PanningDirectionsEnum} from '../panningViews/panningProvider'; -export {PanningDirections as DialogDirections, PanningDirectionsEnum as DialogDirectionsEnum}; - -// TODO: KNOWN ISSUES -// 1. iOS pressing on the background while enter animation is happening will not call onDismiss -// Touch events are not registered? -// 2. SafeArea is transparent -// 3. Check why we need the state change in DialogDismissibleView -> onLayout -> animateTo - -interface RNPartialProps - extends Pick, - Pick {} - -export interface DialogProps extends AlignmentModifiers, RNPartialProps { - /** - * Control visibility of the dialog - */ - visible?: boolean; - /** - * Dismiss callback for when clicking on the background - */ - onDismiss?: (props?: any) => void; - /** - * Whether or not to ignore background press - */ - ignoreBackgroundPress?: boolean; - /** - * The color of the overlay background - */ - overlayBackgroundColor?: string; - /** - * The dialog width (default: 90%) - */ - width?: DimensionValue; - /** - * The dialog height (default: undefined) - */ - height?: DimensionValue; - /** - * The direction of the allowed pan (default is DOWN). - * Types: UP, DOWN, LEFT and RIGHT (using PanningProvider.Directions.###). - * Pass null to remove pan. - */ - panDirection?: PanningDirections; - /** - * Whether or not to handle SafeArea - */ - useSafeArea?: boolean; - /** - * Called once the dialog has been dismissed completely - */ - onDialogDismissed?: (props: any) => void; - /** - * If this is added only the header will be pannable; - * this allows for scrollable content (the children of the dialog) - * props are transferred to the renderPannableHeader - */ - renderPannableHeader?: (props: any) => JSX.Element; - /** - * The props that will be passed to the pannable header - */ - pannableHeaderProps?: any; - /** - * Additional props for the modal. - */ - modalProps?: ModalProps; - /** - * The Dialog`s container style - */ - containerStyle?: StyleProp; - /** - * Used as a testing identifier - */ - testID?: string; - children?: React.ReactNode; -} - -interface DialogState { - alignments: AlignmentModifiers; - modalVisibility?: boolean; - dialogVisibility?: boolean; - fadeOut?: boolean; -} - -const DEFAULT_OVERLAY_BACKGROUND_COLOR = Colors.rgba(Colors.$backgroundInverted, 0.3); - -/** - * @description: Dialog component for displaying custom content inside a popup dialog - * @notes: Use alignment modifiers to control the dialog position - * (top, bottom, centerV, centerH, etc... by default the dialog is aligned to center) - * @modifiers: alignment - * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/DialogScreen.js - * @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Dialog/Dialog.gif?raw=true - */ -class Dialog extends Component { - static displayName = 'Dialog'; - static directions = PanningDirectionsEnum; - - static defaultProps = { - overlayBackgroundColor: DEFAULT_OVERLAY_BACKGROUND_COLOR - }; - - private styles: any; - - constructor(props: DialogProps) { - super(props); - - this.state = { - alignments: extractAlignmentsValues(props), - modalVisibility: props.visible, - dialogVisibility: props.visible - }; - - this.styles = createStyles(this.props); - this.setAlignment(); - } - - UNSAFE_componentWillReceiveProps(nextProps: DialogProps) { - const {visible: nexVisible} = nextProps; - const {visible} = this.props; - - if (nexVisible && !visible) { - this.setState({modalVisibility: true, dialogVisibility: true}); - } else if (visible && !nexVisible) { - this.hideDialogView(); - } - } - - setAlignment() { - const {alignments} = this.state; - if (_.isEmpty(alignments)) { - this.styles.alignments = this.styles.centerContent; - } else { - this.styles.alignments = alignments; - } - } - - // TODO: revert adding this workaround once RN fixes https://github.com/facebook/react-native/issues/29455 - onFadeDone = () => { - if (!this.state.modalVisibility) { - setTimeout(() => { - // unfortunately this is needed if a modal needs to open on iOS - this.props.onDialogDismissed?.(this.props); - }, 100); - } - }; - - _onDismiss = () => { - this.setState({modalVisibility: false, fadeOut: false}, () => { - const props = this.props; - if (props.visible) { - props.onDismiss?.(props); - } - // Parity with iOS Modal's onDismiss - if (Constants.isAndroid) { - props.onDialogDismissed?.(props); - } - }); - }; - - onDismiss = () => { - const fadeOut = Constants.isIOS && this.props.visible; - - if (fadeOut) { - this.setState({fadeOut}, this._onDismiss); - } else { - this._onDismiss(); - } - }; - - hideDialogView = () => { - this.setState({dialogVisibility: false}); - }; - - renderPannableHeader = (directions: PanningDirections[]) => { - const {renderPannableHeader, pannableHeaderProps} = this.props; - if (renderPannableHeader) { - return {renderPannableHeader(pannableHeaderProps)}; - } - }; - - getContainerType = () => { - const {panDirection, renderPannableHeader} = this.props; - if (!panDirection || renderPannableHeader) { - return View; - } - return PanListenerView; - }; - - renderDialogView = () => { - const {children, panDirection = PanningProvider.Directions.DOWN, containerStyle, testID} = this.props; - const {dialogVisibility} = this.state; - const Container = this.getContainerType(); - - return ( - - - - - {this.renderPannableHeader([panDirection])} - {children} - - - - - ); - }; - - // TODO: renderOverlay {_.invoke(this.props, 'renderOverlay')} - renderDialogContainer = () => { - const {modalVisibility, dialogVisibility, fadeOut} = this.state; - const {useSafeArea, bottom, overlayBackgroundColor, testID} = this.props; - const addBottomSafeArea = Constants.isIphoneX && useSafeArea && bottom; - const bottomInsets = Constants.getSafeAreaInsets().bottom - 8; // TODO: should this be here or in the input style? - const onFadeDone = Constants.isIOS ? this.onFadeDone : undefined; - - return ( - - - {this.renderDialogView()} - {addBottomSafeArea && } - - ); - }; - - render = () => { - const {modalVisibility} = this.state; - const {testID, supportedOrientations, accessibilityLabel, ignoreBackgroundPress, modalProps} = this.props; - const onBackgroundPress = !ignoreBackgroundPress ? this.hideDialogView : undefined; - - return ( - - {this.renderDialogContainer()} - - ); - }; -} - -function createStyles(props: DialogProps) { - const {width = '90%', height} = props; - const flexType = height ? {flex: 1} : {flex: 0}; - return StyleSheet.create({ - dialogViewSize: {width, height: height ?? undefined}, - flexType, - container: { - flex: 1 - }, - centerHorizontal: { - alignItems: 'center' - }, - centerContent: { - justifyContent: 'center' - }, - overflow: { - overflow: 'hidden' - } - }); -} - -export default asBaseComponent(Dialog); diff --git a/src/components/picker/__tests__/index.spec.tsx b/src/components/picker/__tests__/index.spec.tsx index 90cd39eeae..f91632bc97 100644 --- a/src/components/picker/__tests__/index.spec.tsx +++ b/src/components/picker/__tests__/index.spec.tsx @@ -159,7 +159,7 @@ describe('Picker', () => { }); describe('Dialog', () => { - const dialogProps = {useDialog: true, customPickerProps: {migrateDialog: true}}; + const dialogProps = {useDialog: true}; describe('Test value', () => { it('Get correct value of a single item', () => { diff --git a/src/components/picker/index.tsx b/src/components/picker/index.tsx index a0382dbf34..5010946226 100644 --- a/src/components/picker/index.tsx +++ b/src/components/picker/index.tsx @@ -310,7 +310,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { ref={pickerExpandable} useDialog={useDialog || useWheelPicker} dialogProps={DEFAULT_DIALOG_PROPS} - migrateDialog expandableContent={expandableModalContent} renderCustomOverlay={renderOverlay ? _renderOverlay : undefined} onPress={onPress} diff --git a/src/incubator/dialog/index.tsx b/src/incubator/dialog/index.tsx index bf64bb2856..83eab737c3 100644 --- a/src/incubator/dialog/index.tsx +++ b/src/incubator/dialog/index.tsx @@ -25,8 +25,8 @@ import {extractAlignmentsValues} from '../../commons/modifiers'; import useHiddenLocation from '../hooks/useHiddenLocation'; import DialogHeader from './DialogHeader'; import useDialogContent from './useDialogContent'; -import {DialogProps, DialogDirections, DialogDirectionsEnum, DialogHeaderProps, DialogMigrationProps} from './types'; -export {DialogProps, DialogDirections, DialogDirectionsEnum, DialogHeaderProps, DialogMigrationProps}; +import {DialogProps, DialogDirections, DialogDirectionsEnum, DialogHeaderProps} from './types'; +export {DialogProps, DialogDirections, DialogDirectionsEnum, DialogHeaderProps}; const THRESHOLD_VELOCITY = 750; diff --git a/src/incubator/dialog/types.ts b/src/incubator/dialog/types.ts index f89127a2ce..eb039a6e65 100644 --- a/src/incubator/dialog/types.ts +++ b/src/incubator/dialog/types.ts @@ -1,7 +1,6 @@ import {PropsWithChildren, ReactElement} from 'react'; import {type DimensionValue, type StyleProp, type TextStyle, type ViewStyle} from 'react-native'; import {AlignmentModifiers} from '../../commons/modifiers'; -import {DialogProps as DialogPropsOld} from '../../components/dialog'; import {ButtonProps} from '../../components/button'; import {ModalProps} from '../../components/modal'; import {PanningDirections, PanningDirectionsEnum} from '../panView'; @@ -134,25 +133,3 @@ export interface _DialogProps extends AlignmentModifiers, Pick; - -// For migration purposes -export interface _DialogPropsOld { - /** - * The props to pass to the dialog expandable container - */ - dialogProps?: DialogPropsOld; - migrateDialog?: false; -} - -export interface _DialogPropsNew { - /** - * The props to pass to the dialog expandable container - */ - dialogProps?: DialogProps; - /** - * Migrate the Dialog to DialogNew (make sure you use only new props in dialogProps) - */ - migrateDialog: true; -} - -export type DialogMigrationProps = _DialogPropsOld | _DialogPropsNew; diff --git a/src/incubator/expandableOverlay/__tests__/index.spec.tsx b/src/incubator/expandableOverlay/__tests__/index.spec.tsx index 8188b0abda..1b8074262a 100644 --- a/src/incubator/expandableOverlay/__tests__/index.spec.tsx +++ b/src/incubator/expandableOverlay/__tests__/index.spec.tsx @@ -17,7 +17,7 @@ const universe = 'Hello Universe'; const TestCase = (props: Omit) => { return ( - {universe}} {...props} testID={testID}> + {universe}} {...props} testID={testID}> {helloWorld} diff --git a/src/incubator/expandableOverlay/index.tsx b/src/incubator/expandableOverlay/index.tsx index 22f06452fc..bf97c02953 100644 --- a/src/incubator/expandableOverlay/index.tsx +++ b/src/incubator/expandableOverlay/index.tsx @@ -3,8 +3,7 @@ import {AccessibilityInfo, findNodeHandle} from 'react-native'; import TouchableOpacity, {TouchableOpacityProps} from '../../components/touchableOpacity'; import View from '../../components/view'; import Modal, {ModalProps, ModalTopBarProps} from '../../components/modal'; -import DialogOld from '../../components/dialog'; -import DialogNew, {DialogMigrationProps} from '../dialog'; +import Dialog, {DialogProps} from '../dialog'; import {Colors} from 'style'; export interface ExpandableOverlayMethods { @@ -18,7 +17,6 @@ export interface RenderCustomOverlayProps extends ExpandableOverlayMethods { } export type ExpandableOverlayProps = TouchableOpacityProps & - DialogMigrationProps & PropsWithChildren<{ /** * The content to render inside the expandable modal/dialog @@ -28,6 +26,10 @@ export type ExpandableOverlayProps = TouchableOpacityProps & * Whether to use a dialog as expandable container (by default the container will be a full screen modal) */ useDialog?: boolean; + /** + * The props to pass to the dialog expandable container + */ + dialogProps?: DialogProps; /** * The props to pass to the modal expandable container */ @@ -57,7 +59,6 @@ const ExpandableOverlay = (props: ExpandableOverlayProps, ref: any) => { useDialog, modalProps, dialogProps, - migrateDialog, showTopBar, topBarProps, renderCustomOverlay, @@ -115,7 +116,6 @@ const ExpandableOverlay = (props: ExpandableOverlayProps, ref: any) => { }; const renderDialog = () => { - const Dialog = migrateDialog ? DialogNew : DialogOld; return ( {expandableContent} diff --git a/src/index.ts b/src/index.ts index b8fdcec2fe..5df44c1b1b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,7 +64,7 @@ export {default as ColorSwatch, ColorSwatchProps, ColorInfo} from './components/ export {default as ConnectionStatusBar, ConnectionStatusBarProps} from './components/connectionStatusBar'; export {default as Dash, DashProps} from './components/dash'; export {default as DateTimePicker, DateTimePickerProps, DateTimePickerMode} from './components/dateTimePicker'; -export {default as Dialog, DialogProps, DialogDirections, DialogDirectionsEnum} from './components/dialog'; +export {default as Dialog, DialogProps, DialogDirections, DialogDirectionsEnum} from './incubator/dialog'; export {default as Drawer, DrawerProps, DrawerItemProps} from './components/drawer'; export {default as ExpandableSection, ExpandableSectionProps} from './components/expandableSection'; export {default as Fader, FaderProps, FaderPosition} from './components/fader'; From 64da54118eae6c841ff0931ab7e13b1a83ec1c02 Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Wed, 22 Jan 2025 15:09:29 +0200 Subject: [PATCH 02/49] Move Incubator.Dialog to Dialog --- demo/src/index.js | 3 - demo/src/screens/MenuStructure.js | 1 - .../screens/componentScreens/DialogScreen.js | 343 ------------------ .../DialogScreen.tsx} | 12 +- .../screens/componentScreens/PickerScreen.tsx | 6 +- .../componentScreens/WheelPickerScreen.tsx | 6 +- demo/src/screens/incubatorScreens/index.js | 1 - src/components/actionSheet/index.tsx | 2 +- .../colorPicker/ColorPickerDialog.tsx | 2 +- src/components/dateTimePicker/index.tsx | 2 +- .../dialog/Dialog.driver.new.ts | 0 .../dialog/DialogHeader.tsx | 0 .../dialog/__tests__/index.new.spec.tsx | 2 +- .../dialog/dialog.api.json | 2 +- .../dialog/dialogHeader.api.json | 2 +- .../dialog/index.tsx | 4 +- src/{incubator => components}/dialog/types.ts | 2 +- .../dialog/useDialogContent.tsx | 0 src/components/picker/Picker.driver.new.ts | 2 +- src/components/picker/index.tsx | 2 +- .../ExpandableOverlay.driver.ts | 2 +- src/incubator/expandableOverlay/index.tsx | 2 +- src/incubator/index.ts | 1 - src/index.ts | 10 +- src/testkit/index.ts | 2 +- webDemo/src/examples/Picker.tsx | 6 +- 26 files changed, 38 insertions(+), 379 deletions(-) delete mode 100644 demo/src/screens/componentScreens/DialogScreen.js rename demo/src/screens/{incubatorScreens/IncubatorDialogScreen.tsx => componentScreens/DialogScreen.tsx} (88%) rename src/{incubator => components}/dialog/Dialog.driver.new.ts (100%) rename src/{incubator => components}/dialog/DialogHeader.tsx (100%) rename src/{incubator => components}/dialog/__tests__/index.new.spec.tsx (98%) rename src/{incubator => components}/dialog/dialog.api.json (97%) rename src/{incubator => components}/dialog/dialogHeader.api.json (96%) rename src/{incubator => components}/dialog/index.tsx (98%) rename src/{incubator => components}/dialog/types.ts (97%) rename src/{incubator => components}/dialog/useDialogContent.tsx (100%) diff --git a/demo/src/index.js b/demo/src/index.js index bc0c42b433..5a4dde2499 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -251,9 +251,6 @@ module.exports = { get IncubatorSlider() { return require('./screens/incubatorScreens/IncubatorSliderScreen').default; }, - get IncubatorDialog() { - return require('./screens/incubatorScreens/IncubatorDialogScreen').default; - }, get IncubatorToast() { return require('./screens/incubatorScreens/IncubatorToastScreen').default; }, diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index c755c44436..60240d1662 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -194,7 +194,6 @@ export const navigationData = { {title: 'Calendar', tags: 'calendar', screen: 'unicorn.components.IncubatorCalendarScreen'}, {title: 'ChipsInput', tags: 'chips input', screen: 'unicorn.components.ChipsInputScreen'}, {title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'}, - {title: 'Dialog (New)', tags: 'dialog modal popup alert', screen: 'unicorn.incubator.IncubatorDialogScreen'}, {title: 'Toast (New)', tags: 'toast', screen: 'unicorn.components.IncubatorToastScreen'}, { title: 'ExpandableOverlay', diff --git a/demo/src/screens/componentScreens/DialogScreen.js b/demo/src/screens/componentScreens/DialogScreen.js deleted file mode 100644 index f56fbb56f7..0000000000 --- a/demo/src/screens/componentScreens/DialogScreen.js +++ /dev/null @@ -1,343 +0,0 @@ -import React, {Component} from 'react'; -import {FlatList, ScrollView, StyleSheet, Alert} from 'react-native'; -import { - Text, - View, - Button, - Dialog, - Colors, - PanningProvider, - RadioGroup, - RadioButton, - Switch, - Constants -} from 'react-native-ui-lib'; // eslint-disable-line - -export default class DialogScreen extends Component { - constructor(props) { - super(props); - - this.SCROLL_TYPE = { - NONE: 'none', - VERTICAL: 'vertical', - HORIZONTAL: 'horizontal' - }; - - this.pannableTitle = {title: 'This is a pannable header Dialog'}; - this.title = 'This is a Dialog'; - this.supportedOrientations = ['portrait', 'landscape']; - this.colors = [ - {value: Colors.red10, label: 'Red10'}, - {value: Colors.red30, label: 'Red30'}, - {value: Colors.red50, label: 'Red50'}, - {value: Colors.red70, label: 'Red70'}, - {value: Colors.blue10, label: 'Blue10'}, - {value: Colors.blue30, label: 'Blue30'}, - {value: Colors.blue50, label: 'Blue50'}, - {value: Colors.blue70, label: 'Blue70'}, - {value: Colors.purple10, label: 'Purple10'}, - {value: Colors.purple30, label: 'Purple30'}, - {value: Colors.purple50, label: 'Purple50'}, - {value: Colors.purple70, label: 'Purple70'}, - {value: Colors.green10, label: 'Green10'}, - {value: Colors.green30, label: 'Green30'}, - {value: Colors.green50, label: 'Green50'}, - {value: Colors.green70, label: 'Green70'}, - {value: Colors.yellow10, label: 'Yellow10'}, - {value: Colors.yellow30, label: 'Yellow30'}, - {value: Colors.yellow50, label: 'Yellow50'}, - {value: Colors.yellow70, label: 'Yellow70'} - ]; - - this.state = { - panDirection: Dialog.directions.DOWN, - position: 'bottom', - scroll: this.SCROLL_TYPE.NONE, - showHeader: true, - isRounded: true, - showDialog: false, - ignoreBackgroundPress: false - }; - } - - titlePressed = ({title}) => { - Alert.alert('Pressed on', title); - }; - - setPanDirection = panDirection => { - if (panDirection !== this.state.panDirection) { - this.setState({panDirection}); - } - }; - - setPosition = position => { - if (position !== this.state.position) { - this.setState({position}); - } - }; - - setScroll = scroll => { - if (scroll !== this.state.scroll) { - this.setState({scroll}); - } - }; - - toggleShowHeader = () => { - this.setState({ - showHeader: !this.state.showHeader - }); - }; - - toggleIsRounded = () => { - this.setState({ - isRounded: !this.state.isRounded - }); - }; - - toggleIgnoreBackgroundPress = () => { - this.setState({ - ignoreBackgroundPress: !this.state.ignoreBackgroundPress - }); - }; - - showDialog = () => { - this.setState({showDialog: true}); - }; - - hideDialog = () => { - this.setState({showDialog: false}); - }; - - getWarning = () => { - const {showHeader, scroll, panDirection} = this.state; - if (!showHeader && scroll !== this.SCROLL_TYPE.NONE) { - return ( - It is recommended to have pannable header with scrollable content - ); - } else if (showHeader && panDirection !== Dialog.directions.DOWN) { - return It is recommended to have pannable header with direction=down; - } - }; - - getMessage = () => { - const {panDirection, position, scroll} = this.state; - - return `Panning direction: ${panDirection ? panDirection : 'none'} -Position: ${position ? position : 'center'} -Scroll: ${scroll}`; - }; - - renderPannableHeader = props => { - const {title} = props; - return ( - - - {title} - - - - ); - }; - - renderPlainContent = () => { - return ( - - ); } diff --git a/demo/src/screens/componentScreens/PickerScreen.tsx b/demo/src/screens/componentScreens/PickerScreen.tsx index 489a23372a..0c8650691d 100644 --- a/demo/src/screens/componentScreens/PickerScreen.tsx +++ b/demo/src/screens/componentScreens/PickerScreen.tsx @@ -5,7 +5,7 @@ import { Assets, Colors, Typography, - Incubator, + Dialog, View, Text, Button, @@ -130,7 +130,7 @@ export default class PickerScreen extends Component { renderDialog: PickerProps['renderOverlay'] = (modalProps: RenderCustomModalProps) => { const {visible, children, toggleModal, onDone} = modalProps; return ( - { onDone(); @@ -145,7 +145,7 @@ export default class PickerScreen extends Component { headerProps={{title: 'Custom modal'}} > {children} - + ); }; diff --git a/demo/src/screens/componentScreens/WheelPickerScreen.tsx b/demo/src/screens/componentScreens/WheelPickerScreen.tsx index 6b4f33b01c..8d12da940b 100644 --- a/demo/src/screens/componentScreens/WheelPickerScreen.tsx +++ b/demo/src/screens/componentScreens/WheelPickerScreen.tsx @@ -1,6 +1,6 @@ import _ from 'lodash'; import React, {useCallback, useState} from 'react'; -import {View, Text, Incubator, WheelPicker, WheelPickerAlign, Colors, Typography, Button} from 'react-native-ui-lib'; +import {View, Text, Dialog, WheelPicker, WheelPickerAlign, Colors, Typography, Button} from 'react-native-ui-lib'; const monthItems = _.map([ 'January', @@ -88,7 +88,7 @@ export default () => { Days