mirror of
https://github.com/M4X809/list-of-lp.git
synced 2025-12-25 19:12:48 +00:00
Refactor TrackCard and TrackModal components for improved functionality and styling
- Replaced hash-based modal opening in TrackCard with state management for better control. - Integrated TrackModal into TrackCard, allowing for direct interaction and improved user experience. - Updated TrackModal to accept props for track data and theme, enhancing its reusability. - Refined styling in both components to maintain visual consistency with dynamic theming.
This commit is contained in:
parent
b93b0c2160
commit
9fad47fc65
4 changed files with 155 additions and 161 deletions
|
|
@ -4,7 +4,7 @@ import { Card, Group, Text, Badge, Box } from "@mantine/core";
|
|||
import { useState } from "react";
|
||||
import type { AlbumTheme } from "@/lib/themes";
|
||||
import type { Track } from "@/lib/ListTypes";
|
||||
import { useHash } from "@mantine/hooks";
|
||||
import TrackModal from "./TrackModal";
|
||||
|
||||
interface TrackCardProps {
|
||||
track: Track;
|
||||
|
|
@ -14,100 +14,103 @@ interface TrackCardProps {
|
|||
|
||||
export default function TrackCard({ track, index, theme }: TrackCardProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [_hash, setHash] = useHash();
|
||||
const [modalOpened, setModalOpened] = useState(false);
|
||||
|
||||
return (
|
||||
<Card
|
||||
onClick={() => setHash(`trackModal_${track.id}`)}
|
||||
className="backdrop-blur-sm transition-all duration-300"
|
||||
style={{
|
||||
background: isHovered ? theme.card.backgroundHover : theme.card.background,
|
||||
border: `1px solid ${isHovered ? theme.border.light : theme.card.border}`,
|
||||
cursor: "pointer",
|
||||
// transform: isHovered ? "translateY(-2px)" : "translateY(0)",
|
||||
boxShadow: isHovered ? `0 8px 24px ${theme.primary.DEFAULT}30` : "none",
|
||||
}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Group gap="md">
|
||||
<Box
|
||||
style={{
|
||||
minWidth: "40px",
|
||||
height: "40px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: theme.primary.DEFAULT,
|
||||
borderRadius: "8px",
|
||||
fontWeight: 700,
|
||||
fontSize: "18px",
|
||||
color: theme.text.contrast,
|
||||
boxShadow: `0 4px 12px ${theme.primary.DEFAULT}60`,
|
||||
}}
|
||||
>
|
||||
{index + 1}
|
||||
</Box>
|
||||
<Text size="lg" fw={500} style={{ color: theme.text.primary }}>
|
||||
{track.label}
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Group gap="md">
|
||||
<Group gap="xs">
|
||||
{track.studioUrl && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.studio,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
Studio
|
||||
</Badge>
|
||||
)}
|
||||
{track.emilyLive && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.liveEmily,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
Emily Live
|
||||
</Badge>
|
||||
)}
|
||||
{track.lpLive && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.liveLP,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
LP Live
|
||||
</Badge>
|
||||
)}
|
||||
<>
|
||||
<Card
|
||||
onClick={() => setModalOpened(true)}
|
||||
className="backdrop-blur-sm transition-all duration-300"
|
||||
style={{
|
||||
background: isHovered ? theme.card.backgroundHover : theme.card.background,
|
||||
border: `1px solid ${isHovered ? theme.border.light : theme.card.border}`,
|
||||
cursor: "pointer",
|
||||
// transform: isHovered ? "translateY(-2px)" : "translateY(0)",
|
||||
boxShadow: isHovered ? `0 8px 24px ${theme.primary.DEFAULT}30` : "none",
|
||||
}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Group gap="md">
|
||||
<Box
|
||||
style={{
|
||||
minWidth: "40px",
|
||||
height: "40px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: theme.primary.DEFAULT,
|
||||
borderRadius: "8px",
|
||||
fontWeight: 700,
|
||||
fontSize: "18px",
|
||||
color: theme.text.contrast,
|
||||
boxShadow: `0 4px 12px ${theme.primary.DEFAULT}60`,
|
||||
}}
|
||||
>
|
||||
{index + 1}
|
||||
</Box>
|
||||
<Text size="lg" fw={500} style={{ color: theme.text.primary }}>
|
||||
{track.label}
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Group gap="md">
|
||||
<Group gap="xs">
|
||||
{track.studioUrl && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.studio,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
Studio
|
||||
</Badge>
|
||||
)}
|
||||
{track.emilyLive && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.liveEmily,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
Emily Live
|
||||
</Badge>
|
||||
)}
|
||||
{track.lpLive && (
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
style={{
|
||||
background: theme.badges.liveLP,
|
||||
color: theme.text.contrast,
|
||||
}}
|
||||
>
|
||||
LP Live
|
||||
</Badge>
|
||||
)}
|
||||
</Group>
|
||||
<Text
|
||||
size="sm"
|
||||
fw={500}
|
||||
style={{
|
||||
color: theme.text.muted,
|
||||
fontFamily: "monospace",
|
||||
background: theme.background.tertiary,
|
||||
padding: "4px 12px",
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
>
|
||||
{track.duration}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text
|
||||
size="sm"
|
||||
fw={500}
|
||||
style={{
|
||||
color: theme.text.muted,
|
||||
fontFamily: "monospace",
|
||||
background: theme.background.tertiary,
|
||||
padding: "4px 12px",
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
>
|
||||
{track.duration}
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Card>
|
||||
</Card>
|
||||
<TrackModal opened={modalOpened} onClose={() => setModalOpened(false)} track={track} theme={theme} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,72 +1,53 @@
|
|||
"use client";
|
||||
import { albums } from "@/lib/list";
|
||||
import { getSongLink } from "@/lib/songLinks";
|
||||
import { getThemeColors } from "@/lib/themes";
|
||||
import { faApple, faSpotify, faYoutube, faYoutubeSquare, IconDefinition } from "@fortawesome/free-brands-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { AlbumTheme } from "@/lib/themes";
|
||||
import type { Track } from "@/lib/ListTypes";
|
||||
import {
|
||||
ActionIcon,
|
||||
Anchor,
|
||||
Box,
|
||||
Divider,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure, useHash } from "@mantine/hooks";
|
||||
faApple,
|
||||
faSpotify,
|
||||
faYoutube,
|
||||
faYoutubeSquare,
|
||||
type IconDefinition,
|
||||
} from "@fortawesome/free-brands-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { ActionIcon, Box, Divider, Grid, GridCol, Group, Modal, Stack, Text, Title, Tooltip } from "@mantine/core";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function TrackModal() {
|
||||
const [hash, setHash] = useHash();
|
||||
console.log("🚀 ~ TrackModal.tsx:8 ~ TrackModal ~ hash:", hash);
|
||||
const close = () => {
|
||||
setHash("");
|
||||
};
|
||||
interface TrackModalProps {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
track: Track;
|
||||
theme: AlbumTheme;
|
||||
}
|
||||
|
||||
const album = albums.find((album) => {
|
||||
return album.id === hash.split("_")[1];
|
||||
});
|
||||
const track = album?.tracks.find((track) => {
|
||||
return track.id === `${hash.split("_")[1]}_${hash.split("_")[2]}`;
|
||||
});
|
||||
|
||||
const songLink = getSongLink(track?.id || "");
|
||||
console.log("🚀 ~ TrackModal.tsx:25 ~ TrackModal ~ songLink:", songLink);
|
||||
|
||||
const theme = getThemeColors(album?.id || "");
|
||||
export default function TrackModal({ opened, onClose, track, theme }: TrackModalProps) {
|
||||
const songLink = getSongLink(track.id);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={hash.startsWith("#trackModal_")}
|
||||
onClose={close}
|
||||
centered
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
withCloseButton={false}
|
||||
transitionProps={{ duration: 150, transition: "fade" }}
|
||||
styles={{
|
||||
// content: {
|
||||
// background: theme.background.gradient,
|
||||
// border: `2px solid ${theme.border.light}`,
|
||||
// boxShadow: "none",
|
||||
// },
|
||||
// header: {
|
||||
// background: theme.background.gradient,
|
||||
// border: `2px solid ${theme.border.light}`,
|
||||
// boxShadow: "none",
|
||||
// },
|
||||
body: {
|
||||
background: theme.background.gradient,
|
||||
// border: `2px solid ${theme.border.light}`,
|
||||
|
||||
boxShadow: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack>
|
||||
<Title order={1}>{track?.label}</Title>
|
||||
<Divider size={"lg"} label={<Title order={3}>Studio Version</Title>} />
|
||||
<Title order={1}>{track.label}</Title>
|
||||
<Divider
|
||||
color={theme.accent.DEFAULT}
|
||||
size={"lg"}
|
||||
label={
|
||||
<Title c={theme.accent.DEFAULT} order={3}>
|
||||
Studio Version
|
||||
</Title>
|
||||
}
|
||||
/>
|
||||
<Grid columns={songLink && Object.keys(songLink).length > 0 ? Object.keys(songLink).length : 4}>
|
||||
{songLink &&
|
||||
Object.entries(songLink).map(([key, value]) => {
|
||||
|
|
@ -113,11 +94,12 @@ export default function TrackModal() {
|
|||
return (
|
||||
<GridCol
|
||||
span={1}
|
||||
key={`${track?.id}_${key}`}
|
||||
key={`${track.id}_${key}`}
|
||||
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<Tooltip label={themeColor.tooltip}>
|
||||
<ActionIcon
|
||||
tabIndex={-1}
|
||||
variant="filled"
|
||||
color={themeColor.color}
|
||||
size="lg"
|
||||
|
|
@ -133,11 +115,19 @@ export default function TrackModal() {
|
|||
);
|
||||
})}
|
||||
</Grid>
|
||||
{Array.isArray(track?.emilyLive) && (
|
||||
{Array.isArray(track.emilyLive) && (
|
||||
<>
|
||||
<Divider size={"lg"} label={<Title order={3}>Fan Live Versions</Title>} />
|
||||
<Divider
|
||||
color={theme.accent.DEFAULT}
|
||||
size={"lg"}
|
||||
label={
|
||||
<Title c={theme.accent.DEFAULT} order={3}>
|
||||
Fan Live Versions
|
||||
</Title>
|
||||
}
|
||||
/>
|
||||
<Stack>
|
||||
{track.emilyLive.map((live, idx) => (
|
||||
{track.emilyLive.map((live) => (
|
||||
<Box style={{ width: "100%" }} key={`emilyLive_${live.url}`}>
|
||||
<Group justify="space-between" align="center">
|
||||
<Box style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
||||
|
|
@ -148,6 +138,7 @@ export default function TrackModal() {
|
|||
</Text>
|
||||
</Box>
|
||||
<ActionIcon
|
||||
tabIndex={-1}
|
||||
variant="filled"
|
||||
color={"#FF0000"}
|
||||
size="lg"
|
||||
|
|
@ -165,26 +156,35 @@ export default function TrackModal() {
|
|||
</>
|
||||
)}
|
||||
|
||||
{track?.lpLive && (
|
||||
{track.lpLive && (
|
||||
<>
|
||||
<Divider size={"lg"} label={<Title order={3}>Linkin Park Live Versions</Title>} />
|
||||
<Divider
|
||||
color={theme.accent.DEFAULT}
|
||||
size={"lg"}
|
||||
label={
|
||||
<Title c={theme.accent.DEFAULT} order={3}>
|
||||
Linkin Park Live Versions
|
||||
</Title>
|
||||
}
|
||||
/>
|
||||
<Group justify="space-between" align="center">
|
||||
<Box style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
||||
<Text>
|
||||
{new Date(track?.lpLive.date).toLocaleDateString("en-US", {
|
||||
{new Date(track.lpLive.date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</Text>
|
||||
<Text>{track?.lpLive.location}</Text>
|
||||
<Text>{track.lpLive.location}</Text>
|
||||
</Box>
|
||||
<ActionIcon
|
||||
tabIndex={-1}
|
||||
variant="filled"
|
||||
color={"#FF0000"}
|
||||
size="lg"
|
||||
component={Link}
|
||||
href={track?.lpLive.url}
|
||||
href={track.lpLive.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { albums } from "../../../lib/list";
|
|||
import BackButton from "../../../Components/BackButton";
|
||||
import { getThemeColors } from "@/lib/themes";
|
||||
import TrackCard from "@/Components/TrackCard";
|
||||
import TrackModal from "@/Components/TrackModal";
|
||||
|
||||
export function generateStaticParams() {
|
||||
return albums.map((album) => ({
|
||||
|
|
@ -46,7 +45,6 @@ export default async function AlbumDetail({ params }: { params: Promise<{ albumI
|
|||
viewTransitionName: `album-card-background-${album.id}`,
|
||||
}}
|
||||
>
|
||||
<TrackModal />
|
||||
<Container size="xl" className="py-12">
|
||||
{/* Back Button */}
|
||||
<BackButton theme={theme} />
|
||||
|
|
@ -57,7 +55,6 @@ export default async function AlbumDetail({ params }: { params: Promise<{ albumI
|
|||
style={{
|
||||
border: `3px solid ${theme.border.light}`,
|
||||
borderRadius: "8px",
|
||||
padding: "4px",
|
||||
background: theme.background.secondary,
|
||||
boxShadow: `0 8px 32px ${theme.primary.DEFAULT}40`,
|
||||
viewTransitionName: `album-card-image-${album.id}`,
|
||||
|
|
|
|||
|
|
@ -22,12 +22,6 @@ export const albums: Album[] = [
|
|||
location: "Austin, Texas, USA",
|
||||
author: "Erynn Halvorson",
|
||||
},
|
||||
{
|
||||
url: "https://youtu.be/miQ9Y5UW08dasdasdg",
|
||||
date: "2025-04-26",
|
||||
location: "Austin, Texas, USdasdsaA",
|
||||
author: "Erynn Halvorson",
|
||||
},
|
||||
],
|
||||
lpLive: {
|
||||
url: "https://www.youtube.com/watch?v=DOKcCl6iKaA",
|
||||
|
|
|
|||
Loading…
Reference in a new issue