|
|
@@ -1,8 +1,7 @@
|
|
|
import React, {
|
|
|
- useMemo, useState, useRef, useEffect, useCallback,
|
|
|
+ useState, useRef, useEffect, useCallback,
|
|
|
} from 'react';
|
|
|
|
|
|
-import PropTypes from 'prop-types';
|
|
|
import StickyEvents from 'sticky-events';
|
|
|
import { debounce } from 'throttle-debounce';
|
|
|
|
|
|
@@ -10,87 +9,49 @@ import { useSWRxCurrentPage } from '~/stores/page';
|
|
|
import { useSidebarCollapsed } from '~/stores/ui';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
-
|
|
|
import GrowiContextualSubNavigation from './GrowiContextualSubNavigation';
|
|
|
|
|
|
import styles from './GrowiSubNavigationSwitcher.module.scss';
|
|
|
|
|
|
const logger = loggerFactory('growi:cli:GrowiSubNavigationSticky');
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * Subnavigation
|
|
|
+ * GrowiSubNavigation
|
|
|
*
|
|
|
* needs:
|
|
|
* #grw-subnav-fixed-container element
|
|
|
* #grw-subnav-sticky-trigger element
|
|
|
- *
|
|
|
- * @param {object} props
|
|
|
*/
|
|
|
-const GrowiSubNavigationSwitcher = (props) => {
|
|
|
+export const GrowiSubNavigationSwitcher = (): JSX.Element => {
|
|
|
|
|
|
const { data: currentPage } = useSWRxCurrentPage();
|
|
|
const { data: isSidebarCollapsed } = useSidebarCollapsed();
|
|
|
|
|
|
- const [isVisible, setVisible] = useState(false);
|
|
|
- const [width, setWidth] = useState(null);
|
|
|
+ const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
|
+ const [width, setWidth] = useState<number>(0);
|
|
|
|
|
|
- const fixedContainerRef = useRef();
|
|
|
- /*
|
|
|
- * Comment out to prevent err >>> TypeError: Cannot read properties of null (reading 'bottom')
|
|
|
- * The above err occurs when moving to admin page after rendering normal pages.
|
|
|
- * This is because id "grw-subnav-sticky-trigger" does not exist on admin pages.
|
|
|
- */
|
|
|
- const stickyEvents = useMemo(() => new StickyEvents({ stickySelector: '#grw-subnav-sticky-trigger' }), []);
|
|
|
+ // use more specific type HTMLDivElement for avoid assertion error.
|
|
|
+ // see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement
|
|
|
+ const fixedContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
const initWidth = useCallback(() => {
|
|
|
- const instance = fixedContainerRef.current;
|
|
|
-
|
|
|
- if (instance == null || instance.parentNode == null) {
|
|
|
- return;
|
|
|
+ if (fixedContainerRef.current && fixedContainerRef.current.parentElement) {
|
|
|
+ // get parent elements width
|
|
|
+ const { clientWidth } = fixedContainerRef.current.parentElement;
|
|
|
+ setWidth(clientWidth);
|
|
|
}
|
|
|
-
|
|
|
- // 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);
|
|
|
+ setIsVisible(event.detail.isSticky);
|
|
|
}, []);
|
|
|
|
|
|
- // setup effect by sticky event
|
|
|
+ // setup effect by sticky-events
|
|
|
useEffect(() => {
|
|
|
- // sticky
|
|
|
+ // sticky-events
|
|
|
// See: https://github.com/ryanwalters/sticky-events
|
|
|
- const { stickySelector } = stickyEvents;
|
|
|
+ const { stickySelector } = new StickyEvents({ stickySelector: '#grw-subnav-sticky-trigger' });
|
|
|
const elem = document.querySelector(stickySelector);
|
|
|
elem.addEventListener(StickyEvents.CHANGE, stickyChangeHandler);
|
|
|
|
|
|
@@ -98,7 +59,18 @@ const GrowiSubNavigationSwitcher = (props) => {
|
|
|
return () => {
|
|
|
elem.removeEventListener(StickyEvents.CHANGE, stickyChangeHandler);
|
|
|
};
|
|
|
- }, [stickyChangeHandler, stickyEvents]);
|
|
|
+ }, [stickyChangeHandler]);
|
|
|
+
|
|
|
+ // 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]);
|
|
|
|
|
|
// update width when sidebar collapsing changed
|
|
|
useEffect(() => {
|
|
|
@@ -107,16 +79,14 @@ const GrowiSubNavigationSwitcher = (props) => {
|
|
|
}
|
|
|
}, [isSidebarCollapsed, initWidth]);
|
|
|
|
|
|
- // initialize
|
|
|
+ // initialize width
|
|
|
useEffect(() => {
|
|
|
initWidth();
|
|
|
+ }, [initWidth]);
|
|
|
|
|
|
- // check sticky state several times
|
|
|
- setTimeout(initVisible, 100);
|
|
|
- setTimeout(initVisible, 300);
|
|
|
- setTimeout(initVisible, 2000);
|
|
|
-
|
|
|
- }, [initWidth, initVisible]);
|
|
|
+ if (currentPage == null) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
|
|
|
return (
|
|
|
<div className={`${styles['grw-subnav-switcher']} ${isVisible ? '' : 'grw-subnav-switcher-hidden'}`}>
|
|
|
@@ -131,9 +101,3 @@ const GrowiSubNavigationSwitcher = (props) => {
|
|
|
</div>
|
|
|
);
|
|
|
};
|
|
|
-
|
|
|
-GrowiSubNavigationSwitcher.propTypes = {
|
|
|
- isLinkSharingDisabled: PropTypes.bool,
|
|
|
-};
|
|
|
-
|
|
|
-export default GrowiSubNavigationSwitcher;
|