Skip to content
Merged
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
2 changes: 2 additions & 0 deletions src/app/routes/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { JSX } from "react";
import { Outlet } from "react-router-dom";

import Footer from "@widgets/Footer";
import Header from "@widgets/Header/Header";

const MainLayout = (): JSX.Element => {
Expand All @@ -10,6 +11,7 @@ const MainLayout = (): JSX.Element => {
<main>
<Outlet />
</main>
<Footer />
</>
);
};
Expand Down
184 changes: 184 additions & 0 deletions src/shared/ui/DevelopersDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import {
Box,
Typography,
Collapse,
IconButton,
Link,
styled,
useTheme,
useMediaQuery,
} from "@mui/material";
import type { JSX } from "react";
import { useState } from "react";

import GradientGitHubIcon from "@shared/ui/icons/GradientGitHubIcon";

const developers = [
{ name: "윤다빈", url: "https://github.com/czmcm5" },
{ name: "윤태관", url: "https://github.com/tkyoun0421" },
{ name: "석민영", url: "https://github.com/MINYOUNG-SEOK" },
{ name: "한사라", url: "https://github.com/namee-h" },
];

export default function DevelopersDropdown(): JSX.Element {
const [open, setOpen] = useState(false);
const [hover, setHover] = useState(false);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));

const isActive = open || hover;

return (
<DropdownContainer>
{/* 타이틀 박스 */}
<DropdownTitle
$active={isActive}
onClick={() => setOpen((prev) => !prev)}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<DropdownTitleCenter>
<GradientGitHubIcon
size={isMobile ? 22 : 32}
color={isActive ? undefined : "#888"}
/>
<DropdownTitleText $active={isActive}>개발자들</DropdownTitleText>
</DropdownTitleCenter>
<IconButton
size="small"
sx={{
transition: "transform 0.2s, color 0.2s",
transform: open ? "rotate(180deg)" : "rotate(0deg)",
color: isActive ? "#2563eb" : "#23294a",
}}
disableRipple
disableFocusRipple
>
<ExpandMoreIcon />
</IconButton>
</DropdownTitle>
{/* 리스트 */}
<Collapse in={open}>
<DropdownList>
{developers.map((dev) => (
<DropdownItem key={dev.name}>
<NameText>{dev.name}</NameText>
<GithubLink
href={dev.url}
target="_blank"
rel="noopener noreferrer"
underline="hover"
>
{dev.url}
</GithubLink>
</DropdownItem>
))}
</DropdownList>
</Collapse>
</DropdownContainer>
);
}

const DropdownContainer = styled(Box)({
width: 380,
maxWidth: "100%",
});

const DropdownTitle = styled(Box)<{ $active?: boolean }>(
({ $active, theme }) => ({
display: "flex",
alignItems: "center",
gap: 16,
padding: "16px 24px",
borderRadius: 8,
background: $active ? "#f5f5f5" : "transparent",
boxShadow: "none",
cursor: "pointer",
transition:
"box-shadow 0.2s, background 0.2s, transform 0.18s cubic-bezier(0.4,0,0.2,1)",
fontWeight: 600,
fontSize: 18,
color: "#23294a",
marginBottom: 8,
transform: $active ? "scale(1.04)" : "scale(1)",
[theme.breakpoints.down("sm")]: {
padding: "12px 8px",
gap: 8,
},
})
);

const DropdownTitleCenter = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
flex: 1,
justifyContent: "center",
[theme.breakpoints.down("sm")]: {
gap: 4,
},
}));

const DropdownTitleText = styled(Typography, {
shouldForwardProp: (prop) => prop !== "$active",
})<{ $active: boolean }>(({ $active, theme }) => ({
fontWeight: 600,
fontSize: 18,
color: $active ? "#2563eb" : "#888",
letterSpacing: "0.2rem",
transition: "color 0.2s",
[theme.breakpoints.down("sm")]: {
fontSize: 15,
},
}));

const DropdownList = styled(Box)({
background: "transparent",
borderRadius: 0,
padding: "4px 12px",
boxShadow: "none",
});

const DropdownItem = styled(Box)({
display: "flex",
alignItems: "center",
padding: "4px 8px",
borderRadius: 0,
transition: "background 0.15s",
margin: "2px 0",
overflow: "hidden",
cursor: "pointer",
"&:hover": {
background: "#f5f5f5",
border: "none",
borderRadius: 0,
boxShadow: "none",
},
});

const NameText = styled(Typography)({
fontWeight: 500,
color: "#888",
minWidth: 70,
fontSize: 10,
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
});

const GithubLink = styled(Link)({
marginLeft: 8,
color: "#bbb",
fontSize: 12,
textDecoration: "none",
wordBreak: "break-all",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
maxWidth: 220,
transition: "color 0.18s cubic-bezier(0.4,0,0.2,1)",
"&:hover": {
color: "#2563eb",
textDecoration: "none",
},
});
56 changes: 36 additions & 20 deletions src/shared/ui/LogoBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Box, styled, alpha } from "@mui/material";
import type { SxProps, Theme } from "@mui/material";
import type { JSX } from "react";
import { useNavigate } from "react-router-dom";

Expand All @@ -8,6 +9,8 @@ interface LogoBoxProps {
showText?: boolean;
text?: string;
className?: string;
disableHover?: boolean;
sx?: SxProps<Theme>;
}

const LogoBox = ({
Expand All @@ -16,6 +19,8 @@ const LogoBox = ({
showText = true,
text = "프로젝트 잼",
className,
disableHover = false,
sx,
}: LogoBoxProps): JSX.Element => {
const navigate = useNavigate();

Expand All @@ -28,7 +33,13 @@ const LogoBox = ({
};

return (
<StyledLogoBox onClick={handleClick} $size={size} className={className}>
<StyledLogoBox
onClick={handleClick}
$size={size}
className={className}
$disableHover={disableHover}
sx={sx}
>
<LogoImage src="/public/logo.svg" alt="프로젝트 잼" $size={size} />
{showText && <LogoText $size={size}>{text}</LogoText>}
</StyledLogoBox>
Expand All @@ -37,25 +48,30 @@ const LogoBox = ({

export default LogoBox;

const StyledLogoBox = styled(Box)<{ $size: "small" | "medium" | "large" }>(
({ theme, $size }) => ({
display: "flex",
alignItems: "center",
cursor: "pointer",
padding:
$size === "small"
? "0.3rem 0.8rem"
: $size === "medium"
? "0.5rem 1rem"
: "0.8rem 1.5rem",
borderRadius: theme.spacing(1.5),
transition: "all 0.2s ease-in-out",
"&:hover": {
backgroundColor: alpha(theme.palette.primary.main, 0.08),
transform: "translateY(-1px)",
},
})
);
const StyledLogoBox = styled(Box)<{
$size: "small" | "medium" | "large";
$disableHover?: boolean;
}>(({ theme, $size, $disableHover }) => ({
display: "flex",
alignItems: "center",
cursor: "pointer",
padding:
$size === "small"
? "0.3rem 0.8rem"
: $size === "medium"
? "0.5rem 1rem"
: "0.8rem 1.5rem",
borderRadius: theme.spacing(1.5),
transition: "all 0.2s ease-in-out",
...(!!$disableHover
? {}
: {
"&:hover": {
backgroundColor: alpha(theme.palette.primary.main, 0.08),
transform: "translateY(-1px)",
},
}),
}));

const LogoImage = styled("img")<{ $size: "small" | "medium" | "large" }>(
({ theme, $size }) => ({
Expand Down
3 changes: 3 additions & 0 deletions src/shared/ui/SnackbarAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const SnackbarAlert = ({
autoHideDuration={duration}
onClose={onClose}
anchorOrigin={anchorOrigin}
sx={{
marginTop: { xs: "6.4rem", md: "8rem" },
}}
>
<Alert severity={severity} sx={{ width: "100%" }}>
{message}
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ui/icons/CommonIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ export { default as HistoryToggleOffIcon } from "@mui/icons-material/HistoryTogg
export { default as ChevronLeftIcon } from "@mui/icons-material/ChevronLeft";
export { default as ChevronRightIcon } from "@mui/icons-material/ChevronRight";
export { default as MoreHorizIcon } from "@mui/icons-material/MoreHoriz";

export { default as GitHubIcon } from "@mui/icons-material/GitHub";
37 changes: 37 additions & 0 deletions src/shared/ui/icons/GradientGitHubIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { JSX } from "react";

export default function GradientGitHubIcon({
size = 32,
color,
}: {
size?: number;
color?: string;
}): JSX.Element {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient
id="github-gradient"
x1="0"
y1="0"
x2="24"
y2="24"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#7B3FE4" />
<stop offset="1" stopColor="#2DB6F5" />
</linearGradient>
</defs>
<path
fill={color ? color : "url(#github-gradient)"}
d="M12 2C6.48 2 2 6.58 2 12.26c0 4.48 2.87 8.28 6.84 9.63.5.09.68-.22.68-.48 0-.24-.01-.87-.01-1.7-2.78.62-3.37-1.36-3.37-1.36-.45-1.17-1.1-1.48-1.1-1.48-.9-.63.07-.62.07-.62 1 .07 1.53 1.05 1.53 1.05.89 1.56 2.34 1.11 2.91.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.7 0 0 .84-.28 2.75 1.05A9.38 9.38 0 0 1 12 6.84c.85.004 1.71.12 2.51.35 1.91-1.33 2.75-1.05 2.75-1.05.55 1.4.2 2.44.1 2.7.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.81-4.57 5.07.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.81 0 .27.18.58.69.48A10.01 10.01 0 0 0 22 12.26C22 6.58 17.52 2 12 2Z"
/>
</svg>
);
}
Loading