{
  "version": 3,
  "sources": ["../../src/hooks/use-data.ts"],
  "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useState, useEffect, useMemo, useRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport type { View } from '../types';\n\ntype PaginationInfo = {\n\ttotalItems: number;\n\ttotalPages: number;\n};\n\ninterface UseDataParams< Item > {\n\tview: View;\n\tdata: Item[];\n\tgetItemId: ( item: Item ) => string;\n\tisLoading?: boolean;\n\tpaginationInfo: PaginationInfo;\n\tselection?: string[];\n}\n\ninterface UseDataResult< Item > {\n\tdata: ( Item & { position?: number } )[];\n\tpaginationInfo: PaginationInfo;\n\thasInitiallyLoaded: boolean;\n\tsetVisibleEntries?: React.Dispatch< React.SetStateAction< number[] > >;\n}\n\n/**\n * Hook to manage data for DataViews.\n *\n * When infinite scroll is enabled, this hook handles:\n * - Loading more data when scrolling up or down\n * - Maintaining stable positions for items\n * - Unloading items that are no longer visible (with a buffer)\n *\n * When infinite scroll is disabled, it preserves the previous data and\n * pagination info while loading, so the UI doesn't flash empty.\n *\n * In both cases, it tracks whether data has initially loaded.\n *\n * @param params                - Configuration parameters\n * @param params.view           - Current view configuration\n * @param params.data           - Current page of data\n * @param params.getItemId      - Function to extract item ID\n * @param params.isLoading      - Whether data is currently loading\n * @param params.paginationInfo - Pagination info (totalItems, totalPages)\n * @param params.selection      - Currently selected item IDs\n * @return Object containing data, paginationInfo, hasInitiallyLoaded,\n *         and optional setVisibleEntries callback\n */\nexport default function useData< Item >( {\n\tview,\n\tdata: shownData,\n\tgetItemId,\n\tisLoading,\n\tpaginationInfo,\n\tselection,\n}: UseDataParams< Item > ): UseDataResult< Item > {\n\tconst isInfiniteScrollEnabled = view.infiniteScrollEnabled;\n\n\tconst [ hasInitiallyLoaded, setHasInitiallyLoaded ] = useState(\n\t\t! isLoading\n\t);\n\tuseEffect( () => {\n\t\tif ( ! isLoading ) {\n\t\t\tsetHasInitiallyLoaded( true );\n\t\t}\n\t}, [ isLoading ] );\n\n\tconst previousDataRef = useRef< Item[] >( shownData );\n\tconst previousPaginationInfoRef =\n\t\tuseRef< PaginationInfo >( paginationInfo );\n\tuseEffect( () => {\n\t\tif ( ! isLoading ) {\n\t\t\tpreviousDataRef.current = shownData;\n\t\t\tpreviousPaginationInfoRef.current = paginationInfo;\n\t\t}\n\t}, [ shownData, isLoading, paginationInfo ] );\n\n\t// Infinite scroll state.\n\tconst [ visibleEntries, setVisibleEntries ] = useState< number[] >( [] );\n\n\t// Track the mapping of item IDs to their positions in the full dataset\n\tconst positionMapRef = useRef< Map< string, number > >( new Map() );\n\n\t// Store accumulated records in a ref for persistence across renders\n\tconst allLoadedRecordsRef = useRef< Item[] >( [] );\n\n\t// Track previous view parameters to detect when we need to reset\n\tconst prevViewParamsRef = useRef< {\n\t\tsearch: string | undefined;\n\t\tfilters: string | undefined;\n\t\tperPage: number | undefined;\n\t} >( {\n\t\tsearch: undefined,\n\t\tfilters: undefined,\n\t\tperPage: undefined,\n\t} );\n\n\t// Determine scroll direction based on position changes\n\tconst scrollDirectionRef = useRef< 'up' | 'down' | undefined >( undefined );\n\tconst prevStartPositionRef = useRef< number | undefined >( undefined );\n\n\t// Track whether we've done initial load\n\tconst hasInitializedRef = useRef( false );\n\n\t// Compute data synchronously during render using useMemo\n\t// This ensures the returned data is always in sync with shownData\n\tconst allLoadedRecords = useMemo( () => {\n\t\t// Update scroll direction based on position changes\n\t\tif (\n\t\t\tview.startPosition !== undefined &&\n\t\t\tprevStartPositionRef.current !== undefined\n\t\t) {\n\t\t\tif ( view.startPosition < prevStartPositionRef.current ) {\n\t\t\t\tscrollDirectionRef.current = 'up';\n\t\t\t} else if ( view.startPosition > prevStartPositionRef.current ) {\n\t\t\t\tscrollDirectionRef.current = 'down';\n\t\t\t}\n\t\t}\n\t\tprevStartPositionRef.current = view.startPosition;\n\n\t\t// Serialize filters for comparison\n\t\tconst currentFiltersKey = JSON.stringify( view.filters ?? [] );\n\t\tconst prevFiltersKey = prevViewParamsRef.current.filters;\n\n\t\t// Check if view parameters that require a reset have changed\n\t\tconst shouldReset =\n\t\t\t! hasInitializedRef.current ||\n\t\t\t! view.infiniteScrollEnabled ||\n\t\t\tview.search !== prevViewParamsRef.current.search ||\n\t\t\tcurrentFiltersKey !== prevFiltersKey ||\n\t\t\tview.perPage !== prevViewParamsRef.current.perPage;\n\t\thasInitializedRef.current = true;\n\t\t// Update tracked view parameters\n\t\tprevViewParamsRef.current = {\n\t\t\tsearch: view.search,\n\t\t\tfilters: currentFiltersKey,\n\t\t\tperPage: view.perPage,\n\t\t};\n\n\t\tif ( shouldReset ) {\n\t\t\t// Reset - clear position map and replace all data\n\t\t\tpositionMapRef.current.clear();\n\t\t\t// Reset scroll direction to prevent stale directional filtering\n\t\t\tscrollDirectionRef.current = undefined;\n\t\t\t// Use the view's startPosition if defined, otherwise default to 1\n\t\t\tconst startPosition = view.search ? 1 : view.startPosition ?? 1;\n\t\t\tconst records = shownData.map( ( record, index ) => {\n\t\t\t\tconst position = startPosition + index;\n\t\t\t\tpositionMapRef.current.set( getItemId( record ), position );\n\t\t\t\treturn {\n\t\t\t\t\t...record,\n\t\t\t\t\tposition,\n\t\t\t\t};\n\t\t\t} );\n\t\t\tallLoadedRecordsRef.current = records;\n\t\t\treturn records;\n\t\t}\n\n\t\t// Subsequent pages - merge with existing data\n\t\tconst prev = allLoadedRecordsRef.current;\n\t\tconst shownDataIds = new Set( shownData.map( getItemId ) );\n\t\tconst scrollDirection = scrollDirectionRef.current;\n\n\t\t// The position for each item in shownData should be based on the\n\t\t// current startPosition from the view, which reflects the actual\n\t\t// offset in the dataset. This ensures aria-posinset values are\n\t\t// semantically correct - if startPosition is 40, there are exactly\n\t\t// 39 items before the first item in shownData.\n\t\t// When there's an active search, always start from position 1 since\n\t\t// search results are a filtered subset, not a paginated view.\n\t\tconst basePosition = view.search ? 1 : view.startPosition ?? 1;\n\t\tconst newRecords = shownData.map( ( record, index ) => {\n\t\t\tconst itemId = getItemId( record );\n\t\t\tconst position = view.infiniteScrollEnabled\n\t\t\t\t? basePosition + index\n\t\t\t\t: undefined;\n\n\t\t\t// Always update the position map with the correct position\n\t\t\t// based on the current query's startPosition\n\t\t\tif ( position !== undefined ) {\n\t\t\t\tpositionMapRef.current.set( itemId, position );\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...record,\n\t\t\t\tposition,\n\t\t\t};\n\t\t} );\n\n\t\tif ( newRecords.length === 0 ) {\n\t\t\treturn prev;\n\t\t}\n\n\t\t// Remove duplicates from prev, keeping only records not in shownData\n\t\tconst prevWithoutDuplicates = prev.filter(\n\t\t\t( record ) => ! shownDataIds.has( getItemId( record ) )\n\t\t);\n\n\t\t// Update the loaded range\n\t\tconst allRecords =\n\t\t\tscrollDirection === 'up'\n\t\t\t\t? [ ...newRecords, ...prevWithoutDuplicates ]\n\t\t\t\t: [ ...prevWithoutDuplicates, ...newRecords ];\n\n\t\t// Sort all records by position to ensure correct order\n\t\t// This is crucial when items are reloaded after scrolling in different directions\n\t\tallRecords.sort( ( a, b ) => {\n\t\t\tconst posA = ( a as Item & { position: number } ).position;\n\t\t\tconst posB = ( b as Item & { position: number } ).position;\n\t\t\treturn posA - posB;\n\t\t} );\n\n\t\tlet result = allRecords;\n\n\t\tif ( visibleEntries.length > 0 ) {\n\t\t\tconst visibleMin = Math.min( ...visibleEntries );\n\t\t\tconst visibleMax = Math.max( ...visibleEntries );\n\t\t\t// Buffer size balances allowing new items to render (when prepended\n\t\t\t// during scroll up) while unloading items no longer on screen.\n\t\t\t// Use a larger buffer to prevent scrollbar from jumping when items\n\t\t\t// are unloaded, which could trigger unwanted scroll events.\n\t\t\tconst buffer = 20;\n\n\t\t\tconst recordPositions = allRecords.map(\n\t\t\t\t( r ) => ( r as Item & { position: number } ).position\n\t\t\t);\n\t\t\tconst minRecordPos = Math.min( ...recordPositions );\n\t\t\tconst maxRecordPos = Math.max( ...recordPositions );\n\n\t\t\t// Check if there's any overlap between visible range and actual record positions\n\t\t\t// to avoid filtering when visibleEntries are stale (e.g., after search/filter reset)\n\t\t\tconst hasOverlap = ! (\n\t\t\t\tmaxRecordPos < visibleMin - buffer ||\n\t\t\t\tminRecordPos > visibleMax + buffer\n\t\t\t);\n\n\t\t\tif ( hasOverlap ) {\n\t\t\t\tresult = allRecords.filter( ( record ) => {\n\t\t\t\t\tconst itemId = getItemId( record );\n\t\t\t\t\tconst isSelected = selection?.includes( itemId );\n\t\t\t\t\t// Never unload selected items, even if outside visible range\n\t\t\t\t\tif ( isSelected ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst itemPosition = (\n\t\t\t\t\t\trecord as Item & { position: number }\n\t\t\t\t\t ).position;\n\t\t\t\t\t// When scrolling, only trim items from the end we're scrolling away from\n\t\t\t\t\tif ( scrollDirection === 'up' ) {\n\t\t\t\t\t\t// When scrolling up, only trim items below the visible range\n\t\t\t\t\t\treturn itemPosition <= visibleMax + buffer;\n\t\t\t\t\t} else if ( scrollDirection === 'down' ) {\n\t\t\t\t\t\t// When scrolling down, only trim items above the visible range\n\t\t\t\t\t\treturn itemPosition >= visibleMin - buffer;\n\t\t\t\t\t}\n\t\t\t\t\t// When not scrolling or first load, keep items within buffer range\n\t\t\t\t\treturn (\n\t\t\t\t\t\titemPosition >= visibleMin - buffer &&\n\t\t\t\t\t\titemPosition <= visibleMax + buffer\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\tallLoadedRecordsRef.current = result;\n\t\treturn result;\n\t}, [\n\t\tshownData,\n\t\tview.search,\n\t\tview.filters,\n\t\tview.perPage,\n\t\tview.startPosition,\n\t\tview.infiniteScrollEnabled,\n\t\tvisibleEntries,\n\t\tselection,\n\t\tgetItemId,\n\t] );\n\n\t// When infinite scroll is disabled, preserve previous data while loading\n\tif ( ! isInfiniteScrollEnabled ) {\n\t\tconst dataToReturn =\n\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t? previousDataRef.current\n\t\t\t\t: shownData;\n\t\treturn {\n\t\t\tdata: dataToReturn.map( ( item ) => ( {\n\t\t\t\t...item,\n\t\t\t\tposition: undefined,\n\t\t\t} ) ) as ( Item & { position?: number } )[],\n\t\t\tpaginationInfo:\n\t\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t\t? previousPaginationInfoRef.current\n\t\t\t\t\t: paginationInfo,\n\t\t\thasInitiallyLoaded,\n\t\t\tsetVisibleEntries: undefined,\n\t\t};\n\t}\n\n\treturn {\n\t\tdata: allLoadedRecords as ( Item & { position?: number } )[],\n\t\tpaginationInfo,\n\t\thasInitiallyLoaded,\n\t\tsetVisibleEntries,\n\t};\n}\n"],
  "mappings": "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",
  "names": []
}
