Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"release": "release-it",
"example": "yarn --cwd example",
"pods": "cd example && pod-install --quiet",
"bootstrap": "yarn example && yarn && yarn pods"
"bootstrap": "yarn example && yarn && yarn pods",
"format": "prettier --write \"src/**/*.{js,jsx,json,md,ts,tsx}\""
},
"keywords": [
"react-native",
Expand Down
5 changes: 3 additions & 2 deletions src/Backdrop/Backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Pressable,
StyleSheet,
} from 'react-native';
import { ANIMATION_DURATION } from '../constants';
// import { ANIMATION_DURATION } from '../constants';
import type { BackdropProps } from './Backdrop.d';

// On iOS, Modal orientations need to be manually specified
Expand Down Expand Up @@ -34,7 +34,8 @@ export default function Backdrop({
if (visible) {
setDelayedVisible(true);
} else {
setTimeout(() => setDelayedVisible(false), ANIMATION_DURATION);
setDelayedVisible(false);
// setTimeout(() => setDelayedVisible(false), ANIMATION_DURATION);
}
}, [visible]);

Expand Down
54 changes: 40 additions & 14 deletions src/Caret.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,76 @@ export type CaretProps = {
style?: ViewProps['style'];
};

export default ({ align, backgroundColor, position, style }: CaretProps) => {
export default function Caret({
align,
backgroundColor,
position,
style,
}: CaretProps) {
return (
<View
style={[
styles.container,
align === 'left' && styles.containerLeft,
align === 'center' && styles.containerCenter,
align === 'right' && styles.containerRight,
!!backgroundColor && { backgroundColor },
// !!backgroundColor && { borderColor: backgroundColor },
position === 'top' && styles.containerPositionTop,
position === 'bottom' && styles.containerPositionBottom,
position === 'left' && styles.containerPositionLeft,
position === 'right' && styles.containerPositionRight,
position === 'top' && {
borderTopColor: backgroundColor ?? POPOVER_BACKGROUND_COLOR,
},
position === 'bottom' && {
borderBottomColor: backgroundColor ?? POPOVER_BACKGROUND_COLOR,
},
position === 'left' && {
borderLeftColor: backgroundColor ?? POPOVER_BACKGROUND_COLOR,
},
position === 'right' && {
borderRightColor: backgroundColor ?? POPOVER_BACKGROUND_COLOR,
},
style,
]}
/>
);
};
}

const styles = StyleSheet.create({
container: {
width: CARET_SIDE_SIZE,
height: CARET_SIDE_SIZE,
backgroundColor: POPOVER_BACKGROUND_COLOR,
transform: [{ rotate: '45deg' }],
borderRadius: BORDER_RADIUS,
// borderColor: "transparent",
borderBottomColor: 'transparent',
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
borderTopColor: 'transparent',
borderWidth: CARET_SIDE_SIZE,
height: CARET_SIDE_SIZE * 2,
width: CARET_SIDE_SIZE * 2,
},
containerPositionTop: {
marginTop: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
marginBottom: CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2,
containerCenter: {
alignSelf: 'center',
},
containerLeft: {},
containerPositionBottom: {
bottom: CARET_SIDE_SIZE / 2,
marginBottom: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
marginTop: CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2,
},
containerPositionLeft: {
left: CARET_SIDE_SIZE / 2,
marginLeft: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
marginRight: CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2,
},
containerPositionRight: {
marginRight: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
marginLeft: CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2,
marginRight: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
right: CARET_SIDE_SIZE / 2,
},
containerCenter: {
alignSelf: 'center',
containerPositionTop: {
marginBottom: CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2,
marginTop: (CARET_SIDE_SIZE / 2 + BORDER_RADIUS / 2) * -1,
top: CARET_SIDE_SIZE / 2,
},
containerRight: {
alignSelf: 'flex-end',
Expand Down
63 changes: 48 additions & 15 deletions src/Popable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react';
Expand All @@ -26,6 +27,7 @@ export type PopableProps = {
backgroundColor?: PopoverProps['backgroundColor'];
caret?: PopoverProps['caret'];
caretPosition?: PopoverProps['caretPosition'];
caretStyle?: ViewProps['style'];
children: any;
content: PopoverProps['children'];
numberOfLines?: PopoverProps['numberOfLines'];
Expand All @@ -35,6 +37,10 @@ export type PopableProps = {
style?: PopoverProps['style'];
visible?: boolean;
wrapperStyle?: ViewProps['style'];
manualPopupPosition?: {
left: number;
top: number;
};
};

const DEFAULT_LAYOUT = {
Expand All @@ -53,6 +59,7 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
children,
caret,
caretPosition,
caretStyle,
content,
numberOfLines,
onAction,
Expand All @@ -61,6 +68,10 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
style,
visible,
wrapperStyle,
manualPopupPosition = {
left: 0,
top: 0,
},
},
ref
) {
Expand All @@ -75,12 +86,19 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
const [childrenLayout, setChildrenLayout] = useState(DEFAULT_LAYOUT);
const [computedPosition, setComputedPosition] = useState(position);
const isInteractive = typeof visible === 'undefined';
const isPopoverVisible = isInteractive ? popoverVisible : visible;
const isPopoverVisible = useMemo(() => {
return isInteractive ? popoverVisible : visible;
}, [isInteractive, popoverVisible, visible]);
const childrenRef = useRef<View>(null);
const popoverRef = useRef<View>(null);

useImperativeHandle(ref, () => ({
show: () => setPopoverVisible(true),
show: () => {
popoverRef.current?.measureInWindow((_x, _y, _width, _height) => {
setPopoverPagePosition({ left: _x, top: _y });
});
setPopoverVisible(true);
},
hide: () => setPopoverVisible(false),
}));

Expand All @@ -103,15 +121,15 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
) {
handlers.onPress = () => {
if (!visible) {
popoverRef.current?.measure(
(_x, _y, _width, _height, pageX, pageY) => {
setPopoverPagePosition({ left: pageX, top: pageY });
}
);
popoverRef.current?.measureInWindow((_x, _y, _width, _height) => {
setPopoverPagePosition({ left: _x, top: _y });
});
}

onAction?.(!visible);
setPopoverVisible(!visible);
if (!ref) {
setPopoverVisible(!visible);
}
};
} else {
handlers.onLongPress = () => {
Expand Down Expand Up @@ -197,6 +215,7 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
backgroundColor,
caret,
caretPosition,
caretStyle,
children: content,
numberOfLines,
position: computedPosition,
Expand Down Expand Up @@ -228,8 +247,14 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
{
position: 'absolute',
transform: [
{ translateX: popoverPagePosition.left },
{ translateY: popoverPagePosition.top },
{
translateX:
popoverPagePosition.left + manualPopupPosition.left,
},
{
translateY:
popoverPagePosition.top + manualPopupPosition.top,
},
],
},
style,
Expand All @@ -242,7 +267,11 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(
<Popover
ref={popoverRef}
{...sharedPopoverProps}
onLayout={handlePopoverLayout}
onLayout={() => {
setTimeout(() => {
handlePopoverLayout();
}, 100);
}}
visible={Platform.OS === 'web' ? isPopoverVisible : false}
style={[
computedPosition === 'top' && styles.popoverTop,
Expand All @@ -265,7 +294,11 @@ const Popable = forwardRef<PopableManager, PopableProps>(function Popable(

<Pressable
ref={childrenRef}
onLayout={handleChildrenLayout}
onLayout={() => {
setTimeout(() => {
handleChildrenLayout();
}, 100);
}}
{...handlers}
>
{children}
Expand All @@ -279,12 +312,12 @@ const styles = StyleSheet.create({
position: 'relative',
zIndex: 1,
},
popoverTop: {
bottom: '100%',
},
popoverBottom: {
top: '100%',
},
popoverTop: {
bottom: '100%',
},
});

export default Popable;
65 changes: 32 additions & 33 deletions src/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type PopoverProps = {
backgroundColor?: string;
caret?: boolean;
caretPosition?: 'left' | 'center' | 'right';
caretStyle?: ViewProps['style'];
children: string | React.ReactElement;
forceInitialAnimation?: boolean;
numberOfLines?: number;
Expand All @@ -31,6 +32,7 @@ const Popover = React.forwardRef<View, PopoverProps>(function Popover(
backgroundColor,
caret: withCaret = true,
caretPosition = 'center',
caretStyle = {},
children,
forceInitialAnimation = false,
numberOfLines,
Expand All @@ -50,41 +52,38 @@ const Popover = React.forwardRef<View, PopoverProps>(function Popover(
)
).current;

useEffect(
() => {
let animation: Animated.CompositeAnimation | undefined;
useEffect(() => {
let animation: Animated.CompositeAnimation | undefined;

if (animated) {
if (visible && (!prevVisible.current || forceInitialAnimation)) {
animation = Animated[animationType](opacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true,
});
} else if (!visible && (prevVisible.current || forceInitialAnimation)) {
animation = Animated[animationType](opacity, {
toValue: 0,
duration: ANIMATION_DURATION,
useNativeDriver: true,
});
}

animation?.start();
if (animated) {
if (visible && (!prevVisible.current || forceInitialAnimation)) {
animation = Animated[animationType](opacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true,
});
} else if (!visible && (prevVisible.current || forceInitialAnimation)) {
animation = Animated[animationType](opacity, {
toValue: 0,
duration: ANIMATION_DURATION,
useNativeDriver: true,
});
}

prevVisible.current = visible;
animation?.start();
}

return () => animation?.stop();
},
[visible] // eslint-disable-line react-hooks/exhaustive-deps
);
prevVisible.current = visible;

return () => animation?.stop();
}, [visible]);

const caret = (
<Caret
align={caretPosition}
position={position}
backgroundColor={backgroundColor}
style={styles.caret}
style={[styles.caret, caretStyle]}
/>
);

Expand Down Expand Up @@ -144,31 +143,31 @@ const Popover = React.forwardRef<View, PopoverProps>(function Popover(
});

const styles = StyleSheet.create({
caret: {
zIndex: 0,
},
container: {
width: POPOVER_WIDTH,
overflow: 'hidden',
width: POPOVER_WIDTH,
},
containerHorizontal: {
flexDirection: 'row',
},
content: {
flex: 1,
zIndex: 1,
backgroundColor: POPOVER_BACKGROUND_COLOR,
borderRadius: BORDER_RADIUS * 2,
flex: 1,
overflow: 'hidden',
},
contentTextOnly: {
padding: POPOVER_PADDING,
zIndex: 1,
},
contentText: {
color: POPOVER_FONT_COLOR,
fontSize: POPOVER_FONT_SIZE,
fontWeight: 'bold',
textAlign: 'center',
},
caret: {
zIndex: 0,
contentTextOnly: {
padding: POPOVER_PADDING,
},
});

Expand Down