Filter.tsx
1 import React, { useEffect, useState } from 'react' 2 import styled from 'styled-components' 3 import { Colors } from '../constants/styles' 4 import filterIcon from '../assets/images/filter.svg' 5 import arrowDownIcon from '../assets/images/arrowDown.svg' 6 7 export type FilterListProps = { 8 value: number 9 setValue: (value: number) => void 10 options: { value: number; text: string }[] 11 } 12 13 export const FilterList = ({ value, setValue, options }: FilterListProps) => { 14 const [isOpened, setIsOpened] = useState(false) 15 16 useEffect(() => { 17 window.addEventListener('click', () => setIsOpened(false)) 18 return () => { 19 window.removeEventListener('click', () => setIsOpened(false)) 20 } 21 }, []) 22 23 return ( 24 <Filter 25 onClick={(e) => { 26 e.stopPropagation() 27 setIsOpened(!isOpened) 28 }} 29 > 30 <Select> 31 <SelectTrigger>{options.find((option) => option.value === value)?.text}</SelectTrigger> 32 <SelectOptions className={isOpened ? 'opened' : undefined}> 33 {options.map((option, key) => ( 34 <SelectOption 35 className={option.value === value ? 'selected' : ''} 36 key={key} 37 onClick={() => setValue(option.value)} 38 > 39 {option.text} 40 </SelectOption> 41 ))} 42 </SelectOptions> 43 </Select> 44 </Filter> 45 ) 46 } 47 48 const Filter = styled.button` 49 display: flex; 50 align-items: center; 51 height: 36px; 52 position: relative; 53 padding-left: 5px; 54 padding-right: 1px; 55 font-weight: 500; 56 font-size: 15px; 57 line-height: 22px; 58 color: ${Colors.VioletDark}; 59 border: 1px solid #e6ecf0; 60 border-radius: 14px; 61 appearance: none; 62 outline: none; 63 64 &:focus, 65 &:active { 66 border: 1px solid ${Colors.Violet}; 67 } 68 ` 69 70 const Select = styled.div` 71 position: relative; 72 display: flex; 73 flex-direction: column; 74 ` 75 const SelectTrigger = styled.div` 76 position: relative; 77 min-width: 167px; 78 padding: 0 28px; 79 box-sizing: border-box; 80 81 @media (max-width: 600px) { 82 font-size: 0; 83 min-width: unset; 84 } 85 86 &::before { 87 content: ''; 88 width: 24px; 89 height: 24px; 90 position: absolute; 91 top: 50%; 92 left: 0; 93 transform: translateY(-50%); 94 background-image: url(${filterIcon}); 95 } 96 97 &::after { 98 content: ''; 99 width: 24px; 100 height: 24px; 101 position: absolute; 102 top: 50%; 103 right: 0; 104 transform: translateY(-50%); 105 background-image: url(${arrowDownIcon}); 106 } 107 ` 108 const SelectOptions = styled.div` 109 position: absolute; 110 display: block; 111 width: 175px; 112 top: calc(100% + 11px); 113 right: 0; 114 opacity: 0; 115 visibility: hidden; 116 pointer-events: none; 117 transition: all 0.3s; 118 border: 1px solid ${Colors.GrayBorder}; 119 border-radius: 16px 4px 16px 16px; 120 121 &.opened { 122 opacity: 1; 123 background: ${Colors.White}; 124 visibility: visible; 125 pointer-events: all; 126 z-index: 10; 127 } 128 ` 129 const SelectOption = styled.span` 130 position: relative; 131 display: block; 132 width: 100%; 133 font-weight: 500; 134 font-size: 15px; 135 line-height: 22px; 136 text-align: center; 137 padding: 11px 0; 138 cursor: pointer; 139 transition: all 0.3s; 140 141 &:first-child { 142 border-radius: 16px 4px 0 0; 143 } 144 145 &:last-child { 146 border-radius: 0 0 16px 16px; 147 } 148 149 &:not(:last-child) { 150 border-bottom: 1px solid ${Colors.GrayBorder}; 151 } 152 153 &:hover { 154 background: ${Colors.Violet}; 155 color: ${Colors.White}; 156 } 157 158 &.selected { 159 @media (max-width: 600px) { 160 background: ${Colors.Violet}; 161 color: ${Colors.White}; 162 } 163 } 164 `