diff --git a/package.json b/package.json
index c70143cd..b90a5765 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"@tanstack/react-query-devtools": "^5.17.12",
"@vanilla-extract/integration": "^6.2.4",
"@vanilla-extract/next-plugin": "^2.3.2",
+ "@yaireo/tagify": "^4.19.0",
"axios": "^1.6.5",
"next": "14.0.4",
"react": "^18",
@@ -37,6 +38,7 @@
"zustand": "^4.4.7"
},
"devDependencies": {
+ "@svgr/webpack": "^8.1.0",
"@commitlint/cli": "^18.6.0",
"@commitlint/config-conventional": "^18.6.0",
"@svgr/webpack": "^8.1.0",
diff --git a/public/fonts/Pretendard-Black.woff2 b/public/fonts/Pretendard-Black.woff2
new file mode 100644
index 00000000..e409cc0a
Binary files /dev/null and b/public/fonts/Pretendard-Black.woff2 differ
diff --git a/public/fonts/Pretendard-Bold.woff2 b/public/fonts/Pretendard-Bold.woff2
new file mode 100644
index 00000000..8975b802
Binary files /dev/null and b/public/fonts/Pretendard-Bold.woff2 differ
diff --git a/public/fonts/Pretendard-ExtraBold.woff2 b/public/fonts/Pretendard-ExtraBold.woff2
new file mode 100644
index 00000000..1a9caaf8
Binary files /dev/null and b/public/fonts/Pretendard-ExtraBold.woff2 differ
diff --git a/public/fonts/Pretendard-ExtraLight.woff2 b/public/fonts/Pretendard-ExtraLight.woff2
new file mode 100644
index 00000000..a6bf1857
Binary files /dev/null and b/public/fonts/Pretendard-ExtraLight.woff2 differ
diff --git a/public/fonts/Pretendard-Light.woff2 b/public/fonts/Pretendard-Light.woff2
new file mode 100644
index 00000000..a86436a3
Binary files /dev/null and b/public/fonts/Pretendard-Light.woff2 differ
diff --git a/public/fonts/Pretendard-Medium.woff2 b/public/fonts/Pretendard-Medium.woff2
new file mode 100644
index 00000000..153fd556
Binary files /dev/null and b/public/fonts/Pretendard-Medium.woff2 differ
diff --git a/public/fonts/Pretendard-Regular.woff2 b/public/fonts/Pretendard-Regular.woff2
new file mode 100644
index 00000000..ca8008ff
Binary files /dev/null and b/public/fonts/Pretendard-Regular.woff2 differ
diff --git a/public/fonts/Pretendard-SemiBold.woff2 b/public/fonts/Pretendard-SemiBold.woff2
new file mode 100644
index 00000000..79f80890
Binary files /dev/null and b/public/fonts/Pretendard-SemiBold.woff2 differ
diff --git a/public/fonts/Pretendard-Thin.woff2 b/public/fonts/Pretendard-Thin.woff2
new file mode 100644
index 00000000..809bf22d
Binary files /dev/null and b/public/fonts/Pretendard-Thin.woff2 differ
diff --git a/public/fonts/init.ts b/public/fonts/init.ts
deleted file mode 100644
index e099ee9f..00000000
--- a/public/fonts/init.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// 보일러플레이트용 임시 파일
-// 추후 이 파일은 지워주세요
diff --git a/public/icons/close_button.svg b/public/icons/close_button.svg
new file mode 100644
index 00000000..80319e7d
--- /dev/null
+++ b/public/icons/close_button.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/default_profile.svg b/public/icons/default_profile.svg
new file mode 100644
index 00000000..6322bad5
--- /dev/null
+++ b/public/icons/default_profile.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/icons/search.svg b/public/icons/search.svg
new file mode 100644
index 00000000..d244da79
--- /dev/null
+++ b/public/icons/search.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/x_circle_fill.svg b/public/icons/x_circle_fill.svg
new file mode 100644
index 00000000..7cfdd245
--- /dev/null
+++ b/public/icons/x_circle_fill.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/init.ts b/public/images/init.ts
deleted file mode 100644
index e099ee9f..00000000
--- a/public/images/init.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// 보일러플레이트용 임시 파일
-// 추후 이 파일은 지워주세요
diff --git a/public/images/mock_profile.png b/public/images/mock_profile.png
new file mode 100644
index 00000000..a435695d
Binary files /dev/null and b/public/images/mock_profile.png differ
diff --git a/public/styles/init.ts b/public/styles/init.ts
deleted file mode 100644
index e099ee9f..00000000
--- a/public/styles/init.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// 보일러플레이트용 임시 파일
-// 추후 이 파일은 지워주세요
diff --git a/src/app/(BeforeLogin)/_components/init.ts b/src/app/(BeforeLogin)/_components/init.ts
deleted file mode 100644
index e099ee9f..00000000
--- a/src/app/(BeforeLogin)/_components/init.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// 보일러플레이트용 임시 파일
-// 추후 이 파일은 지워주세요
diff --git a/src/app/(BeforeLogin)/login/_components/init.ts b/src/app/(BeforeLogin)/login/_components/init.ts
deleted file mode 100644
index e099ee9f..00000000
--- a/src/app/(BeforeLogin)/login/_components/init.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// 보일러플레이트용 임시 파일
-// 추후 이 파일은 지워주세요
diff --git a/src/app/(BeforeLogin)/login/page.tsx b/src/app/(BeforeLogin)/login/page.tsx
deleted file mode 100644
index e0e5ebeb..00000000
--- a/src/app/(BeforeLogin)/login/page.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function Login() {
- return
로그인페이지
;
-}
diff --git a/src/app/create/CreateListMock.ts b/src/app/create/CreateListMock.ts
new file mode 100644
index 00000000..a1a5f0dd
--- /dev/null
+++ b/src/app/create/CreateListMock.ts
@@ -0,0 +1,71 @@
+interface UserProfileType {
+ id: number;
+ profileImageUrl: string;
+ nickname: string;
+}
+
+const generateMockData = (count: number): UserProfileType[] => {
+ const mockData: UserProfileType[] = [];
+
+ mockData.push({
+ id: 101,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '안유진',
+ });
+
+ mockData.push({
+ id: 102,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '강나현',
+ });
+
+ mockData.push({
+ id: 103,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '민서영',
+ });
+
+ mockData.push({
+ id: 104,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '박소현',
+ });
+
+ mockData.push({
+ id: 105,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '강현지',
+ });
+
+ mockData.push({
+ id: 106,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '신은서',
+ });
+
+ mockData.push({
+ id: 107,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: '동호',
+ });
+
+ mockData.push({
+ id: 108,
+ profileImageUrl: `/images/mock_profile.png`,
+ nickname: 'JJUNGSU',
+ });
+
+ for (let i = 1; i <= count; i++) {
+ const user: UserProfileType = {
+ id: i,
+ profileImageUrl: '',
+ nickname: `User${i}`,
+ };
+
+ mockData.push(user);
+ }
+
+ return mockData;
+};
+
+export default generateMockData(20);
diff --git a/src/app/create/_components/CreateList.css.ts b/src/app/create/_components/CreateList.css.ts
new file mode 100644
index 00000000..98ef3122
--- /dev/null
+++ b/src/app/create/_components/CreateList.css.ts
@@ -0,0 +1,351 @@
+import { style } from '@vanilla-extract/css';
+import * as GlobalStyles from '@/styles/globalStyles.css';
+
+GlobalStyles;
+
+export const header = style({
+ width: '100%',
+ height: '90px',
+ paddingLeft: '20px',
+ paddingRight: '20px',
+
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+
+ borderBottom: '1px solid rgba(0, 0, 0, 0.10)',
+});
+
+export const headerTitle = style({
+ fontSize: '2rem',
+});
+
+export const headerNextButton = style({
+ fontSize: '1.6rem',
+ color: '#8d8d8d',
+});
+
+export const body = style({
+ width: '100vw',
+ padding: '37px 20px 100px',
+
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ rowGap: '50px',
+});
+
+export const title = style({
+ marginBottom: '16px',
+ fontSize: '1.6rem',
+ fontWeight: '600',
+});
+
+export const required = style({
+ color: '#ff0000',
+});
+
+export const content = style({
+ fontSize: '1.5rem',
+});
+
+export const error = style({
+ margin: '10px',
+
+ fontSize: '1.5rem',
+
+ color: 'red',
+});
+
+export const listTitleContainer = style({
+ position: 'relative',
+});
+
+export const titleInputBox = style({
+ width: '100%',
+ padding: '11px',
+
+ position: 'relative',
+
+ fontSize: '1.5rem',
+ border: '0px',
+ borderBottom: '1px solid rgba(0, 0, 0, 0.10)',
+ outline: 'none',
+});
+
+export const clearButton = style({
+ position: 'absolute',
+ top: '20px',
+ right: '8px',
+ transform: 'translateY(-50%)',
+ cursor: 'pointer',
+});
+
+export const listDescriptionContainer = style({
+ position: 'relative',
+});
+
+export const descriptionInputBox = style({
+ width: '100%',
+ padding: '12px',
+
+ fontSize: '1.5rem',
+
+ resize: 'none',
+ whiteSpace: 'pre-wrap',
+ // overflowY: 'hidden',
+
+ border: '1px solid rgba(0, 0, 0, 0.10)',
+ borderRadius: '8px',
+
+ outline: 'none',
+});
+
+export const dragIcon = style({
+ position: 'absolute',
+ top: '50%',
+ right: '5px',
+ cursor: 'pointer',
+});
+
+export const categoryContainer = style({
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ columnGap: '12px',
+
+ overflow: 'auto',
+ whiteSpace: 'nowrap',
+ scrollbarWidth: 'none',
+ '::-webkit-scrollbar': {
+ width: '0',
+ },
+});
+
+export const categoryInputBox = style({
+ display: 'none',
+});
+
+export const categoryButton = style({
+ width: '',
+ height: '40px',
+ padding: '8px 12px',
+
+ fontSize: '1.6rem',
+ fontWeight: '600',
+
+ backgroundColor: 'transparent',
+
+ whiteSpace: 'nowrap',
+
+ border: '1px solid #DEDEDE',
+ borderRadius: '10px',
+});
+
+export const categoryButtonActive = style({
+ backgroundColor: '#EBF4FF',
+});
+
+export const labelContainer = style({
+ display: 'flex',
+ flexDirection: 'column',
+});
+
+export const labelInputBox = style({
+ width: '100%',
+ padding: '10px',
+
+ fontSize: '1.5rem',
+
+ borderRadius: '10px',
+ border: '1px solid rgba(0, 0, 0, 0.1)',
+ outline: 'none',
+ cursor: 'pointer',
+});
+
+export const labels = style({
+ marginTop: '10px',
+
+ display: 'flex',
+ flexDirection: 'row',
+ columnGap: '5px',
+});
+
+export const label = style({
+ width: 'fit-content',
+ padding: '7px',
+ paddingRight: '20px',
+
+ position: 'relative',
+
+ color: '#333333',
+ backgroundColor: '#EBF4FF',
+
+ fontSize: '1.3rem',
+
+ borderRadius: '10px',
+ border: '1px solid rgba(0, 0, 0, 0.1)',
+ cursor: 'pointer',
+});
+
+export const labelDeleteButton = style({
+ position: 'absolute',
+ top: '10px',
+ right: '5px',
+
+ stroke: '#8E8E93',
+ strokeWidth: '1.5',
+
+ cursor: 'pointer',
+});
+
+export const colaboContainer = style({
+ position: 'relative',
+});
+
+export const colaboInputBox = style({
+ width: '100%',
+ padding: '10px',
+ paddingLeft: '30px',
+
+ fontSize: '1.5rem',
+
+ borderRadius: '10px',
+ border: '1px solid rgba(0, 0, 0, 0.1)',
+ outline: 'none',
+ cursor: 'pointer',
+});
+
+export const colaboDropdown = style({
+ height: '152px',
+ marginTop: '5px',
+ marginBottom: '10px',
+ padding: '11px',
+
+ display: 'flex',
+ flexDirection: 'column',
+ rowGap: '5px',
+
+ borderRadius: '10px',
+ border: '1px solid rgba(0, 0, 0, 0.1)',
+
+ overflowY: 'auto',
+});
+
+export const colaboProfileContainer = style({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ columnGap: '16px',
+
+ fontSize: '1.5rem',
+});
+
+export const colaboList = style({
+ padding: '4.5px',
+
+ display: 'flex',
+ flexDirection: 'column',
+ rowGap: '5px',
+});
+
+export const colaboItem = style({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+});
+
+export const colaboPlaceholder = style({
+ fontSize: '1.5rem',
+ color: '#61646B',
+});
+
+export const colaboProfileImage = style({
+ borderRadius: '50%',
+});
+
+export const searchIcon = style({
+ width: '15.7px',
+ height: '15.7px',
+
+ position: 'absolute',
+ top: '20px',
+ left: '8px',
+ transform: 'translateY(-50%)',
+});
+
+export const backgroundContainer = style({
+ display: 'flex',
+ flexDirection: 'row',
+ columnGap: '12px',
+});
+
+export const colorCircle = style({
+ width: '50px',
+ height: '50px',
+
+ appearance: 'none',
+ MozAppearance: 'none',
+ WebkitAppearance: 'none',
+ outline: 'none',
+
+ accentColor: 'red',
+ backgroundColor: '#ffffff',
+
+ border: '3px #ffffff solid',
+ WebkitBorderBefore: '3px #ffffff solid',
+ borderRadius: '50%',
+ WebkitBorderRadius: '50%',
+ MozBorderRadius: '50%',
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
+
+ cursor: 'pointer',
+});
+
+export const checkedColor = style({
+ borderColor: '#0047FF',
+});
+
+export const white = style({
+ backgroundColor: '#FFFFFF',
+});
+export const yellow = style({
+ backgroundColor: '#FFF6A5',
+});
+export const orange = style({
+ backgroundColor: '#FFDCB2',
+});
+export const green = style({
+ backgroundColor: '#D0FF89',
+});
+export const blue = style({
+ backgroundColor: '#B7EEFF',
+});
+export const purple = style({
+ backgroundColor: '#E6C6FF',
+});
+
+export const publicContainer = style({
+ marginBottom: '8px',
+
+ display: 'flex',
+ flexDirection: 'row',
+ columnGap: '16px',
+
+ accentColor: 'black',
+});
+
+export const publicMessage = style({
+ marginLeft: '5px',
+
+ fontSize: '1.4rem',
+ color: '#909090',
+});
+
+export const checkedIcon = style({
+ marginLeft: '5px',
+ color: '#008000',
+});
diff --git a/src/app/create/_components/CreateList.tsx b/src/app/create/_components/CreateList.tsx
new file mode 100644
index 00000000..adf67112
--- /dev/null
+++ b/src/app/create/_components/CreateList.tsx
@@ -0,0 +1,428 @@
+'use client';
+
+import React, { useEffect, useRef, useState } from 'react';
+import Link from 'next/link';
+import { useFormContext, useWatch } from 'react-hook-form';
+
+import '@/styles/globalStyles.css';
+import * as styles from './CreateList.css';
+
+import CloseButton from '/public/icons/close_button.svg';
+import EraseButton from '/public/icons/x_circle_fill.svg';
+import SearchIcon from '/public/icons/search.svg';
+import DefaultProfile from '/public/icons/default_profile.svg';
+
+import mockdata from '../CreateListMock';
+import Image from 'next/image';
+
+interface UserProfileType {
+ id: number;
+ profileImageUrl: string;
+ nickname: string;
+}
+
+function CreateList() {
+ const { register, getValues, setValue, setError, control, formState } = useFormContext();
+ const { errors, isValid } = formState;
+
+ const category = useWatch({ control, name: 'category' });
+ const labels = useWatch({ control, name: 'labels' });
+ const colaboIDs = useWatch({ control, name: 'collaboratorIds' });
+ const backgroundColor = useWatch({ control, name: 'backgroundColor' });
+ const isPublic = useWatch({ control, name: 'isPublic' });
+
+ const [labelInput, setLabelInput] = useState('');
+ const [colaboInput, setColaboInput] = useState('');
+ const [colaboList, setColabolist] = useState([]);
+ const [isDropDownOpen, setIsDropDownOpen] = useState(false);
+
+ const colaboInputRef = useRef(null);
+ const dropdownRef = useRef(null);
+
+ useEffect(() => {
+ const closeDropdown = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ colaboInputRef.current &&
+ !dropdownRef.current.contains(event.target as Node) &&
+ !colaboInputRef.current.contains(event.target as Node)
+ ) {
+ setIsDropDownOpen(false);
+ }
+ };
+ document.addEventListener('click', closeDropdown);
+
+ return () => {
+ document.removeEventListener('click', closeDropdown);
+ };
+ }, []);
+
+ return (
+
+ {/* 헤더 */}
+
+
+
리스트 생성
+
+ 다음
+
+
+
+
+ {/* 제목 */}
+
+
+ 타이틀 *
+
+
+
+
+
{
+ setValue('title', '');
+ }}
+ />
+ {errors.title && {errors.title.message?.toString()}
}
+
+
+
+
+ {/* 한 줄 소개 */}
+
+
소개
+
+
+
+ {errors.description &&
{errors.description.message?.toString()}
}
+
+
+
+
+ {/* 카테고리 */}
+
+
+ 카테고리 *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 라벨 */}
+
+
라벨
+
+
+
{
+ if (e.nativeEvent.isComposing) {
+ return;
+ }
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ if (labelInput.length >= 10) {
+ setError('labels', {
+ type: 'manual',
+ message: '라벨은 최대 10자까지 입력할 수 있어요.',
+ });
+ return;
+ }
+ if (labels.includes(labelInput)) {
+ setError('labels', {
+ type: 'manual',
+ message: '같은 라벨을 2개 이상 등록할 수 없어요.',
+ });
+ return;
+ }
+ if (labels.length >= 3) {
+ setError('labels', {
+ type: 'manual',
+ message: '라벨은 최대 3개까지 입력할 수 있어요.',
+ });
+ return;
+ }
+ setValue('labels', [...labels, labelInput]);
+ setLabelInput('');
+ }
+ }}
+ onChange={(e) => {
+ setError('labels', {});
+ setLabelInput(e.target.value);
+ }}
+ />
+ {errors.labels &&
{errors.labels.message?.toString()}
}
+
+ {labels.map((label: string) => (
+
+ {label}
+ {
+ setValue(
+ 'labels',
+ labels.filter((l: string) => l !== label)
+ );
+ setError('labels', {});
+ }}
+ />
+
+ ))}
+
+
+
+
+
+ {/* 콜라보레이터 */}
+
+
콜라보레이터 추가
+
+
+
{
+ if (!isDropDownOpen) {
+ setIsDropDownOpen(true);
+ }
+ }}
+ onChange={(e) => {
+ setColaboInput(e.target.value);
+ }}
+ />
+
+ {isDropDownOpen && (
+
+ {mockdata
+ .filter((user) => user.nickname.toLocaleLowerCase().includes(colaboInput.toLocaleLowerCase()))
+ .map((user) => (
+
{
+ if (!colaboList.find((colaboUser: UserProfileType) => colaboUser.id === user.id)) {
+ setColabolist([...colaboList, user]);
+ setValue('collaboratorIds', [...colaboIDs, user.id]);
+ }
+ }}
+ >
+ {user.profileImageUrl ? (
+
+ ) : (
+
+ )}
+ {user.nickname}
+ {colaboList.find((colaboUser: UserProfileType) => colaboUser.id === user.id) && (
+ ✓
+ )}
+
+ ))}
+
+ )}
+
+ {colaboList.map((colaboUser) => (
+
+
+ {colaboUser.profileImageUrl ? (
+
+ ) : (
+
+ )}{' '}
+ {colaboUser.nickname}
+
+
{
+ setColabolist(colaboList.filter((user) => user.id !== colaboUser.id));
+ }}
+ />
+
+ ))}
+
+
+
+
+
+ {/* 배경 색상 */}
+
+
+ {/* 공개 설정 */}
+
+
+
+ );
+}
+
+export default CreateList;
diff --git a/src/app/create/page.tsx b/src/app/create/page.tsx
index 4503dc5d..178d38b0 100644
--- a/src/app/create/page.tsx
+++ b/src/app/create/page.tsx
@@ -1,7 +1,8 @@
'use client';
import { FieldErrors, FormProvider, useForm } from 'react-hook-form';
-import CreateItem from '@/app/create/_components/CreateItem';
+// import CreateItem from '@/app/create/_components/CreateItem';
+import CreateList from '@/app/create/_components/CreateList';
interface Item {
rank: number;
@@ -13,10 +14,10 @@ interface Item {
interface FormValues {
ownerId: number;
category: string;
- labels: string[] | null;
- collaboratorIds: number[] | null;
+ labels: string[];
+ collaboratorIds: number[];
title: string;
- description: string | null;
+ description: string;
isPublic: boolean;
backgroundColor: string;
items: Item[];
@@ -29,31 +30,31 @@ export default function CreatePage() {
mode: 'onChange',
defaultValues: {
ownerId: 0,
- category: '',
+ category: '문화',
labels: [],
collaboratorIds: [],
title: '',
description: '',
isPublic: true,
- backgroundColor: '#000000',
+ backgroundColor: '#FFFFFF',
items: [
{
rank: 0,
title: '',
- comment: '',
- link: '',
+ comment: null,
+ link: null,
},
{
rank: 0,
title: '',
comment: null,
- link: '',
+ link: null,
},
{
rank: 0,
title: '',
- comment: '',
- link: '',
+ comment: null,
+ link: null,
},
],
},
@@ -69,7 +70,8 @@ export default function CreatePage() {
diff --git a/src/styles/pretendard.css b/src/styles/pretendard.css
new file mode 100644
index 00000000..ad802f3a
--- /dev/null
+++ b/src/styles/pretendard.css
@@ -0,0 +1,8 @@
+/* @font-face {
+ font-family: 'Pretendard';
+ font-weight: 400;
+ font-display: swap;
+ src:
+ local('Pretendard Regular'),
+ url('%PUBLIC_URL%/fonts/Pretendard-Regular.woff2') format('woff2');
+} */
diff --git a/src/styles/tagify.css b/src/styles/tagify.css
new file mode 100644
index 00000000..0b6c4482
--- /dev/null
+++ b/src/styles/tagify.css
@@ -0,0 +1,589 @@
+@charset "UTF-8";
+:root {
+ --tagify-dd-color-primary: rgb(53, 149, 246);
+ --tagify-dd-bg-color: white;
+ --tagify-dd-item-pad: 0.3em 0.5em;
+ --tagify-dd-max-height: 300px;
+}
+.tagify {
+ --tags-disabled-bg: #f1f1f1;
+ --tags-border-color: #ddd;
+ --tags-hover-border-color: #ccc;
+ --tags-focus-border-color: #3595f6;
+ --tag-border-radius: 3px;
+ --tag-bg: #e5e5e5;
+ --tag-hover: #d3e2e2;
+ --tag-text-color: black;
+ --tag-text-color--edit: black;
+ --tag-pad: 0.3em 0.5em;
+ --tag-inset-shadow-size: 1.1em;
+ --tag-invalid-color: #d39494;
+ --tag-invalid-bg: rgba(211, 148, 148, 0.5);
+ --tag--min-width: 1ch;
+ --tag--max-width: 100%;
+ --tag-hide-transition: 0.3s;
+ --tag-remove-bg: rgba(211, 148, 148, 0.3);
+ --tag-remove-btn-color: black;
+ --tag-remove-btn-bg: none;
+ --tag-remove-btn-bg--hover: #c77777;
+ --input-color: inherit;
+ --placeholder-color: rgba(0, 0, 0, 0.4);
+ --placeholder-color-focus: rgba(0, 0, 0, 0.25);
+ --loader-size: 0.8em;
+ --readonly-striped: 1;
+ display: inline-flex;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ padding: 0;
+ line-height: 0;
+ cursor: text;
+ outline: 0;
+ position: relative;
+ box-sizing: border-box;
+ transition: 0.1s;
+ white-space: nowrap;
+}
+@keyframes tags--bump {
+ 30% {
+ transform: scale(1.2);
+ }
+}
+@keyframes rotateLoader {
+ to {
+ transform: rotate(1turn);
+ }
+}
+.tagify:hover:not(.tagify--focus):not(.tagify--invalid) {
+ --tags-border-color: var(--tags-hover-border-color);
+}
+.tagify[disabled] {
+ background: var(--tags-disabled-bg);
+ filter: saturate(0);
+ opacity: 0.5;
+ pointer-events: none;
+}
+.tagify[disabled].tagify--select,
+.tagify[readonly].tagify--select {
+ pointer-events: none;
+}
+.tagify[disabled]:not(.tagify--mix):not(.tagify--select),
+.tagify[readonly]:not(.tagify--mix):not(.tagify--select) {
+ cursor: default;
+}
+.tagify[disabled]:not(.tagify--mix):not(.tagify--select) > .tagify__input,
+.tagify[readonly]:not(.tagify--mix):not(.tagify--select) > .tagify__input {
+ visibility: hidden;
+ width: 0;
+ margin: 5px 0;
+}
+.tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div,
+.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div {
+ padding: var(--tag-pad);
+}
+.tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before,
+.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before {
+ animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
+}
+@keyframes readonlyStyles {
+ 0% {
+ background: linear-gradient(
+ 45deg,
+ var(--tag-bg) 25%,
+ transparent 25%,
+ transparent 50%,
+ var(--tag-bg) 50%,
+ var(--tag-bg) 75%,
+ transparent 75%,
+ transparent
+ )
+ 0/5px 5px;
+ box-shadow: none;
+ filter: brightness(0.95);
+ }
+}
+.tagify[disabled] .tagify__tag__removeBtn,
+.tagify[readonly] .tagify__tag__removeBtn {
+ display: none;
+}
+.tagify--loading .tagify__input > br:last-child {
+ display: none;
+}
+.tagify--loading .tagify__input::before {
+ content: none;
+}
+.tagify--loading .tagify__input::after {
+ content: '';
+ vertical-align: middle;
+ opacity: 1;
+ width: 0.7em;
+ height: 0.7em;
+ width: var(--loader-size);
+ height: var(--loader-size);
+ min-width: 0;
+ border: 3px solid;
+ border-color: #eee #bbb #888 transparent;
+ border-radius: 50%;
+ animation: rotateLoader 0.4s infinite linear;
+ content: '' !important;
+ margin: -2px 0 -2px 0.5em;
+}
+.tagify--loading .tagify__input:empty::after {
+ margin-left: 0;
+}
+.tagify + input,
+.tagify + textarea {
+ position: absolute !important;
+ left: -9999em !important;
+ transform: scale(0) !important;
+}
+.tagify__tag {
+ display: inline-flex;
+ align-items: center;
+ max-width: calc(var(--tag--max-width) - 10px);
+ margin-inline: 5px 0;
+ margin-block: 5px;
+ position: relative;
+ z-index: 1;
+ outline: 0;
+ line-height: normal;
+ cursor: default;
+ transition: 0.13s ease-out;
+}
+.tagify__tag > div {
+ flex: 1;
+ vertical-align: top;
+ box-sizing: border-box;
+ max-width: 100%;
+ padding: var(--tag-pad);
+ color: var(--tag-text-color);
+ line-height: inherit;
+ border-radius: var(--tag-border-radius);
+ white-space: nowrap;
+ transition: 0.13s ease-out;
+}
+.tagify__tag > div > * {
+ white-space: pre-wrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ vertical-align: top;
+ min-width: var(--tag--min-width);
+ max-width: var(--tag--max-width);
+ transition:
+ 0.8s ease,
+ 0.1s color;
+}
+.tagify__tag > div > [contenteditable] {
+ outline: 0;
+ -webkit-user-select: text;
+ user-select: text;
+ cursor: text;
+ margin: -2px;
+ padding: 2px;
+ max-width: 350px;
+}
+.tagify__tag > div > :only-child {
+ width: 100%;
+}
+.tagify__tag > div::before {
+ content: '';
+ position: absolute;
+ border-radius: inherit;
+ inset: var(--tag-bg-inset, 0);
+ z-index: -1;
+ pointer-events: none;
+ transition: 120ms ease;
+ animation: tags--bump 0.3s ease-out 1;
+ box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset;
+}
+.tagify__tag:focus div::before,
+.tagify__tag:hover:not([readonly]) div::before {
+ --tag-bg-inset: -2.5px;
+ --tag-bg: var(--tag-hover);
+}
+.tagify__tag--loading {
+ pointer-events: none;
+}
+.tagify__tag--loading .tagify__tag__removeBtn {
+ display: none;
+}
+.tagify__tag--loading::after {
+ --loader-size: 0.4em;
+ content: '';
+ vertical-align: middle;
+ opacity: 1;
+ width: 0.7em;
+ height: 0.7em;
+ width: var(--loader-size);
+ height: var(--loader-size);
+ min-width: 0;
+ border: 3px solid;
+ border-color: #eee #bbb #888 transparent;
+ border-radius: 50%;
+ animation: rotateLoader 0.4s infinite linear;
+ margin: 0 0.5em 0 -0.1em;
+}
+.tagify__tag--flash div::before {
+ animation: none;
+}
+.tagify__tag--hide {
+ width: 0 !important;
+ padding-left: 0;
+ padding-right: 0;
+ margin-left: 0;
+ margin-right: 0;
+ opacity: 0;
+ transform: scale(0);
+ transition: var(--tag-hide-transition);
+ pointer-events: none;
+}
+.tagify__tag--hide > div > * {
+ white-space: nowrap;
+}
+.tagify__tag.tagify--noAnim > div::before {
+ animation: none;
+}
+.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div > span {
+ opacity: 0.5;
+}
+.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div::before {
+ --tag-bg: var(--tag-invalid-bg);
+ transition: 0.2s;
+}
+.tagify__tag[readonly] .tagify__tag__removeBtn {
+ display: none;
+}
+.tagify__tag[readonly] > div::before {
+ animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
+}
+@keyframes readonlyStyles {
+ 0% {
+ background: linear-gradient(
+ 45deg,
+ var(--tag-bg) 25%,
+ transparent 25%,
+ transparent 50%,
+ var(--tag-bg) 50%,
+ var(--tag-bg) 75%,
+ transparent 75%,
+ transparent
+ )
+ 0/5px 5px;
+ box-shadow: none;
+ filter: brightness(0.95);
+ }
+}
+.tagify__tag--editable > div {
+ color: var(--tag-text-color--edit);
+}
+.tagify__tag--editable > div::before {
+ box-shadow: 0 0 0 2px var(--tag-hover) inset !important;
+}
+.tagify__tag--editable > .tagify__tag__removeBtn {
+ pointer-events: none;
+}
+.tagify__tag--editable > .tagify__tag__removeBtn::after {
+ opacity: 0;
+ transform: translateX(100%) translateX(5px);
+}
+.tagify__tag--editable.tagify--invalid > div::before {
+ box-shadow: 0 0 0 2px var(--tag-invalid-color) inset !important;
+}
+.tagify__tag__removeBtn {
+ order: 5;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50px;
+ cursor: pointer;
+ font: 14px/1 Arial;
+ background: var(--tag-remove-btn-bg);
+ color: var(--tag-remove-btn-color);
+ width: 14px;
+ height: 14px;
+ margin-inline: auto 4.6666666667px;
+ overflow: hidden;
+ transition: 0.2s ease-out;
+}
+.tagify__tag__removeBtn::after {
+ content: '×';
+ transition:
+ 0.3s,
+ color 0s;
+}
+.tagify__tag__removeBtn:hover {
+ color: #fff;
+ background: var(--tag-remove-btn-bg--hover);
+}
+.tagify__tag__removeBtn:hover + div > span {
+ opacity: 0.5;
+}
+.tagify__tag__removeBtn:hover + div::before {
+ box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg, rgba(211, 148, 148, 0.3)) inset !important;
+ transition: box-shadow 0.2s;
+}
+.tagify:not(.tagify--mix) .tagify__input br {
+ display: none;
+}
+.tagify:not(.tagify--mix) .tagify__input * {
+ display: inline;
+ white-space: nowrap;
+}
+.tagify__input {
+ flex-grow: 1;
+ display: inline-block;
+ min-width: 110px;
+ margin: 5px;
+ padding: var(--tag-pad);
+ line-height: normal;
+ position: relative;
+ white-space: pre-wrap;
+ color: var(--input-color);
+ box-sizing: inherit;
+}
+.tagify__input:empty::before {
+ position: static;
+}
+.tagify__input:focus {
+ outline: 0;
+}
+.tagify__input:focus::before {
+ transition: 0.2s ease-out;
+ opacity: 0;
+ transform: translatex(6px);
+}
+@supports (-ms-ime-align: auto) {
+ .tagify__input:focus::before {
+ display: none;
+ }
+}
+.tagify__input:focus:empty::before {
+ transition: 0.2s ease-out;
+ opacity: 1;
+ transform: none;
+ color: rgba(0, 0, 0, 0.25);
+ color: var(--placeholder-color-focus);
+}
+@-moz-document url-prefix() {
+ .tagify__input:focus:empty::after {
+ display: none;
+ }
+}
+.tagify__input::before {
+ content: attr(data-placeholder);
+ height: 1em;
+ line-height: 1em;
+ margin: auto 0;
+ z-index: 1;
+ color: var(--placeholder-color);
+ white-space: nowrap;
+ pointer-events: none;
+ opacity: 0;
+ position: absolute;
+}
+.tagify__input::after {
+ content: attr(data-suggest);
+ display: inline-block;
+ vertical-align: middle;
+ position: absolute;
+ min-width: calc(100% - 1.5em);
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: pre;
+ color: var(--tag-text-color);
+ opacity: 0.3;
+ pointer-events: none;
+ max-width: 100px;
+}
+.tagify__input .tagify__tag {
+ margin: 0 1px;
+}
+.tagify--mix {
+ display: block;
+}
+.tagify--mix .tagify__input {
+ padding: 5px;
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ line-height: 1.5;
+ display: block;
+}
+.tagify--mix .tagify__input::before {
+ height: auto;
+ display: none;
+ line-height: inherit;
+}
+.tagify--mix .tagify__input::after {
+ content: none;
+}
+.tagify--select {
+ cursor: default;
+}
+.tagify--select::after {
+ content: '>';
+ opacity: 0.5;
+ position: absolute;
+ top: 50%;
+ right: 0;
+ bottom: 0;
+ font: 16px monospace;
+ line-height: 8px;
+ height: 8px;
+ pointer-events: none;
+ transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg);
+ transition: 0.2s ease-in-out;
+}
+.tagify--select[aria-expanded='true']::after {
+ transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2);
+}
+.tagify--select .tagify__tag {
+ flex: 1;
+ max-width: none;
+ margin-inline-end: 2em;
+ cursor: text;
+}
+.tagify--select .tagify__tag div::before {
+ display: none;
+}
+.tagify--select .tagify__tag + .tagify__input {
+ display: none;
+}
+.tagify--empty .tagify__input::before {
+ transition: 0.2s ease-out;
+ opacity: 1;
+ transform: none;
+ display: inline-block;
+ width: auto;
+}
+.tagify--mix .tagify--empty .tagify__input::before {
+ display: inline-block;
+}
+.tagify--focus {
+ --tags-border-color: var(--tags-focus-border-color);
+ transition: 0s;
+}
+.tagify--invalid {
+ --tags-border-color: #d39494;
+}
+.tagify__dropdown {
+ position: absolute;
+ z-index: 9999;
+ transform: translateY(-1px);
+ border-top: 1px solid var(--tagify-dd-color-primary);
+ overflow: hidden;
+}
+.tagify__dropdown[dir='rtl'] {
+ transform: translate(-100%, -1px);
+}
+.tagify__dropdown[placement='top'] {
+ margin-top: 0;
+ transform: translateY(-100%);
+}
+.tagify__dropdown[placement='top'] .tagify__dropdown__wrapper {
+ border-top-width: 1.1px;
+ border-bottom-width: 0;
+}
+.tagify__dropdown[position='text'] {
+ box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1);
+ font-size: 0.9em;
+}
+.tagify__dropdown[position='text'] .tagify__dropdown__wrapper {
+ border-width: 1px;
+}
+.tagify__dropdown__wrapper {
+ max-height: var(--tagify-dd-max-height);
+ overflow: hidden;
+ overflow-x: hidden;
+ background: var(--tagify-dd-bg-color);
+ border: 1px solid;
+ border-color: var(--tagify-dd-color-primary);
+ border-bottom-width: 1.5px;
+ border-top-width: 0;
+ box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.2);
+ transition:
+ 0.3s cubic-bezier(0.5, 0, 0.3, 1),
+ transform 0.15s;
+ animation: dd-wrapper-show 0s 0.3s forwards;
+}
+@keyframes dd-wrapper-show {
+ to {
+ overflow-y: auto;
+ }
+}
+.tagify__dropdown__header:empty {
+ display: none;
+}
+.tagify__dropdown__footer {
+ display: inline-block;
+ margin-top: 0.5em;
+ padding: var(--tagify-dd-item-pad);
+ font-size: 0.7em;
+ font-style: italic;
+ opacity: 0.5;
+}
+.tagify__dropdown__footer:empty {
+ display: none;
+}
+.tagify__dropdown--initial .tagify__dropdown__wrapper {
+ max-height: 20px;
+ transform: translateY(-1em);
+}
+.tagify__dropdown--initial[placement='top'] .tagify__dropdown__wrapper {
+ transform: translateY(2em);
+}
+.tagify__dropdown__item {
+ box-sizing: border-box;
+ padding: var(--tagify-dd-item-pad);
+ margin: 1px;
+ white-space: pre-wrap;
+ cursor: pointer;
+ border-radius: 2px;
+ position: relative;
+ outline: 0;
+ max-height: 60px;
+ max-width: 100%;
+ line-height: normal;
+}
+.tagify__dropdown__item--active {
+ background: var(--tagify-dd-color-primary);
+ color: #fff;
+}
+.tagify__dropdown__item:active {
+ filter: brightness(105%);
+}
+.tagify__dropdown__item--hidden {
+ padding-top: 0;
+ padding-bottom: 0;
+ margin: 0 1px;
+ pointer-events: none;
+ overflow: hidden;
+ max-height: 0;
+ transition: var(--tagify-dd-item--hidden-duration, 0.3s) !important;
+}
+.tagify__dropdown__item--hidden > * {
+ transform: translateY(-100%);
+ opacity: 0;
+ transition: inherit;
+}
+
+.tagify--outside {
+ position: relative;
+ cursor: pointer;
+}
+
+.tagify--outside .tagify__input {
+ order: -1;
+ flex: 100%;
+ margin-bottom: 1em;
+ transition: 0.1s;
+
+ padding: 10px;
+
+ border-radius: 10px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.tagify--outside.tagify--focus .tagify__input {
+ transition: 0s;
+}
diff --git a/tsconfig.json b/tsconfig.json
index e59724b2..97442403 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,8 @@
{
"compilerOptions": {
+ "typeRoots": ["./types ", "./node_modules/@types", "./node_modules/@types/@yaireo"],
+ "declaration": true,
+ "declarationDir": "./types",
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,