66// This software is released under the PostgreSQL Licence
77//
88//////////////////////////////////////////////////////////////
9- import React , { useEffect , useRef } from 'react' ;
9+ import React , { useEffect , useRef , useMemo } from 'react' ;
1010import { styled } from '@mui/material/styles' ;
1111import ReactDOMServer from 'react-dom/server' ;
1212import _ from 'lodash' ;
@@ -49,6 +49,8 @@ const StyledBox = styled(Box)(({theme}) => ({
4949 } ,
5050} ) ) ;
5151
52+ const PK_COLUMN_NAMES = [ 'id' , 'oid' , 'ctid' ] ;
53+
5254function parseEwkbData ( rows , column ) {
5355 let key = column . key ;
5456 const maxRenderByteLength = 20 * 1024 * 1024 ; //render geometry data up to 20MB
@@ -191,6 +193,33 @@ function parseData(rows, columns, column) {
191193 } ;
192194}
193195
196+ // Find primary key column from columns array
197+ function findPkColumn ( columns ) {
198+ return columns . find ( c => PK_COLUMN_NAMES . includes ( c . name ) ) ;
199+ }
200+
201+ // Get unique row identifier using PK column or first column
202+ function getRowIdentifier ( row , pkColumn , columns ) {
203+ if ( pkColumn ?. key && row [ pkColumn . key ] !== undefined ) {
204+ return row [ pkColumn . key ] ;
205+ }
206+ const firstKey = columns [ 0 ] ?. key ;
207+ return firstKey && row [ firstKey ] !== undefined ? row [ firstKey ] : JSON . stringify ( row ) ;
208+ }
209+
210+ // Create Set of row identifiers
211+ function createIdentifierSet ( rowData , pkColumn , columns ) {
212+ return new Set ( rowData . map ( row => getRowIdentifier ( row , pkColumn , columns ) ) ) ;
213+ }
214+
215+ // Match rows from previous selection to current rows
216+ function matchRowSelection ( prevRowData , currentRows , pkColumn , columns ) {
217+ if ( prevRowData . length === 0 ) return [ ] ;
218+
219+ const prevIdSet = createIdentifierSet ( prevRowData , pkColumn , columns ) ;
220+ return currentRows . filter ( row => prevIdSet . has ( getRowIdentifier ( row , pkColumn , columns ) ) ) ;
221+ }
222+
194223function PopupTable ( { data} ) {
195224
196225 return (
@@ -438,20 +467,22 @@ export function GeometryViewer({rows, columns, column}) {
438467 const contentRef = React . useRef ( ) ;
439468 const queryToolCtx = React . useContext ( QueryToolContext ) ;
440469
441- // Track previous state to detect changes
470+ // Track previous column state AND selected row data
442471 const prevStateRef = React . useRef ( {
443472 columnKey : null ,
444473 columnNames : null ,
445- selectedRowPKs : [ ] ,
474+ selectedRowData : [ ] ,
446475 } ) ;
447476
448477 const [ mapKey , setMapKey ] = React . useState ( 0 ) ;
449- const currentColumnKey = column ?. key ;
478+ const currentColumnKey = useMemo ( ( ) => column ?. key , [ column ] ) ;
450479 const currentColumnNames = React . useMemo (
451480 ( ) => columns . map ( c => c . key ) . sort ( ) . join ( ',' ) ,
452481 [ columns ]
453482 ) ;
454483
484+ const pkColumn = useMemo ( ( ) => findPkColumn ( columns ) , [ columns ] ) ;
485+
455486 // Detect when to clear, filter, or re-render the map based on changes in geometry column, columns list, or rows
456487 useEffect ( ( ) => {
457488 const prevState = prevStateRef . current ;
@@ -461,7 +492,7 @@ export function GeometryViewer({rows, columns, column}) {
461492 prevStateRef . current = {
462493 columnKey : null ,
463494 columnNames : null ,
464- selectedRowPKs : [ ] ,
495+ selectedRowData : [ ] ,
465496 } ;
466497 return ;
467498 }
@@ -472,38 +503,39 @@ export function GeometryViewer({rows, columns, column}) {
472503 prevStateRef . current = {
473504 columnKey : currentColumnKey ,
474505 columnNames : currentColumnNames ,
475- selectedRowPKs : [ ] ,
506+ selectedRowData : [ ] ,
476507 } ;
477508 return ;
478509 }
479510
480511 if ( currentColumnKey === prevState . columnKey &&
481512 currentColumnNames === prevState . columnNames &&
482513 rows . length > 0 ) {
483-
484- // If user previously selected specific rows, filter them from new data
485- if ( prevState . selectedRowPKs . length > 0 && prevState . selectedRowPKs . length < rows . length ) {
486- const newSelectedPKs = rows
487- . filter ( row => prevState . selectedRowPKs . includes ( row . __temp_PK ) )
488- . map ( row => row . __temp_PK ) ;
489-
490- prevStateRef . current . selectedRowPKs = newSelectedPKs . length > 0 ? newSelectedPKs : rows . map ( r => r . __temp_PK ) ;
514+ let newSelectedRowData ;
515+ if ( prevState . selectedRowData . length === 0 ) {
516+ // No previous selection, show all rows
517+ newSelectedRowData = rows ;
518+ } else if ( prevState . selectedRowData . length < rows . length ) {
519+ const matched = matchRowSelection ( prevState . selectedRowData , rows , pkColumn , columns ) ;
520+ newSelectedRowData = matched . length > 0 ? matched : rows ;
491521 } else {
492- // All rows are displayed
493- const allPKs = rows . map ( r => r . __temp_PK ) ;
494- prevStateRef . current . selectedRowPKs = allPKs ;
522+ newSelectedRowData = rows ;
495523 }
524+ prevStateRef . current . selectedRowData = newSelectedRowData ;
496525 }
497- } , [ currentColumnKey , currentColumnNames , rows ] ) ;
526+ } , [ currentColumnKey , currentColumnNames , rows , pkColumn , columns ] ) ;
498527
528+ // Get rows to display based on selection
499529 const displayRows = React . useMemo ( ( ) => {
500530 if ( ! currentColumnKey || rows . length === 0 ) return [ ] ;
531+ const prevState = prevStateRef . current ;
532+ if ( currentColumnKey !== prevState . columnKey || currentColumnNames !== prevState . columnNames ) {
533+ return rows ;
534+ }
501535
502- const selectedPKs = prevStateRef . current . selectedRowPKs ;
503- return selectedPKs . length > 0 && selectedPKs . length < rows . length
504- ? rows . filter ( row => selectedPKs . includes ( row . __temp_PK ) )
505- : rows ;
506- } , [ rows , currentColumnKey ] ) ;
536+ const selected = prevState . selectedRowData ;
537+ return selected . length > 0 && selected . length < rows . length ? selected : rows ;
538+ } , [ rows , currentColumnKey , currentColumnNames ] ) ;
507539
508540 // Parse geometry data only when needed
509541 const data = React . useMemo ( ( ) => {
@@ -537,7 +569,7 @@ export function GeometryViewer({rows, columns, column}) {
537569 } ;
538570 } , [ queryToolCtx ] ) ;
539571
540- // Dyanmic CRS is not supported. Use srid and mapKey as key and recreate the map on change
572+ // Dynamic CRS is not supported. Use srid and mapKey as key and recreate the map on change
541573 return (
542574 < StyledBox ref = { contentRef } width = "100%" height = "100%" key = { `${ data . selectedSRID } -${ mapKey } ` } >
543575 < MapContainer
0 commit comments