diff --git a/src/assets/scss/_recordset-table.scss b/src/assets/scss/_recordset-table.scss index 7c9038931..9c7a86047 100644 --- a/src/assets/scss/_recordset-table.scss +++ b/src/assets/scss/_recordset-table.scss @@ -253,3 +253,12 @@ margin-left: 5px; } } + +.chaise-table-top-scroll-wrapper { + position: sticky; + top: 0; +} +.no-scroll-bar { + display: none !important; + position: absolute !important; +} diff --git a/src/components/record/record-main-section.tsx b/src/components/record/record-main-section.tsx index d871d98dd..0f9fb42d6 100644 --- a/src/components/record/record-main-section.tsx +++ b/src/components/record/record-main-section.tsx @@ -128,6 +128,7 @@ const RecordMainSection = (): JSX.Element => { diff --git a/src/components/record/record.tsx b/src/components/record/record.tsx index bf9414eb7..ae5fc58c3 100644 --- a/src/components/record/record.tsx +++ b/src/components/record/record.tsx @@ -684,6 +684,7 @@ const RecordInner = ({ diff --git a/src/components/record/related-table.tsx b/src/components/record/related-table.tsx index fa1f4c9c3..36b0e3053 100644 --- a/src/components/record/related-table.tsx +++ b/src/components/record/related-table.tsx @@ -17,14 +17,12 @@ import { RecordRelatedModel } from '@isrd-isi-edu/chaise/src/models/record'; // providers import RecordsetProvider from '@isrd-isi-edu/chaise/src/providers/recordset'; -// services -import $log from '@isrd-isi-edu/chaise/src/services/logger'; - // utils import { CLASS_NAMES } from '@isrd-isi-edu/chaise/src/utils/constants'; -import { determineScrollElement, displayCustomModeRelated } from '@isrd-isi-edu/chaise/src/utils/record-utils'; +import { displayCustomModeRelated } from '@isrd-isi-edu/chaise/src/utils/record-utils'; import { makeSafeIdAttr } from '@isrd-isi-edu/chaise/src/utils/string-utils'; + type RelatedTableProps = { /** * the related model that we want to represent @@ -34,6 +32,10 @@ type RelatedTableProps = { * the displayname for the reference to be used in the id attached to the container */ displaynameForID: string + /** + * Determines if both horizontal scrollbars should always be visible, or if only one should appear at a time. + */ + showSingleScrollbar?: boolean, }; /** @@ -44,19 +46,21 @@ type RelatedTableProps = { const RelatedTable = ({ relatedModel, displaynameForID, + showSingleScrollbar }: RelatedTableProps): JSX.Element => { return ( - + ) } const RelatedTableInner = ({ relatedModel, - displaynameForID + displaynameForID, + showSingleScrollbar }: RelatedTableProps) => { const { page, isInitialized, hasTimeoutError, isLoading, @@ -111,6 +115,7 @@ const RelatedTableInner = ({ diff --git a/src/components/recordedit/recordedit.tsx b/src/components/recordedit/recordedit.tsx index 0542c5a70..8bafbb89a 100644 --- a/src/components/recordedit/recordedit.tsx +++ b/src/components/recordedit/recordedit.tsx @@ -804,7 +804,9 @@ const RecordeditInner = ({

} - + {/* Intersecting behaviour of scroll should be visible if there are multiple tables + on one page which here seems to be the case when there are successful as well as failed records */} + {resultsetProps.failed && @@ -815,7 +817,7 @@ const RecordeditInner = ({ exploreLink={resultsetProps.failed.exploreLink} /> - + } diff --git a/src/components/recordedit/resultset-table.tsx b/src/components/recordedit/resultset-table.tsx index be3a32330..512b6dbfd 100644 --- a/src/components/recordedit/resultset-table.tsx +++ b/src/components/recordedit/resultset-table.tsx @@ -4,7 +4,7 @@ import RecordsetTable from '@isrd-isi-edu/chaise/src/components/recordset/record // models import { RecordsetConfig } from '@isrd-isi-edu/chaise/src/models/recordset' import { RecordsetDisplayMode, RecordsetSelectMode } from '@isrd-isi-edu/chaise/src/models/recordset'; -import { LogActions, LogReloadCauses, LogStackPaths, LogStackTypes } from '@isrd-isi-edu/chaise/src/models/log'; +import { LogStackPaths } from '@isrd-isi-edu/chaise/src/models/log'; // providers import RecordsetProvider from '@isrd-isi-edu/chaise/src/providers/recordset'; @@ -13,11 +13,16 @@ import RecordsetProvider from '@isrd-isi-edu/chaise/src/providers/recordset'; import { LogService } from '@isrd-isi-edu/chaise/src/services/log'; type ResultsetTableProps = { - page: any + page: any, + /** + * Determines if both horizontal scrollbars should always be visible, or if only one should appear at a time. + */ + showSingleScrollbar: boolean, } const ResultsetTable = ({ page, + showSingleScrollbar, }: ResultsetTableProps) : JSX.Element => { const logStack = [LogService.getStackNode(LogStackPaths.SET, page.reference.table, page.reference.filterInfo)]; @@ -47,21 +52,23 @@ const ResultsetTable = ({ }} initialPage={page} > - + ) } const ResultsetTableInner = ({ reference, - config + config, + showSingleScrollbar, }: { config: RecordsetConfig reference: any + showSingleScrollbar: boolean, }) : JSX.Element => { return (
- +
) } diff --git a/src/components/recordset/recordset-table.tsx b/src/components/recordset/recordset-table.tsx index 44e7d595e..9f5174904 100644 --- a/src/components/recordset/recordset-table.tsx +++ b/src/components/recordset/recordset-table.tsx @@ -29,12 +29,17 @@ import { addTopHorizontalScroll, fireCustomEvent } from '@isrd-isi-edu/chaise/sr type RecordsetTableProps = { config: RecordsetConfig, initialSortObject: any, + /** + * Determines if both horizontal scrollbars should always be visible, or if only one should appear at a time. + */ + showSingleScrollbar?: boolean, sortCallback?: (sortColumn: SortColumn) => any } const RecordsetTable = ({ config, - initialSortObject + initialSortObject, + showSingleScrollbar = false }: RecordsetTableProps): JSX.Element => { const { @@ -53,6 +58,9 @@ const RecordsetTable = ({ } = useRecordset(); const tableContainer = useRef(null); + const stickyScrollbarRef = useRef(null); + const tableEndRef = useRef(null); + const [currSortColumn, setCurrSortColumn] = useState( Array.isArray(initialSortObject) ? initialSortObject[0] : null @@ -90,7 +98,7 @@ const RecordsetTable = ({ isDisabled: false, disabledType: undefined }; - // page.tuples.forEach((tuple: any, index: number) => { + // page.tuples.forEach((tuple: any, index: number) => { if (hasSelectedRows) { const row = selectedRows.find((obj: SelectedRow) => { // ermrestjs always returns a string for uniqueId, but internally we don't @@ -115,12 +123,45 @@ const RecordsetTable = ({ } tempRowDetails[i] = rowConfig; - // }); + // }); } rowDetails = tempRowDetails; } + useEffect(() => { + //Only implement intersection observer for top scrollbar when showSingleScrollbar is true otherwise top scrollbar will be shown as sticky + if (!showSingleScrollbar) return; + + // Create a new IntersectionObserver instance to track the visibility of the bottom scrollbar(end of table) + const observer = new IntersectionObserver( + ([entry]) => { + //Updating isBottomVisible when bottom scrollbar is visible in the viewport + if (stickyScrollbarRef.current) { + if (entry.isIntersecting) { + stickyScrollbarRef.current.classList.add('no-scroll-bar'); + } + else { + stickyScrollbarRef.current.classList.remove('no-scroll-bar'); + } + } + }, + { + root: null, // Use viewport as the root + threshold: 0.1, // Triggers when 10% of the element is visible + } + ); + //Observes when the table end is visible on viewport + if (tableEndRef.current) { + observer.observe(tableEndRef.current); + } + + return () => { + observer.disconnect(); + } + }, []); + + /** * add the top horizontal scroll if needed */ @@ -472,10 +513,10 @@ const RecordsetTable = ({ const tableSchemaNames = `s_${makeSafeIdAttr(reference.table.schema.name)} t_${makeSafeIdAttr(reference.table.name)}`; return classNameString + ' ' + tableSchemaNames; } - return (
-
+
@@ -491,6 +532,10 @@ const RecordsetTable = ({
+ {/* This div will be used as the target (end of table) for the intersection observer to hide the + top scrollbar when the bottom one is visible */} +
+ {!hasTimeoutError && numHiddenRecords > 0 &&