mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 13:00:13 +02:00
refactor album expansion to global scope
This commit is contained in:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user