refactor album expansion to global scope

This commit is contained in:
jeffvli
2026-02-13 14:59:15 -08:00
parent e855f7dd01
commit 70fdd4bdc3
14 changed files with 297 additions and 225 deletions
@@ -1,3 +1,3 @@
.container {
height: 500px;
.list-expanded-container {
overflow: auto;
}
@@ -1,32 +1,23 @@
import { motion, Variants } from 'motion/react';
import { ReactNode } from 'react';
import styles from './expanded-list-container.module.css';
const expandedAnimationVariants: Variants = {
hidden: {
height: 0,
minHeight: 0,
},
show: {
minHeight: '300px',
transition: {
duration: 0.3,
ease: 'easeInOut',
},
},
};
const EXPANDED_HEIGHT = 300;
export const ExpandedListContainer = ({ children }: { children: ReactNode }) => {
export interface ExpandedListContainerProps {
children: ReactNode;
}
export const ExpandedListContainer = ({ children }: ExpandedListContainerProps) => {
return (
<motion.div
animate="show"
<div
className={styles.listExpandedContainer}
exit="hidden"
initial="hidden"
variants={expandedAnimationVariants}
style={{
height: EXPANDED_HEIGHT,
overflow: 'auto',
}}
>
{children}
</motion.div>
</div>
);
};
@@ -2,27 +2,18 @@ import { Suspense } from 'react';
import styles from './expanded-list-item.module.css';
import {
ItemListStateActions,
ItemListStateItem,
useItemListStateSubscription,
} from '/@/renderer/components/item-list/helpers/item-list-state';
import { ItemListStateItem } from '/@/renderer/components/item-list/helpers/item-list-state';
import { ExpandedAlbumListItem } from '/@/renderer/features/albums/components/expanded-album-list-item';
import { Spinner } from '/@/shared/components/spinner/spinner';
import { LibraryItem } from '/@/shared/types/domain-types';
interface ExpandedListItemProps {
internalState: ItemListStateActions;
item?: ItemListStateItem;
itemType: LibraryItem;
}
export const ExpandedListItem = ({ internalState, itemType }: ExpandedListItemProps) => {
const expandedItems = useItemListStateSubscription(internalState, () =>
internalState ? internalState.getExpandedItemsCached() : [],
);
const currentItem = expandedItems[0];
if (!currentItem) {
export const ExpandedListItem = ({ item, itemType }: ExpandedListItemProps) => {
if (!item) {
return null;
}
@@ -30,11 +21,7 @@ export const ExpandedListItem = ({ internalState, itemType }: ExpandedListItemPr
<div className={styles.container}>
<div className={styles.inner}>
<Suspense fallback={<Spinner container />}>
<SelectedItem
internalState={internalState}
item={currentItem as ItemListStateItem}
itemType={itemType}
/>
<SelectedItem item={item} itemType={itemType} />
</Suspense>
</div>
</div>
@@ -42,15 +29,14 @@ export const ExpandedListItem = ({ internalState, itemType }: ExpandedListItemPr
};
interface SelectedItemProps {
internalState: ItemListStateActions;
item: ItemListStateItem;
itemType: LibraryItem;
}
const SelectedItem = ({ internalState, item, itemType }: SelectedItemProps) => {
const SelectedItem = ({ item, itemType }: SelectedItemProps) => {
switch (itemType) {
case LibraryItem.ALBUM:
return <ExpandedAlbumListItem internalState={internalState} item={item} />;
return <ExpandedAlbumListItem item={item} />;
default:
return null;
}
@@ -8,6 +8,7 @@ import { ContextMenuController } from '/@/renderer/features/context-menu/context
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite';
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
import { useAppStore } from '/@/renderer/store';
import { LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
import { Play, TableColumn } from '/@/shared/types/types';
@@ -277,19 +278,27 @@ export const useDefaultItemListControls = (args?: UseDefaultItemListControlsArgs
}
},
onExpand: ({ internalState, item }: DefaultItemControlProps) => {
if (!item || !internalState) {
return;
}
onExpand: ({ item, itemType }: DefaultItemControlProps) => {
if (!item) return;
// Extract rowId from the item
const rowId = internalState.extractRowId(item);
if (!rowId) return;
// Use the item directly (rowId is separate, used only as key in state)
const itemListItem = item as ItemListStateItemWithRequiredProperties;
const setGlobalExpanded = useAppStore.getState().actions.setGlobalExpanded;
const globalExpanded = useAppStore.getState().globalExpanded;
return internalState?.toggleExpanded(itemListItem);
if (globalExpanded?.item?.id === item.id) {
setGlobalExpanded(null);
} else {
const itemForStore: ItemListStateItemWithRequiredProperties & {
imageId: null | string;
} = {
...itemListItem,
imageId: (itemListItem as { imageId?: null | string }).imageId ?? null,
};
setGlobalExpanded({
item: itemForStore,
itemType,
});
}
},
onFavorite: ({
@@ -1,6 +1,6 @@
import clsx from 'clsx';
import throttle from 'lodash/throttle';
import { AnimatePresence, motion } from 'motion/react';
import { motion } from 'motion/react';
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import React, {
CSSProperties,
@@ -31,15 +31,12 @@ import {
ItemCard,
ItemCardProps,
} from '/@/renderer/components/item-card/item-card';
import { ExpandedListContainer } from '/@/renderer/components/item-list/expanded-list-container';
import { ExpandedListItem } from '/@/renderer/components/item-list/expanded-list-item';
import { createExtractRowId } from '/@/renderer/components/item-list/helpers/extract-row-id';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import {
ItemListStateActions,
ItemListStateItemWithRequiredProperties,
useItemListState,
useItemListStateSubscription,
} from '/@/renderer/components/item-list/helpers/item-list-state';
import { useListHotkeys } from '/@/renderer/components/item-list/helpers/use-list-hotkeys';
import { ItemControls, ItemListHandle } from '/@/renderer/components/item-list/types';
@@ -829,10 +826,6 @@ const BaseItemGridList = ({
/>
)}
</AutoSizer>
<AnimatePresence presenceAffectsLayout>
<ExpandedContainer internalState={internalState} itemType={itemType} />
{/* {enableSelectionDialog && <SelectionDialog internalState={internalState} />} */}
</AnimatePresence>
</motion.div>
);
};
@@ -903,25 +896,3 @@ const ListComponent = memo((props: ListChildComponentProps<GridItemProps>) => {
export const ItemGridList = memo(BaseItemGridList);
ItemGridList.displayName = 'ItemGridList';
const ExpandedContainer = ({
internalState,
itemType,
}: {
internalState: ItemListStateActions;
itemType: LibraryItem;
}) => {
const hasExpanded = useItemListStateSubscription(internalState, (state) =>
state ? state.expanded.size > 0 : false,
);
return (
<AnimatePresence initial={false}>
{hasExpanded && (
<ExpandedListContainer>
<ExpandedListItem internalState={internalState} itemType={itemType} />
</ExpandedListContainer>
)}
</AnimatePresence>
);
};
@@ -1,7 +1,7 @@
// Component adapted from https://github.com/bvaughn/react-window/issues/826
import clsx from 'clsx';
import { AnimatePresence, motion } from 'motion/react';
import { motion } from 'motion/react';
import React, {
type JSXElementConstructor,
memo,
@@ -18,15 +18,12 @@ import { type CellComponentProps, Grid } from 'react-window-v2';
import styles from './item-table-list.module.css';
import { ExpandedListContainer } from '/@/renderer/components/item-list/expanded-list-container';
import { ExpandedListItem } from '/@/renderer/components/item-list/expanded-list-item';
import { createExtractRowId } from '/@/renderer/components/item-list/helpers/extract-row-id';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import {
ItemListStateActions,
ItemListStateItemWithRequiredProperties,
useItemListState,
useItemListStateSubscription,
} from '/@/renderer/components/item-list/helpers/item-list-state';
import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
import { useListHotkeys } from '/@/renderer/components/item-list/helpers/use-list-hotkeys';
@@ -1651,8 +1648,6 @@ const BaseItemTableList = ({
totalColumnCount={totalColumnCount}
totalRowCount={totalRowCount}
/>
<ExpandedContainer internalState={internalState} itemType={itemType} />
{/* {enableSelectionDialog && <SelectionDialog internalState={internalState} />} */}
</motion.div>
</ItemTableListConfigProvider>
</ItemTableListStoreProvider>
@@ -1661,26 +1656,4 @@ const BaseItemTableList = ({
export const ItemTableList = memo(BaseItemTableList);
const ExpandedContainer = ({
internalState,
itemType,
}: {
internalState: ItemListStateActions;
itemType: LibraryItem;
}) => {
const hasExpanded = useItemListStateSubscription(internalState, (state) =>
state ? state.expanded.size > 0 : false,
);
return (
<AnimatePresence initial={false}>
{hasExpanded && (
<ExpandedListContainer>
<ExpandedListItem internalState={internalState} itemType={itemType} />
</ExpandedListContainer>
)}
</AnimatePresence>
);
};
ItemTableList.displayName = 'ItemTableList';