Skip to content

Commit

Permalink
feat: 타임테이블 편집 플로팅 버튼 컴포넌트 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
m2na7 committed Jan 15, 2025
1 parent 47a5963 commit a848512
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
3 changes: 3 additions & 0 deletions apps/client/src/pages/home/page/home.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Footer, Navigation } from '@confeti/design-system';
import { TAB_MENU } from '../constants/menu';
import EditFloatingButton from '@pages/time-table/components/edit-floating-button';

const Home = () => {
return (
Expand All @@ -16,6 +17,8 @@ const Home = () => {
</Navigation.Panels>
</Navigation.Root>
<Footer />

<EditFloatingButton />
</>
);
};
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { recipe } from '@vanilla-extract/recipes';
import { keyframes, style } from '@vanilla-extract/css';
import { themeVars } from '@confeti/design-system/styles';

const fadeInBox = keyframes({
from: { opacity: 0, transform: 'translateY(80%)' },
to: { opacity: 1, transform: 'translateY(0)' },
});

const fadeInText = keyframes({
from: { opacity: 0, transform: 'translateY(20%)' },
to: { opacity: 1, transform: 'translateY(0)' },
});

const fadeOutText = keyframes({
from: { opacity: 1, transform: 'translateY(0)' },
to: { opacity: 0, transform: 'translateY(20%)' },
});

export const box = style({
padding: '1rem 5.3rem 0.9rem 1.6rem',
bottom: '17rem',
right: '2rem',

...themeVars.display.flexCenter,

position: 'absolute',
zIndex: themeVars.zIndex.floatingButton.content,
transition: 'width 0.3s ease-in-out',
...themeVars.fontStyles.subtitle4_b_14,
flexDirection: 'column',
alignItems: 'flex-start',

borderRadius: '0.5rem',
backgroundColor: themeVars.color.white,

animation: `${fadeInBox} 0.3s ease-out`,
});

export const boxButton = style({
...themeVars.display.flexCenter,
padding: '0.8rem 0',
cursor: 'pointer',
});

export const buttonVariants = recipe({
base: {
height: '5rem',
position: 'absolute',
right: '2rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '3rem',
backgroundColor: themeVars.color.gray800,
zIndex: themeVars.zIndex.floatingButton.content,
transition: 'width 0.3s ease-in-out',
cursor: 'pointer',
},
variants: {
variant: {
close: {
bottom: '11rem',
padding: '1.3rem',
width: '5rem',
},
edit: {
bottom: '11rem',
padding: '0.5rem',
width: '11rem',
},
complete: {
bottom: '2rem',
padding: '0.5rem',
width: '11rem',
gap: '0.2rem',
},
},
},
defaultVariants: {
variant: 'edit',
},
});

export const text = style({
...themeVars.fontStyles.subtitle4_b_14,
color: themeVars.color.confeti_lime2,
whiteSpace: 'nowrap',
transition: 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out',
});

export const textVisible = style({
animation: `${fadeInText} 0.3s ease-out`,
});

export const textHidden = style({
animation: `${fadeOutText} 0.3s ease-in`,
opacity: 0,
});

export const background = style({
position: 'fixed',
top: 0,
left: '50%',
transform: 'translateX(-50%)',
width: '100%',
height: 'var(--height)',
maxWidth: 'var(--max-width)',
minWidth: 'var(--min-width)',
transition: 'background-color 0.3s ease-in-out',
});

// 전체 뷰포트
// export const background = style({
// position: 'fixed',
// top: 0,
// left: 0,
// width: '100vw',
// height: '100vh',
// zIndex: themeVars.zIndex.floatingButton.content,
// transition: 'background-color 0.3s ease-in-out',
// });

export const backgroundVisible = style({
backgroundColor: themeVars.color.black_op,
zIndex: themeVars.zIndex.floatingButton.content,
});
128 changes: 128 additions & 0 deletions apps/client/src/pages/time-table/components/edit-floating-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useState } from 'react';

import {
IcTimetableFloatClose,
IcFloatEditLime24,
IcFloatEdit24,
IcFloatDelete24,
IcTimetableFloatFinish,
} from '@confeti/design-system/icons';
import * as styles from './edit-floating-button.css';

type ModeSetter = React.Dispatch<React.SetStateAction<boolean>>;

interface RenderActionButtonProps {
variant: 'close' | 'edit' | 'complete';
icon: JSX.Element;
text: string | null;
onClick: () => void;
}

const EditFloatingButton = () => {
const [isEditMode, setIsEditMode] = useState(false);
const [isEditTimeTableMode, setIsEditTimeTableMode] = useState(false);
const [isFestivalDeleteMode, setIsFestivalDeleteMode] = useState(false);
const [isTextVisible, setIsTextVisible] = useState(true);

const resetModes = () => {
setIsEditTimeTableMode(false);
setIsFestivalDeleteMode(false);
};

const handleToggleButton = () => {
setIsEditMode((prev) => !prev);
setIsTextVisible(true);
if (isEditTimeTableMode || isFestivalDeleteMode) resetModes();
};

const handleModeToggle = (modeSetter: ModeSetter): void => {
modeSetter((prev: boolean) => !prev);
};

const getBackgroundClassName = () => {
return `${styles.background} ${
isEditMode && !isEditTimeTableMode && !isFestivalDeleteMode
? styles.backgroundVisible
: ''
}`;
};

const renderButton = () => {
if (isEditMode && (isEditTimeTableMode || isFestivalDeleteMode)) {
return renderActionButton({
variant: 'complete',
icon: <IcTimetableFloatFinish width="2.4rem" height="2.4rem" />,
text: '완료하기',
onClick: handleToggleButton,
});
}

if (isEditMode) {
return renderActionButton({
variant: 'close',
icon: <IcTimetableFloatClose width="2.4rem" height="2.4rem" />,
text: null,
onClick: handleToggleButton,
});
}

return renderActionButton({
variant: 'edit',
icon: <IcFloatEditLime24 width="2.4rem" height="2.4rem" />,
text: '편집하기',
onClick: handleToggleButton,
});
};

const renderActionButton = ({
variant,
icon,
text,
onClick,
}: RenderActionButtonProps) => (
<button className={styles.buttonVariants({ variant })} onClick={onClick}>
{icon}
{text && (
<span
className={`${styles.text} ${isTextVisible ? styles.textVisible : styles.textHidden}`}
>
{text}
</span>
)}
</button>
);

const renderActionButtons = () => {
if (isEditMode && !isEditTimeTableMode && !isFestivalDeleteMode) {
return (
<div className={styles.box}>
<button
onClick={() => handleModeToggle(setIsEditTimeTableMode)}
className={styles.boxButton}
>
<IcFloatEdit24 width="2.4rem" height="2.4rem" />
<span>타임테이블 편집</span>
</button>
<button
onClick={() => handleModeToggle(setIsFestivalDeleteMode)}
className={styles.boxButton}
>
<IcFloatDelete24 width="2.4rem" height="2.4rem" />
<span>페스티벌 삭제</span>
</button>
</div>
);
}
return null;
};

return (
<>
<div className={getBackgroundClassName()} />
{renderActionButtons()}
{renderButton()}
</>
);
};

export default EditFloatingButton;

0 comments on commit a848512

Please sign in to comment.