dgate 2.1.0

DGate API Gateway - High-performance API gateway with JavaScript module support
Documentation
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, {
  useState,
  useContext,
  useEffect,
  useMemo,
  useCallback,
  type ReactNode,
} from 'react';
import {ReactContextError, usePrevious} from '../../utils/reactUtils';
import {useNavbarMobileSidebar} from '../navbarMobileSidebar';
import {useNavbarSecondaryMenuContent, type Content} from './content';

type ContextValue = [
  shown: boolean,
  setShown: React.Dispatch<React.SetStateAction<boolean>>,
];

const Context = React.createContext<ContextValue | null>(null);

function useContextValue(): ContextValue {
  const mobileSidebar = useNavbarMobileSidebar();
  const content = useNavbarSecondaryMenuContent();

  const [shown, setShown] = useState(false);

  const hasContent = content.component !== null;
  const previousHasContent = usePrevious(hasContent);

  // When content is become available for the first time (set in useEffect)
  // we set this content to be shown!
  useEffect(() => {
    const contentBecameAvailable = hasContent && !previousHasContent;
    if (contentBecameAvailable) {
      setShown(true);
    }
  }, [hasContent, previousHasContent]);

  // On sidebar close, secondary menu is set to be shown on next re-opening
  // (if any secondary menu content available)
  useEffect(() => {
    if (!hasContent) {
      setShown(false);
      return;
    }
    if (!mobileSidebar.shown) {
      setShown(true);
    }
  }, [mobileSidebar.shown, hasContent]);

  return useMemo(() => [shown, setShown], [shown]);
}

/** @internal */
export function NavbarSecondaryMenuDisplayProvider({
  children,
}: {
  children: ReactNode;
}): JSX.Element {
  const value = useContextValue();
  return <Context.Provider value={value}>{children}</Context.Provider>;
}

function renderElement(content: Content): JSX.Element | undefined {
  if (content.component) {
    const Comp = content.component;
    return <Comp {...content.props} />;
  }
  return undefined;
}

/** Wires the logic for rendering the mobile navbar secondary menu. */
export function useNavbarSecondaryMenu(): {
  /** Whether secondary menu is displayed. */
  shown: boolean;
  /**
   * Hide the secondary menu; fired either when hiding the entire sidebar, or
   * when going back to the primary menu.
   */
  hide: () => void;
  /** The content returned from the current secondary menu filler. */
  content: JSX.Element | undefined;
} {
  const value = useContext(Context);
  if (!value) {
    throw new ReactContextError('NavbarSecondaryMenuDisplayProvider');
  }
  const [shown, setShown] = value;
  const hide = useCallback(() => setShown(false), [setShown]);
  const content = useNavbarSecondaryMenuContent();

  return useMemo(
    () => ({shown, hide, content: renderElement(content)}),
    [hide, content, shown],
  );
}