Skip to content

Commit

Permalink
Added sticky scrollbar and intersection API to recordset tables (#2564)
Browse files Browse the repository at this point in the history
  • Loading branch information
KenyShah24 authored Nov 5, 2024
1 parent 93f3f95 commit 8f612ca
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 18 deletions.
9 changes: 9 additions & 0 deletions src/assets/scss/_recordset-table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions src/components/record/record-main-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const RecordMainSection = (): JSX.Element => {
<RelatedTable
relatedModel={cm.relatedModel}
displaynameForID={cm.column.displayname.value}
showSingleScrollbar={true}
/>
</div>
</span>
Expand Down
1 change: 1 addition & 0 deletions src/components/record/record.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ const RecordInner = ({
<RelatedTable
relatedModel={rm}
displaynameForID={rm.initialReference.displayname.value}
showSingleScrollbar={true}
/>
</Accordion.Body>
</Accordion.Item>
Expand Down
17 changes: 11 additions & 6 deletions src/components/record/related-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
};

/**
Expand All @@ -44,19 +46,21 @@ type RelatedTableProps = {
const RelatedTable = ({
relatedModel,
displaynameForID,
showSingleScrollbar
}: RelatedTableProps): JSX.Element => {
return (
<RecordsetProvider
initialReference={relatedModel.initialReference}
{...relatedModel.recordsetProps}
>
<RelatedTableInner relatedModel={relatedModel} displaynameForID={displaynameForID} />
<RelatedTableInner relatedModel={relatedModel} displaynameForID={displaynameForID} showSingleScrollbar={showSingleScrollbar}/>
</RecordsetProvider>
)
}
const RelatedTableInner = ({
relatedModel,
displaynameForID
displaynameForID,
showSingleScrollbar
}: RelatedTableProps) => {
const {
page, isInitialized, hasTimeoutError, isLoading,
Expand Down Expand Up @@ -111,6 +115,7 @@ const RelatedTableInner = ({
<RecordsetTable
config={relatedModel.recordsetProps.config}
initialSortObject={usedRef.location.sortObject}
showSingleScrollbar={showSingleScrollbar}
/>
</div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions src/components/recordedit/recordedit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,9 @@ const RecordeditInner = ({
</p>
</div>
}
<ResultsetTable page={resultsetProps.success.page} />
{/* 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 */}
<ResultsetTable page={resultsetProps.success.page} showSingleScrollbar={!!resultsetProps.failed}/>
</Accordion.Body>
</Accordion.Item>
{resultsetProps.failed &&
Expand All @@ -815,7 +817,7 @@ const RecordeditInner = ({
exploreLink={resultsetProps.failed.exploreLink}
/>
</Accordion.Button>
<Accordion.Body><ResultsetTable page={resultsetProps.failed.page} /></Accordion.Body>
<Accordion.Body><ResultsetTable page={resultsetProps.failed.page} showSingleScrollbar={!!resultsetProps.failed}/></Accordion.Body>
</Accordion.Item>
}
</Accordion>
Expand Down
17 changes: 12 additions & 5 deletions src/components/recordedit/resultset-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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)];
Expand Down Expand Up @@ -47,21 +52,23 @@ const ResultsetTable = ({
}}
initialPage={page}
>
<ResultsetTableInner config={config} reference={page.reference} />
<ResultsetTableInner config={config} reference={page.reference} showSingleScrollbar={showSingleScrollbar}/>
</RecordsetProvider>
)
}

const ResultsetTableInner = ({
reference,
config
config,
showSingleScrollbar,
}: {
config: RecordsetConfig
reference: any
showSingleScrollbar: boolean,
}) : JSX.Element => {
return (
<div>
<RecordsetTable config={config} initialSortObject={reference.location.sortObject} />
<RecordsetTable config={config} initialSortObject={reference.location.sortObject} showSingleScrollbar={showSingleScrollbar}/>
</div>
)
}
Expand Down
55 changes: 50 additions & 5 deletions src/components/recordset/recordset-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -53,6 +58,9 @@ const RecordsetTable = ({
} = useRecordset();

const tableContainer = useRef<HTMLDivElement>(null);
const stickyScrollbarRef = useRef<HTMLDivElement>(null);
const tableEndRef = useRef<HTMLDivElement>(null);


const [currSortColumn, setCurrSortColumn] = useState<SortColumn | null>(
Array.isArray(initialSortObject) ? initialSortObject[0] : null
Expand Down Expand Up @@ -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
Expand All @@ -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
*/
Expand Down Expand Up @@ -472,10 +513,10 @@ const RecordsetTable = ({
const tableSchemaNames = `s_${makeSafeIdAttr(reference.table.schema.name)} t_${makeSafeIdAttr(reference.table.name)}`;
return classNameString + ' ' + tableSchemaNames;
}

return (
<div className='recordset-table-container' ref={tableContainer}>
<div className='chaise-table-top-scroll-wrapper'>
<div ref={stickyScrollbarRef}
className='chaise-table-top-scroll-wrapper'>
<div className='chaise-table-top-scroll'></div>
</div>
<div className={outerTableClassname()}>
Expand All @@ -491,6 +532,10 @@ const RecordsetTable = ({
</tbody>
</table>
</div>
{/* 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 */}
<div className='dummy-table-end-div' ref={tableEndRef}/>

{!hasTimeoutError && numHiddenRecords > 0 &&
<div className='chaise-table-footer'>
<button onClick={() => setShowAllRows(!showAllRows)} className='show-all-rows-btn chaise-btn chaise-btn-primary'>
Expand Down

0 comments on commit 8f612ca

Please sign in to comment.