@syncfusion/react-navigations
Version:
Syncfusion React Navigations with Toolbar and Context Menu for seamless page navigation
127 lines (126 loc) • 5.57 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { useRef, useState, memo, useMemo, useCallback, forwardRef, useImperativeHandle, useEffect, Children } from 'react';
import { isVisible, isNullOrUndefined } from '@syncfusion/react-base';
import { Orientation } from './toolbar';
import { HScroll, VScroll } from '../common/index';
const CLS_ITEMS = 'sf-toolbar-items';
const CLS_HSCROLLBAR = 'sf-hscroll-bar';
const CLS_VSCROLLBAR = 'sf-vscroll-bar';
const CLS_HSCROLLCNT = 'sf-hscroll-content';
const CLS_VSCROLLCNT = 'sf-vscroll-content';
/**
* ToolbarScrollable component that renders toolbar items with scrolling capability.
*
* This component manages the display of toolbar items that don't fit in the available space
* by providing horizontal or vertical scrolling functionality. It automatically detects
* when content overflows and renders appropriate scroll controls based on the toolbar's
* orientation.
*/
const ToolbarScrollable = memo(forwardRef((props, ref) => {
const { toolbarRef, orientation, scrollStep, children, className, onOverflowChange } = props;
const resizeObserverRef = useRef(null);
const previousChildrenCountRef = useRef(null);
const resizeTimerRef = useRef(null);
const [isOverflow, setIsOverflow] = useState(false);
const [activeScrollStep, setActiveScrollStep] = useState(scrollStep);
const getScrollCntEle = useCallback((itemsElement) => {
return itemsElement.querySelector('.' + (orientation === Orientation.Vertical ? CLS_VSCROLLCNT : CLS_HSCROLLCNT));
}, [orientation]);
const checkOverflow = useCallback(() => {
let itemsOverflowing = false;
if (toolbarRef.current && isVisible(toolbarRef.current)) {
let itemsElement = toolbarRef.current.querySelector(`.${CLS_ITEMS}`);
itemsElement = isOverflow ? getScrollCntEle(itemsElement) : itemsElement;
const isVertical = orientation === Orientation.Vertical;
const toolbarWidth = isVertical ? toolbarRef.current.offsetHeight : toolbarRef.current.offsetWidth;
const itemsWidth = isVertical ? itemsElement.scrollHeight : itemsElement.scrollWidth;
if (itemsWidth > toolbarWidth) {
itemsOverflowing = true;
}
else {
itemsOverflowing = false;
}
}
return itemsOverflowing;
}, [isOverflow, orientation, getScrollCntEle]);
const resize = useCallback((updateScrollStep = true) => {
if (toolbarRef.current) {
if (resizeTimerRef.current) {
clearTimeout(resizeTimerRef.current);
}
const isOverflowing = checkOverflow();
if (isOverflowing !== isOverflow) {
setIsOverflow(isOverflowing);
}
if (isOverflowing && updateScrollStep) {
resizeTimerRef.current = window.setTimeout(() => {
if (toolbarRef.current) {
const selector = orientation === Orientation.Vertical ? `.${CLS_VSCROLLBAR}` : `.${CLS_HSCROLLBAR}`;
const scrollBar = toolbarRef.current.querySelector(selector);
if (scrollBar) {
setActiveScrollStep(orientation === Orientation.Vertical ? scrollBar.offsetHeight : scrollBar.offsetWidth);
}
}
}, 500);
}
}
}, [checkOverflow, orientation, isOverflow]);
const resizeRef = useRef(resize);
useEffect(() => {
resizeRef.current = resize;
}, [resize]);
const refreshOverflow = useCallback(() => {
resizeRef.current();
}, []);
useEffect(() => {
if (toolbarRef.current) {
let isFirstObservation = true;
resizeObserverRef.current = new ResizeObserver(() => {
if (isFirstObservation) {
isFirstObservation = false;
return;
}
resizeRef.current();
});
resizeObserverRef.current.observe(toolbarRef.current);
}
return () => {
resizeObserverRef.current?.disconnect();
resizeObserverRef.current = null;
};
}, []);
useEffect(() => {
const currentCount = Children.count(children);
if (isNullOrUndefined(previousChildrenCountRef.current) || previousChildrenCountRef.current !== currentCount) {
previousChildrenCountRef.current = Children.count(children);
onOverflowChange();
resizeRef.current(false);
}
}, [children, onOverflowChange]);
useEffect(() => {
onOverflowChange();
}, [isOverflow, onOverflowChange]);
useImperativeHandle(ref, () => ({
refreshOverflow
}), [refreshOverflow]);
const classes = useMemo(() => {
const classArray = [CLS_ITEMS];
if (className) {
classArray.push(className);
}
return classArray.join(' ');
}, [className]);
if (isOverflow) {
if (orientation === Orientation.Horizontal) {
return (_jsx(HScroll, { scrollStep: activeScrollStep, className: classes, children: children }));
}
else {
return (_jsx(VScroll, { scrollStep: activeScrollStep, className: classes, children: children }));
}
}
else {
return _jsx("div", { className: classes, children: children });
}
}));
ToolbarScrollable.displayName = 'ToolbarScrollable';
export { ToolbarScrollable };