@toolbox-web/grid 1.24.0 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all.js +1 -1
- package/all.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +9 -0
- package/lib/core/grid.d.ts.map +1 -1
- package/lib/core/internal/event-delegation.d.ts.map +1 -1
- package/lib/core/plugin/base-plugin.d.ts +6 -0
- package/lib/core/plugin/base-plugin.d.ts.map +1 -1
- package/lib/core/plugin/types.d.ts +2 -0
- package/lib/core/plugin/types.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.js +1 -1
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/index.js +1 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/index.js +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.js +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js +2 -2
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +1 -1
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/index.js +1 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder-columns/index.js +1 -1
- package/lib/plugins/reorder-columns/index.js.map +1 -1
- package/lib/plugins/reorder-rows/RowReorderPlugin.d.ts +5 -7
- package/lib/plugins/reorder-rows/RowReorderPlugin.d.ts.map +1 -1
- package/lib/plugins/reorder-rows/index.js +1 -1
- package/lib/plugins/reorder-rows/index.js.map +1 -1
- package/lib/plugins/responsive/ResponsivePlugin.d.ts.map +1 -1
- package/lib/plugins/responsive/index.js +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/selection/index.js +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/ServerSidePlugin.d.ts +2 -0
- package/lib/plugins/server-side/ServerSidePlugin.d.ts.map +1 -1
- package/lib/plugins/server-side/index.js +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/index.js +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/reorder-rows.umd.js +1 -1
- package/umd/plugins/reorder-rows.umd.js.map +1 -1
- package/umd/plugins/responsive.umd.js +1 -1
- package/umd/plugins/responsive.umd.js.map +1 -1
- package/umd/plugins/server-side.umd.js +1 -1
- package/umd/plugins/server-side.umd.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-side.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/server-side/datasource.ts","../../../../../libs/grid/src/lib/plugins/server-side/ServerSidePlugin.ts"],"sourcesContent":["import type { ServerSideDataSource, GetRowsParams, GetRowsResult } from './types';\n\nexport function getBlockNumber(rowIndex: number, blockSize: number): number {\n return Math.floor(rowIndex / blockSize);\n}\n\nexport function getBlockRange(blockNumber: number, blockSize: number): { start: number; end: number } {\n return {\n start: blockNumber * blockSize,\n end: (blockNumber + 1) * blockSize,\n };\n}\n\nexport function getRequiredBlocks(startRow: number, endRow: number, blockSize: number): number[] {\n const startBlock = getBlockNumber(startRow, blockSize);\n const endBlock = getBlockNumber(endRow - 1, blockSize);\n\n const blocks: number[] = [];\n for (let i = startBlock; i <= endBlock; i++) {\n blocks.push(i);\n }\n return blocks;\n}\n\nexport async function loadBlock(\n dataSource: ServerSideDataSource,\n blockNumber: number,\n blockSize: number,\n params: Partial<GetRowsParams>\n): Promise<GetRowsResult> {\n const range = getBlockRange(blockNumber, blockSize);\n\n return dataSource.getRows({\n startRow: range.start,\n endRow: range.end,\n sortModel: params.sortModel,\n filterModel: params.filterModel,\n });\n}\n\nexport function getRowFromCache(\n rowIndex: number,\n blockSize: number,\n loadedBlocks: Map<number, any[]>\n): any | undefined {\n const blockNumber = getBlockNumber(rowIndex, blockSize);\n const block = loadedBlocks.get(blockNumber);\n if (!block) return undefined;\n\n const indexInBlock = rowIndex % blockSize;\n return block[indexInBlock];\n}\n\nexport function isBlockLoaded(blockNumber: number, loadedBlocks: Map<number, any[]>): boolean {\n return loadedBlocks.has(blockNumber);\n}\n\nexport function isBlockLoading(blockNumber: number, loadingBlocks: Set<number>): boolean {\n return loadingBlocks.has(blockNumber);\n}\n","/**\n * Server-Side Data Plugin (Class-based)\n *\n * Provides server-side data loading with caching and lazy loading.\n */\n\nimport { BaseGridPlugin, ScrollEvent } from '../../core/plugin/base-plugin';\nimport { getBlockNumber, getRequiredBlocks, getRowFromCache, loadBlock } from './datasource';\nimport type { ServerSideConfig, ServerSideDataSource } from './types';\n\n/** Scroll debounce delay in ms */\nconst SCROLL_DEBOUNCE_MS = 100;\n\n/**\n * Server-Side Data Plugin for tbw-grid\n *\n * Enables lazy loading of data from a remote server with caching and block-based fetching.\n * Ideal for large datasets where loading all data upfront is impractical.\n *\n * ## Installation\n *\n * ```ts\n * import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `pageSize` | `number` | `100` | Rows per block |\n * | `cacheBlockSize` | `number` | `pageSize` | Cache block size |\n * | `maxConcurrentRequests` | `number` | `2` | Max parallel data requests |\n *\n * ## DataSource Interface\n *\n * ```ts\n * interface ServerSideDataSource {\n * getRows(params: GetRowsParams): Promise<GetRowsResult>;\n * }\n * ```\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `setDataSource` | `(ds: ServerSideDataSource) => void` | Set the data source |\n * | `refresh` | `() => void` | Refresh current data |\n * | `clearCache` | `() => void` | Clear all cached blocks |\n *\n * @example Basic Server-Side Loading\n * ```ts\n * import '@toolbox-web/grid';\n * import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\n *\n * const dataSource = {\n * async getRows(params) {\n * const response = await fetch(\n * `/api/data?start=${params.startRow}&end=${params.endRow}`\n * );\n * const data = await response.json();\n * return { rows: data.rows, totalRowCount: data.total };\n * },\n * };\n *\n * const plugin = new ServerSidePlugin({ pageSize: 50 });\n * grid.gridConfig = {\n * columns: [...],\n * plugins: [plugin],\n * };\n *\n * grid.ready().then(() => plugin.setDataSource(dataSource));\n * ```\n *\n * @see {@link ServerSideConfig} for configuration options\n * @see {@link ServerSideDataSource} for data source interface\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ServerSidePlugin extends BaseGridPlugin<ServerSideConfig> {\n /** @internal */\n readonly name = 'serverSide';\n\n /** @internal */\n protected override get defaultConfig(): Partial<ServerSideConfig> {\n return {\n pageSize: 100,\n cacheBlockSize: 100,\n maxConcurrentRequests: 2,\n };\n }\n\n // #region Internal State\n private dataSource: ServerSideDataSource | null = null;\n private totalRowCount = 0;\n private loadedBlocks = new Map<number, unknown[]>();\n private loadingBlocks = new Set<number>();\n private lastRequestId = 0;\n private scrollDebounceTimer?: ReturnType<typeof setTimeout>;\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.dataSource = null;\n this.totalRowCount = 0;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.lastRequestId = 0;\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n this.scrollDebounceTimer = undefined;\n }\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Check current viewport and load any missing blocks.\n */\n private loadRequiredBlocks(): void {\n if (!this.dataSource) return;\n\n // Get fresh viewport from grid's virtualization state\n const gridRef = this.grid as unknown as { _virtualization: { start: number; end: number } };\n const blockSize = this.config.cacheBlockSize ?? 100;\n const viewport = { startRow: gridRef._virtualization.start, endRow: gridRef._virtualization.end };\n\n // Determine which blocks are needed for current viewport\n const requiredBlocks = getRequiredBlocks(viewport.startRow, viewport.endRow, blockSize);\n\n // Load missing blocks\n for (const blockNum of requiredBlocks) {\n if (this.loadedBlocks.has(blockNum) || this.loadingBlocks.has(blockNum)) {\n continue;\n }\n\n // Check concurrent request limit\n if (this.loadingBlocks.size >= (this.config.maxConcurrentRequests ?? 2)) {\n break;\n }\n\n this.loadingBlocks.add(blockNum);\n\n loadBlock(this.dataSource, blockNum, blockSize, {})\n .then((result) => {\n this.loadedBlocks.set(blockNum, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.loadingBlocks.delete(blockNum);\n this.requestRender();\n // Re-check with fresh viewport: user may have scrolled further\n this.loadRequiredBlocks();\n })\n .catch(() => {\n this.loadingBlocks.delete(blockNum);\n });\n }\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processRows(rows: readonly unknown[]): unknown[] {\n if (!this.dataSource) return [...rows];\n\n // Create placeholder rows for total count\n const result: unknown[] = [];\n for (let i = 0; i < this.totalRowCount; i++) {\n const cached = getRowFromCache(i, this.config.cacheBlockSize ?? 100, this.loadedBlocks);\n result.push(cached ?? { __loading: true, __index: i });\n }\n\n return result;\n }\n\n /** @internal */\n override onScroll(event: ScrollEvent): void {\n if (!this.dataSource) return;\n\n // Immediate check for blocks\n this.loadRequiredBlocks();\n\n // Debounce: when scrolling stops, do a final check with fresh viewport\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n }\n this.scrollDebounceTimer = setTimeout(() => {\n this.loadRequiredBlocks();\n }, SCROLL_DEBOUNCE_MS);\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Set the data source for server-side loading.\n * @param dataSource - Data source implementing the getRows method\n */\n setDataSource(dataSource: ServerSideDataSource): void {\n this.dataSource = dataSource;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n\n // Load first block\n const blockSize = this.config.cacheBlockSize ?? 100;\n loadBlock(dataSource, 0, blockSize, {}).then((result) => {\n this.loadedBlocks.set(0, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.requestRender();\n });\n }\n\n /**\n * Refresh all data from the server.\n */\n refresh(): void {\n if (!this.dataSource) return;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.requestRender();\n }\n\n /**\n * Clear all cached data without refreshing.\n */\n purgeCache(): void {\n this.loadedBlocks.clear();\n }\n\n /**\n * Get the total row count from the server.\n */\n getTotalRowCount(): number {\n return this.totalRowCount;\n }\n\n /**\n * Check if a specific row is loaded in the cache.\n * @param rowIndex - Row index to check\n */\n isRowLoaded(rowIndex: number): boolean {\n const blockSize = this.config.cacheBlockSize ?? 100;\n const blockNum = getBlockNumber(rowIndex, blockSize);\n return this.loadedBlocks.has(blockNum);\n }\n\n /**\n * Get the number of loaded cache blocks.\n */\n getLoadedBlockCount(): number {\n return this.loadedBlocks.size;\n }\n // #endregion\n}\n"],"names":["getBlockNumber","rowIndex","blockSize","Math","floor","async","loadBlock","dataSource","blockNumber","params","range","start","end","getBlockRange","getRows","startRow","endRow","sortModel","filterModel","getRowFromCache","loadedBlocks","block","get","ServerSidePlugin","BaseGridPlugin","name","defaultConfig","pageSize","cacheBlockSize","maxConcurrentRequests","totalRowCount","Map","loadingBlocks","Set","lastRequestId","scrollDebounceTimer","detach","this","clear","clearTimeout","loadRequiredBlocks","gridRef","grid","config","viewport","_virtualization","requiredBlocks","startBlock","endBlock","blocks","i","push","getRequiredBlocks","blockNum","has","size","add","then","result","set","rows","delete","requestRender","catch","processRows","cached","__loading","__index","onScroll","event","setTimeout","setDataSource","refresh","purgeCache","getTotalRowCount","isRowLoaded","getLoadedBlockCount"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server-side.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/server-side/datasource.ts","../../../../../libs/grid/src/lib/plugins/server-side/ServerSidePlugin.ts"],"sourcesContent":["import type { ServerSideDataSource, GetRowsParams, GetRowsResult } from './types';\n\nexport function getBlockNumber(rowIndex: number, blockSize: number): number {\n return Math.floor(rowIndex / blockSize);\n}\n\nexport function getBlockRange(blockNumber: number, blockSize: number): { start: number; end: number } {\n return {\n start: blockNumber * blockSize,\n end: (blockNumber + 1) * blockSize,\n };\n}\n\nexport function getRequiredBlocks(startRow: number, endRow: number, blockSize: number): number[] {\n const startBlock = getBlockNumber(startRow, blockSize);\n const endBlock = getBlockNumber(endRow - 1, blockSize);\n\n const blocks: number[] = [];\n for (let i = startBlock; i <= endBlock; i++) {\n blocks.push(i);\n }\n return blocks;\n}\n\nexport async function loadBlock(\n dataSource: ServerSideDataSource,\n blockNumber: number,\n blockSize: number,\n params: Partial<GetRowsParams>\n): Promise<GetRowsResult> {\n const range = getBlockRange(blockNumber, blockSize);\n\n return dataSource.getRows({\n startRow: range.start,\n endRow: range.end,\n sortModel: params.sortModel,\n filterModel: params.filterModel,\n });\n}\n\nexport function getRowFromCache(\n rowIndex: number,\n blockSize: number,\n loadedBlocks: Map<number, any[]>\n): any | undefined {\n const blockNumber = getBlockNumber(rowIndex, blockSize);\n const block = loadedBlocks.get(blockNumber);\n if (!block) return undefined;\n\n const indexInBlock = rowIndex % blockSize;\n return block[indexInBlock];\n}\n\nexport function isBlockLoaded(blockNumber: number, loadedBlocks: Map<number, any[]>): boolean {\n return loadedBlocks.has(blockNumber);\n}\n\nexport function isBlockLoading(blockNumber: number, loadingBlocks: Set<number>): boolean {\n return loadingBlocks.has(blockNumber);\n}\n","/**\n * Server-Side Data Plugin (Class-based)\n *\n * Provides server-side data loading with caching and lazy loading.\n */\n\nimport { BaseGridPlugin, ScrollEvent } from '../../core/plugin/base-plugin';\nimport { getBlockNumber, getRequiredBlocks, getRowFromCache, loadBlock } from './datasource';\nimport type { ServerSideConfig, ServerSideDataSource } from './types';\n\n/** Scroll debounce delay in ms */\nconst SCROLL_DEBOUNCE_MS = 100;\n\n/**\n * Server-Side Data Plugin for tbw-grid\n *\n * Enables lazy loading of data from a remote server with caching and block-based fetching.\n * Ideal for large datasets where loading all data upfront is impractical.\n *\n * ## Installation\n *\n * ```ts\n * import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `pageSize` | `number` | `100` | Rows per block |\n * | `cacheBlockSize` | `number` | `pageSize` | Cache block size |\n * | `maxConcurrentRequests` | `number` | `2` | Max parallel data requests |\n *\n * ## DataSource Interface\n *\n * ```ts\n * interface ServerSideDataSource {\n * getRows(params: GetRowsParams): Promise<GetRowsResult>;\n * }\n * ```\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `setDataSource` | `(ds: ServerSideDataSource) => void` | Set the data source |\n * | `refresh` | `() => void` | Refresh current data |\n * | `clearCache` | `() => void` | Clear all cached blocks |\n *\n * @example Basic Server-Side Loading\n * ```ts\n * import '@toolbox-web/grid';\n * import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\n *\n * const dataSource = {\n * async getRows(params) {\n * const response = await fetch(\n * `/api/data?start=${params.startRow}&end=${params.endRow}`\n * );\n * const data = await response.json();\n * return { rows: data.rows, totalRowCount: data.total };\n * },\n * };\n *\n * const plugin = new ServerSidePlugin({ pageSize: 50 });\n * grid.gridConfig = {\n * columns: [...],\n * plugins: [plugin],\n * };\n *\n * grid.ready().then(() => plugin.setDataSource(dataSource));\n * ```\n *\n * @see {@link ServerSideConfig} for configuration options\n * @see {@link ServerSideDataSource} for data source interface\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ServerSidePlugin extends BaseGridPlugin<ServerSideConfig> {\n /** @internal */\n readonly name = 'serverSide';\n\n /** @internal */\n protected override get defaultConfig(): Partial<ServerSideConfig> {\n return {\n pageSize: 100,\n cacheBlockSize: 100,\n maxConcurrentRequests: 2,\n };\n }\n\n // #region Internal State\n private dataSource: ServerSideDataSource | null = null;\n private totalRowCount = 0;\n private loadedBlocks = new Map<number, unknown[]>();\n private loadingBlocks = new Set<number>();\n private lastRequestId = 0;\n private scrollDebounceTimer?: ReturnType<typeof setTimeout>;\n /** Persistent row array with stable placeholder references to avoid unnecessary DOM rebuilds. */\n private managedRows: unknown[] = [];\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.dataSource = null;\n this.totalRowCount = 0;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.managedRows = [];\n this.lastRequestId = 0;\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n this.scrollDebounceTimer = undefined;\n }\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Check current viewport and load any missing blocks.\n */\n private loadRequiredBlocks(): void {\n if (!this.dataSource) return;\n\n // Get fresh viewport from grid's virtualization state\n const gridRef = this.grid as unknown as { _virtualization: { start: number; end: number } };\n const blockSize = this.config.cacheBlockSize ?? 100;\n const viewport = { startRow: gridRef._virtualization.start, endRow: gridRef._virtualization.end };\n\n // Determine which blocks are needed for current viewport\n const requiredBlocks = getRequiredBlocks(viewport.startRow, viewport.endRow, blockSize);\n\n // Load missing blocks\n for (const blockNum of requiredBlocks) {\n if (this.loadedBlocks.has(blockNum) || this.loadingBlocks.has(blockNum)) {\n continue;\n }\n\n // Check concurrent request limit\n if (this.loadingBlocks.size >= (this.config.maxConcurrentRequests ?? 2)) {\n break;\n }\n\n this.loadingBlocks.add(blockNum);\n\n loadBlock(this.dataSource, blockNum, blockSize, {})\n .then((result) => {\n this.loadedBlocks.set(blockNum, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.loadingBlocks.delete(blockNum);\n\n // Update managed rows in place for this block (avoids full processRows rebuild)\n const start = blockNum * blockSize;\n for (let i = 0; i < result.rows.length; i++) {\n if (start + i < this.managedRows.length) {\n this.managedRows[start + i] = result.rows[i];\n }\n }\n\n // Re-render visible rows without force geometry recalculation.\n // requestVirtualRefresh() skips spacer height writes that cause oscillation\n // with the scheduler's afterRender microtask. Row count hasn't changed —\n // only cached data replaced placeholders — so geometry is stable.\n this.requestVirtualRefresh();\n\n // Re-check with fresh viewport: user may have scrolled further\n this.loadRequiredBlocks();\n })\n .catch(() => {\n this.loadingBlocks.delete(blockNum);\n });\n }\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processRows(rows: readonly unknown[]): unknown[] {\n if (!this.dataSource) return [...rows];\n\n const blockSize = this.config.cacheBlockSize ?? 100;\n\n // Grow array with stable placeholder objects (created once, reused across renders)\n while (this.managedRows.length < this.totalRowCount) {\n const i = this.managedRows.length;\n this.managedRows.push({ __loading: true, __index: i });\n }\n // Shrink if total decreased\n this.managedRows.length = this.totalRowCount;\n\n // Replace placeholders with cached data (stable refs for unchanged entries)\n for (let i = 0; i < this.totalRowCount; i++) {\n const cached = getRowFromCache(i, blockSize, this.loadedBlocks);\n if (cached) {\n this.managedRows[i] = cached;\n }\n }\n\n return this.managedRows;\n }\n\n /** @internal */\n override onScroll(event: ScrollEvent): void {\n if (!this.dataSource) return;\n\n // Immediate check for blocks\n this.loadRequiredBlocks();\n\n // Debounce: when scrolling stops, do a final check with fresh viewport\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n }\n this.scrollDebounceTimer = setTimeout(() => {\n this.loadRequiredBlocks();\n }, SCROLL_DEBOUNCE_MS);\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Set the data source for server-side loading.\n * @param dataSource - Data source implementing the getRows method\n */\n setDataSource(dataSource: ServerSideDataSource): void {\n this.dataSource = dataSource;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.managedRows = [];\n\n // Load first block\n const blockSize = this.config.cacheBlockSize ?? 100;\n loadBlock(dataSource, 0, blockSize, {}).then((result) => {\n this.loadedBlocks.set(0, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.requestRender();\n });\n }\n\n /**\n * Refresh all data from the server.\n */\n refresh(): void {\n if (!this.dataSource) return;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.managedRows = [];\n this.requestRender();\n }\n\n /**\n * Clear all cached data without refreshing.\n */\n purgeCache(): void {\n this.loadedBlocks.clear();\n this.managedRows = [];\n }\n\n /**\n * Get the total row count from the server.\n */\n getTotalRowCount(): number {\n return this.totalRowCount;\n }\n\n /**\n * Check if a specific row is loaded in the cache.\n * @param rowIndex - Row index to check\n */\n isRowLoaded(rowIndex: number): boolean {\n const blockSize = this.config.cacheBlockSize ?? 100;\n const blockNum = getBlockNumber(rowIndex, blockSize);\n return this.loadedBlocks.has(blockNum);\n }\n\n /**\n * Get the number of loaded cache blocks.\n */\n getLoadedBlockCount(): number {\n return this.loadedBlocks.size;\n }\n // #endregion\n}\n"],"names":["getBlockNumber","rowIndex","blockSize","Math","floor","async","loadBlock","dataSource","blockNumber","params","range","start","end","getBlockRange","getRows","startRow","endRow","sortModel","filterModel","getRowFromCache","loadedBlocks","block","get","ServerSidePlugin","BaseGridPlugin","name","defaultConfig","pageSize","cacheBlockSize","maxConcurrentRequests","totalRowCount","Map","loadingBlocks","Set","lastRequestId","scrollDebounceTimer","managedRows","detach","this","clear","clearTimeout","loadRequiredBlocks","gridRef","grid","config","viewport","_virtualization","requiredBlocks","startBlock","endBlock","blocks","i","push","getRequiredBlocks","blockNum","has","size","add","then","result","set","rows","delete","length","requestVirtualRefresh","catch","processRows","__loading","__index","cached","onScroll","event","setTimeout","setDataSource","requestRender","refresh","purgeCache","getTotalRowCount","isRowLoaded","getLoadedBlockCount"],"mappings":"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"}
|