| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import React, {
- useMemo, useState, useRef, useEffect, useCallback,
- } from 'react';
- import StickyEvents from 'sticky-events';
- import { debounce } from 'throttle-debounce';
- import loggerFactory from '~/utils/logger';
- import { useSidebarCollapsed } from '~/stores/ui';
- import GrowiContextualSubNavigation from './GrowiContextualSubNavigation';
- const logger = loggerFactory('growi:cli:GrowiSubNavigationSticky');
- /**
- * Subnavigation
- *
- * needs:
- * #grw-subnav-fixed-container element
- * #grw-subnav-sticky-trigger element
- *
- * @param {object} props
- */
- const GrowiSubNavigationSwitcher = (props) => {
- const { data: isSidebarCollapsed } = useSidebarCollapsed();
- const [isVisible, setVisible] = useState(false);
- const [width, setWidth] = useState(null);
- const fixedContainerRef = useRef();
- const stickyEvents = useMemo(() => new StickyEvents({ stickySelector: '#grw-subnav-sticky-trigger' }), []);
- const initWidth = useCallback(() => {
- const instance = fixedContainerRef.current;
- if (instance == null || instance.parentNode == null) {
- return;
- }
- // get parent width
- const { clientWidth } = instance.parentNode;
- // update style
- setWidth(clientWidth);
- }, []);
- const initVisible = useCallback(() => {
- const elements = stickyEvents.stickyElements;
- for (const elem of elements) {
- const bool = stickyEvents.isSticking(elem);
- if (bool) {
- setVisible(bool);
- break;
- }
- }
- }, [stickyEvents]);
- // setup effect by resizing event
- useEffect(() => {
- const resizeHandler = debounce(100, initWidth);
- window.addEventListener('resize', resizeHandler);
- // return clean up handler
- return () => {
- window.removeEventListener('resize', resizeHandler);
- };
- }, [initWidth]);
- const stickyChangeHandler = useCallback((event) => {
- logger.debug('StickyEvents.CHANGE detected');
- setVisible(event.detail.isSticky);
- }, []);
- // setup effect by sticky event
- useEffect(() => {
- // sticky
- // See: https://github.com/ryanwalters/sticky-events
- const { stickySelector } = stickyEvents;
- const elem = document.querySelector(stickySelector);
- elem.addEventListener(StickyEvents.CHANGE, stickyChangeHandler);
- // return clean up handler
- return () => {
- elem.removeEventListener(StickyEvents.CHANGE, stickyChangeHandler);
- };
- }, [stickyChangeHandler, stickyEvents]);
- // update width when sidebar collapsing changed
- useEffect(() => {
- if (isSidebarCollapsed != null) {
- setTimeout(initWidth, 300);
- }
- }, [isSidebarCollapsed, initWidth]);
- // initialize
- useEffect(() => {
- initWidth();
- // check sticky state several times
- setTimeout(initVisible, 100);
- setTimeout(initVisible, 300);
- setTimeout(initVisible, 2000);
- }, [initWidth, initVisible]);
- return (
- <div className={`grw-subnav-switcher ${isVisible ? '' : 'grw-subnav-switcher-hidden'}`}>
- <div id="grw-subnav-fixed-container" className="grw-subnav-fixed-container position-fixed" ref={fixedContainerRef} style={{ width }}>
- <GrowiContextualSubNavigation isCompactMode />
- </div>
- </div>
- );
- };
- GrowiSubNavigationSwitcher.propTypes = {
- };
- export default GrowiSubNavigationSwitcher;
|