Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
browser: true,
es2021: true,
},
root: true,
extends: [
"standard-with-typescript",
"plugin:react/recommended",
Expand All @@ -13,14 +14,17 @@ module.exports = {
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
project: ["./tsconfig.json"],
createDefaultProgram: true,
},
plugins: ["react", "@typescript-eslint"],
rules: {
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/restrict-template-expressions": "off",
},
ignorePatterns: [".eslintrc.cjs", "vite.config.ts"],
};
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</head>
<body>
<div id="root"></div>
<div id="modal-root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
41 changes: 4 additions & 37 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,42 +1,9 @@
#root {
display: flex;
flex-direction: column;
align-items: center;

max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
17 changes: 7 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import './reset.css';
import './App.css';
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import NavBar from './components/NavBar';

const queryClient = new QueryClient();

function App() {
const App = () => {
return (
<QueryClientProvider client={queryClient}>
hello, world!
<h1>hello, world!</h1>
<NavBar />
</QueryClientProvider>
);
}
};

export default App;
7 changes: 7 additions & 0 deletions src/apis/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getCategories = async () =>
await fetch('/categories', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
10 changes: 10 additions & 0 deletions src/apis/memory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { NewMemory } from '../types/memory';

export const postMemory = async (form: NewMemory) =>
await fetch('/memories', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(form),
});
20 changes: 20 additions & 0 deletions src/assets/Icons/FilledStar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const MaterialSymbolsStar = (props: React.SVGProps<SVGSVGElement>) => {
const { color } = props;

return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
{...props}
>
<path
fill={color}
d="m5.825 22l1.625-7.025L2 10.25l7.2-.625L12 3l2.8 6.625l7.2.625l-5.45 4.725L18.175 22L12 18.275L5.825 22Z"
></path>
</svg>
);
};

export default MaterialSymbolsStar;
42 changes: 42 additions & 0 deletions src/components/ImageUploader/ImageUploader.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { style } from '@vanilla-extract/css';

export const hidden = style({
border: 'none',
clip: 'rect(0 0 0 0)',
height: '1px',
margin: '-1px',
overflow: 'hidden',
padding: 0,
position: 'absolute',
width: '1px',
});

export const uploadButton = style({
cursor: 'pointer',

border: '1px solid #DDDDDD',
backgroundColor: '#ffffff',
fontSize: '14px',

transition: 'all 0.15s linear',

':focus': {
outline: 'none',
border: '1px solid #735BF2',
},

':hover': {
backgroundColor: '#CAC2F2',
},
});

export const thumbnail = style({
objectFit: 'cover',

width: '100%',
height: '200px',
padding: 0,
margin: 0,

borderRadius: '5px',
});
40 changes: 40 additions & 0 deletions src/components/ImageUploader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useRef } from 'react';
import { hidden, thumbnail, uploadButton } from './ImageUploader.css';

interface ImageUploaderProps {
images: string[];
setImages: (fileList: FileList) => void;
}

const ImageUploader = (props: ImageUploaderProps) => {
const { images, setImages } = props;

const inputRef = useRef<HTMLInputElement>(null);

const accessImageInput = () => {
inputRef.current?.click();
};

return (
<>
<button type="button" onClick={accessImageInput} className={`${thumbnail} ${uploadButton}`}>
{images.length > 0
? images.map((image) => (
<img key={image} src={image} alt={'내 추억'} className={thumbnail} />
))
: '추억 사진을 올려주세요!'}
</button>
<input
ref={inputRef}
type="file"
accept="image/jpg, impge/png, image/jpeg"
onChange={({ target: { files } }) => {
setImages(files);
}}
className={hidden}
/>
</>
);
};

export default ImageUploader;
7 changes: 7 additions & 0 deletions src/components/Modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ModalProvider from '../../contexts/modalContext';
import Content from './ingredients/Content';
import Trigger from './ingredients/Trigger';

const Modal = Object.assign(ModalProvider, { Content, Trigger });

export default Modal;
43 changes: 43 additions & 0 deletions src/components/Modal/ingredients/Content.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { keyframes, style } from '@vanilla-extract/css';

const showModal = keyframes({
'0%': {
bottom: '-100%',
},

'100%': {
bottom: '0',
},
});

export const backdrop = style({
position: 'fixed',
top: '0',
left: '0',
width: '100%',
height: '100%',
backgroundColor: '#33333311',
});

export const content = style({
position: 'fixed',
bottom: '0',
left: '0',

overflowX: 'hidden',
overflowY: 'auto',

display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',

width: '100%',
minHeight: '10%',
maxHeight: '90%',

borderRadius: '10px 10px 0 0',
backgroundColor: '#ffffff',

animation: `${showModal} 0.4s ease-out`,
});
23 changes: 23 additions & 0 deletions src/components/Modal/ingredients/Content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createPortal } from 'react-dom';
import useModal from '../../../hooks/useModal';
import { backdrop, content } from './Content.css';

const Content = (props: React.PropsWithChildren) => {
const { children } = props;
const { isOpen, hide } = useModal();

const modalRoot = document.getElementById('modal-root');

return (
isOpen &&
createPortal(
<>
<div onClick={hide} className={backdrop} />
<section className={content}>{children}</section>
</>,
modalRoot
)
);
};

export default Content;
31 changes: 31 additions & 0 deletions src/components/Modal/ingredients/Trigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import useModal from '../../../hooks/useModal';

export interface TriggerProps {
type: 'open' | 'close' | 'toggle';
className?: string;
ariaLabel?: string;
}

const Trigger = (props: React.PropsWithChildren<TriggerProps>) => {
const { type, children, className, ariaLabel } = props;
const { show, hide, toggle } = useModal();

const triggerModal = () => {
if (type === 'open') show();
if (type === 'close') hide();
if (type === 'toggle') toggle();
};

return (
<button
type="button"
className={className}
onClick={triggerModal}
aria-label={ariaLabel}
>
{children}
</button>
);
};

export default Trigger;
19 changes: 19 additions & 0 deletions src/components/NavBar/NavBar.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { style } from '@vanilla-extract/css';

export const bottomNav = style({
display: 'block',

position: 'fixed',
bottom: '0',
left: '0',

minWidth: '350px',
width: '100%',
height: '50px',

margin: 'auto auto 0 auto',

borderRadius: '5px',
border: '1px solid pink',
backgroundColor: '#ffffff',
});
12 changes: 12 additions & 0 deletions src/components/NavBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import NewMemoryModalButton from '../NewMemoryModalButton';
import { bottomNav } from './NavBar.css';

const NavBar = () => {
return (
<nav className={bottomNav}>
<NewMemoryModalButton />
</nav>
);
};

export default NavBar;
Loading