slideOverPanel.tsx 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import {ForwardedRef, forwardRef, useEffect} from 'react';
  2. import isPropValid from '@emotion/is-prop-valid';
  3. import styled from '@emotion/styled';
  4. import {motion} from 'framer-motion';
  5. const PANEL_WIDTH = '50vw';
  6. const PANEL_HEIGHT = '50vh';
  7. const INITIAL_STYLES = {
  8. bottom: {opacity: 0, x: 0, y: 0},
  9. right: {opacity: 0, x: PANEL_WIDTH, y: 0},
  10. };
  11. const FINAL_STYLES = {
  12. bottom: {opacity: 0, x: 0, y: PANEL_HEIGHT},
  13. right: {opacity: 0, x: PANEL_WIDTH},
  14. };
  15. type SlideOverPanelProps = {
  16. children: React.ReactNode;
  17. collapsed: boolean;
  18. onOpen?: () => void;
  19. slidePosition?: 'right' | 'bottom';
  20. };
  21. export default forwardRef(SlideOverPanel);
  22. function SlideOverPanel(
  23. {collapsed, children, onOpen, slidePosition}: SlideOverPanelProps,
  24. ref: ForwardedRef<HTMLDivElement>
  25. ) {
  26. useEffect(() => {
  27. if (!collapsed && onOpen) {
  28. onOpen();
  29. }
  30. }, [collapsed, onOpen]);
  31. const initial = slidePosition ? INITIAL_STYLES[slidePosition] : INITIAL_STYLES.right;
  32. const final = slidePosition ? FINAL_STYLES[slidePosition] : FINAL_STYLES.right;
  33. return (
  34. <_SlideOverPanel
  35. ref={ref}
  36. collapsed={collapsed}
  37. initial={initial}
  38. animate={!collapsed ? {opacity: 1, x: 0, y: 0} : final}
  39. slidePosition={slidePosition}
  40. transition={{
  41. type: 'spring',
  42. stiffness: 500,
  43. damping: 50,
  44. }}
  45. >
  46. {children}
  47. </_SlideOverPanel>
  48. );
  49. }
  50. const _SlideOverPanel = styled(motion.div, {
  51. shouldForwardProp: prop =>
  52. ['animate', 'transition', 'initial'].includes(prop) ||
  53. (prop !== 'collapsed' && isPropValid(prop)),
  54. })<{
  55. collapsed: boolean;
  56. slidePosition?: 'right' | 'bottom';
  57. }>`
  58. ${p =>
  59. p.slidePosition === 'bottom'
  60. ? `
  61. width: 100%;
  62. height: ${PANEL_HEIGHT};
  63. position: sticky;
  64. border-top: 1px solid ${p.theme.border};
  65. `
  66. : `
  67. width: ${PANEL_WIDTH};
  68. height: 100%;
  69. position: fixed;
  70. top: 0;
  71. border-left: 1px solid ${p.theme.border};
  72. `}
  73. bottom: 0;
  74. right: 0;
  75. background: ${p => p.theme.background};
  76. color: ${p => p.theme.textColor};
  77. text-align: left;
  78. z-index: ${p => p.theme.zIndex.sidebar - 1};
  79. ${p =>
  80. p.collapsed
  81. ? 'overflow: hidden;'
  82. : `overflow-x: hidden;
  83. overflow-y: scroll;`}
  84. `;