123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- import type {ForwardedRef} from 'react';
- import {forwardRef, useEffect} from 'react';
- import isPropValid from '@emotion/is-prop-valid';
- import {css} from '@emotion/react';
- import styled from '@emotion/styled';
- import {AnimatePresence, motion} from 'framer-motion';
- import {space} from 'sentry/styles/space';
- const PANEL_WIDTH = '50vw';
- const PANEL_HEIGHT = '50vh';
- const OPEN_STYLES = {
- bottom: {opacity: 1, x: 0, y: 0},
- right: {opacity: 1, x: 0, y: 0},
- };
- const COLLAPSED_STYLES = {
- bottom: {opacity: 0, x: 0, y: PANEL_HEIGHT},
- right: {opacity: 0, x: PANEL_WIDTH, y: 0},
- };
- type SlideOverPanelProps = {
- children: React.ReactNode;
- collapsed: boolean;
- onOpen?: () => void;
- slidePosition?: 'right' | 'bottom';
- };
- export default forwardRef(SlideOverPanel);
- function SlideOverPanel(
- {collapsed, children, onOpen, slidePosition}: SlideOverPanelProps,
- ref: ForwardedRef<HTMLDivElement>
- ) {
- useEffect(() => {
- if (!collapsed && onOpen) {
- onOpen();
- }
- }, [collapsed, onOpen]);
- const openStyle = slidePosition ? OPEN_STYLES[slidePosition] : OPEN_STYLES.right;
- const collapsedStyle = slidePosition
- ? COLLAPSED_STYLES[slidePosition]
- : COLLAPSED_STYLES.right;
- return (
- <AnimatePresence>
- {!collapsed && (
- <_SlideOverPanel
- ref={ref}
- initial={collapsedStyle}
- animate={openStyle}
- exit={collapsedStyle}
- slidePosition={slidePosition}
- transition={{
- type: 'spring',
- stiffness: 500,
- damping: 50,
- }}
- >
- {children}
- </_SlideOverPanel>
- )}
- </AnimatePresence>
- );
- }
- const _SlideOverPanel = styled(motion.div, {
- shouldForwardProp: prop =>
- ['initial', 'animate', 'exit', 'transition'].includes(prop) ||
- (prop !== 'collapsed' && isPropValid(prop)),
- })<{
- slidePosition?: 'right' | 'bottom';
- }>`
- position: fixed;
- top: ${space(2)};
- right: 0;
- bottom: ${space(2)};
- left: ${space(2)};
- overflow: auto;
- z-index: ${p => p.theme.zIndex.modal + 1};
- box-shadow: ${p => p.theme.dropShadowHeavy};
- background: ${p => p.theme.background};
- color: ${p => p.theme.textColor};
- text-align: left;
- @media (min-width: ${p => p.theme.breakpoints.small}) {
- ${p =>
- p.slidePosition === 'bottom'
- ? css`
- position: sticky;
- width: 100%;
- height: ${PANEL_HEIGHT};
- right: 0;
- bottom: 0;
- left: 0;
- `
- : css`
- position: fixed;
- width: ${PANEL_WIDTH};
- height: 100%;
- top: 0;
- right: 0;
- bottom: 0;
- left: auto;
- `}
- }
- `;
|