omnivore/packages/web/components/elements/DropdownElements.tsx

226 lines
5 KiB
TypeScript
Raw Permalink Normal View History

2022-02-11 17:24:33 +00:00
import {
Separator,
Item,
Content,
Root,
2023-10-04 08:18:50 +00:00
Trigger,
2022-02-11 17:24:33 +00:00
Arrow,
Label,
Portal,
2022-02-11 17:24:33 +00:00
} from '@radix-ui/react-dropdown-menu'
2023-02-27 02:34:12 +00:00
import { PopperContentProps } from '@radix-ui/react-popover'
import { CSS } from '@stitches/react'
2022-02-11 17:24:33 +00:00
import { styled } from './../tokens/stitches.config'
2023-03-03 10:54:09 +00:00
const StyledItem = styled(Item, {
fontSize: '14px',
fontWeight: '400',
py: '10px',
px: '15px',
2022-02-11 17:24:33 +00:00
cursor: 'default',
color: '$utilityTextDefault',
2022-02-11 17:24:33 +00:00
'&:focus': {
outline: 'none',
backgroundColor: '$thLeftMenuBackground',
2022-02-11 17:24:33 +00:00
},
2023-03-03 10:54:09 +00:00
})
2022-02-11 17:24:33 +00:00
const DropdownTrigger = styled(Trigger, {
fontSize: '100%',
border: 0,
padding: 0,
backgroundColor: 'transparent',
'&:hover': {
opacity: 1.0,
2022-02-11 17:24:33 +00:00
},
2023-06-12 06:53:47 +00:00
'&:focus': {
outline: 'none',
},
2022-02-11 17:24:33 +00:00
})
2023-10-04 08:18:50 +00:00
const StyledTriggerItem = styled(Trigger, {
2022-02-11 17:24:33 +00:00
'&[data-state="open"]': {
outline: 'none',
backgroundColor: '$grayBgHover',
},
})
2022-03-30 12:24:29 +00:00
export const DropdownContent = styled(Content, {
width: 195,
2024-03-04 14:49:03 +00:00
zIndex: 100,
backgroundColor: '$grayBg',
borderRadius: '6px',
outline: '1px solid #323232',
border: '1px solid $grayBorder',
2023-08-31 09:34:06 +00:00
boxShadow: '0px 1px 2px 0px rgba(0, 0, 0, 0.05);',
'--arrow-visibility': '',
'&[data-side="top"]': {
'--arrow-visibility': 'collapse',
},
'&[data-side="bottom"]': {
'--arrow-top': -13,
},
'&[data-align="start"]': {
'--arrow-visibility': 'collapse',
},
'&[data-align="center"]': {
'--arrow-before-left': 'auto',
'--arrow-before-right': '90px',
'--arrow-after-left': 'auto',
'--arrow-after-right': '91px',
},
'&[data-align="end"]': {
'--arrow-before-left': 'auto',
'--arrow-before-right': '9px',
'--arrow-after-left': 'auto',
'--arrow-after-right': '10px',
},
'&:before': {
top: 'calc(var(--arrow-top) * 1px - 2px)',
left: 'var(--arrow-before-left)',
right: 'var(--arrow-before-right)',
border: '8px solid transparent',
borderBottomColor: '$grayBorder',
},
'&:after': {
top: 'calc(var(--arrow-top) * 1px)',
left: 'var(--arrow-after-left)',
right: 'var(--arrow-after-right)',
border: '7px solid transparent',
borderBottomColor: '$grayBg',
},
'&:before, &:after': {
visibility: 'var(--arrow-visibility)',
position: 'absolute',
display: 'inline-block',
content: '',
},
2022-02-11 17:24:33 +00:00
})
const StyledArrow = styled(Arrow, {
visibility: 'hidden',
2022-02-11 17:24:33 +00:00
})
const StyledLabel = styled(Label, {
color: '$utilityTextDefault',
2022-02-11 17:24:33 +00:00
fontSize: 13,
padding: '5px 10px',
cursor: 'default',
})
2022-03-24 01:25:40 +00:00
export type DropdownAlignment = 'start' | 'end' | 'center'
export type DropdownSide = 'top' | 'right' | 'bottom' | 'left'
2022-03-22 02:15:04 +00:00
2022-02-11 17:24:33 +00:00
type DropdownProps = {
labelText?: string
triggerElement: React.ReactNode
2022-03-31 13:03:50 +00:00
children: React.ReactNode
2022-03-30 12:24:29 +00:00
styledArrow?: boolean
2022-03-31 13:03:50 +00:00
align?: DropdownAlignment
side?: DropdownSide
sideOffset?: number
2022-03-31 13:03:50 +00:00
disabled?: boolean
2022-03-30 12:24:29 +00:00
css?: CSS
modal?: boolean
onOpenChange?: (open: boolean) => void
2022-02-11 17:24:33 +00:00
}
export const DropdownSeparator = styled(Separator, {
2022-03-30 12:24:29 +00:00
height: '1px',
2022-02-11 17:24:33 +00:00
margin: 0,
backgroundColor: '$homeDivider',
2022-02-11 17:24:33 +00:00
})
type DropdownOptionProps = {
onSelect: () => void
title?: string
children?: React.ReactNode
hideSeparator?: boolean
}
export function DropdownOption(props: DropdownOptionProps): JSX.Element {
return (
<>
<StyledItem
onSelect={() => {
props.onSelect()
}}
onClick={(event) => event.stopPropagation()}
>
2022-02-11 17:24:33 +00:00
{props.title ?? props.children}
</StyledItem>
</>
)
}
2023-02-27 02:34:12 +00:00
export function Dropdown(
props: DropdownProps & PopperContentProps
): JSX.Element {
const {
children,
align,
triggerElement,
labelText,
disabled = false,
side = 'bottom',
sideOffset = 0,
alignOffset = 0,
css,
2023-02-27 02:34:12 +00:00
modal,
onOpenChange,
} = props
2022-02-11 17:24:33 +00:00
return (
<Root modal={modal} onOpenChange={props.onOpenChange}>
2023-06-26 04:30:15 +00:00
<DropdownTrigger
disabled={disabled}
2024-02-07 17:49:12 +00:00
css={{
height: '100%',
cursor: 'pointer',
'&:hover': { opacity: '1.0' },
}}
2023-06-26 04:30:15 +00:00
>
{triggerElement}
</DropdownTrigger>
<Portal>
<DropdownContent
css={css}
onInteractOutside={() => {
// remove focus from dropdown
;(document.activeElement as HTMLElement).blur()
}}
side={side}
sideOffset={sideOffset}
align={align ? align : 'center'}
alignOffset={alignOffset}
onCloseAutoFocus={(event) => {
event.preventDefault()
}}
>
{labelText && <StyledLabel>{labelText}</StyledLabel>}
{children}
<StyledArrow />
</DropdownContent>
</Portal>
2022-02-11 17:24:33 +00:00
</Root>
)
}
export function NestedDropdown(props: DropdownProps): JSX.Element {
return (
<Root>
<StyledTriggerItem>{props.triggerElement}</StyledTriggerItem>
<DropdownContent
sideOffset={12}
onInteractOutside={() => {
// remove focus from sub-dropdown
;(document.activeElement as HTMLElement).blur()
}}
>
{props.children}
<StyledArrow offset={13} />
</DropdownContent>
</Root>
)
}