103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import React, { ReactNode } from 'react'
|
|
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
|
import { styled, keyframes } from '../stitches.config'
|
|
|
|
const slideUpAndFade = keyframes({
|
|
'0%': { opacity: 0, transform: 'translateY(2px)' },
|
|
'100%': { opacity: 1, transform: 'translateY(0)' }
|
|
})
|
|
|
|
const slideRightAndFade = keyframes({
|
|
'0%': { opacity: 0, transform: 'translateX(-2px)' },
|
|
'100%': { opacity: 1, transform: 'translateX(0)' }
|
|
})
|
|
|
|
const slideDownAndFade = keyframes({
|
|
'0%': { opacity: 0, transform: 'translateY(-2px)' },
|
|
'100%': { opacity: 1, transform: 'translateY(0)' }
|
|
})
|
|
|
|
const slideLeftAndFade = keyframes({
|
|
'0%': { opacity: 0, transform: 'translateX(2px)' },
|
|
'100%': { opacity: 1, transform: 'translateX(0)' }
|
|
})
|
|
const StyledContent = styled(PopoverPrimitive.Content, {
|
|
borderRadius: 4,
|
|
padding: '$3 $3',
|
|
fontSize: 12,
|
|
lineHeight: 1,
|
|
color: '$text',
|
|
backgroundColor: '$background',
|
|
boxShadow:
|
|
'0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)',
|
|
'@media (prefers-reduced-motion: no-preference)': {
|
|
animationDuration: '400ms',
|
|
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
willChange: 'transform, opacity',
|
|
'&[data-state="open"]': {
|
|
'&[data-side="top"]': { animationName: slideDownAndFade },
|
|
'&[data-side="right"]': { animationName: slideLeftAndFade },
|
|
'&[data-side="bottom"]': { animationName: slideUpAndFade },
|
|
'&[data-side="left"]': { animationName: slideRightAndFade }
|
|
}
|
|
},
|
|
'.dark &': {
|
|
backgroundColor: '$mauve5',
|
|
boxShadow: '0px 5px 38px -2px rgba(22, 23, 24, 1), 0px 10px 20px 0px rgba(22, 23, 24, 1)'
|
|
}
|
|
})
|
|
|
|
const StyledArrow = styled(PopoverPrimitive.Arrow, {
|
|
fill: '$colors$mauve2',
|
|
'.dark &': {
|
|
fill: '$mauve5'
|
|
}
|
|
})
|
|
|
|
const StyledClose = styled(PopoverPrimitive.Close, {
|
|
all: 'unset',
|
|
fontFamily: 'inherit',
|
|
borderRadius: '100%',
|
|
height: 25,
|
|
width: 25,
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: '$text',
|
|
position: 'absolute',
|
|
top: 5,
|
|
right: 5
|
|
})
|
|
|
|
// Exports
|
|
export const PopoverRoot = PopoverPrimitive.Root
|
|
export const PopoverTrigger = PopoverPrimitive.Trigger
|
|
export const PopoverContent = StyledContent
|
|
export const PopoverArrow = StyledArrow
|
|
export const PopoverClose = StyledClose
|
|
|
|
interface IPopover {
|
|
content: string | ReactNode
|
|
open?: boolean
|
|
defaultOpen?: boolean
|
|
onOpenChange?: (open: boolean) => void
|
|
}
|
|
|
|
const Popover: React.FC<IPopover & React.ComponentProps<typeof PopoverContent>> = ({
|
|
children,
|
|
content,
|
|
open,
|
|
defaultOpen = false,
|
|
onOpenChange,
|
|
...rest
|
|
}) => (
|
|
<PopoverRoot open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
|
|
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
<PopoverContent sideOffset={5} {...rest}>
|
|
{content} <PopoverArrow offset={5} className="arrow" />
|
|
</PopoverContent>
|
|
</PopoverRoot>
|
|
)
|
|
|
|
export default Popover
|