Add dynamic grid sizing
This commit is contained in:
parent
1ab75f7187
commit
f09ad1da89
15 changed files with 127 additions and 52 deletions
|
@ -20,6 +20,7 @@ interface BaseGridCardProps {
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
}) => void;
|
}) => void;
|
||||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||||
|
itemGap: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
playButtonBehavior: Play;
|
playButtonBehavior: Play;
|
||||||
resetInfiniteLoaderCache: () => void;
|
resetInfiniteLoaderCache: () => void;
|
||||||
|
@ -30,12 +31,12 @@ interface BaseGridCardProps {
|
||||||
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultCardContainer = styled.div<{ $isHidden?: boolean }>`
|
const DefaultCardContainer = styled.div<{ $isHidden?: boolean; $itemGap: number }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 2rem);
|
height: calc(100% - 2rem);
|
||||||
margin: 0.5rem;
|
margin: ${({ $itemGap }) => $itemGap}px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--card-default-bg);
|
background: var(--card-default-bg);
|
||||||
border-radius: var(--card-default-radius);
|
border-radius: var(--card-default-radius);
|
||||||
|
@ -172,6 +173,7 @@ export const DefaultCard = ({
|
||||||
return (
|
return (
|
||||||
<DefaultCardContainer
|
<DefaultCardContainer
|
||||||
key={`card-${columnIndex}-${listChildProps.index}`}
|
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||||
|
$itemGap={controls.itemGap}
|
||||||
onClick={() => navigate(path)}
|
onClick={() => navigate(path)}
|
||||||
>
|
>
|
||||||
<InnerCardContainer>
|
<InnerCardContainer>
|
||||||
|
@ -221,6 +223,7 @@ export const DefaultCard = ({
|
||||||
<DefaultCardContainer
|
<DefaultCardContainer
|
||||||
key={`card-${columnIndex}-${listChildProps.index}`}
|
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||||
$isHidden={isHidden}
|
$isHidden={isHidden}
|
||||||
|
$itemGap={controls.itemGap}
|
||||||
>
|
>
|
||||||
<InnerCardContainer>
|
<InnerCardContainer>
|
||||||
<ImageContainer>
|
<ImageContainer>
|
||||||
|
|
|
@ -123,7 +123,7 @@ export const GridCardControls = ({
|
||||||
handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void;
|
||||||
itemData: any;
|
itemData: any;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
resetInfiniteLoaderCache: () => void;
|
resetInfiniteLoaderCache?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [isFavorite, setIsFavorite] = useState(itemData?.userFavorite);
|
const [isFavorite, setIsFavorite] = useState(itemData?.userFavorite);
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
const playButtonBehavior = usePlayButtonBehavior();
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const GridCard = memo(({ data, index, style }: ListChildComponentProps) =
|
||||||
cardRows,
|
cardRows,
|
||||||
itemData,
|
itemData,
|
||||||
itemType,
|
itemType,
|
||||||
|
itemGap,
|
||||||
playButtonBehavior,
|
playButtonBehavior,
|
||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
handleFavorite,
|
handleFavorite,
|
||||||
|
@ -40,6 +41,7 @@ export const GridCard = memo(({ data, index, style }: ListChildComponentProps) =
|
||||||
cardRows,
|
cardRows,
|
||||||
handleFavorite,
|
handleFavorite,
|
||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
|
itemGap,
|
||||||
itemType,
|
itemType,
|
||||||
playButtonBehavior,
|
playButtonBehavior,
|
||||||
resetInfiniteLoaderCache,
|
resetInfiniteLoaderCache,
|
||||||
|
|
|
@ -20,6 +20,7 @@ interface BaseGridCardProps {
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
}) => void;
|
}) => void;
|
||||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||||
|
itemGap: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
playButtonBehavior: Play;
|
playButtonBehavior: Play;
|
||||||
resetInfiniteLoaderCache: () => void;
|
resetInfiniteLoaderCache: () => void;
|
||||||
|
@ -30,12 +31,12 @@ interface BaseGridCardProps {
|
||||||
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PosterCardContainer = styled.div<{ $isHidden?: boolean }>`
|
const PosterCardContainer = styled.div<{ $isHidden?: boolean; $itemGap: number }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 1rem;
|
margin: ${({ $itemGap }) => $itemGap}px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)};
|
opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)};
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
@ -158,7 +159,10 @@ export const PosterCard = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PosterCardContainer key={`card-${columnIndex}-${listChildProps.index}`}>
|
<PosterCardContainer
|
||||||
|
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||||
|
$itemGap={controls.itemGap}
|
||||||
|
>
|
||||||
<LinkContainer onClick={() => navigate(path)}>
|
<LinkContainer onClick={() => navigate(path)}>
|
||||||
<ImageContainer $isFavorite={data?.userFavorite}>
|
<ImageContainer $isFavorite={data?.userFavorite}>
|
||||||
{data?.imageUrl ? (
|
{data?.imageUrl ? (
|
||||||
|
@ -205,6 +209,7 @@ export const PosterCard = ({
|
||||||
<PosterCardContainer
|
<PosterCardContainer
|
||||||
key={`card-${columnIndex}-${listChildProps.index}`}
|
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||||
$isHidden={isHidden}
|
$isHidden={isHidden}
|
||||||
|
$itemGap={controls.itemGap}
|
||||||
>
|
>
|
||||||
<Skeleton
|
<Skeleton
|
||||||
visible
|
visible
|
||||||
|
|
|
@ -72,7 +72,7 @@ export const VirtualInfiniteGrid = forwardRef(
|
||||||
const [itemData, setItemData] = useState<any[]>(fetchInitialData?.() || []);
|
const [itemData, setItemData] = useState<any[]>(fetchInitialData?.() || []);
|
||||||
|
|
||||||
const { itemHeight, rowCount, columnCount } = useMemo(() => {
|
const { itemHeight, rowCount, columnCount } = useMemo(() => {
|
||||||
const itemsPerRow = itemSize;
|
const itemsPerRow = width ? Math.floor(width / itemSize) : 5;
|
||||||
const widthPerItem = Number(width) / itemsPerRow;
|
const widthPerItem = Number(width) / itemsPerRow;
|
||||||
const itemHeight = widthPerItem + cardRows.length * 26;
|
const itemHeight = widthPerItem + cardRows.length * 26;
|
||||||
|
|
||||||
|
|
|
@ -224,8 +224,8 @@ export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={initialScrollOffset}
|
initialScrollOffset={initialScrollOffset}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemGap={20}
|
itemGap={grid?.itemGap ?? 10}
|
||||||
itemSize={grid?.itemsPerRow || 5}
|
itemSize={grid?.itemSize || 200}
|
||||||
itemType={LibraryItem.ALBUM}
|
itemType={LibraryItem.ALBUM}
|
||||||
loading={itemCount === undefined || itemCount === null}
|
loading={itemCount === undefined || itemCount === null}
|
||||||
minimumBatchSize={40}
|
minimumBatchSize={40}
|
||||||
|
|
|
@ -207,12 +207,16 @@ export const AlbumListHeaderFilters = ({ gridRef, tableRef }: AlbumListHeaderFil
|
||||||
|
|
||||||
const handleItemSize = (e: number) => {
|
const handleItemSize = (e: number) => {
|
||||||
if (isGrid) {
|
if (isGrid) {
|
||||||
setGrid({ data: { itemsPerRow: e }, key: pageKey });
|
setGrid({ data: { itemSize: e }, key: pageKey });
|
||||||
} else {
|
} else {
|
||||||
setTable({ data: { rowHeight: e }, key: pageKey });
|
setTable({ data: { rowHeight: e }, key: pageKey });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleItemGap = (e: number) => {
|
||||||
|
setGrid({ data: { itemGap: e }, key: pageKey });
|
||||||
|
};
|
||||||
|
|
||||||
const handleSetViewType = useCallback(
|
const handleSetViewType = useCallback(
|
||||||
(e: MouseEvent<HTMLButtonElement>) => {
|
(e: MouseEvent<HTMLButtonElement>) => {
|
||||||
if (!e.currentTarget?.value) return;
|
if (!e.currentTarget?.value) return;
|
||||||
|
@ -449,17 +453,28 @@ export const AlbumListHeaderFilters = ({ gridRef, tableRef }: AlbumListHeaderFil
|
||||||
Table (paginated)
|
Table (paginated)
|
||||||
</DropdownMenu.Item> */}
|
</DropdownMenu.Item> */}
|
||||||
<DropdownMenu.Divider />
|
<DropdownMenu.Divider />
|
||||||
<DropdownMenu.Label>
|
<DropdownMenu.Label>Item size</DropdownMenu.Label>
|
||||||
{isGrid ? 'Items per row' : 'Item size'}
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Item closeMenuOnClick={false}>
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={isGrid ? grid?.itemsPerRow || 0 : table.rowHeight}
|
defaultValue={isGrid ? grid?.itemSize || 0 : table.rowHeight}
|
||||||
max={isGrid ? 14 : 100}
|
max={isGrid ? 300 : 100}
|
||||||
min={isGrid ? 2 : 25}
|
min={isGrid ? 150 : 25}
|
||||||
onChangeEnd={handleItemSize}
|
onChangeEnd={handleItemSize}
|
||||||
/>
|
/>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
{isGrid && (
|
||||||
|
<>
|
||||||
|
<DropdownMenu.Label>Item gap</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
|
<Slider
|
||||||
|
defaultValue={grid?.itemGap || 0}
|
||||||
|
max={30}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={handleItemGap}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{(display === ListDisplayType.TABLE ||
|
{(display === ListDisplayType.TABLE ||
|
||||||
display === ListDisplayType.TABLE_PAGINATED) && (
|
display === ListDisplayType.TABLE_PAGINATED) && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -158,8 +158,8 @@ export const AlbumArtistListGridView = ({ itemCount, gridRef }: AlbumArtistListG
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={grid?.scrollOffset || 0}
|
initialScrollOffset={grid?.scrollOffset || 0}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemGap={20}
|
itemGap={grid?.itemGap ?? 10}
|
||||||
itemSize={grid?.itemsPerRow || 5}
|
itemSize={grid?.itemSize || 200}
|
||||||
itemType={LibraryItem.ALBUM_ARTIST}
|
itemType={LibraryItem.ALBUM_ARTIST}
|
||||||
loading={itemCount === undefined || itemCount === null}
|
loading={itemCount === undefined || itemCount === null}
|
||||||
minimumBatchSize={40}
|
minimumBatchSize={40}
|
||||||
|
|
|
@ -83,10 +83,14 @@ export const AlbumArtistListHeaderFilters = ({
|
||||||
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
||||||
setTable({ data: { rowHeight: e }, key: pageKey });
|
setTable({ data: { rowHeight: e }, key: pageKey });
|
||||||
} else {
|
} else {
|
||||||
setGrid({ data: { itemsPerRow: e }, key: pageKey });
|
setGrid({ data: { itemSize: e }, key: pageKey });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleItemGap = (e: number) => {
|
||||||
|
setGrid({ data: { itemGap: e }, key: pageKey });
|
||||||
|
};
|
||||||
|
|
||||||
const debouncedHandleItemSize = debounce(handleItemSize, 20);
|
const debouncedHandleItemSize = debounce(handleItemSize, 20);
|
||||||
|
|
||||||
const fetch = useCallback(
|
const fetch = useCallback(
|
||||||
|
@ -422,22 +426,33 @@ export const AlbumArtistListHeaderFilters = ({
|
||||||
{display === ListDisplayType.CARD ||
|
{display === ListDisplayType.CARD ||
|
||||||
display === ListDisplayType.POSTER ? (
|
display === ListDisplayType.POSTER ? (
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={grid?.itemsPerRow}
|
defaultValue={grid?.itemSize}
|
||||||
label={null}
|
max={300}
|
||||||
max={10}
|
min={150}
|
||||||
min={2}
|
|
||||||
onChange={debouncedHandleItemSize}
|
onChange={debouncedHandleItemSize}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={table.rowHeight}
|
defaultValue={table.rowHeight}
|
||||||
label={null}
|
|
||||||
max={100}
|
max={100}
|
||||||
min={30}
|
min={30}
|
||||||
onChange={debouncedHandleItemSize}
|
onChange={debouncedHandleItemSize}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
{isGrid && (
|
||||||
|
<>
|
||||||
|
<DropdownMenu.Label>Item gap</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
|
<Slider
|
||||||
|
defaultValue={grid?.itemGap || 0}
|
||||||
|
max={30}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={handleItemGap}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{!isGrid && (
|
{!isGrid && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenu.Label>Table Columns</DropdownMenu.Label>
|
<DropdownMenu.Label>Table Columns</DropdownMenu.Label>
|
||||||
|
|
|
@ -87,7 +87,7 @@ export const useHandleGeneralContextMenu = (
|
||||||
export const useHandleGridContextMenu = (
|
export const useHandleGridContextMenu = (
|
||||||
itemType: LibraryItem,
|
itemType: LibraryItem,
|
||||||
contextMenuItems: SetContextMenuItems,
|
contextMenuItems: SetContextMenuItems,
|
||||||
resetGridCache: () => void,
|
resetGridCache?: () => void,
|
||||||
context?: any,
|
context?: any,
|
||||||
) => {
|
) => {
|
||||||
const handleContextMenu = (
|
const handleContextMenu = (
|
||||||
|
|
|
@ -97,8 +97,8 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={initialScrollOffset}
|
initialScrollOffset={initialScrollOffset}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemGap={20}
|
itemGap={grid?.itemGap ?? 10}
|
||||||
itemSize={grid?.itemsPerRow || 5}
|
itemSize={grid?.itemSize || 200}
|
||||||
itemType={LibraryItem.GENRE}
|
itemType={LibraryItem.GENRE}
|
||||||
loading={itemCount === undefined || itemCount === null}
|
loading={itemCount === undefined || itemCount === null}
|
||||||
minimumBatchSize={40}
|
minimumBatchSize={40}
|
||||||
|
|
|
@ -109,12 +109,16 @@ export const GenreListHeaderFilters = ({ gridRef, tableRef }: GenreListHeaderFil
|
||||||
|
|
||||||
const handleItemSize = (e: number) => {
|
const handleItemSize = (e: number) => {
|
||||||
if (isGrid) {
|
if (isGrid) {
|
||||||
setGrid({ data: { itemsPerRow: e }, key: pageKey });
|
setGrid({ data: { itemSize: e }, key: pageKey });
|
||||||
} else {
|
} else {
|
||||||
setTable({ data: { rowHeight: e }, key: pageKey });
|
setTable({ data: { rowHeight: e }, key: pageKey });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleItemGap = (e: number) => {
|
||||||
|
setGrid({ data: { itemGap: e }, key: pageKey });
|
||||||
|
};
|
||||||
|
|
||||||
const handleSetViewType = useCallback(
|
const handleSetViewType = useCallback(
|
||||||
(e: MouseEvent<HTMLButtonElement>) => {
|
(e: MouseEvent<HTMLButtonElement>) => {
|
||||||
if (!e.currentTarget?.value) return;
|
if (!e.currentTarget?.value) return;
|
||||||
|
@ -256,17 +260,28 @@ export const GenreListHeaderFilters = ({ gridRef, tableRef }: GenreListHeaderFil
|
||||||
Table
|
Table
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Divider />
|
<DropdownMenu.Divider />
|
||||||
<DropdownMenu.Label>
|
<DropdownMenu.Label>Item size</DropdownMenu.Label>
|
||||||
{isGrid ? 'Items per row' : 'Item size'}
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Item closeMenuOnClick={false}>
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={isGrid ? grid?.itemsPerRow || 0 : table.rowHeight}
|
defaultValue={isGrid ? grid?.itemSize || 0 : table.rowHeight}
|
||||||
max={isGrid ? 14 : 100}
|
max={isGrid ? 300 : 100}
|
||||||
min={isGrid ? 2 : 25}
|
min={isGrid ? 150 : 25}
|
||||||
onChangeEnd={handleItemSize}
|
onChangeEnd={handleItemSize}
|
||||||
/>
|
/>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
{isGrid && (
|
||||||
|
<>
|
||||||
|
<DropdownMenu.Label>Item gap</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
|
<Slider
|
||||||
|
defaultValue={grid?.itemGap || 0}
|
||||||
|
max={30}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={handleItemGap}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{(display === ListDisplayType.TABLE ||
|
{(display === ListDisplayType.TABLE ||
|
||||||
display === ListDisplayType.TABLE_PAGINATED) && (
|
display === ListDisplayType.TABLE_PAGINATED) && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -141,8 +141,8 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={grid?.scrollOffset || 0}
|
initialScrollOffset={grid?.scrollOffset || 0}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemGap={20}
|
itemGap={grid?.itemGap ?? 10}
|
||||||
itemSize={grid?.itemsPerRow || 5}
|
itemSize={grid?.itemSize || 200}
|
||||||
itemType={LibraryItem.PLAYLIST}
|
itemType={LibraryItem.PLAYLIST}
|
||||||
loading={itemCount === undefined || itemCount === null}
|
loading={itemCount === undefined || itemCount === null}
|
||||||
minimumBatchSize={40}
|
minimumBatchSize={40}
|
||||||
|
|
|
@ -230,12 +230,16 @@ export const PlaylistListHeaderFilters = ({
|
||||||
|
|
||||||
const handleItemSize = (e: number) => {
|
const handleItemSize = (e: number) => {
|
||||||
if (isGrid) {
|
if (isGrid) {
|
||||||
setGrid({ data: { itemsPerRow: e }, key: pageKey });
|
setGrid({ data: { itemSize: e }, key: pageKey });
|
||||||
} else {
|
} else {
|
||||||
setTable({ data: { rowHeight: e }, key: pageKey });
|
setTable({ data: { rowHeight: e }, key: pageKey });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleItemGap = (e: number) => {
|
||||||
|
setGrid({ data: { itemGap: e }, key: pageKey });
|
||||||
|
};
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
queryClient.invalidateQueries(queryKeys.playlists.list(server?.id || '', filter));
|
queryClient.invalidateQueries(queryKeys.playlists.list(server?.id || '', filter));
|
||||||
handleFilterChange(filter);
|
handleFilterChange(filter);
|
||||||
|
@ -344,16 +348,27 @@ export const PlaylistListHeaderFilters = ({
|
||||||
Table (paginated)
|
Table (paginated)
|
||||||
</DropdownMenu.Item> */}
|
</DropdownMenu.Item> */}
|
||||||
<DropdownMenu.Divider />
|
<DropdownMenu.Divider />
|
||||||
<DropdownMenu.Label>
|
<DropdownMenu.Label>Item size</DropdownMenu.Label>
|
||||||
{isGrid ? 'Items per row' : 'Item size'}
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Item closeMenuOnClick={false}>
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={isGrid ? grid?.itemsPerRow || 0 : table.rowHeight}
|
defaultValue={isGrid ? grid?.itemSize || 0 : table.rowHeight}
|
||||||
max={isGrid ? 14 : 100}
|
max={isGrid ? 300 : 100}
|
||||||
min={isGrid ? 2 : 25}
|
min={isGrid ? 150 : 25}
|
||||||
onChangeEnd={handleItemSize}
|
onChangeEnd={handleItemSize}
|
||||||
/>
|
/>
|
||||||
|
{isGrid && (
|
||||||
|
<>
|
||||||
|
<DropdownMenu.Label>Item gap</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Item closeMenuOnClick={false}>
|
||||||
|
<Slider
|
||||||
|
defaultValue={grid?.itemGap || 0}
|
||||||
|
max={30}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={handleItemGap}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
{!isGrid && (
|
{!isGrid && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -43,6 +43,8 @@ export type ListTableProps = {
|
||||||
} & DataTableProps;
|
} & DataTableProps;
|
||||||
|
|
||||||
export type ListGridProps = {
|
export type ListGridProps = {
|
||||||
|
itemGap?: number;
|
||||||
|
itemSize?: number;
|
||||||
itemsPerRow?: number;
|
itemsPerRow?: number;
|
||||||
scrollOffset?: number;
|
scrollOffset?: number;
|
||||||
};
|
};
|
||||||
|
@ -222,9 +224,12 @@ export const useListStore = create<ListSlice>()(
|
||||||
state.detail[args.key] = {
|
state.detail[args.key] = {
|
||||||
filter: {} as FilterType,
|
filter: {} as FilterType,
|
||||||
grid: {
|
grid: {
|
||||||
itemsPerRow:
|
itemGap:
|
||||||
state.item[page as keyof ListState['item']].grid
|
state.item[page as keyof ListState['item']].grid
|
||||||
?.itemsPerRow || 5,
|
?.itemGap || 10,
|
||||||
|
itemSize:
|
||||||
|
state.item[page as keyof ListState['item']].grid
|
||||||
|
?.itemSize || 5,
|
||||||
scrollOffset: 0,
|
scrollOffset: 0,
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
|
@ -342,7 +347,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: AlbumListSort.RECENTLY_ADDED,
|
sortBy: AlbumListSort.RECENTLY_ADDED,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -383,7 +388,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: AlbumArtistListSort.NAME,
|
sortBy: AlbumArtistListSort.NAME,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -412,7 +417,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: AlbumListSort.RECENTLY_ADDED,
|
sortBy: AlbumListSort.RECENTLY_ADDED,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -453,7 +458,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: SongListSort.RECENTLY_ADDED,
|
sortBy: SongListSort.RECENTLY_ADDED,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -510,7 +515,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: GenreListSort.NAME,
|
sortBy: GenreListSort.NAME,
|
||||||
sortOrder: SortOrder.ASC,
|
sortOrder: SortOrder.ASC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -539,7 +544,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: PlaylistListSort.NAME,
|
sortBy: PlaylistListSort.NAME,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { scrollOffset: 0, size: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -572,7 +577,7 @@ export const useListStore = create<ListSlice>()(
|
||||||
sortBy: SongListSort.RECENTLY_ADDED,
|
sortBy: SongListSort.RECENTLY_ADDED,
|
||||||
sortOrder: SortOrder.DESC,
|
sortOrder: SortOrder.DESC,
|
||||||
},
|
},
|
||||||
grid: { itemsPerRow: 5, scrollOffset: 0 },
|
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||||
table: {
|
table: {
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
columns: [
|
columns: [
|
||||||
|
|
Reference in a new issue