From 2eeddff6609c5dcbb5d8ebcee2bea7ad057ab901 Mon Sep 17 00:00:00 2001 From: Yauhen Penkin Date: Thu, 28 Feb 2019 14:19:19 +0300 Subject: [PATCH 1/7] refactor(ui): modal service/panel/component refactor --- .../component/modal/modalPanel.component.tsx | 37 +++-- .../theme/service/modal/modal.service.tsx | 32 +++- src/framework/theme/type.ts | 3 +- src/framework/ui/modal/modal.component.tsx | 67 +++----- .../ui/popover/popover.component.tsx | 4 +- .../src/ui/screen/dialog.component.tsx | 148 ++++++++++-------- .../src/ui/screen/modal.component.tsx | 5 +- 7 files changed, 162 insertions(+), 134 deletions(-) diff --git a/src/framework/theme/component/modal/modalPanel.component.tsx b/src/framework/theme/component/modal/modalPanel.component.tsx index 4657d61d5..787c2596a 100644 --- a/src/framework/theme/component/modal/modalPanel.component.tsx +++ b/src/framework/theme/component/modal/modalPanel.component.tsx @@ -4,19 +4,23 @@ import { StyleSheet, ViewProps, } from 'react-native'; -import { ModalService } from '../../service'; +import { + ModalPresenting, + ModalService, +} from '../../service'; import { Modal } from '../../../ui/modal/modal.component'; +import { ModalComponentCloseProps } from '@kitten/theme'; export interface ModalPanelProps { children: React.ReactElement | React.ReactElement[]; } interface ModalPanelState { - dialogComponents: Map>; + dialogComponents: Map>; backdropValues: Map; } -export class ModalPanel extends React.Component { +export class ModalPanel extends React.Component implements ModalPresenting { public state: ModalPanelState = { dialogComponents: new Map(), @@ -24,33 +28,40 @@ export class ModalPanel extends React.Component { + public hide = (identifier: string): void => { + const component: React.ReactElement = this.state.dialogComponents + .get(identifier); + if (component) { + component.props.onRequestClose && component.props.onRequestClose(); + } const components: Map> = this.state.dialogComponents; components.delete(identifier); - const backdropValues: Map = this.state.backdropValues; - backdropValues.delete(identifier); + const backdrops: Map = this.state.backdropValues; + backdrops.delete(identifier); this.setState({ dialogComponents: components, - backdropValues: backdropValues, + backdropValues: backdrops, }); }; - public showDialog(dialogComponent: React.ReactElement, closeOnBackDrop: boolean): void { + public show(dialogComponent: React.ReactElement, closeOnBackDrop: boolean): string { const key: string = this.generateUniqueComponentKey(); const componentsMap: Map> = this.state.dialogComponents .set(key, dialogComponent); - const backdropsMap: Map = this.state.backdropValues.set(key, closeOnBackDrop); + const backdrops: Map = this.state.backdropValues + .set(key, closeOnBackDrop); this.setState({ dialogComponents: componentsMap, - backdropValues: backdropsMap, + backdropValues: backdrops, }); + return key; } private generateUniqueComponentKey = (): string => { @@ -72,7 +83,7 @@ export class ModalPanel extends React.Component {modal} diff --git a/src/framework/theme/service/modal/modal.service.tsx b/src/framework/theme/service/modal/modal.service.tsx index 14181c0ad..58c1d63e8 100644 --- a/src/framework/theme/service/modal/modal.service.tsx +++ b/src/framework/theme/service/modal/modal.service.tsx @@ -1,21 +1,37 @@ import React from 'react'; -import { ModalPanel } from '../../component'; +import { ModalComponentCloseProps } from '@kitten/theme'; class ModalServiceType { - component: ModalPanel | null = null; + panel: ModalPresenting | null = null; - public setComponent(component: ModalPanel | null): void { - this.component = component; + public mount(panel: ModalPresenting | null): void { + this.panel = panel; } - public showDialog(dialogComponent: React.ReactElement, - closeOnBackDrop: boolean = false): void { + public unmount(): void { + this.panel = null; + } + + public show(element: React.ReactElement, + closeOnBackDrop: boolean = false): string { + if (this.panel) { + return this.panel.show(element, closeOnBackDrop); + } + } - if (this.component) { - this.component.showDialog(dialogComponent, closeOnBackDrop); + public hide(identifier: string): void { + if (this.panel) { + this.panel.hide(identifier); } } } +export interface ModalPresenting { + show(element: React.ReactElement, + closeOnBackDrop: boolean): string; + + hide(identifier: string): void; +} + export const ModalService = new ModalServiceType(); diff --git a/src/framework/theme/type.ts b/src/framework/theme/type.ts index 6d3153160..561a2c505 100644 --- a/src/framework/theme/type.ts +++ b/src/framework/theme/type.ts @@ -27,5 +27,6 @@ export type ThemedStyleType = any; export type StyleSheetType = any; export interface ModalComponentCloseProps { - onCloseModal?: () => void; + [key: string]: any; + onRequestClose: () => void; } diff --git a/src/framework/ui/modal/modal.component.tsx b/src/framework/ui/modal/modal.component.tsx index a28e9c517..67a9e356d 100644 --- a/src/framework/ui/modal/modal.component.tsx +++ b/src/framework/ui/modal/modal.component.tsx @@ -1,10 +1,8 @@ import React from 'react'; import { - View, ViewProps, TouchableWithoutFeedbackProps, StyleSheet, - TouchableWithoutFeedback, Dimensions, Animated, } from 'react-native'; @@ -63,19 +61,6 @@ export class Modal extends React.Component { } } - private getComponentStyle = (style: StyleType): StyleType | null => { - return { - container: style ? { - paddingHorizontal: style.paddingHorizontal, - paddingVertical: style.paddingVertical, - backgroundColor: style.backgroundColor, - borderColor: style.borderColor, - borderRadius: style.borderRadius, - borderWidth: style.borderWidth, - } : null, - }; - }; - private getAnimationStyle(type: ModalAnimationType): StyleType | undefined { switch (type) { case 'none': @@ -117,42 +102,45 @@ export class Modal extends React.Component { } }; + private onStartShouldSetResponder = (): boolean => true; + + private onStartShouldSetResponderCapture = (): boolean => false; + private createComponentChild = (source: React.ReactElement): React.ReactElement => { return React.cloneElement(source, { + ...source.props, onCloseModal: this.closeModal, - pointerEvents: 'box-none', + onStartShouldSetResponder: () => true, + onResponderRelease: () => {}, }); }; - private createBackdropElement = (): React.ReactElement => { - return this.props.isBackDropAllowed ? ( - - - - ) : null; - }; - private createComponentChildren = (source: React.ReactNode): React.ReactElement[] => { return React.Children.map(source, this.createComponentChild); }; private renderComponent = (): React.ReactElement => { - const { style, themedStyle, animationType, children, ...derivedProps } = this.props; - const componentStyle: StyleType = this.getComponentStyle(themedStyle); + const { style, animationType, children, isBackDropAllowed, ...derivedProps } = this.props; const animationStyle: StyleType = this.getAnimationStyle(animationType); - - const backdropElement: React.ReactElement = this.createBackdropElement(); const componentChildren: React.ReactElement[] = this.createComponentChildren(children); - return ( - - {backdropElement} - - {componentChildren} - - + const dialog: React.ReactElement = + + {componentChildren} + ; + + return isBackDropAllowed ? ( + React.cloneElement(dialog, { + onStartShouldSetResponder: this.onStartShouldSetResponder, + onResponderRelease: this.closeOnBackdrop, + onStartShouldSetResponderCapture: this.onStartShouldSetResponderCapture, + }) + ) : ( + React.cloneElement(dialog, { + pointerEvents: 'box-none', + }) ); }; @@ -163,11 +151,6 @@ export class Modal extends React.Component { const styles = StyleSheet.create({ container: { - position: 'absolute', - }, - backdrop: { - width: width, - height: height, ...StyleSheet.absoluteFillObject, }, }); diff --git a/src/framework/ui/popover/popover.component.tsx b/src/framework/ui/popover/popover.component.tsx index 7c564c1af..0ad011d2e 100644 --- a/src/framework/ui/popover/popover.component.tsx +++ b/src/framework/ui/popover/popover.component.tsx @@ -82,9 +82,9 @@ export class Popover extends React.Component { // Retrieve `content` from popover children // and clone it with measured position const { [TAG_CONTENT]: popoverView } = container.props.children; - const popover: React.ReactElement = React.cloneElement(popoverView, { style }); + const popover: React.ReactElement = React.cloneElement(popoverView, { style }); - ModalService.showDialog(popover, true); + ModalService.show(popover, true); } } diff --git a/src/playground/src/ui/screen/dialog.component.tsx b/src/playground/src/ui/screen/dialog.component.tsx index 75b629535..9c2e3ea4d 100644 --- a/src/playground/src/ui/screen/dialog.component.tsx +++ b/src/playground/src/ui/screen/dialog.component.tsx @@ -1,9 +1,10 @@ import React from 'react'; import { View, + Text, Button, - ViewStyle, - ViewProps, + StyleSheet, + Alert, } from 'react-native'; import { ThemedComponentProps, @@ -16,92 +17,107 @@ import { NavigationScreenProps } from 'react-navigation'; type Props = & ThemedComponentProps & NavigationScreenProps; -interface EndlessModalProps { - onContinue?: () => void; - onDismiss?: () => void; -} - -const EndlessModal = (props?: EndlessModalProps & ModalComponentCloseProps & ViewProps) => { - - const { onContinue, onDismiss, onCloseModal, ...derivedProps } = props; - - const onCloseButtonPress = () => { - if (onDismiss) { - onDismiss(); - } - onCloseModal(); - }; - - return ( - -