mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
refactor item list to use accessor function
This commit is contained in:
@@ -32,6 +32,13 @@ export const getListQueryKeyName = (itemType: LibraryItem): string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type InfiniteLoaderCacheData = {
|
||||||
|
dataMap: Map<number, unknown>;
|
||||||
|
idToIndexMap: Map<string, number>;
|
||||||
|
pagesLoaded: Record<string, boolean>;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
|
||||||
interface UseItemListInfiniteLoaderProps {
|
interface UseItemListInfiniteLoaderProps {
|
||||||
eventKey: string;
|
eventKey: string;
|
||||||
fetchThreshold?: number;
|
fetchThreshold?: number;
|
||||||
@@ -43,10 +50,12 @@ interface UseItemListInfiniteLoaderProps {
|
|||||||
serverId: string;
|
serverId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInitialData(itemCount: number) {
|
function getInitialData(): InfiniteLoaderCacheData {
|
||||||
return {
|
return {
|
||||||
data: Array.from({ length: itemCount }, () => undefined),
|
dataMap: new Map(),
|
||||||
|
idToIndexMap: new Map(),
|
||||||
pagesLoaded: {},
|
pagesLoaded: {},
|
||||||
|
version: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,28 +127,27 @@ export const useItemListInfiniteLoader = ({
|
|||||||
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
|
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
|
||||||
});
|
});
|
||||||
|
|
||||||
const endIndex = startIndex + itemsPerPage;
|
|
||||||
|
|
||||||
// Update the query data with the fetched page
|
// Update the query data with the fetched page
|
||||||
queryClient.setQueryData(
|
queryClient.setQueryData(dataQueryKey, (oldData: InfiniteLoaderCacheData) => {
|
||||||
dataQueryKey,
|
const nextDataMap = new Map(oldData.dataMap);
|
||||||
(oldData: { data: unknown[]; pagesLoaded: Record<string, boolean> }) => {
|
const nextIdToIndexMap = new Map(oldData.idToIndexMap);
|
||||||
const newData = [
|
|
||||||
...oldData.data.slice(0, startIndex),
|
result.items.forEach((item, offset) => {
|
||||||
...result.items,
|
const index = startIndex + offset;
|
||||||
...oldData.data.slice(endIndex),
|
nextDataMap.set(index, item);
|
||||||
];
|
if (item && typeof item === 'object' && 'id' in (item as any)) {
|
||||||
const newPagesLoaded = {
|
const id = String((item as any).id);
|
||||||
...oldData.pagesLoaded,
|
nextIdToIndexMap.set(id, index);
|
||||||
[pageNumber]: true,
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: newData,
|
dataMap: nextDataMap,
|
||||||
pagesLoaded: newPagesLoaded,
|
idToIndexMap: nextIdToIndexMap,
|
||||||
|
pagesLoaded: { ...oldData.pagesLoaded, [pageNumber]: true },
|
||||||
|
version: oldData.version + 1,
|
||||||
};
|
};
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Track the last fetched page
|
// Track the last fetched page
|
||||||
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
||||||
@@ -179,7 +187,10 @@ export const useItemListInfiniteLoader = ({
|
|||||||
if (!oldData) return oldData;
|
if (!oldData) return oldData;
|
||||||
return {
|
return {
|
||||||
...oldData,
|
...oldData,
|
||||||
|
dataMap: new Map(),
|
||||||
|
idToIndexMap: new Map(),
|
||||||
pagesLoaded: {},
|
pagesLoaded: {},
|
||||||
|
version: (oldData?.version ?? 0) + 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -211,11 +222,11 @@ export const useItemListInfiniteLoader = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dataQueryKey, queryClient, fetchPage, itemsPerPage]);
|
}, [dataQueryKey, queryClient, fetchPage, itemsPerPage]);
|
||||||
|
|
||||||
const { data } = useQuery<{ data: unknown[]; pagesLoaded: Record<string, boolean> }>({
|
const { data } = useQuery<InfiniteLoaderCacheData>({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
initialData: getInitialData(totalItemCount),
|
initialData: getInitialData(),
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return getInitialData(totalItemCount);
|
return getInitialData();
|
||||||
},
|
},
|
||||||
queryKey: dataQueryKey,
|
queryKey: dataQueryKey,
|
||||||
});
|
});
|
||||||
@@ -233,7 +244,7 @@ export const useItemListInfiniteLoader = ({
|
|||||||
const pageNumber = Math.floor(range.startIndex / itemsPerPage);
|
const pageNumber = Math.floor(range.startIndex / itemsPerPage);
|
||||||
|
|
||||||
const currentData = queryClient.getQueryData<{
|
const currentData = queryClient.getQueryData<{
|
||||||
data: unknown[];
|
dataMap: Map<number, unknown>;
|
||||||
pagesLoaded: Record<string, boolean>;
|
pagesLoaded: Record<string, boolean>;
|
||||||
}>(dataQueryKey);
|
}>(dataQueryKey);
|
||||||
|
|
||||||
@@ -289,18 +300,20 @@ export const useItemListInfiniteLoader = ({
|
|||||||
|
|
||||||
// Reset the infinite list data
|
// Reset the infinite list data
|
||||||
const currentData = queryClient.getQueryData<{
|
const currentData = queryClient.getQueryData<{
|
||||||
data: unknown[];
|
dataMap: Map<number, unknown>;
|
||||||
pagesLoaded: Record<string, boolean>;
|
pagesLoaded: Record<string, boolean>;
|
||||||
}>(dataQueryKey);
|
}>(dataQueryKey);
|
||||||
|
|
||||||
if (force || currentData) {
|
if (force || currentData) {
|
||||||
// Reset data to initial state and clear all loaded pages
|
// Reset data to initial state and clear all loaded pages
|
||||||
await queryClient.setQueryData(dataQueryKey, (oldData: any) => {
|
await queryClient.setQueryData(dataQueryKey, (oldData: any) => {
|
||||||
if (!oldData) return getInitialData(totalItemCount);
|
if (!oldData) return getInitialData();
|
||||||
return {
|
return {
|
||||||
...oldData,
|
...oldData,
|
||||||
data: Array.from({ length: totalItemCount }, () => undefined),
|
dataMap: new Map(),
|
||||||
|
idToIndexMap: new Map(),
|
||||||
pagesLoaded: {},
|
pagesLoaded: {},
|
||||||
|
version: (oldData?.version ?? 0) + 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
lastFetchedPageRef.current = -1;
|
lastFetchedPageRef.current = -1;
|
||||||
@@ -336,28 +349,23 @@ export const useItemListInfiniteLoader = ({
|
|||||||
|
|
||||||
const updateItems = useCallback(
|
const updateItems = useCallback(
|
||||||
(indexes: number[], value: object) => {
|
(indexes: number[], value: object) => {
|
||||||
queryClient.setQueryData(
|
queryClient.setQueryData(dataQueryKey, (prev: InfiniteLoaderCacheData) => {
|
||||||
dataQueryKey,
|
const nextDataMap = new Map(prev.dataMap);
|
||||||
(prev: { data: unknown[]; pagesLoaded: Record<string, boolean> }) => {
|
|
||||||
|
indexes.forEach((index) => {
|
||||||
|
const existing = nextDataMap.get(index);
|
||||||
|
if (!existing || typeof existing !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextDataMap.set(index, { ...(existing as any), ...(value as any) });
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
data: prev.data.map((item: any, index) => {
|
dataMap: nextDataMap,
|
||||||
if (!item) {
|
version: prev.version + 1,
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!indexes.includes(index)) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
...value,
|
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[queryClient, dataQueryKey],
|
[queryClient, dataQueryKey],
|
||||||
);
|
);
|
||||||
@@ -384,16 +392,9 @@ export const useItemListInfiniteLoader = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const idToIndexMap = data.data
|
|
||||||
.filter(Boolean)
|
|
||||||
.reduce((acc: Record<string, number>, item: any, index: number) => {
|
|
||||||
acc[item.id] = index;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const dataIndexes = payload.id
|
const dataIndexes = payload.id
|
||||||
.map((id: string) => idToIndexMap[id])
|
.map((id: string) => (data as any).idToIndexMap?.get(id))
|
||||||
.filter((idx) => idx !== undefined);
|
.filter((idx): idx is number => typeof idx === 'number');
|
||||||
|
|
||||||
if (dataIndexes.length === 0) {
|
if (dataIndexes.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -407,16 +408,9 @@ export const useItemListInfiniteLoader = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const idToIndexMap = data.data
|
|
||||||
.filter(Boolean)
|
|
||||||
.reduce((acc: Record<string, number>, item: any, index: number) => {
|
|
||||||
acc[item.id] = index;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const dataIndexes = payload.id
|
const dataIndexes = payload.id
|
||||||
.map((id: string) => idToIndexMap[id])
|
.map((id: string) => (data as any).idToIndexMap?.get(id))
|
||||||
.filter((idx) => idx !== undefined);
|
.filter((idx): idx is number => typeof idx === 'number');
|
||||||
|
|
||||||
if (dataIndexes.length === 0) {
|
if (dataIndexes.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -434,7 +428,40 @@ export const useItemListInfiniteLoader = ({
|
|||||||
};
|
};
|
||||||
}, [data, eventKey, itemType, serverId, updateItems]);
|
}, [data, eventKey, itemType, serverId, updateItems]);
|
||||||
|
|
||||||
return { data: data.data, onRangeChanged, refresh, updateItems };
|
const itemCount = totalItemCount ?? 0;
|
||||||
|
|
||||||
|
const getItem = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
return (data as any).dataMap?.get(index);
|
||||||
|
},
|
||||||
|
[data],
|
||||||
|
);
|
||||||
|
|
||||||
|
const getItemIndex = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
return (data as any).idToIndexMap?.get(id);
|
||||||
|
},
|
||||||
|
[data],
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadedItems = useMemo(() => {
|
||||||
|
const map: Map<number, unknown> | undefined = (data as any).dataMap;
|
||||||
|
if (!map || map.size === 0) return [];
|
||||||
|
return Array.from(map.entries())
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([, v]) => v);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataVersion: (data as any).version ?? 0,
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
|
itemCount,
|
||||||
|
loadedItems,
|
||||||
|
onRangeChanged,
|
||||||
|
refresh,
|
||||||
|
updateItems,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseListCountQuery = (query: any) => {
|
export const parseListCountQuery = (query: any) => {
|
||||||
|
|||||||
@@ -53,14 +53,16 @@ interface VirtualizedGridListProps {
|
|||||||
_tableMetaVersion: number; // Used to trigger rerenders via React.memo comparison
|
_tableMetaVersion: number; // Used to trigger rerenders via React.memo comparison
|
||||||
controls: ItemControls;
|
controls: ItemControls;
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
data: unknown[];
|
dataVersion?: number;
|
||||||
enableDrag?: boolean;
|
enableDrag?: boolean;
|
||||||
enableExpansion: boolean;
|
enableExpansion: boolean;
|
||||||
enableSelection: boolean;
|
enableSelection: boolean;
|
||||||
gap: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
gap: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||||
|
getItem?: (index: number) => ItemCardProps['data'];
|
||||||
height: number;
|
height: number;
|
||||||
initialTop?: ItemGridListProps['initialTop'];
|
initialTop?: ItemGridListProps['initialTop'];
|
||||||
internalState: ItemListStateActions;
|
internalState: ItemListStateActions;
|
||||||
|
itemCount: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
onRangeChanged?: ItemGridListProps['onRangeChanged'];
|
onRangeChanged?: ItemGridListProps['onRangeChanged'];
|
||||||
onScroll?: ItemGridListProps['onScroll'];
|
onScroll?: ItemGridListProps['onScroll'];
|
||||||
@@ -81,14 +83,16 @@ const VirtualizedGridList = React.memo(
|
|||||||
({
|
({
|
||||||
controls,
|
controls,
|
||||||
currentPage,
|
currentPage,
|
||||||
data,
|
dataVersion,
|
||||||
enableDrag,
|
enableDrag,
|
||||||
enableExpansion,
|
enableExpansion,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
gap,
|
gap,
|
||||||
|
getItem,
|
||||||
height,
|
height,
|
||||||
initialTop,
|
initialTop,
|
||||||
internalState,
|
internalState,
|
||||||
|
itemCount,
|
||||||
itemType,
|
itemType,
|
||||||
onRangeChanged,
|
onRangeChanged,
|
||||||
onScroll,
|
onScroll,
|
||||||
@@ -107,12 +111,14 @@ const VirtualizedGridList = React.memo(
|
|||||||
return {
|
return {
|
||||||
columns: tableMeta?.columnCount || 0,
|
columns: tableMeta?.columnCount || 0,
|
||||||
controls,
|
controls,
|
||||||
data,
|
dataVersion,
|
||||||
enableDrag,
|
enableDrag,
|
||||||
enableExpansion,
|
enableExpansion,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
gap,
|
gap,
|
||||||
|
getItem,
|
||||||
internalState,
|
internalState,
|
||||||
|
itemCount,
|
||||||
itemType,
|
itemType,
|
||||||
rows,
|
rows,
|
||||||
size,
|
size,
|
||||||
@@ -122,7 +128,9 @@ const VirtualizedGridList = React.memo(
|
|||||||
tableMeta,
|
tableMeta,
|
||||||
controls,
|
controls,
|
||||||
rows,
|
rows,
|
||||||
data,
|
getItem,
|
||||||
|
itemCount,
|
||||||
|
dataVersion,
|
||||||
enableDrag,
|
enableDrag,
|
||||||
enableExpansion,
|
enableExpansion,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
@@ -285,12 +293,14 @@ const createThrottledSetTableMeta = (
|
|||||||
export interface GridItemProps {
|
export interface GridItemProps {
|
||||||
columns: number;
|
columns: number;
|
||||||
controls: ItemCardProps['controls'];
|
controls: ItemCardProps['controls'];
|
||||||
data: any[];
|
dataVersion?: number;
|
||||||
enableDrag?: boolean;
|
enableDrag?: boolean;
|
||||||
enableExpansion?: boolean;
|
enableExpansion?: boolean;
|
||||||
enableSelection?: boolean;
|
enableSelection?: boolean;
|
||||||
gap: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
gap: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||||
|
getItem?: (index: number) => ItemCardProps['data'];
|
||||||
internalState: ItemListStateActions;
|
internalState: ItemListStateActions;
|
||||||
|
itemCount: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
rows?: ItemCardProps['rows'];
|
rows?: ItemCardProps['rows'];
|
||||||
size?: 'compact' | 'default' | 'large';
|
size?: 'compact' | 'default' | 'large';
|
||||||
@@ -304,17 +314,21 @@ export interface GridItemProps {
|
|||||||
export interface ItemGridListProps {
|
export interface ItemGridListProps {
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
data: unknown[];
|
data: unknown[];
|
||||||
|
dataVersion?: number;
|
||||||
enableDrag?: boolean;
|
enableDrag?: boolean;
|
||||||
enableEntranceAnimation?: boolean;
|
enableEntranceAnimation?: boolean;
|
||||||
enableExpansion?: boolean;
|
enableExpansion?: boolean;
|
||||||
enableSelection?: boolean;
|
enableSelection?: boolean;
|
||||||
enableSelectionDialog?: boolean;
|
enableSelectionDialog?: boolean;
|
||||||
gap?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
gap?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||||
|
getItem?: (index: number) => ItemCardProps['data'];
|
||||||
|
getItemIndex?: (rowId: string) => number | undefined;
|
||||||
getRowId?: ((item: unknown) => string) | string;
|
getRowId?: ((item: unknown) => string) | string;
|
||||||
initialTop?: {
|
initialTop?: {
|
||||||
to: number;
|
to: number;
|
||||||
type: 'index' | 'offset';
|
type: 'index' | 'offset';
|
||||||
};
|
};
|
||||||
|
itemCount?: number;
|
||||||
itemsPerRow?: number;
|
itemsPerRow?: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
onRangeChanged?: (range: { startIndex: number; stopIndex: number }) => void;
|
onRangeChanged?: (range: { startIndex: number; stopIndex: number }) => void;
|
||||||
@@ -329,13 +343,17 @@ export interface ItemGridListProps {
|
|||||||
const BaseItemGridList = ({
|
const BaseItemGridList = ({
|
||||||
currentPage,
|
currentPage,
|
||||||
data,
|
data,
|
||||||
|
dataVersion,
|
||||||
enableDrag = true,
|
enableDrag = true,
|
||||||
enableEntranceAnimation = true,
|
enableEntranceAnimation = true,
|
||||||
enableExpansion = false,
|
enableExpansion = false,
|
||||||
enableSelection = true,
|
enableSelection = true,
|
||||||
gap = 'sm',
|
gap = 'sm',
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
getRowId,
|
getRowId,
|
||||||
initialTop,
|
initialTop,
|
||||||
|
itemCount,
|
||||||
itemsPerRow,
|
itemsPerRow,
|
||||||
itemType,
|
itemType,
|
||||||
onRangeChanged,
|
onRangeChanged,
|
||||||
@@ -354,6 +372,14 @@ const BaseItemGridList = ({
|
|||||||
const handleRef = useRef<ItemListHandle | null>(null);
|
const handleRef = useRef<ItemListHandle | null>(null);
|
||||||
const mergedContainerRef = useMergedRef(containerRef, rootRef, containerFocusRef);
|
const mergedContainerRef = useMergedRef(containerRef, rootRef, containerFocusRef);
|
||||||
|
|
||||||
|
const resolvedItemCount = itemCount ?? data.length;
|
||||||
|
const resolvedGetItem = useCallback<(index: number) => ItemCardProps['data']>(
|
||||||
|
(index: number) => {
|
||||||
|
return (getItem ? getItem(index) : (data as any[])[index]) as ItemCardProps['data'];
|
||||||
|
},
|
||||||
|
[data, getItem],
|
||||||
|
);
|
||||||
|
|
||||||
const getDataFn = useCallback(() => {
|
const getDataFn = useCallback(() => {
|
||||||
return data;
|
return data;
|
||||||
}, [data]);
|
}, [data]);
|
||||||
@@ -442,7 +468,7 @@ const BaseItemGridList = ({
|
|||||||
const { current: container } = containerRef;
|
const { current: container } = containerRef;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
throttledSetTableMeta(containerWidth, data.length, (meta) => {
|
throttledSetTableMeta(containerWidth, resolvedItemCount, (meta) => {
|
||||||
if (!meta) return;
|
if (!meta) return;
|
||||||
|
|
||||||
const current = tableMetaRef.current;
|
const current = tableMetaRef.current;
|
||||||
@@ -459,7 +485,7 @@ const BaseItemGridList = ({
|
|||||||
setTableMetaVersion((v) => v + 1);
|
setTableMetaVersion((v) => v + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [containerWidth, data.length, throttledSetTableMeta, containerRef]);
|
}, [containerWidth, resolvedItemCount, throttledSetTableMeta, containerRef]);
|
||||||
|
|
||||||
const controls = useDefaultItemListControls({ overrides: overrideControls });
|
const controls = useDefaultItemListControls({ overrides: overrideControls });
|
||||||
|
|
||||||
@@ -512,7 +538,9 @@ const BaseItemGridList = ({
|
|||||||
const lastSelected = selected[selected.length - 1];
|
const lastSelected = selected[selected.length - 1];
|
||||||
const lastRowId = internalState.extractRowId(lastSelected);
|
const lastRowId = internalState.extractRowId(lastSelected);
|
||||||
if (lastRowId) {
|
if (lastRowId) {
|
||||||
currentIndex = data.findIndex((d: any) => {
|
currentIndex =
|
||||||
|
getItemIndex?.(lastRowId) ??
|
||||||
|
data.findIndex((d: any) => {
|
||||||
const rowId = internalState.extractRowId(d);
|
const rowId = internalState.extractRowId(d);
|
||||||
return rowId === lastRowId;
|
return rowId === lastRowId;
|
||||||
});
|
});
|
||||||
@@ -526,7 +554,7 @@ const BaseItemGridList = ({
|
|||||||
: 0;
|
: 0;
|
||||||
const currentCol =
|
const currentCol =
|
||||||
currentIndex !== -1 ? currentIndex % tableMetaRef.current.columnCount : 0;
|
currentIndex !== -1 ? currentIndex % tableMetaRef.current.columnCount : 0;
|
||||||
const totalRows = Math.ceil(data.length / tableMetaRef.current.columnCount);
|
const totalRows = Math.ceil(resolvedItemCount / tableMetaRef.current.columnCount);
|
||||||
|
|
||||||
let newIndex = 0;
|
let newIndex = 0;
|
||||||
if (currentIndex !== -1) {
|
if (currentIndex !== -1) {
|
||||||
@@ -538,7 +566,7 @@ const BaseItemGridList = ({
|
|||||||
const nextRowStart = nextRow * tableMetaRef.current.columnCount;
|
const nextRowStart = nextRow * tableMetaRef.current.columnCount;
|
||||||
const nextRowEnd = Math.min(
|
const nextRowEnd = Math.min(
|
||||||
nextRowStart + tableMetaRef.current.columnCount - 1,
|
nextRowStart + tableMetaRef.current.columnCount - 1,
|
||||||
data.length - 1,
|
resolvedItemCount - 1,
|
||||||
);
|
);
|
||||||
// Keep same column position, or use last item in row if column doesn't exist
|
// Keep same column position, or use last item in row if column doesn't exist
|
||||||
newIndex = Math.min(nextRowStart + currentCol, nextRowEnd);
|
newIndex = Math.min(nextRowStart + currentCol, nextRowEnd);
|
||||||
@@ -559,7 +587,7 @@ const BaseItemGridList = ({
|
|||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
newIndex = Math.min(newIndex, data.length - 1);
|
newIndex = Math.min(newIndex, resolvedItemCount - 1);
|
||||||
} else {
|
} else {
|
||||||
newIndex = currentIndex;
|
newIndex = currentIndex;
|
||||||
}
|
}
|
||||||
@@ -569,14 +597,14 @@ const BaseItemGridList = ({
|
|||||||
// Move right, wrap to next row if at end of row
|
// Move right, wrap to next row if at end of row
|
||||||
if (
|
if (
|
||||||
currentCol < tableMetaRef.current.columnCount - 1 &&
|
currentCol < tableMetaRef.current.columnCount - 1 &&
|
||||||
currentIndex < data.length - 1
|
currentIndex < resolvedItemCount - 1
|
||||||
) {
|
) {
|
||||||
newIndex = currentIndex + 1;
|
newIndex = currentIndex + 1;
|
||||||
} else if (currentRow < totalRows - 1) {
|
} else if (currentRow < totalRows - 1) {
|
||||||
// Wrap to start of next row
|
// Wrap to start of next row
|
||||||
newIndex = Math.min(
|
newIndex = Math.min(
|
||||||
(currentRow + 1) * tableMetaRef.current.columnCount,
|
(currentRow + 1) * tableMetaRef.current.columnCount,
|
||||||
data.length - 1,
|
resolvedItemCount - 1,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
newIndex = currentIndex;
|
newIndex = currentIndex;
|
||||||
@@ -590,7 +618,7 @@ const BaseItemGridList = ({
|
|||||||
const prevRowStart = prevRow * tableMetaRef.current.columnCount;
|
const prevRowStart = prevRow * tableMetaRef.current.columnCount;
|
||||||
const prevRowEnd = Math.min(
|
const prevRowEnd = Math.min(
|
||||||
prevRowStart + tableMetaRef.current.columnCount - 1,
|
prevRowStart + tableMetaRef.current.columnCount - 1,
|
||||||
data.length - 1,
|
resolvedItemCount - 1,
|
||||||
);
|
);
|
||||||
// Keep same column position, or use last item in row if column doesn't exist
|
// Keep same column position, or use last item in row if column doesn't exist
|
||||||
newIndex = Math.min(prevRowStart + currentCol, prevRowEnd);
|
newIndex = Math.min(prevRowStart + currentCol, prevRowEnd);
|
||||||
@@ -605,7 +633,7 @@ const BaseItemGridList = ({
|
|||||||
newIndex = 0;
|
newIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newItem: any = data[newIndex];
|
const newItem: any = resolvedGetItem(newIndex);
|
||||||
if (!newItem) return;
|
if (!newItem) return;
|
||||||
|
|
||||||
// Handle Shift + Arrow for incremental range selection (matches shift+click behavior)
|
// Handle Shift + Arrow for incremental range selection (matches shift+click behavior)
|
||||||
@@ -618,7 +646,9 @@ const BaseItemGridList = ({
|
|||||||
const lastRowId = internalState.extractRowId(lastSelectedItem);
|
const lastRowId = internalState.extractRowId(lastSelectedItem);
|
||||||
if (!lastRowId) return;
|
if (!lastRowId) return;
|
||||||
|
|
||||||
const lastIndex = data.findIndex((d: any) => {
|
const lastIndex =
|
||||||
|
getItemIndex?.(lastRowId) ??
|
||||||
|
data.findIndex((d: any) => {
|
||||||
const rowId = internalState.extractRowId(d);
|
const rowId = internalState.extractRowId(d);
|
||||||
return rowId === lastRowId;
|
return rowId === lastRowId;
|
||||||
});
|
});
|
||||||
@@ -630,7 +660,7 @@ const BaseItemGridList = ({
|
|||||||
|
|
||||||
const rangeItems: ItemListStateItemWithRequiredProperties[] = [];
|
const rangeItems: ItemListStateItemWithRequiredProperties[] = [];
|
||||||
for (let i = startIndex; i <= stopIndex; i++) {
|
for (let i = startIndex; i <= stopIndex; i++) {
|
||||||
const rangeItem = data[i];
|
const rangeItem = resolvedGetItem(i);
|
||||||
if (
|
if (
|
||||||
rangeItem &&
|
rangeItem &&
|
||||||
typeof rangeItem === 'object' &&
|
typeof rangeItem === 'object' &&
|
||||||
@@ -695,7 +725,15 @@ const BaseItemGridList = ({
|
|||||||
|
|
||||||
scrollToIndex(newIndex);
|
scrollToIndex(newIndex);
|
||||||
},
|
},
|
||||||
[data, enableSelection, internalState, scrollToIndex],
|
[
|
||||||
|
data,
|
||||||
|
enableSelection,
|
||||||
|
getItemIndex,
|
||||||
|
internalState,
|
||||||
|
resolvedGetItem,
|
||||||
|
resolvedItemCount,
|
||||||
|
scrollToIndex,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const imperativeHandle: ItemListHandle = useMemo(() => {
|
const imperativeHandle: ItemListHandle = useMemo(() => {
|
||||||
@@ -740,14 +778,16 @@ const BaseItemGridList = ({
|
|||||||
_tableMetaVersion={tableMetaVersion}
|
_tableMetaVersion={tableMetaVersion}
|
||||||
controls={controls}
|
controls={controls}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
data={data}
|
dataVersion={dataVersion}
|
||||||
enableDrag={enableDrag}
|
enableDrag={enableDrag}
|
||||||
enableExpansion={enableExpansion}
|
enableExpansion={enableExpansion}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={resolvedGetItem}
|
||||||
height={height}
|
height={height}
|
||||||
initialTop={initialTop}
|
initialTop={initialTop}
|
||||||
internalState={internalState}
|
internalState={internalState}
|
||||||
|
itemCount={resolvedItemCount}
|
||||||
itemType={itemType}
|
itemType={itemType}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
onScroll={onScroll ?? (() => {})}
|
onScroll={onScroll ?? (() => {})}
|
||||||
@@ -771,10 +811,10 @@ const BaseItemGridList = ({
|
|||||||
|
|
||||||
const ListComponent = memo((props: ListChildComponentProps<GridItemProps>) => {
|
const ListComponent = memo((props: ListChildComponentProps<GridItemProps>) => {
|
||||||
const { index, style } = props;
|
const { index, style } = props;
|
||||||
const { columns, controls, data, enableDrag, gap, itemType, rows, size } = props.data;
|
const { columns, controls, enableDrag, gap, getItem, itemCount, itemType, rows, size } =
|
||||||
|
props.data;
|
||||||
|
|
||||||
const items: ReactNode[] = [];
|
const items: ReactNode[] = [];
|
||||||
const itemCount = data.length;
|
|
||||||
const startIndex = index * columns;
|
const startIndex = index * columns;
|
||||||
const stopIndex = Math.min(itemCount - 1, startIndex + columns - 1);
|
const stopIndex = Math.min(itemCount - 1, startIndex + columns - 1);
|
||||||
|
|
||||||
@@ -787,7 +827,8 @@ const ListComponent = memo((props: ListChildComponentProps<GridItemProps>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = startIndex; i <= stopIndex + columnCountToAdd; i += 1) {
|
for (let i = startIndex; i <= stopIndex + columnCountToAdd; i += 1) {
|
||||||
if (i < data.length) {
|
if (i < itemCount) {
|
||||||
|
const item = getItem ? getItem(i) : undefined;
|
||||||
items.push(
|
items.push(
|
||||||
<div
|
<div
|
||||||
className={clsx(styles.itemRow, styles[`gap-${gap}`])}
|
className={clsx(styles.itemRow, styles[`gap-${gap}`])}
|
||||||
@@ -796,7 +837,7 @@ const ListComponent = memo((props: ListChildComponentProps<GridItemProps>) => {
|
|||||||
>
|
>
|
||||||
<ItemCard
|
<ItemCard
|
||||||
controls={controls}
|
controls={controls}
|
||||||
data={data[i]}
|
data={item}
|
||||||
enableDrag={enableDrag}
|
enableDrag={enableDrag}
|
||||||
enableExpansion={props.data.enableExpansion}
|
enableExpansion={props.data.enableExpansion}
|
||||||
internalState={props.data.internalState}
|
internalState={props.data.internalState}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ItemListItem } from '/@/renderer/components/item-list/types';
|
|||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
|
|
||||||
export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: any = (props.data as (any | undefined)[])[props.rowIndex];
|
const row: any = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
|
|
||||||
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ import { JoinedArtists } from '/@/renderer/features/albums/components/joined-art
|
|||||||
import { Album, RelatedAlbumArtist, Song } from '/@/shared/types/domain-types';
|
import { Album, RelatedAlbumArtist, Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: RelatedAlbumArtist[] | undefined = (
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.data as (RelatedAlbumArtist[] | undefined)[]
|
const row: RelatedAlbumArtist[] | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
)[props.rowIndex]?.[props.columns[props.columnIndex].id];
|
|
||||||
|
|
||||||
const item = props.data[props.rowIndex] as Album | Song | undefined;
|
const item = rowItem as Album | Song | undefined;
|
||||||
const albumArtistString = item && 'albumArtistName' in item ? item.albumArtistName : '';
|
const albumArtistString = item && 'albumArtistName' in item ? item.albumArtistName : '';
|
||||||
|
|
||||||
if (Array.isArray(row)) {
|
if (Array.isArray(row)) {
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ import { Text } from '/@/shared/components/text/text';
|
|||||||
import { Song } from '/@/shared/types/domain-types';
|
import { Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
const AlbumColumn = (props: ItemTableListInnerColumn) => {
|
const AlbumColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: null | string | undefined = (props.data as (null | string | undefined)[])[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.rowIndex
|
const row: null | string | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
]?.[props.columns[props.columnIndex].id];
|
|
||||||
|
|
||||||
const song = props.data[props.rowIndex] as Song | undefined;
|
const song = rowItem as Song | undefined;
|
||||||
const albumId = song?.albumId;
|
const albumId = song?.albumId;
|
||||||
|
|
||||||
const albumPath = useMemo(() => {
|
const albumPath = useMemo(() => {
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ import { Text } from '/@/shared/components/text/text';
|
|||||||
import { LibraryItem, RelatedAlbumArtist, Song } from '/@/shared/types/domain-types';
|
import { LibraryItem, RelatedAlbumArtist, Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: RelatedAlbumArtist[] | undefined = (
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.data as (RelatedAlbumArtist[] | undefined)[]
|
const row: RelatedAlbumArtist[] | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
)[props.rowIndex]?.[props.columns[props.columnIndex].id];
|
|
||||||
|
|
||||||
const artists = useMemo(() => {
|
const artists = useMemo(() => {
|
||||||
if (!row) return [];
|
if (!row) return [];
|
||||||
@@ -67,7 +66,8 @@ const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SongArtistsColumn = (props: ItemTableListInnerColumn) => {
|
const SongArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: Song | undefined = (props.data as (Song | undefined)[])[props.rowIndex];
|
const row: Song | undefined =
|
||||||
|
(props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex]) as Song | undefined;
|
||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const CountColumn = (props: ItemTableListInnerColumn) => {
|
export const CountColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'number') {
|
if (typeof row === 'number') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ const getDateTooltipLabel = (utcString: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DateColumn = (props: ItemTableListInnerColumn) => {
|
export const DateColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string' && row) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
@@ -52,12 +51,11 @@ export const DateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (props.type === TableColumn.RELEASE_DATE) {
|
if (props.type === TableColumn.RELEASE_DATE) {
|
||||||
const item = (props.data as (any | undefined)[])[props.rowIndex];
|
const item = rowItem as any;
|
||||||
if (item && 'releaseDate' in item && item.releaseDate) {
|
if (item && 'releaseDate' in item && item.releaseDate) {
|
||||||
const releaseDate = item.releaseDate;
|
const releaseDate = item.releaseDate;
|
||||||
const originalDate =
|
const originalDate =
|
||||||
@@ -115,9 +113,8 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RelativeDateColumn = (props: ItemTableListInnerColumn) => {
|
export const RelativeDateColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string') {
|
if (typeof row === 'string') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const DefaultColumn = (props: ItemTableListInnerColumn) => {
|
export const DefaultColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: any | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: any | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string') {
|
if (typeof row === 'string') {
|
||||||
return <TableColumnTextContainer {...props}>{row}</TableColumnTextContainer>;
|
return <TableColumnTextContainer {...props}>{row}</TableColumnTextContainer>;
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const DurationColumn = (props: ItemTableListInnerColumn) => {
|
export const DurationColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'number') {
|
if (typeof row === 'number') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutatio
|
|||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
|
|
||||||
export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: boolean | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: boolean | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
const isMutatingCreateFavorite = useIsMutatingCreateFavorite();
|
const isMutatingCreateFavorite = useIsMutatingCreateFavorite();
|
||||||
const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite();
|
const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite();
|
||||||
@@ -31,7 +30,7 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const item = props.data[props.rowIndex] as ItemListItem;
|
const item = rowItem as ItemListItem;
|
||||||
const rowId = props.internalState.extractRowId(item);
|
const rowId = props.internalState.extractRowId(item);
|
||||||
const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
|
const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
|
||||||
props.controls.onFavorite?.({
|
props.controls.onFavorite?.({
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ import { stringToColor } from '/@/shared/utils/string-to-color';
|
|||||||
const MAX_GENRES = 4;
|
const MAX_GENRES = 4;
|
||||||
|
|
||||||
const GenreBadgeColumn = (props: ItemTableListInnerColumn) => {
|
const GenreBadgeColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: Genre[] | undefined = (props.data as (Genre[] | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
'genres'
|
const row: Genre[] | undefined = (rowItem as any)?.genres;
|
||||||
];
|
|
||||||
|
|
||||||
const genres = useMemo(() => {
|
const genres = useMemo(() => {
|
||||||
if (!row) return [];
|
if (!row) return [];
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ import { Text } from '/@/shared/components/text/text';
|
|||||||
import { Genre } from '/@/shared/types/domain-types';
|
import { Genre } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
const GenreColumn = (props: ItemTableListInnerColumn) => {
|
const GenreColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: Genre[] | undefined = (props.data as (Genre[] | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: Genre[] | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
const genres = useMemo(() => {
|
const genres = useMemo(() => {
|
||||||
if (!row) return [];
|
if (!row) return [];
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import { Folder, LibraryItem } from '/@/shared/types/domain-types';
|
|||||||
import { Play } from '/@/shared/types/types';
|
import { Play } from '/@/shared/types/types';
|
||||||
|
|
||||||
export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.id;
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const item = props.data[props.rowIndex] as any;
|
const row: string | undefined = rowItem?.id;
|
||||||
|
const item = rowItem as any;
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
const playButtonBehavior = usePlayButtonBehavior();
|
||||||
const internalState = (props as any).internalState;
|
const internalState = (props as any).internalState;
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
@@ -113,7 +114,7 @@ export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((props.data[props.rowIndex] as unknown as Folder)?._itemType === LibraryItem.FOLDER) {
|
if ((rowItem as unknown as Folder)?._itemType === LibraryItem.FOLDER) {
|
||||||
return (
|
return (
|
||||||
<TableColumnContainer {...props}>
|
<TableColumnContainer {...props}>
|
||||||
<Icon className={styles.folderIcon} icon="folder" size="2xl" />
|
<Icon className={styles.folderIcon} icon="folder" size="2xl" />
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const NumericColumn = (props: ItemTableListInnerColumn) => {
|
export const NumericColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'number') {
|
if (typeof row === 'number') {
|
||||||
return <TableColumnTextContainer {...props}>{row}</TableColumnTextContainer>;
|
return <TableColumnTextContainer {...props}>{row}</TableColumnTextContainer>;
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const PathColumn = (props: ItemTableListInnerColumn) => {
|
export const PathColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string' && row) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
+5
-3
@@ -24,7 +24,9 @@ export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
const { playlistId } = useParams() as { playlistId?: string };
|
const { playlistId } = useParams() as { playlistId?: string };
|
||||||
const isHeaderEnabled = !!props.enableHeader;
|
const isHeaderEnabled = !!props.enableHeader;
|
||||||
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
||||||
const item = isDataRow ? props.data[props.rowIndex] : null;
|
const item = isDataRow
|
||||||
|
? (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex])
|
||||||
|
: null;
|
||||||
|
|
||||||
const isPlaylistSong = props.itemType === LibraryItem.PLAYLIST_SONG;
|
const isPlaylistSong = props.itemType === LibraryItem.PLAYLIST_SONG;
|
||||||
|
|
||||||
@@ -153,8 +155,8 @@ export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
const isDragging = props.internalState ? isDraggingState : isDraggingLocal;
|
const isDragging = props.internalState ? isDraggingState : isDraggingLocal;
|
||||||
|
|
||||||
const getValidDataItems = useCallback(() => {
|
const getValidDataItems = useCallback(() => {
|
||||||
return props.data.filter((d) => d !== null && (d as any).id);
|
return props.internalState.getData().filter((d) => d !== null && (d as any).id);
|
||||||
}, [props.data]);
|
}, [props.internalState]);
|
||||||
|
|
||||||
const handleMoveUp = useCallback(() => {
|
const handleMoveUp = useCallback(() => {
|
||||||
if (!item || !isDataRow || !isPlaylistSong || !playlistId) {
|
if (!item || !isDataRow || !isPlaylistSong || !playlistId) {
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-r
|
|||||||
import { Rating } from '/@/shared/components/rating/rating';
|
import { Rating } from '/@/shared/components/rating/rating';
|
||||||
|
|
||||||
export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: null | number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: null | number | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
const isMutatingRating = useIsMutatingRating();
|
const isMutatingRating = useIsMutatingRating();
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
<Rating
|
<Rating
|
||||||
className={row ? undefined : 'hover-only-flex'}
|
className={row ? undefined : 'hover-only-flex'}
|
||||||
onChange={(rating) => {
|
onChange={(rating) => {
|
||||||
const item = props.data[props.rowIndex] as ItemListItem;
|
const item = rowItem as ItemListItem;
|
||||||
const rowId = props.internalState.extractRowId(item);
|
const rowId = props.internalState.extractRowId(item);
|
||||||
const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
|
const index = rowId ? props.internalState.findItemIndex(rowId) : -1;
|
||||||
props.controls.onRating?.({
|
props.controls.onRating?.({
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const DefaultRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
icon="arrowDownS"
|
icon="arrowDownS"
|
||||||
iconProps={{ color: 'muted', size: 'md' }}
|
iconProps={{ color: 'muted', size: 'md' }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const item = data[rowIndex] as ItemListItem;
|
const item = (props.getRowItem?.(rowIndex) ?? data[rowIndex]) as ItemListItem;
|
||||||
const rowId = internalState.extractRowId(item);
|
const rowId = internalState.extractRowId(item);
|
||||||
const index = rowId ? internalState.findItemIndex(rowId) : -1;
|
const index = rowId ? internalState.findItemIndex(rowId) : -1;
|
||||||
controls.onExpand?.({
|
controls.onExpand?.({
|
||||||
@@ -87,7 +87,7 @@ const DefaultRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
const QueueSongRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
const QueueSongRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const status = usePlayerStatus();
|
const status = usePlayerStatus();
|
||||||
const song = props.data[props.rowIndex] as QueueSong;
|
const song = (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex]) as QueueSong;
|
||||||
const isActive = useIsActiveRow(song?.id, song?._uniqueId);
|
const isActive = useIsActiveRow(song?.id, song?._uniqueId);
|
||||||
|
|
||||||
const isActiveAndPlaying = isActive && status === PlayerStatus.PLAYING;
|
const isActiveAndPlaying = isActive && status === PlayerStatus.PLAYING;
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import {
|
|||||||
import { formatSizeString } from '/@/renderer/utils/format';
|
import { formatSizeString } from '/@/renderer/utils/format';
|
||||||
|
|
||||||
export const SizeColumn = (props: ItemTableListInnerColumn) => {
|
export const SizeColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'number') {
|
if (typeof row === 'number') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
|
||||||
export const TextColumn = (props: ItemTableListInnerColumn) => {
|
export const TextColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string' && row) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -29,13 +29,12 @@ export const TitleColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function DefaultTitleColumn(props: ItemTableListInnerColumn) {
|
function DefaultTitleColumn(props: ItemTableListInnerColumn) {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
if (typeof row === 'string') {
|
if (typeof row === 'string') {
|
||||||
const path = getTitlePath(props.itemType, (props.data[props.rowIndex] as any).id as string);
|
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
|
|
||||||
const titleLinkProps = path
|
const titleLinkProps = path
|
||||||
? {
|
? {
|
||||||
@@ -71,16 +70,15 @@ function DefaultTitleColumn(props: ItemTableListInnerColumn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function QueueSongTitleColumn(props: ItemTableListInnerColumn) {
|
function QueueSongTitleColumn(props: ItemTableListInnerColumn) {
|
||||||
const row: string | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
props.columns[props.columnIndex].id
|
const row: string | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
];
|
|
||||||
|
|
||||||
const song = props.data[props.rowIndex] as QueueSong;
|
const song = rowItem as QueueSong;
|
||||||
const isActive = useIsActiveRow(song?.id, song?._uniqueId);
|
const isActive = useIsActiveRow(song?.id, song?._uniqueId);
|
||||||
|
|
||||||
if (typeof row === 'string') {
|
if (typeof row === 'string') {
|
||||||
const path = getTitlePath(props.itemType, (props.data[props.rowIndex] as any).id as string);
|
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
|
|
||||||
const titleLinkProps = path
|
const titleLinkProps = path
|
||||||
? {
|
? {
|
||||||
|
|||||||
+15
-13
@@ -26,8 +26,9 @@ import { Folder, LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
|||||||
import { Play } from '/@/shared/types/types';
|
import { Play } from '/@/shared/types/types';
|
||||||
|
|
||||||
export const DefaultTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
export const DefaultTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: object | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.id;
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const item = props.data[props.rowIndex] as any;
|
const row: object | undefined = (rowItem as any)?.id;
|
||||||
|
const item = rowItem as any;
|
||||||
const internalState = (props as any).internalState;
|
const internalState = (props as any).internalState;
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
const playButtonBehavior = usePlayButtonBehavior();
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
@@ -76,9 +77,9 @@ export const DefaultTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
if (item && 'name' in item && 'imageUrl' in item && 'artists' in item) {
|
if (item && 'name' in item && 'imageUrl' in item && 'artists' in item) {
|
||||||
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
||||||
const path = getTitlePath(props.itemType, (props.data[props.rowIndex] as any).id as string);
|
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
|
||||||
|
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
const titleLinkProps = path
|
const titleLinkProps = path
|
||||||
? {
|
? {
|
||||||
component: Link,
|
component: Link,
|
||||||
@@ -156,10 +157,11 @@ export const DefaultTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const row: object | undefined = (props.data as (any | undefined)[])[props.rowIndex];
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
|
const row: object | undefined = rowItem as any;
|
||||||
|
|
||||||
const song = props.data[props.rowIndex] as QueueSong;
|
const song = rowItem as QueueSong;
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
const internalState = (props as any).internalState;
|
const internalState = (props as any).internalState;
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
const playButtonBehavior = usePlayButtonBehavior();
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
@@ -209,9 +211,9 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
|||||||
|
|
||||||
if (row && 'name' in row && 'imageUrl' in row && 'artists' in row) {
|
if (row && 'name' in row && 'imageUrl' in row && 'artists' in row) {
|
||||||
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
||||||
const path = getTitlePath(props.itemType, (props.data[props.rowIndex] as any).id as string);
|
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
|
||||||
|
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
|
|
||||||
const titleLinkProps = path
|
const titleLinkProps = path
|
||||||
? {
|
? {
|
||||||
@@ -306,11 +308,11 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((props.data[props.rowIndex] as unknown as Folder)?._itemType === LibraryItem.FOLDER) {
|
if ((rowItem as unknown as Folder)?._itemType === LibraryItem.FOLDER) {
|
||||||
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
const rowHeight = props.getRowHeight(props.rowIndex, props);
|
||||||
const path = getTitlePath(props.itemType, (props.data[props.rowIndex] as any).id as string);
|
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
|
||||||
|
|
||||||
const item = props.data[props.rowIndex] as any;
|
const item = rowItem as any;
|
||||||
const textStyles = isActive ? { color: 'var(--theme-colors-primary)' } : {};
|
const textStyles = isActive ? { color: 'var(--theme-colors-primary)' } : {};
|
||||||
|
|
||||||
const titleLinkProps = path
|
const titleLinkProps = path
|
||||||
@@ -322,7 +324,7 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
|||||||
}
|
}
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
const title = (props.data[props.rowIndex] as unknown as Folder)?.name;
|
const title = (rowItem as unknown as Folder)?.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableColumnContainer
|
<TableColumnContainer
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
|
|
||||||
export const YearColumn = (props: ItemTableListInnerColumn) => {
|
export const YearColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const item = (props.data as (any | undefined)[])[props.rowIndex];
|
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
|
const item = rowItem as any;
|
||||||
|
|
||||||
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
||||||
const releaseYear = item.releaseYear;
|
const releaseYear = item.releaseYear;
|
||||||
@@ -29,9 +30,7 @@ export const YearColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
props.columns[props.columnIndex].id
|
|
||||||
];
|
|
||||||
|
|
||||||
if (row === null) {
|
if (row === null) {
|
||||||
return <ColumnNullFallback {...props} />;
|
return <ColumnNullFallback {...props} />;
|
||||||
|
|||||||
+24
-6
@@ -17,11 +17,14 @@ interface UseTableKeyboardNavigationProps {
|
|||||||
enableHeader: boolean;
|
enableHeader: boolean;
|
||||||
enableSelection: boolean;
|
enableSelection: boolean;
|
||||||
extractRowId: (item: unknown) => string | undefined;
|
extractRowId: (item: unknown) => string | undefined;
|
||||||
|
getItem?: (index: number) => undefined | unknown;
|
||||||
|
getItemIndex?: (rowId: string) => number | undefined;
|
||||||
getStateItem: (item: any) => ItemListStateItemWithRequiredProperties | null;
|
getStateItem: (item: any) => ItemListStateItemWithRequiredProperties | null;
|
||||||
hasRequiredStateItemProperties: (
|
hasRequiredStateItemProperties: (
|
||||||
item: unknown,
|
item: unknown,
|
||||||
) => item is ItemListStateItemWithRequiredProperties;
|
) => item is ItemListStateItemWithRequiredProperties;
|
||||||
internalState: ItemListStateActions;
|
internalState: ItemListStateActions;
|
||||||
|
itemCount?: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
parsedColumns: TableItemProps['columns'];
|
parsedColumns: TableItemProps['columns'];
|
||||||
pinnedRightColumnCount: number;
|
pinnedRightColumnCount: number;
|
||||||
@@ -45,9 +48,12 @@ export const useTableKeyboardNavigation = ({
|
|||||||
enableHeader,
|
enableHeader,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
extractRowId,
|
extractRowId,
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
getStateItem,
|
getStateItem,
|
||||||
hasRequiredStateItemProperties,
|
hasRequiredStateItemProperties,
|
||||||
internalState,
|
internalState,
|
||||||
|
itemCount,
|
||||||
itemType,
|
itemType,
|
||||||
parsedColumns,
|
parsedColumns,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnCount,
|
||||||
@@ -69,23 +75,26 @@ export const useTableKeyboardNavigation = ({
|
|||||||
const selected = internalState.getSelected();
|
const selected = internalState.getSelected();
|
||||||
const validSelected = selected.filter(hasRequiredStateItemProperties);
|
const validSelected = selected.filter(hasRequiredStateItemProperties);
|
||||||
let currentIndex = -1;
|
let currentIndex = -1;
|
||||||
|
const totalCount = itemCount ?? data.length;
|
||||||
|
|
||||||
if (validSelected.length > 0) {
|
if (validSelected.length > 0) {
|
||||||
const lastSelected = validSelected[validSelected.length - 1];
|
const lastSelected = validSelected[validSelected.length - 1];
|
||||||
currentIndex = data.findIndex(
|
const rowId = extractRowId(lastSelected);
|
||||||
(d) => extractRowId(d) === extractRowId(lastSelected),
|
if (rowId) {
|
||||||
);
|
currentIndex =
|
||||||
|
getItemIndex?.(rowId) ?? data.findIndex((d) => extractRowId(d) === rowId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let newIndex = 0;
|
let newIndex = 0;
|
||||||
if (currentIndex !== -1) {
|
if (currentIndex !== -1) {
|
||||||
newIndex =
|
newIndex =
|
||||||
e.key === 'ArrowDown'
|
e.key === 'ArrowDown'
|
||||||
? Math.min(currentIndex + 1, data.length - 1)
|
? Math.min(currentIndex + 1, totalCount - 1)
|
||||||
: Math.max(currentIndex - 1, 0);
|
: Math.max(currentIndex - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newItem: any = data[newIndex];
|
const newItem: any = getItem ? getItem(newIndex) : data[newIndex];
|
||||||
if (!newItem) return;
|
if (!newItem) return;
|
||||||
|
|
||||||
const newItemListItem = getStateItem(newItem);
|
const newItemListItem = getStateItem(newItem);
|
||||||
@@ -118,7 +127,7 @@ export const useTableKeyboardNavigation = ({
|
|||||||
cellPadding,
|
cellPadding,
|
||||||
columns: parsedColumns,
|
columns: parsedColumns,
|
||||||
controls: {} as ItemControls,
|
controls: {} as ItemControls,
|
||||||
data: enableHeader ? [null, ...data] : data,
|
data: enableHeader ? [null] : [],
|
||||||
enableAlternateRowColors: false,
|
enableAlternateRowColors: false,
|
||||||
enableExpansion: false,
|
enableExpansion: false,
|
||||||
enableHeader,
|
enableHeader,
|
||||||
@@ -127,6 +136,12 @@ export const useTableKeyboardNavigation = ({
|
|||||||
enableSelection,
|
enableSelection,
|
||||||
enableVerticalBorders: false,
|
enableVerticalBorders: false,
|
||||||
getRowHeight: () => DEFAULT_ROW_HEIGHT,
|
getRowHeight: () => DEFAULT_ROW_HEIGHT,
|
||||||
|
getRowItem: (rowIndex: number) => {
|
||||||
|
if (!getItem) return undefined;
|
||||||
|
if (enableHeader && rowIndex === 0) return null;
|
||||||
|
const dataIndex = enableHeader ? rowIndex - 1 : rowIndex;
|
||||||
|
return getItem(dataIndex);
|
||||||
|
},
|
||||||
internalState: {} as ItemListStateActions,
|
internalState: {} as ItemListStateActions,
|
||||||
itemType,
|
itemType,
|
||||||
playerContext,
|
playerContext,
|
||||||
@@ -174,6 +189,8 @@ export const useTableKeyboardNavigation = ({
|
|||||||
calculateScrollTopForIndex,
|
calculateScrollTopForIndex,
|
||||||
cellPadding,
|
cellPadding,
|
||||||
data,
|
data,
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
DEFAULT_ROW_HEIGHT,
|
DEFAULT_ROW_HEIGHT,
|
||||||
enableHeader,
|
enableHeader,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
@@ -181,6 +198,7 @@ export const useTableKeyboardNavigation = ({
|
|||||||
getStateItem,
|
getStateItem,
|
||||||
hasRequiredStateItemProperties,
|
hasRequiredStateItemProperties,
|
||||||
internalState,
|
internalState,
|
||||||
|
itemCount,
|
||||||
itemType,
|
itemType,
|
||||||
parsedColumns,
|
parsedColumns,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnCount,
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
|||||||
|
|
||||||
const isHeaderEnabled = !!props.enableHeader;
|
const isHeaderEnabled = !!props.enableHeader;
|
||||||
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
||||||
const item = isDataRow ? props.data[props.rowIndex] : null;
|
const item = isDataRow
|
||||||
|
? (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex])
|
||||||
|
: null;
|
||||||
const shouldEnableDrag = !!props.enableDrag && isDataRow && !!item;
|
const shouldEnableDrag = !!props.enableDrag && isDataRow && !!item;
|
||||||
const itemType = (item as unknown as { _itemType?: LibraryItem })?._itemType || props.itemType;
|
const itemType = (item as unknown as { _itemType?: LibraryItem })?._itemType || props.itemType;
|
||||||
|
|
||||||
@@ -585,7 +587,9 @@ export const TableColumnTextContainer = (
|
|||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
|
const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
|
||||||
const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
|
const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
|
||||||
const item = isDataRow ? props.data[props.rowIndex] : null;
|
const item = isDataRow
|
||||||
|
? (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex])
|
||||||
|
: null;
|
||||||
const itemRowId =
|
const itemRowId =
|
||||||
item && typeof item === 'object' && 'id' in item
|
item && typeof item === 'object' && 'id' in item
|
||||||
? props.internalState.extractRowId(item)
|
? props.internalState.extractRowId(item)
|
||||||
@@ -736,7 +740,9 @@ export const TableColumnContainer = (
|
|||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
|
const isDataRow = props.enableHeader ? props.rowIndex > 0 : true;
|
||||||
const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
|
const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex;
|
||||||
const item = isDataRow ? props.data[props.rowIndex] : null;
|
const item = isDataRow
|
||||||
|
? (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex])
|
||||||
|
: null;
|
||||||
const itemRowId =
|
const itemRowId =
|
||||||
item && typeof item === 'object' && 'id' in item
|
item && typeof item === 'object' && 'id' in item
|
||||||
? props.internalState.extractRowId(item)
|
? props.internalState.extractRowId(item)
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ interface VirtualizedTableGridProps {
|
|||||||
enableScrollShadow: boolean;
|
enableScrollShadow: boolean;
|
||||||
enableSelection: boolean;
|
enableSelection: boolean;
|
||||||
enableVerticalBorders: boolean;
|
enableVerticalBorders: boolean;
|
||||||
|
getItem?: (index: number) => undefined | unknown;
|
||||||
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
||||||
groups?: TableGroupHeader[];
|
groups?: TableGroupHeader[];
|
||||||
headerHeight: number;
|
headerHeight: number;
|
||||||
@@ -159,6 +160,7 @@ const VirtualizedTableGrid = ({
|
|||||||
enableScrollShadow,
|
enableScrollShadow,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
enableVerticalBorders,
|
enableVerticalBorders,
|
||||||
|
getItem,
|
||||||
getRowHeight,
|
getRowHeight,
|
||||||
groups,
|
groups,
|
||||||
headerHeight,
|
headerHeight,
|
||||||
@@ -284,6 +286,44 @@ const VirtualizedTableGrid = ({
|
|||||||
[enableHeader, groupHeaderInfoByRowIndex, groupHeaderRowIndexes, groups],
|
[enableHeader, groupHeaderInfoByRowIndex, groupHeaderRowIndexes, groups],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getRowItem = useCallback(
|
||||||
|
(rowIndex: number): null | undefined | unknown => {
|
||||||
|
// Header row
|
||||||
|
if (enableHeader && rowIndex === 0) return null;
|
||||||
|
// Group header rows are represented as null in the row model
|
||||||
|
if (groupHeaderInfoByRowIndex?.has(rowIndex)) return null;
|
||||||
|
|
||||||
|
if (!groups || groups.length === 0) {
|
||||||
|
const dataIndex = enableHeader ? rowIndex - 1 : rowIndex;
|
||||||
|
return getItem ? getItem(dataIndex) : dataWithGroups[rowIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerOffset = enableHeader ? 1 : 0;
|
||||||
|
|
||||||
|
// Count group header rows strictly before this rowIndex (upperBound on groupHeaderRowIndexes)
|
||||||
|
let lo = 0;
|
||||||
|
let hi = groupHeaderRowIndexes.length;
|
||||||
|
const target = rowIndex - 1;
|
||||||
|
while (lo < hi) {
|
||||||
|
const mid = (lo + hi) >>> 1;
|
||||||
|
if (groupHeaderRowIndexes[mid] <= target) lo = mid + 1;
|
||||||
|
else hi = mid;
|
||||||
|
}
|
||||||
|
const groupHeadersBefore = lo;
|
||||||
|
|
||||||
|
const dataIndex = rowIndex - headerOffset - groupHeadersBefore;
|
||||||
|
return getItem ? getItem(dataIndex) : undefined;
|
||||||
|
},
|
||||||
|
[
|
||||||
|
dataWithGroups,
|
||||||
|
enableHeader,
|
||||||
|
getItem,
|
||||||
|
groupHeaderInfoByRowIndex,
|
||||||
|
groupHeaderRowIndexes,
|
||||||
|
groups,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
const stableConfigProps = useMemo(
|
const stableConfigProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
cellPadding,
|
cellPadding,
|
||||||
@@ -317,6 +357,7 @@ const VirtualizedTableGrid = ({
|
|||||||
data: dataWithGroups,
|
data: dataWithGroups,
|
||||||
getAdjustedRowIndex,
|
getAdjustedRowIndex,
|
||||||
getGroupRenderData,
|
getGroupRenderData,
|
||||||
|
getRowItem,
|
||||||
groupHeaderInfoByRowIndex,
|
groupHeaderInfoByRowIndex,
|
||||||
pinnedLeftColumnCount,
|
pinnedLeftColumnCount,
|
||||||
pinnedLeftColumnWidths,
|
pinnedLeftColumnWidths,
|
||||||
@@ -327,6 +368,7 @@ const VirtualizedTableGrid = ({
|
|||||||
[
|
[
|
||||||
calculatedColumnWidths,
|
calculatedColumnWidths,
|
||||||
dataWithGroups,
|
dataWithGroups,
|
||||||
|
getRowItem,
|
||||||
getAdjustedRowIndex,
|
getAdjustedRowIndex,
|
||||||
getGroupRenderData,
|
getGroupRenderData,
|
||||||
groupHeaderInfoByRowIndex,
|
groupHeaderInfoByRowIndex,
|
||||||
@@ -724,6 +766,7 @@ export interface TableItemProps {
|
|||||||
getAdjustedRowIndex?: (rowIndex: number) => number;
|
getAdjustedRowIndex?: (rowIndex: number) => number;
|
||||||
getGroupRenderData?: () => unknown[];
|
getGroupRenderData?: () => unknown[];
|
||||||
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
||||||
|
getRowItem?: (rowIndex: number) => null | undefined | unknown;
|
||||||
groupHeaderInfoByRowIndex?: Map<number, { groupIndex: number; startDataIndex: number }>;
|
groupHeaderInfoByRowIndex?: Map<number, { groupIndex: number; startDataIndex: number }>;
|
||||||
groups?: TableGroupHeader[];
|
groups?: TableGroupHeader[];
|
||||||
internalState: ItemListStateActions;
|
internalState: ItemListStateActions;
|
||||||
@@ -759,6 +802,8 @@ interface ItemTableListProps {
|
|||||||
enableStickyGroupRows?: boolean;
|
enableStickyGroupRows?: boolean;
|
||||||
enableStickyHeader?: boolean;
|
enableStickyHeader?: boolean;
|
||||||
enableVerticalBorders?: boolean;
|
enableVerticalBorders?: boolean;
|
||||||
|
getItem?: (index: number) => undefined | unknown;
|
||||||
|
getItemIndex?: (rowId: string) => number | undefined;
|
||||||
getRowId?: ((item: unknown) => string) | string;
|
getRowId?: ((item: unknown) => string) | string;
|
||||||
groups?: TableGroupHeader[];
|
groups?: TableGroupHeader[];
|
||||||
headerHeight?: number;
|
headerHeight?: number;
|
||||||
@@ -767,6 +812,7 @@ interface ItemTableListProps {
|
|||||||
to: number;
|
to: number;
|
||||||
type: 'index' | 'offset';
|
type: 'index' | 'offset';
|
||||||
};
|
};
|
||||||
|
itemCount?: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
onColumnReordered?: (
|
onColumnReordered?: (
|
||||||
columnIdFrom: TableColumn,
|
columnIdFrom: TableColumn,
|
||||||
@@ -802,10 +848,13 @@ const BaseItemTableList = ({
|
|||||||
enableStickyGroupRows = false,
|
enableStickyGroupRows = false,
|
||||||
enableStickyHeader = false,
|
enableStickyHeader = false,
|
||||||
enableVerticalBorders = false,
|
enableVerticalBorders = false,
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
getRowId,
|
getRowId,
|
||||||
groups,
|
groups,
|
||||||
headerHeight = 40,
|
headerHeight = 40,
|
||||||
initialTop,
|
initialTop,
|
||||||
|
itemCount,
|
||||||
itemType,
|
itemType,
|
||||||
onColumnReordered,
|
onColumnReordered,
|
||||||
onColumnResized,
|
onColumnResized,
|
||||||
@@ -818,7 +867,8 @@ const BaseItemTableList = ({
|
|||||||
startRowIndex,
|
startRowIndex,
|
||||||
}: ItemTableListProps) => {
|
}: ItemTableListProps) => {
|
||||||
const tableId = useId();
|
const tableId = useId();
|
||||||
const totalItemCount = enableHeader ? data.length + 1 : data.length;
|
const baseItemCount = itemCount ?? data.length;
|
||||||
|
const totalItemCount = enableHeader ? baseItemCount + 1 : baseItemCount;
|
||||||
const [centerContainerWidth, setCenterContainerWidth] = useState(0);
|
const [centerContainerWidth, setCenterContainerWidth] = useState(0);
|
||||||
const [totalContainerWidth, setTotalContainerWidth] = useState(0);
|
const [totalContainerWidth, setTotalContainerWidth] = useState(0);
|
||||||
|
|
||||||
@@ -836,12 +886,29 @@ const BaseItemTableList = ({
|
|||||||
});
|
});
|
||||||
const playerContext = usePlayer();
|
const playerContext = usePlayer();
|
||||||
|
|
||||||
const { dataWithGroups, groupHeaderRowCount } = useTableRowModel({
|
const {
|
||||||
|
dataWithGroups: dataWithGroupsFromModel,
|
||||||
|
groupHeaderRowCount: groupHeaderRowCountFromModel,
|
||||||
|
} = useTableRowModel({
|
||||||
data,
|
data,
|
||||||
enableHeader,
|
enableHeader,
|
||||||
groups,
|
groups,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shouldUseAccessor = typeof getItem === 'function' && typeof itemCount === 'number';
|
||||||
|
|
||||||
|
// Avoid constructing a massive row-model array for infinite lists.
|
||||||
|
// Cell renderers use `getRowItem` accessor when provided.
|
||||||
|
const dataWithGroups = useMemo<(null | unknown)[]>(() => {
|
||||||
|
if (!shouldUseAccessor) return dataWithGroupsFromModel;
|
||||||
|
return enableHeader ? [null] : [];
|
||||||
|
}, [dataWithGroupsFromModel, enableHeader, shouldUseAccessor]);
|
||||||
|
|
||||||
|
const groupHeaderRowCount = useMemo(() => {
|
||||||
|
if (!shouldUseAccessor) return groupHeaderRowCountFromModel;
|
||||||
|
return groups?.length ? groups.length : 0;
|
||||||
|
}, [groupHeaderRowCountFromModel, groups, shouldUseAccessor]);
|
||||||
|
|
||||||
const pinnedRowCount = enableHeader ? 1 : 0;
|
const pinnedRowCount = enableHeader ? 1 : 0;
|
||||||
|
|
||||||
// Group headers are inserted at specific indexes, so they add to the total row count
|
// Group headers are inserted at specific indexes, so they add to the total row count
|
||||||
@@ -1007,8 +1074,9 @@ const BaseItemTableList = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getDataFn = useCallback(() => {
|
const getDataFn = useCallback(() => {
|
||||||
return dataWithGroups;
|
// For infinite lists, callers should pass `data` as the currently loaded items only.
|
||||||
}, [dataWithGroups]);
|
return data;
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const extractRowId = useMemo(() => createExtractRowId(getRowId), [getRowId]);
|
const extractRowId = useMemo(() => createExtractRowId(getRowId), [getRowId]);
|
||||||
|
|
||||||
@@ -1041,9 +1109,12 @@ const BaseItemTableList = ({
|
|||||||
enableHeader,
|
enableHeader,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
extractRowId,
|
extractRowId,
|
||||||
|
getItem,
|
||||||
|
getItemIndex,
|
||||||
getStateItem,
|
getStateItem,
|
||||||
hasRequiredStateItemProperties,
|
hasRequiredStateItemProperties,
|
||||||
internalState,
|
internalState,
|
||||||
|
itemCount: baseItemCount,
|
||||||
itemType,
|
itemType,
|
||||||
parsedColumns,
|
parsedColumns,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnCount,
|
||||||
@@ -1499,6 +1570,7 @@ const BaseItemTableList = ({
|
|||||||
enableScrollShadow={enableScrollShadow}
|
enableScrollShadow={enableScrollShadow}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
getRowHeight={getRowHeight}
|
getRowHeight={getRowHeight}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const AlbumListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getAlbumList;
|
const listQueryFn = api.controller.getAlbumList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ALBUM,
|
eventKey: ItemListKey.ALBUM,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ALBUM,
|
itemType: LibraryItem.ALBUM,
|
||||||
@@ -54,13 +55,17 @@ export const AlbumListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
enableExpansion
|
enableExpansion
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.ALBUM}
|
itemType={LibraryItem.ALBUM}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const AlbumListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getAlbumList;
|
const listQueryFn = api.controller.getAlbumList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ALBUM,
|
eventKey: ItemListKey.ALBUM,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ALBUM,
|
itemType: LibraryItem.ALBUM,
|
||||||
@@ -70,16 +71,19 @@ export const AlbumListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.ALBUM}
|
itemType={LibraryItem.ALBUM}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export const AlbumArtistListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getAlbumArtistList;
|
const listQueryFn = api.controller.getAlbumArtistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ALBUM_ARTIST,
|
eventKey: ItemListKey.ALBUM_ARTIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ALBUM_ARTIST,
|
itemType: LibraryItem.ALBUM_ARTIST,
|
||||||
@@ -55,12 +56,16 @@ export const AlbumArtistListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.ALBUM_ARTIST}
|
itemType={LibraryItem.ALBUM_ARTIST}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export const AlbumArtistListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getAlbumArtistList;
|
const listQueryFn = api.controller.getAlbumArtistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ALBUM_ARTIST,
|
eventKey: ItemListKey.ALBUM_ARTIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ALBUM_ARTIST,
|
itemType: LibraryItem.ALBUM_ARTIST,
|
||||||
@@ -71,17 +72,20 @@ export const AlbumArtistListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableExpansion={false}
|
enableExpansion={false}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.ALBUM_ARTIST}
|
itemType={LibraryItem.ALBUM_ARTIST}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const ArtistListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getArtistList;
|
const listQueryFn = api.controller.getArtistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ARTIST,
|
eventKey: ItemListKey.ARTIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ARTIST,
|
itemType: LibraryItem.ARTIST,
|
||||||
@@ -54,12 +55,16 @@ export const ArtistListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.ARTIST}
|
itemType={LibraryItem.ARTIST}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const ArtistListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getArtistList;
|
const listQueryFn = api.controller.getArtistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.ARTIST,
|
eventKey: ItemListKey.ARTIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.ARTIST,
|
itemType: LibraryItem.ARTIST,
|
||||||
@@ -70,17 +71,20 @@ export const ArtistListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableExpansion={false}
|
enableExpansion={false}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.ARTIST}
|
itemType={LibraryItem.ARTIST}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const GenreListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getGenreList;
|
const listQueryFn = api.controller.getGenreList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.GENRE,
|
eventKey: ItemListKey.GENRE,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.GENRE,
|
itemType: LibraryItem.GENRE,
|
||||||
@@ -54,12 +55,16 @@ export const GenreListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.GENRE}
|
itemType={LibraryItem.GENRE}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const GenreListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getGenreList;
|
const listQueryFn = api.controller.getGenreList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.GENRE,
|
eventKey: ItemListKey.GENRE,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.GENRE,
|
itemType: LibraryItem.GENRE,
|
||||||
@@ -70,17 +71,20 @@ export const GenreListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableExpansion={false}
|
enableExpansion={false}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.GENRE}
|
itemType={LibraryItem.GENRE}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const PlaylistListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getPlaylistList;
|
const listQueryFn = api.controller.getPlaylistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.PLAYLIST,
|
eventKey: ItemListKey.PLAYLIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.PLAYLIST,
|
itemType: LibraryItem.PLAYLIST,
|
||||||
@@ -54,12 +55,16 @@ export const PlaylistListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.PLAYLIST}
|
itemType={LibraryItem.PLAYLIST}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const PlaylistListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getPlaylistList;
|
const listQueryFn = api.controller.getPlaylistList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.PLAYLIST,
|
eventKey: ItemListKey.PLAYLIST,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.PLAYLIST,
|
itemType: LibraryItem.PLAYLIST,
|
||||||
@@ -70,16 +71,19 @@ export const PlaylistListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.PLAYLIST}
|
itemType={LibraryItem.PLAYLIST}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export const SongListInfiniteGrid = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getSongList;
|
const listQueryFn = api.controller.getSongList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { dataVersion, getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.SONG,
|
eventKey: ItemListKey.SONG,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.SONG,
|
itemType: LibraryItem.SONG,
|
||||||
@@ -49,12 +50,16 @@ export const SongListInfiniteGrid = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemGridList
|
<ItemGridList
|
||||||
data={data}
|
data={loadedItems}
|
||||||
|
dataVersion={dataVersion}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemsPerRow={itemsPerRow}
|
itemsPerRow={itemsPerRow}
|
||||||
itemType={LibraryItem.SONG}
|
itemType={LibraryItem.SONG}
|
||||||
onRangeChanged={onRangeChanged}
|
onRangeChanged={onRangeChanged}
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export const SongListInfiniteTable = ({
|
|||||||
|
|
||||||
const listQueryFn = api.controller.getSongList;
|
const listQueryFn = api.controller.getSongList;
|
||||||
|
|
||||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
const { getItem, getItemIndex, itemCount, loadedItems, onRangeChanged } =
|
||||||
|
useItemListInfiniteLoader({
|
||||||
eventKey: ItemListKey.SONG,
|
eventKey: ItemListKey.SONG,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
itemType: LibraryItem.SONG,
|
itemType: LibraryItem.SONG,
|
||||||
@@ -69,17 +70,20 @@ export const SongListInfiniteTable = ({
|
|||||||
autoFitColumns={autoFitColumns}
|
autoFitColumns={autoFitColumns}
|
||||||
CellComponent={ItemTableListColumn}
|
CellComponent={ItemTableListColumn}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={loadedItems}
|
||||||
enableAlternateRowColors={enableAlternateRowColors}
|
enableAlternateRowColors={enableAlternateRowColors}
|
||||||
enableExpansion={false}
|
enableExpansion={false}
|
||||||
enableHorizontalBorders={enableHorizontalBorders}
|
enableHorizontalBorders={enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||||
enableSelection={enableSelection}
|
enableSelection={enableSelection}
|
||||||
enableVerticalBorders={enableVerticalBorders}
|
enableVerticalBorders={enableVerticalBorders}
|
||||||
|
getItem={getItem}
|
||||||
|
getItemIndex={getItemIndex}
|
||||||
initialTop={{
|
initialTop={{
|
||||||
to: scrollOffset ?? 0,
|
to: scrollOffset ?? 0,
|
||||||
type: 'offset',
|
type: 'offset',
|
||||||
}}
|
}}
|
||||||
|
itemCount={itemCount}
|
||||||
itemType={LibraryItem.SONG}
|
itemType={LibraryItem.SONG}
|
||||||
onColumnReordered={handleColumnReordered}
|
onColumnReordered={handleColumnReordered}
|
||||||
onColumnResized={handleColumnResized}
|
onColumnResized={handleColumnResized}
|
||||||
|
|||||||
Reference in New Issue
Block a user