diff --git a/src/renderer/features/shared/components/item-image-placeholder.module.scss b/src/renderer/features/shared/components/item-image-placeholder.module.scss
new file mode 100644
index 00000000..afca7b67
--- /dev/null
+++ b/src/renderer/features/shared/components/item-image-placeholder.module.scss
@@ -0,0 +1,12 @@
+.image-placeholder {
+ width: 100%;
+ height: 100%;
+ background-color: var(--placeholder-bg);
+ border-radius: var(--card-default-radius);
+
+ svg {
+ width: 35px;
+ height: 35px;
+ color: var(--placeholder-fg);
+ }
+}
diff --git a/src/renderer/features/shared/components/item-image-placeholder.tsx b/src/renderer/features/shared/components/item-image-placeholder.tsx
new file mode 100644
index 00000000..1944eae3
--- /dev/null
+++ b/src/renderer/features/shared/components/item-image-placeholder.tsx
@@ -0,0 +1,32 @@
+import { Center } from '@mantine/core';
+import { memo } from 'react';
+import { RiAlbumFill, RiPlayListFill, RiUserVoiceFill } from 'react-icons/ri';
+import styles from './item-image-placeholder.module.scss';
+import { LibraryItem } from '/@/renderer/api/types';
+
+interface ItemImagePlaceholderProps {
+ itemType?: LibraryItem;
+}
+
+const Image = memo(function Image(props: ItemImagePlaceholderProps) {
+ switch (props.itemType) {
+ case LibraryItem.ALBUM:
+ return ;
+ case LibraryItem.ARTIST:
+ return ;
+ case LibraryItem.ALBUM_ARTIST:
+ return ;
+ case LibraryItem.PLAYLIST:
+ return ;
+ default:
+ return ;
+ }
+});
+
+export const ItemImagePlaceholder = ({ itemType }: ItemImagePlaceholderProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/renderer/features/shared/components/library-header.module.scss b/src/renderer/features/shared/components/library-header.module.scss
new file mode 100644
index 00000000..1c9f5f9a
--- /dev/null
+++ b/src/renderer/features/shared/components/library-header.module.scss
@@ -0,0 +1,126 @@
+.library-header {
+ position: relative;
+ display: grid;
+ grid-template-areas: 'image info';
+ grid-template-rows: 100%;
+ grid-template-columns: 175px minmax(0, 1fr);
+ gap: 1rem;
+ width: 100%;
+ max-width: 100%;
+ height: 30vh;
+ min-height: 340px;
+ max-height: 500px;
+ padding: 5rem 2rem 2rem;
+
+ @container (min-width: 600px) {
+ grid-template-columns: 175px minmax(0, 1fr);
+
+ h1 {
+ font-size: 3rem;
+ }
+
+ .image {
+ img {
+ width: 175px !important;
+ height: 175px;
+ }
+ }
+ }
+
+ @container (min-width: 600px) {
+ grid-template-columns: 200px minmax(0, 1fr);
+
+ h1 {
+ font-size: 4.5rem;
+ }
+
+ .image {
+ img {
+ width: 200px !important;
+ height: 200px;
+ }
+ }
+ }
+
+ @container (min-width: 768px) {
+ grid-template-columns: 225px minmax(0, 1fr);
+
+ h1 {
+ font-size: 5rem;
+ }
+
+ .image {
+ img {
+ width: 225px !important;
+ height: 225px;
+ }
+ }
+ }
+
+ @container (min-width: 1200px) {
+ grid-template-columns: 250px minmax(0, 1fr);
+
+ h1 {
+ font-size: 5.5rem;
+ }
+
+ .image {
+ img {
+ width: 250px !important;
+ height: 250px;
+ }
+ }
+ }
+}
+
+.image-section {
+ z-index: 15;
+ display: flex;
+ grid-area: image;
+ align-items: flex-end;
+ justify-content: center;
+ height: 100%;
+ filter: drop-shadow(0 0 8px rgb(0, 0, 0, 50%));
+}
+
+.metadata-section {
+ z-index: 15;
+ display: flex;
+ flex-direction: column;
+ grid-area: info;
+ justify-content: flex-end;
+ width: 100%;
+}
+
+.image {
+ img {
+ object-fit: cover;
+ }
+}
+
+.background {
+ position: absolute;
+ top: 0;
+ z-index: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.9;
+}
+
+.background-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--bg-header-overlay);
+}
+
+.title {
+ overflow: hidden;
+ color: var(--main-fg);
+ line-height: 1.15;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
diff --git a/src/renderer/features/shared/components/library-header.tsx b/src/renderer/features/shared/components/library-header.tsx
index bfb9941e..3fc6a961 100644
--- a/src/renderer/features/shared/components/library-header.tsx
+++ b/src/renderer/features/shared/components/library-header.tsx
@@ -1,74 +1,11 @@
+import { Group } from '@mantine/core';
import { forwardRef, ReactNode, Ref } from 'react';
-import { Center, Group } from '@mantine/core';
-import { useMergedRef } from '@mantine/hooks';
-import { RiAlbumFill } from 'react-icons/ri';
import { Link } from 'react-router-dom';
import { SimpleImg } from 'react-simple-img';
-import styled from 'styled-components';
+import styles from './library-header.module.scss';
import { LibraryItem } from '/@/renderer/api/types';
-import { Text, TextTitle } from '/@/renderer/components';
-import { useContainerQuery } from '/@/renderer/hooks';
-
-const HeaderContainer = styled.div<{ imageSize: number }>`
- position: relative;
- display: grid;
- grid-auto-columns: 1fr;
- grid-template-areas: 'image info';
- grid-template-rows: 1fr;
- grid-template-columns: ${(props) => props.imageSize + 25}px minmax(0, 1fr);
- gap: 0.5rem;
- width: 100%;
- max-width: 100%;
- height: 30vh;
- min-height: 340px;
- max-height: 500px;
- padding: 5rem 2rem 2rem;
-`;
-
-const CoverImageWrapper = styled.div`
- z-index: 15;
- display: flex;
- grid-area: image;
- align-items: flex-end;
- justify-content: center;
- height: 100%;
- filter: drop-shadow(0 0 8px rgb(0, 0, 0, 50%));
-`;
-
-const MetadataWrapper = styled.div`
- z-index: 15;
- display: flex;
- flex-direction: column;
- grid-area: info;
- justify-content: flex-end;
- width: 100%;
-`;
-
-const StyledImage = styled(SimpleImg)`
- img {
- object-fit: cover;
- }
-`;
-
-const BackgroundImage = styled.div<{ background: string }>`
- position: absolute;
- top: 0;
- z-index: 0;
- width: 100%;
- height: 100%;
- background: ${(props) => props.background};
- opacity: 0.9;
-`;
-
-const BackgroundImageOverlay = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- z-index: 0;
- width: 100%;
- height: 100%;
- background: var(--bg-header-overlay);
-`;
+import { Text } from '/@/renderer/components';
+import { ItemImagePlaceholder } from '/@/renderer/features/shared/components/item-image-placeholder';
interface LibraryHeaderProps {
background: string;
@@ -84,53 +21,30 @@ export const LibraryHeader = forwardRef(
{ imageUrl, imagePlaceholderUrl, background, title, item, children }: LibraryHeaderProps,
ref: Ref,
) => {
- const cq = useContainerQuery();
- const mergedRef = useMergedRef(ref, cq.ref);
- const titleSize = cq.isXl
- ? '6rem'
- : cq.isLg
- ? '5.5rem'
- : cq.isMd
- ? '5rem'
- : cq.isSm
- ? '4.5rem'
- : '3rem';
-
- const imageSize = cq.isLg ? 250 : cq.isMd ? 225 : cq.isSm ? 200 : 175;
-
return (
-
-
-
-
+
+
+
{imageUrl ? (
-
) : (
-
-
-
+
)}
-
-
+
+
-
- {title}
-
+
{title}
{children}
-
-
+
+
);
},
);