mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-10 22:18:03 -05:00
357 lines
8.8 KiB
TypeScript
357 lines
8.8 KiB
TypeScript
import React, { Fragment } from 'react';
|
|
import {
|
|
TouchableOpacity,
|
|
Modal,
|
|
View,
|
|
StatusBar,
|
|
I18nManager,
|
|
ViewStyle,
|
|
FlexStyle,
|
|
StyleProp,
|
|
StyleSheet,
|
|
ColorValue,
|
|
Platform,
|
|
Keyboard,
|
|
} from 'react-native';
|
|
import { withTheme } from 'react-native-elements/dist/config';
|
|
import { ThemeProps } from 'react-native-elements/dist/config';
|
|
import {
|
|
ScreenWidth,
|
|
ScreenHeight,
|
|
isIOS,
|
|
} from 'react-native-elements/dist/helpers';
|
|
import Triangle from './Triangle';
|
|
import getTooltipCoordinate, {
|
|
getElementVisibleWidth,
|
|
} from './getTooltipCoordinate';
|
|
|
|
export type TooltipProps = {
|
|
withPointer?: boolean;
|
|
popover?: React.ReactElement<{}>;
|
|
toggleOnPress?: boolean;
|
|
toggleAction?: string | 'onPress' | 'onLongPress';
|
|
height?: FlexStyle['height'];
|
|
width?: FlexStyle['width'];
|
|
containerStyle?: StyleProp<ViewStyle>;
|
|
pointerColor?: ColorValue;
|
|
onClose?(): void;
|
|
onOpen?(): void;
|
|
overlayColor?: ColorValue;
|
|
withOverlay?: boolean;
|
|
backgroundColor?: ColorValue;
|
|
highlightColor?: ColorValue;
|
|
skipAndroidStatusBar?: boolean;
|
|
ModalComponent?: typeof React.Component;
|
|
closeOnlyOnBackdropPress?: boolean;
|
|
} & typeof defaultProps;
|
|
|
|
const defaultProps = {
|
|
withOverlay: true,
|
|
overlayColor: 'rgba(250, 250, 250, 0.70)',
|
|
highlightColor: 'transparent',
|
|
withPointer: true,
|
|
toggleOnPress: true,
|
|
toggleAction: 'onPress',
|
|
height: 40,
|
|
width: 150,
|
|
containerStyle: {},
|
|
backgroundColor: '#617080',
|
|
onClose: () => {},
|
|
onOpen: () => {},
|
|
skipAndroidStatusBar: false,
|
|
ModalComponent: Modal,
|
|
closeOnlyOnBackdropPress: false,
|
|
};
|
|
|
|
type TooltipState = {
|
|
isVisible: boolean;
|
|
yOffset: number;
|
|
xOffset: number;
|
|
elementWidth: number;
|
|
elementHeight: number;
|
|
};
|
|
|
|
class Tooltip extends React.Component<
|
|
TooltipProps & Partial<ThemeProps<TooltipProps>>,
|
|
TooltipState
|
|
> {
|
|
static defaultProps = defaultProps;
|
|
_isMounted: boolean = false;
|
|
state = {
|
|
isVisible: false,
|
|
yOffset: 0,
|
|
xOffset: 0,
|
|
elementWidth: 0,
|
|
elementHeight: 0,
|
|
};
|
|
renderedElement?: View | null;
|
|
|
|
toggleTooltip = () => {
|
|
const { onClose } = this.props;
|
|
Keyboard.dismiss();
|
|
setTimeout(() => {
|
|
this.getElementPosition();
|
|
this._isMounted &&
|
|
this.setState((prevState) => {
|
|
if (prevState.isVisible) {
|
|
onClose && onClose();
|
|
}
|
|
return { isVisible: !prevState.isVisible };
|
|
});
|
|
}, 100);
|
|
};
|
|
|
|
wrapWithPress = (
|
|
toggleOnPress: TooltipProps['toggleOnPress'],
|
|
toggleAction: TooltipProps['toggleAction'],
|
|
children: React.ReactNode
|
|
) => {
|
|
if (toggleOnPress) {
|
|
return (
|
|
<TouchableOpacity
|
|
{...{ [toggleAction]: this.toggleTooltip }}
|
|
delayLongPress={250}
|
|
activeOpacity={1}>
|
|
{children}
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
return children;
|
|
};
|
|
|
|
containerStyle = (withOverlay: boolean, overlayColor: string): ViewStyle => {
|
|
return {
|
|
backgroundColor: withOverlay ? overlayColor : 'transparent',
|
|
flex: 1,
|
|
};
|
|
};
|
|
|
|
getTooltipStyle = () => {
|
|
const { yOffset, xOffset, elementHeight, elementWidth } = this.state;
|
|
const { height, backgroundColor, width, withPointer, containerStyle } =
|
|
this.props;
|
|
const { x, y } = getTooltipCoordinate(
|
|
xOffset,
|
|
yOffset,
|
|
elementWidth,
|
|
elementHeight,
|
|
ScreenWidth,
|
|
ScreenHeight,
|
|
width,
|
|
height,
|
|
withPointer
|
|
);
|
|
|
|
return StyleSheet.flatten([
|
|
{
|
|
position: 'absolute',
|
|
[I18nManager.isRTL ? 'right' : 'left']: x,
|
|
top: y,
|
|
width,
|
|
height,
|
|
backgroundColor,
|
|
// default styles
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
flex: 1,
|
|
borderRadius: 10,
|
|
padding: 10,
|
|
},
|
|
containerStyle,
|
|
]);
|
|
};
|
|
|
|
renderPointer = (tooltipY: FlexStyle['top']) => {
|
|
const { yOffset, xOffset, elementHeight, elementWidth } = this.state;
|
|
const { backgroundColor, pointerColor } = this.props;
|
|
const pastMiddleLine = yOffset > (tooltipY || 0);
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
position: 'absolute',
|
|
top: pastMiddleLine ? yOffset - 13 : yOffset + elementHeight - 2,
|
|
[I18nManager.isRTL ? 'right' : 'left']:
|
|
xOffset +
|
|
getElementVisibleWidth(elementWidth, xOffset, ScreenWidth) / 2 -
|
|
7.5,
|
|
}}>
|
|
<Triangle
|
|
style={{ borderBottomColor: pointerColor || backgroundColor }}
|
|
isDown={pastMiddleLine}
|
|
/>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
getTooltipHighlightedButtonStyle = (): ViewStyle => {
|
|
const { highlightColor } = this.props;
|
|
const { yOffset, xOffset, elementWidth, elementHeight } = this.state;
|
|
|
|
return {
|
|
position: 'absolute',
|
|
top: yOffset,
|
|
[I18nManager.isRTL ? 'right' : 'left']: xOffset,
|
|
backgroundColor: highlightColor,
|
|
overflow: 'visible',
|
|
width: elementWidth,
|
|
height: elementHeight,
|
|
};
|
|
};
|
|
|
|
renderTouchableHighlightedButton = () => {
|
|
const TooltipHighlightedButtonStyle =
|
|
this.getTooltipHighlightedButtonStyle();
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
testID="tooltipTouchableHighlightedButton"
|
|
onPress={() => this.toggleTooltip()}
|
|
style={TooltipHighlightedButtonStyle}>
|
|
{this.props.children}
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
renderStaticHighlightedButton = () => {
|
|
const TooltipHighlightedButtonStyle =
|
|
this.getTooltipHighlightedButtonStyle();
|
|
|
|
return (
|
|
<View style={TooltipHighlightedButtonStyle}>{this.props.children}</View>
|
|
);
|
|
};
|
|
|
|
renderHighlightedButton = () => {
|
|
const { closeOnlyOnBackdropPress } = this.props;
|
|
if (closeOnlyOnBackdropPress) {
|
|
return this.renderTouchableHighlightedButton();
|
|
} else {
|
|
return this.renderStaticHighlightedButton();
|
|
}
|
|
};
|
|
|
|
renderContent = (withTooltip: boolean) => {
|
|
const { popover, withPointer, toggleOnPress, toggleAction } = this.props;
|
|
if (!withTooltip) {
|
|
return this.wrapWithPress(
|
|
toggleOnPress,
|
|
toggleAction,
|
|
this.props.children
|
|
);
|
|
}
|
|
|
|
const tooltipStyle = this.getTooltipStyle() as ViewStyle;
|
|
|
|
return (
|
|
<View>
|
|
{this.renderHighlightedButton()}
|
|
{withPointer && this.renderPointer(tooltipStyle.top)}
|
|
<View style={tooltipStyle} testID="tooltipPopoverContainer">
|
|
{popover}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
componentDidMount() {
|
|
this._isMounted = true;
|
|
// wait to compute onLayout values.
|
|
requestAnimationFrame(this.getElementPosition);
|
|
}
|
|
componentWillUnmount() {
|
|
this._isMounted = false;
|
|
}
|
|
|
|
getElementPosition = () => {
|
|
const { skipAndroidStatusBar } = this.props;
|
|
this.renderedElement &&
|
|
this.renderedElement.measure(
|
|
(
|
|
_frameOffsetX,
|
|
_frameOffsetY,
|
|
width,
|
|
height,
|
|
pageOffsetX,
|
|
pageOffsetY
|
|
) => {
|
|
this._isMounted &&
|
|
this.setState({
|
|
xOffset: pageOffsetX,
|
|
yOffset:
|
|
isIOS || skipAndroidStatusBar
|
|
? pageOffsetY
|
|
: pageOffsetY -
|
|
Platform.select({
|
|
android: StatusBar.currentHeight,
|
|
ios: 20,
|
|
default: 0,
|
|
}),
|
|
elementWidth: width,
|
|
elementHeight: height,
|
|
});
|
|
}
|
|
);
|
|
};
|
|
|
|
renderStaticModalContent = () => {
|
|
const { withOverlay, overlayColor } = this.props;
|
|
return (
|
|
<Fragment>
|
|
<TouchableOpacity
|
|
style={this.containerStyle(withOverlay, overlayColor)}
|
|
onPress={this.toggleTooltip}
|
|
activeOpacity={1}
|
|
/>
|
|
<View>{this.renderContent(true)}</View>
|
|
</Fragment>
|
|
);
|
|
};
|
|
|
|
renderTogglingModalContent = () => {
|
|
const { withOverlay, overlayColor } = this.props;
|
|
return (
|
|
<TouchableOpacity
|
|
style={this.containerStyle(withOverlay, overlayColor)}
|
|
onPress={this.toggleTooltip}
|
|
activeOpacity={1}>
|
|
{this.renderContent(true)}
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
renderModalContent = () => {
|
|
const { closeOnlyOnBackdropPress } = this.props;
|
|
if (closeOnlyOnBackdropPress) {
|
|
return this.renderStaticModalContent();
|
|
} else {
|
|
return this.renderTogglingModalContent();
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const { isVisible } = this.state;
|
|
const { onOpen, ModalComponent } = this.props;
|
|
return (
|
|
<View
|
|
collapsable={false}
|
|
ref={(e) => {
|
|
this.renderedElement = e;
|
|
}}>
|
|
{this.renderContent(false)}
|
|
<ModalComponent
|
|
animationType="fade"
|
|
visible={isVisible}
|
|
transparent
|
|
onShow={onOpen}>
|
|
{this.renderModalContent()}
|
|
</ModalComponent>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
export { Tooltip };
|
|
export default withTheme(Tooltip, 'Tooltip');
|