adjust playlist folder design, add animations

This commit is contained in:
jeffvli
2026-05-18 18:34:58 -07:00
parent da4284bac0
commit 3d1095dbd8
4 changed files with 61 additions and 33 deletions
@@ -22,7 +22,7 @@
gap: var(--theme-spacing-md);
align-items: center;
width: 100%;
padding: var(--theme-spacing-xs) var(--theme-spacing-md);
padding: var(--theme-spacing-sm) var(--theme-spacing-md);
font: inherit;
color: inherit;
text-align: left;
@@ -32,12 +32,18 @@
border-radius: var(--theme-radius-md);
&:hover {
background-color: var(--theme-colors-surface);
background-color: var(--theme-colors-background);
}
}
.chevron {
display: flex;
flex-shrink: 0;
align-items: center;
}
.collapse {
overflow: hidden;
}
.name {
@@ -1,9 +1,11 @@
import clsx from 'clsx';
import { motion } from 'motion/react';
import {
ComponentPropsWithoutRef,
CSSProperties,
MouseEvent,
ReactElement,
ReactNode,
useCallback,
useMemo,
useState,
@@ -33,6 +35,25 @@ import { DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-dr
const STORAGE_KEY_PREFIX = 'feishin:playlist-folder-state';
const FOLDER_COLLAPSE_TRANSITION = { duration: 0.2, ease: 'easeInOut' } as const;
interface PlaylistFolderCollapseProps {
children: ReactNode;
className?: string;
open: boolean;
}
const PlaylistFolderCollapse = ({ children, className, open }: PlaylistFolderCollapseProps) => (
<motion.div
animate={{ height: open ? 'auto' : 0, opacity: open ? 1 : 0 }}
className={clsx(styles.collapse, className)}
initial={false}
transition={FOLDER_COLLAPSE_TRANSITION}
>
{children}
</motion.div>
);
export const getPlaylistLeafName = (name: string, separator: string): string => {
if (!separator) return name;
const segments = name.split(separator).filter((segment) => segment.length > 0);
@@ -457,7 +478,7 @@ const PlaylistFolderHeader = ({
type="button"
>
<div className={styles.navFolderIcon}>
<Icon color="muted" icon="folder" size="xl" />
<Icon color="muted" icon={isOpen ? 'folder' : 'folderClosed'} size="md" />
</div>
<Text className={styles.name} fw={500} size="md">
{name}
@@ -482,18 +503,21 @@ const PlaylistFolderHeader = ({
style={{ opacity: isDragging ? 0.5 : 1 }}
type="button"
>
<Icon
className={styles.chevron}
icon={isOpen ? 'arrowDownS' : 'arrowRightS'}
size="sm"
/>
<Icon color="muted" icon="folder" size="sm" />
<Icon color="muted" icon={isOpen ? 'folder' : 'folderClosed'} size="md" />
<Text className={styles.name} fw={500} size="md">
{name}
</Text>
<Text className={styles.count} isMuted size="sm">
{leafCount}
</Text>
<motion.span
animate={{ rotate: isOpen ? 180 : 0 }}
className={styles.chevron}
initial={false}
transition={FOLDER_COLLAPSE_TRANSITION}
>
<Icon icon="arrowUpS" size="md" />
</motion.span>
</button>
);
};
@@ -724,20 +748,18 @@ export const PlaylistFolderTree = ({
onClick={() => onToggleFolder(group.name)}
variant="header"
/>
{isOpen && (
<div className={styles.children}>
{group.items.map((item) => (
<PlaylistRowButton
item={item}
key={item.id}
name={item.name.slice(group.name.length + 1)}
onContextMenu={onContextMenu}
onReorder={onReorder}
to={item.id}
/>
))}
</div>
)}
<PlaylistFolderCollapse className={styles.children} open={isOpen}>
{group.items.map((item) => (
<PlaylistRowButton
item={item}
key={item.id}
name={item.name.slice(group.name.length + 1)}
onContextMenu={onContextMenu}
onReorder={onReorder}
to={item.id}
/>
))}
</PlaylistFolderCollapse>
</div>
);
})}
@@ -788,15 +810,13 @@ export const PlaylistFolderTreeView = ({
onClick={() => onToggleFolder(node.path)}
variant="header"
/>
{isOpen && (
<div className={styles.treeChildren}>
{node.children.map((child) => (
<div className={styles.treeBranch} key={getNodeKey(child)}>
{renderNode(child)}
</div>
))}
</div>
)}
<PlaylistFolderCollapse className={styles.treeChildren} open={isOpen}>
{node.children.map((child) => (
<div className={styles.treeBranch} key={getNodeKey(child)}>
{renderNode(child)}
</div>
))}
</PlaylistFolderCollapse>
</div>
);
};
@@ -571,7 +571,7 @@ export const SidebarPlaylistList = () => {
{inNavigation ? navigation.currentName : t('page.sidebar.playlists')}
</Text>
</Group>
<Group gap="xs">
<Group gap="xs" wrap="nowrap">
<ActionIcon
icon="add"
iconProps={{
+2
View File
@@ -54,6 +54,7 @@ import {
LuExternalLink,
LuFileJson,
LuFlag,
LuFolderClosed,
LuFolderOpen,
LuGauge,
LuGithub,
@@ -278,6 +279,7 @@ export const AppIcon = {
fileJson: LuFileJson,
filter: LuListFilter,
folder: LuFolderOpen,
folderClosed: LuFolderClosed,
genre: LuFlag,
goToItem: LuCornerDownRight,
hash: LuHash,