modal.js
import { useState } from 'react';
import styled from 'styled-components';
export const ModalBackdrop = styled.div` //모달 창 배경화면
//포지션을 고정시키고, z-index를 줌으로 뒤로 보냈다. 상하좌우 0값을 줌으로 화면전체 회색 화면이 차게 값줌. display 값은 그리드를 줌
position: fixed;
z-index: 999;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0,0,0,0.4);
display: grid;
place-items: center;
`;
export const ModalContainer = styled.div` //버튼이 들어갈 박스
height: 15rem;
text-align: center;
margin: 120px auto;
`;
export const ModalBtn = styled.button`// 모달 버튼이다
// curosr: grab은 버튼에 마우스를 가져가면 움켜쥐는 아이콘이 뜬다
background-color: #4000c7;
text-decoration: none;
border: none;
padding: 20px;
color: white;
border-radius: 30px;
cursor: grab;
`;
export const ModalView = styled.div.attrs(props => ({
role: 'dialog'
}))`
border-radius: 10px;
background-color: #ffffff;
width: 300px;
height: 100px;
// span안에 close-btn css설정이다
> span.close-btn {
margin-top: 5px;
cursor: pointer;
}
// div안에 글귀이다
> div.desc {
margin-top: 25px;
color: #4000c7;
}
`;
export const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const openModalHandler = () => {
setIsOpen(!isOpen);
};
return (
<>
<ModalContainer>
<ModalBtn onClick={openModalHandler}>
{isOpen === false ? 'Open Modal' : 'Opened!'}
</ModalBtn>
{isOpen === true ? <ModalBackdrop onClick={openModalHandler}>
<ModalView onClick={(e) => e.stopPropagation()}>
<span onClick={openModalHandler} className='close-btn'>×</span>
<div className='desc'>HELLO CODESTATES!</div>
</ModalView>
</ModalBackdrop> : null}
</ModalContainer>
</>
);
};
tab.js
import { useState } from 'react';
import styled from 'styled-components';
const TabMenu = styled.ul` //ul로 메뉴탭 표현
//배경에 submenu와 선택시 focused, 표시될 글이있다.(div.desc)
background-color: #dcdcdc;
color: rgba(73, 73, 73, 0.5);
font-weight: bold;
display: flex;
flex-direction: row;
justify-items: center;
align-items: center;
list-style: none;
margin-bottom: 7rem;
.submenu {
width: 100%;
padding: 15px 10px;
cursor: pointer;
}
.focused {
background-color: #4000c7;
color: rgba(255, 255, 255, 1);
transition: 0.3s;
}
& div.desc {
text-align: center;
}
`;
const Desc = styled.div`
text-align: center;
`;
export const Tab = () => {
const [currentTab, setCurrentTab] = useState(0);
const menuArr = [
{ name: 'Tab1', content: 'Tab menu ONE' },
{ name: 'Tab2', content: 'Tab menu TWO' },
{ name: 'Tab3', content: 'Tab menu THREE' }
];
const selectMenuHandler = (index) => {
setCurrentTab(index);
};
return (
<>
<div>
<TabMenu>
{menuArr.map((ele, index) => {
return (
<li
key={index}
className={currentTab === index ? 'submenu focused' : 'submenu'}
onClick={() => selectMenuHandler(index)}
>
{ele.name}
</li>
);
})}
</TabMenu>
<Desc>
<p>{menuArr[currentTab].content}</p>
</Desc>
</div>
</>
);
};
TOGGLE
import { useState } from 'react';
import styled from 'styled-components';
const ToggleContainer = styled.div`
//ToggleContainer 안에 toggle-container와 toggle-circle을 넣어준다. transition으로 움직이듯한 효과를 준다.
position: relative;
margin-top: 8rem;
left: 47%;
cursor: pointer;
> .toggle-container {
width: 50px;
height: 24px;
border-radius: 30px;
background-color: #8b8b8b;
transition: all .2s ease;
&.toggle--checked {
background-color: #4000c7;
}
}
> .toggle-circle {
position: absolute;
top: 1px;
left: 1px;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: #fafafa;
transition: all .25s ease;
&.toggle--checked {
left: 27px;
}
}
`;
const Desc = styled.div`
text-align: center;
margin-top: 1rem;
`;
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
setisOn(!isOn);
};
return (
<>
<ToggleContainer onClick={toggleHandler}>
<div className={`toggle-container ${isOn ? 'toggle--checked' : ''}`} />
</ToggleContainer>
{isOn ? <Desc>Toggle Switch ON</Desc> : <Desc>Toggle Switch OFF</Desc>}
</>
);
};
Tag.js
import { useState } from 'react';
import styled from 'styled-components';
export const TagsInput = styled.div`
margin: 8rem auto;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
min-height: 48px;
width: 480px;
padding: 0 8px;
border: 1px solid rgb(214, 216, 218);
border-radius: 6px;
> ul {
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 8px 0 0 0;
> .tag {
width: auto;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding: 0 8px;
font-size: 14px;
list-style: none;
border-radius: 6px;
margin: 0 8px 8px 0;
background: #4000c7;
> .tag-close-icon {
display: block;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
font-size: 14px;
margin-left: 8px;
color: #4000c7;
border-radius: 50%;
background: #fff;
cursor: pointer;
}
}
}
> input {
flex: 1;
border: none;
height: 46px;
font-size: 14px;
padding: 4px 0 0 0;
:focus {
outline: transparent;
}
}
&:focus-within {
border: 1px solid #4000c7;
}
`;
export const Tag = () => {
const initialTags = ['CodeStates', 'kimcoding'];
const [tags, setTags] = useState(initialTags);
const removeTags = (indexToRemove) => {
setTags(tags.filter((_, index) => index !== indexToRemove));
};
const addTags = (event) => {
const filtered = tags.filter((el) => el === event.target.value);
if (event.target.value !== '' && filtered.length === 0) {
setTags([...tags, event.target.value]);
event.target.value = '';
}
};
return (
<>
<TagsInput>
<ul id='tags'>
{tags.map((tag, index) => (
<li key={index} className='tag'>
<span className='tag-title'>{tag}</span>
<span className='tag-close-icon' onClick={() => removeTags(index)}>
×
</span>
</li>
))}
</ul>
<input className='tag-input' type='text' onKeyUp={(event) => (event.key === 'Enter' ? addTags(event) : null)} placeholder='Press enter to add tags' />
</TagsInput>
</>
);
};
Autocomplete.js
import { useState, useEffect } from 'react';
import styled from 'styled-components';
const deselectedOptions = [
'rustic',
'antique',
'vinyl',
'vintage',
'refurbished',
'신품',
'빈티지',
'중고A급',
'중고B급',
'골동품'
];
const boxShadow = '0 4px 6px rgb(32 33 36 / 28%)';
const activeBorderRadius = '1rem 1rem 0 0';
const inactiveBorderRadius = '1rem 1rem 1rem 1rem';
export const InputContainer = styled.div`
margin-top: 8rem;
background-color: #ffffff;
display: flex;
flex-direction: row;
padding: 1rem;
border: 1px solid rgb(223, 225, 229);
border-radius: ${(props) =>
props.hasText ? activeBorderRadius : inactiveBorderRadius};
z-index: 3;
box-shadow: ${(props) => (props.hasText ? boxShadow : 0)};
&:focus-within {
box-shadow: ${boxShadow};
}
> input {
flex: 1 0 0;
background-color: transparent;
border: none;
margin: 0;
padding: 0;
outline: none;
font-size: 16px;
}
> div.delete-button {
cursor: pointer;
}
`;
export const DropDownContainer = styled.ul`
background-color: #ffffff;
display: block;
margin-left: auto;
margin-right: auto;
list-style-type: none;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 0px;
margin-top: -1px;
padding: 0.5rem 0;
border: 1px solid rgb(223, 225, 229);
border-radius: 0 0 1rem 1rem;
box-shadow: ${boxShadow};
z-index: 3;
> li {
padding: 0 1rem;
&:hover {
background-color: #eee;
}
&.selected {
background-color: #ebe5f9;
}
}
`;
export const Autocomplete = () => {
const [hasText, setHasText] = useState(false);
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState(deselectedOptions);
const [selected, setSelected] = useState(-1);
useEffect(() => {
if (inputValue === '') {
setHasText(false);
}
}, [inputValue]);
const handleInputChange = (event) => {
const { value } = event.target;
if (value.includes('\\')) return;
value ? setHasText(true) : setHasText(false);
setInputValue(value);
const filterRegex = new RegExp(value, 'i');
const resultOptions = deselectedOptions.filter((option) =>
option.match(filterRegex)
);
setOptions(resultOptions);
};
const handleDropDownClick = (clickedOption) => {
setInputValue(clickedOption);
const resultOptions = deselectedOptions.filter(
(option) => option === clickedOption
);
setOptions(resultOptions);
};
const handleDeleteButtonClick = () => {
setInputValue('');
};
const handleKeyUp = (event) => {
if (event.getModifierState("Fn") || event.getModifierState("Hyper") || event.getModifierState("OS") || event.getModifierState("Super") || event.getModifierState("Win")) return; if (event.getModifierState("Control") + event.getModifierState("Alt") + event.getModifierState("Meta") > 1) return;
if (hasText) {
if (event.code === 'ArrowDown' && options.length - 1 > selected) {
setSelected(selected + 1);
}
if (event.code === 'ArrowUp' && selected >= 0) {
setSelected(selected - 1);
}
if (event.code === 'Enter' && selected >= 0) {
handleDropDownClick(options[selected]);
setSelected(-1);
}
}
};
return (
<div className='autocomplete-wrapper' onKeyUp={handleKeyUp}>
<InputContainer hasText={hasText}>
<input
type='text'
className='autocomplete-input'
onChange={handleInputChange}
value={inputValue}
/>
<div className='delete-button' onClick={handleDeleteButtonClick}>
×
</div>
</InputContainer>
{hasText ? (
<DropDown
options={options}
handleDropDownClick={handleDropDownClick}
selected={selected}
/>
) : null}
</div>
);
};
export const DropDown = ({ options, handleDropDownClick, selected }) => {
return (
<DropDownContainer>
{options.map((option, idx) => (
<li
key={idx}
onClick={() => handleDropDownClick(option)}
className={selected === idx ? 'selected' : ''}
>
{option}
</li>
))}
</DropDownContainer>
);
};