@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
89 lines • 4.62 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { useEffect, useState } from 'react';
import { getLogicalBoundingClientRect } from '@awsui/component-toolkit/internal';
import globalVars from '../../internal/styles/global-vars';
import { browserScrollbarSize } from '../../internal/utils/browser-scrollbar-size';
import { getContainingBlock } from '../../internal/utils/dom';
import { getOverflowParents } from '../../internal/utils/scrollable-containers';
import styles from './styles.css.js';
export const updatePosition = (tableEl, wrapperEl, scrollbarEl, scrollbarContentEl, inScrollableContainer) => {
if (!tableEl || !scrollbarEl || !wrapperEl) {
return;
}
const { inlineSize: tableInlineSize } = getLogicalBoundingClientRect(tableEl);
const { inlineSize: wrapperInlineSize } = getLogicalBoundingClientRect(wrapperEl);
// using 15 px as a height of transparent scrollbar on mac
const scrollbarHeight = browserScrollbarSize().height;
const areaIsScrollable = tableInlineSize > wrapperInlineSize;
if (!areaIsScrollable) {
scrollbarEl.classList.remove(styles['sticky-scrollbar-visible']);
}
else {
// when scrollbar is not displayed scrollLeft property cannot be set by useScrollSync
// that's why syncing it separately
if (!scrollbarEl.classList.contains(styles['sticky-scrollbar-visible'])) {
requestAnimationFrame(() => {
scrollbarEl.scrollLeft = wrapperEl.scrollLeft;
});
}
scrollbarEl.classList.add(styles['sticky-scrollbar-visible']);
if (!scrollbarHeight) {
/* istanbul ignore next: covered by screenshot tests */
scrollbarEl.classList.add(styles['sticky-scrollbar-native-invisible']);
}
}
if (scrollbarHeight && scrollbarEl && scrollbarContentEl) {
scrollbarEl.style.blockSize = `${scrollbarHeight}px`;
scrollbarContentEl.style.blockSize = `${scrollbarHeight}px`;
}
if (tableEl && wrapperEl && scrollbarContentEl && scrollbarEl) {
const wrapperElRect = getLogicalBoundingClientRect(wrapperEl);
const tableElRect = getLogicalBoundingClientRect(tableEl);
scrollbarEl.style.inlineSize = `${wrapperElRect.inlineSize}px`;
scrollbarContentEl.style.inlineSize = `${tableElRect.inlineSize}px`;
// when using sticky scrollbars in containers
// we agreed to ignore dynamic bottom calculations for footer overlap
scrollbarEl.style.insetBlockEnd = inScrollableContainer
? '0px'
: `var(${globalVars.stickyVerticalBottomOffset}, 0px)`;
}
};
export function useStickyScrollbar(scrollbarRef, scrollbarContentRef, tableRef, wrapperRef, offsetScrollbar) {
const [inScrollableContainer, setInScrollableContainer] = useState(false);
const wrapperEl = wrapperRef.current;
useEffect(() => {
if (wrapperEl) {
setInScrollableContainer(!!getContainingBlock(wrapperEl) || !!getOverflowParents(wrapperEl)[0]);
}
}, [wrapperEl]);
// Update scrollbar position wrapper or table size change.
useEffect(() => {
if (wrapperRef.current && tableRef.current && typeof ResizeObserver !== 'undefined') {
const observer = new ResizeObserver(() => {
if (scrollbarContentRef.current) {
updatePosition(tableRef.current, wrapperRef.current, scrollbarRef.current, scrollbarContentRef.current, inScrollableContainer);
}
});
// Scrollbar width must be in sync with wrapper width.
observer.observe(wrapperRef.current);
// Scrollbar content width must be in sync with table width.
observer.observe(tableRef.current);
return () => {
observer.disconnect();
};
}
}, [scrollbarContentRef, scrollbarRef, tableRef, wrapperRef, inScrollableContainer, offsetScrollbar]);
// Update scrollbar position when window resizes (vertically).
useEffect(() => {
const resizeHandler = () => {
updatePosition(tableRef.current, wrapperRef.current, scrollbarRef.current, scrollbarContentRef.current, inScrollableContainer);
};
resizeHandler();
window.addEventListener('resize', resizeHandler);
return () => {
window.removeEventListener('resize', resizeHandler);
};
}, [tableRef, wrapperRef, scrollbarRef, scrollbarContentRef, inScrollableContainer]);
}
//# sourceMappingURL=use-sticky-scrollbar.js.map