-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c19028f
commit 784f6a9
Showing
7 changed files
with
270 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import DropdownComponent from "./index"; | ||
import { useState } from "react"; | ||
import { Box, Grid } from "../Box"; | ||
import Text from "../Text"; | ||
|
||
export default { | ||
title: "Components/Dropdown", | ||
component: DropdownComponent, | ||
argTypes: {}, | ||
}; | ||
|
||
export const Dropdown = () => { | ||
const [value, setValue] = useState(1); | ||
const [value2, setValue2] = useState(1); | ||
const [value3, setValue3] = useState(1); | ||
return ( | ||
<Grid gridTemplateColumns={"repeat(3, 1fr)"} mt={"10px"}> | ||
<Box> | ||
<Text variant={"h5"} mb="20px"> | ||
Simple | ||
</Text> | ||
<DropdownComponent | ||
items={[ | ||
{ value: 1, title: "1D" }, | ||
{ value: 2, title: "1W" }, | ||
{ value: 3, title: "1M" }, | ||
{ value: 4, title: "1Y" }, | ||
]} | ||
value={value} | ||
onChange={setValue} | ||
/> | ||
</Box> | ||
<Box> | ||
<Text variant={"h5"} mb="20px"> | ||
With long title | ||
</Text> | ||
<DropdownComponent | ||
items={[ | ||
{ value: 1, title: "Short" }, | ||
{ value: 2, title: "Short" }, | ||
{ value: 3, title: "Looooooooooooooooong" }, | ||
{ value: 4, title: "Short" }, | ||
]} | ||
value={value2} | ||
onChange={setValue2} | ||
/> | ||
</Box> | ||
<Box> | ||
<Text variant={"h5"} mb="20px"> | ||
With overflow | ||
</Text> | ||
<DropdownComponent | ||
items={[ | ||
{ value: 1, title: "Item 1" }, | ||
{ value: 2, title: "Item 2" }, | ||
{ value: 3, title: "Item 3" }, | ||
{ value: 4, title: "Item 4" }, | ||
{ value: 5, title: "Item 5" }, | ||
{ value: 6, title: "Item 6" }, | ||
{ value: 7, title: "Item 7" }, | ||
{ value: 8, title: "Item 8" }, | ||
{ value: 9, title: "Item 9" }, | ||
{ value: 10, title: "Item 10" }, | ||
]} | ||
value={value3} | ||
onChange={setValue3} | ||
/> | ||
</Box> | ||
<Text color="strokeGray" variant={"h5"} mt="40px"> | ||
Overlapping content. | ||
</Text> | ||
<Text color="strokeGray" variant={"h5"} mt="40px"> | ||
Overlapping content. | ||
</Text> | ||
<Text color="strokeGray" variant={"h5"} mt="40px"> | ||
Overlapping content. | ||
</Text> | ||
</Grid> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { Box, Grid } from "../Box"; | ||
import styled from "styled-components"; | ||
import { useEffect, useMemo, useRef, useState } from "react"; | ||
import { rgba } from "polished"; | ||
import { useOnClickOutside } from "./useOnClickOutside"; | ||
import { ArrowDownIcon } from "../Svg"; | ||
|
||
type DropdownItemValue = string | number; | ||
interface IProps { | ||
items: { title?: string; value?: DropdownItemValue }[]; | ||
value?: DropdownItemValue; | ||
onChange?: (value: any) => void; | ||
} | ||
|
||
const DropdownWrap = styled(Grid)<{ maxHeight: number; opened?: boolean }>` | ||
width: max-content; | ||
max-height: ${({ maxHeight }) => maxHeight}px; | ||
padding: 0 0 4px; | ||
overflow: hidden; | ||
box-sizing: border-box; | ||
background: ${({ theme, opened }) => | ||
!opened ? rgba(theme.colors.primaryDefault, 0.08) : rgba(theme.colors.strokeGray, 0.16)}; | ||
outline: 0.5px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; | ||
outline-offset: -0.5px; | ||
border-radius: 12px; | ||
backdrop-filter: blur(10px); | ||
& svg { | ||
transform: rotate(${({ opened }) => (opened ? "180deg" : "0")}); | ||
stroke: ${({ theme, opened }) => (!opened ? theme.colors.textGray : theme.colors.white)}; | ||
transition: all 0.15s; | ||
} | ||
&:hover { | ||
background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.16)}; | ||
& svg { | ||
stroke: ${({ theme }) => theme.colors.white}; | ||
} | ||
} | ||
transition: max-height 0.15s, background 0.2s; | ||
`; | ||
|
||
const DropdownItemWrap = styled.button<{ active?: boolean }>` | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
padding: 0 40px 0 20px; | ||
height: 26px; | ||
border: none; | ||
outline: none; | ||
font-size: 14px; | ||
font-weight: 400; | ||
line-height: 18px; | ||
cursor: pointer; | ||
color: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.textGray)}; | ||
background: transparent; | ||
&:hover, | ||
&:focus { | ||
color: ${({ theme }) => theme.colors.primaryDefault}; | ||
} | ||
transition: color 0.15s; | ||
`; | ||
|
||
const DropdownActivatorWrap = styled(DropdownItemWrap)` | ||
height: 34px; | ||
padding: 0 20px; | ||
`; | ||
export default function Dropdown({ items, value, onChange }: IProps) { | ||
const activeItem = useMemo(() => items.find((v) => v.value === value), [items, value]); | ||
const minHeight = 34; | ||
const [maxHeight, setMaxHeight] = useState(minHeight); | ||
|
||
const [opened, setOpened] = useState(false); | ||
|
||
const listRef = useRef<HTMLDivElement>(null); | ||
|
||
useOnClickOutside(listRef, () => { | ||
setOpened(false); | ||
}); | ||
|
||
useEffect(() => { | ||
if (listRef.current) { | ||
setMaxHeight(listRef.current.scrollHeight); | ||
} | ||
}, [items]); | ||
|
||
const height = useMemo(() => (opened ? maxHeight : minHeight), [opened, maxHeight]); | ||
|
||
return ( | ||
<Box height={`${minHeight}px`} overflow={"visible"} position={"relative"} zIndex={100}> | ||
<DropdownWrap | ||
ref={listRef} | ||
maxHeight={height} | ||
onFocus={() => { | ||
setOpened(true); | ||
listRef.current?.scrollTo(0, 0); //autoscroll bug fix | ||
}} | ||
onBlur={() => { | ||
setTimeout(() => { | ||
if (document.activeElement) { | ||
const el = document.activeElement as any; | ||
//close dropdown if new active element is interactive element from outside | ||
if (el.tabIndex !== -1 && !listRef.current?.contains(el)) { | ||
setOpened(false); | ||
} | ||
} | ||
}, 0); | ||
}} | ||
opened={opened} | ||
> | ||
<DropdownActivatorWrap | ||
onMouseDown={() => { | ||
setTimeout(() => { | ||
setOpened(!opened); | ||
}, 0); | ||
}} | ||
active={true} | ||
tabIndex={-1} | ||
> | ||
<span>{activeItem?.title || activeItem?.value || "-"}</span> | ||
<ArrowDownIcon size={"16px"} /> | ||
</DropdownActivatorWrap> | ||
|
||
<Grid maxHeight={200} overflowY={"auto"} gridGap={"4px"} pt={"6px"}> | ||
{items.map((item) => ( | ||
<DropdownItemWrap | ||
key={item.value} | ||
onClick={() => { | ||
if (onChange) onChange(item.value); | ||
setOpened(false); | ||
}} | ||
active={item.value === value} | ||
> | ||
{item.title || item.value} | ||
</DropdownItemWrap> | ||
))} | ||
</Grid> | ||
</DropdownWrap> | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { RefObject, useEffect, useRef } from 'react'; | ||
|
||
export function useOnClickOutside<T extends HTMLElement>(node: RefObject<T | undefined>, handler: undefined | (() => void), ignoredNodes: Array<RefObject<T | undefined>> = []) { | ||
const handlerRef = useRef<undefined | (() => void)>(handler); | ||
|
||
useEffect(() => { | ||
handlerRef.current = handler; | ||
}, [handler]); | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (e: MouseEvent) => { | ||
const nodeClicked = node.current?.contains(e.target as Node); | ||
const ignoredNodeClicked = ignoredNodes.reduce((reducer, val) => reducer || !!val.current?.contains(e.target as Node), false); | ||
|
||
if ((nodeClicked || ignoredNodeClicked) ?? false) { | ||
return; | ||
} | ||
|
||
if (handlerRef.current) handlerRef.current(); | ||
}; | ||
|
||
document.addEventListener('mousedown', handleClickOutside); | ||
|
||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [node, ignoredNodes]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from "react"; | ||
import Svg from "../Svg"; | ||
import { SvgProps } from "../types"; | ||
|
||
const Icon: React.FC<React.PropsWithChildren<SvgProps>> = (props) => ( | ||
<Svg viewBox="0 0 16 16" {...props} fill="none"> | ||
<path d="M13 6L8 11L3 6" strokeLinecap="round" strokeLinejoin="round" /> | ||
</Svg> | ||
); | ||
|
||
export default Icon; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { default as ArrowLeftIcon } from "./Icons/ArrowLeftIcon"; | ||
export { default as QuestionIcon } from "./Icons/QuestionIcon"; | ||
export { default as InfoIcon } from "./Icons/InfoIcon"; | ||
export { default as ArrowDownIcon } from "./Icons/ArrowDownIcon"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters