All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.src.components.Pagination.PaginationOptionsMenu.tsx Maven / Gradle / Ivy

Go to download

This library provides a set of common React components for use with the PatternFly reference implementation.

The newest version!
import * as React from 'react';
import { css } from '@patternfly/react-styles';
import { Menu, MenuContent, MenuList, MenuItem } from '../Menu';
import { MenuToggle } from '../MenuToggle';
import { Popper } from '../../helpers/Popper/Popper';
import { PaginationToggleTemplateProps, ToggleTemplate } from './ToggleTemplate';
import { PerPageOptions, OnPerPageSelect } from './Pagination';
import { fillTemplate } from '../../helpers';

export interface PaginationOptionsMenuProps extends React.HTMLProps {
  /** Custom class name added to the pagination options menu. */
  className?: string;
  /** Id added to the title of the pagination options menu. */
  widgetId?: string;
  /** Flag indicating if pagination options menu is disabled. */
  isDisabled?: boolean;
  /** Menu will open up or open down from the options menu toggle. */
  dropDirection?: 'up' | 'down';
  /** Minimum width of the pagination options menu. If set to "trigger", the minimum width will be set to the toggle width. */
  minWidth?: string | 'trigger';
  /** Array of titles and values which will be the options on the options menu dropdown. */
  perPageOptions?: PerPageOptions[];
  /** The title of the pagination options menu. */
  itemsPerPageTitle?: string;
  /** Current page number. */
  page?: number;
  /** The suffix to be displayed after each option on the options menu dropdown. */
  perPageSuffix?: string;
  /** The type or title of the items being paginated. */
  itemsTitle?: string;
  /** Accessible label for the options toggle. */
  optionsToggleAriaLabel?: string;
  /** The total number of items being paginated. */
  itemCount?: number;
  /** The first index of the items being paginated. */
  firstIndex?: number;
  /** The last index of the items being paginated. */
  lastIndex?: number;
  /** Flag to indicate whether to show last full page of results when user selects perPage
   * value that is greater than remaining rows.
   */
  isLastFullPageShown?: boolean;
  /** The number of items to be displayed per page. */
  perPage?: number;
  /** The number of the last page. */
  lastPage?: number;
  /** This will be shown in pagination toggle span. You can use firstIndex, lastIndex,
   * itemCount, and/or itemsTitle props.
   */
  toggleTemplate: ((props: PaginationToggleTemplateProps) => React.ReactElement) | string;
  /** Function called when user selects number of items per page. */
  onPerPageSelect?: OnPerPageSelect;
  /** Label for the English word "of". */
  ofWord?: string;
}

export const PaginationOptionsMenu: React.FunctionComponent = ({
  className,
  widgetId,
  page: pageProp,
  itemCount,
  isDisabled = false,
  minWidth,
  dropDirection = 'down',
  perPageOptions = [] as PerPageOptions[],
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  itemsPerPageTitle = 'Items per page',
  perPageSuffix = 'per page',
  optionsToggleAriaLabel,
  ofWord = 'of',
  perPage = 0,
  firstIndex = 0,
  lastIndex = 0,
  isLastFullPageShown = false,
  itemsTitle = 'items',
  toggleTemplate,
  onPerPageSelect = () => null as any
}: PaginationOptionsMenuProps) => {
  const [isOpen, setIsOpen] = React.useState(false);
  const toggleRef = React.useRef(null);
  const menuRef = React.useRef(null);
  const containerRef = React.useRef(null);

  const onToggle = () => {
    setIsOpen((prevState) => !prevState);
  };

  const onSelect = () => {
    setIsOpen((prevState) => !prevState);
    toggleRef.current?.focus();
  };

  const handleNewPerPage = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPerPage: number) => {
    let newPage = pageProp;

    while (Math.ceil(itemCount / newPerPage) < newPage) {
      newPage--;
    }

    if (isLastFullPageShown) {
      if (itemCount / newPerPage !== newPage) {
        while (newPage > 1 && itemCount - newPerPage * newPage < 0) {
          newPage--;
        }
      }
    }
    const startIdx = (newPage - 1) * newPerPage;
    const endIdx = newPage * newPerPage;
    return onPerPageSelect(_evt, newPerPage, newPage, startIdx, endIdx);
  };

  React.useEffect(() => {
    const handleMenuKeys = (event: KeyboardEvent) => {
      // Close the menu on tab or escape
      if (
        (isOpen && menuRef.current?.contains(event.target as Node)) ||
        toggleRef.current?.contains(event.target as Node)
      ) {
        if (event.key === 'Escape' || event.key === 'Tab') {
          setIsOpen(false);
          toggleRef.current?.focus();
        }
      }
    };

    const handleClick = (event: MouseEvent) => {
      // If the event is on the toggle and was fired via keyboard 'click', focus the first
      // non-disabled menu item
      // https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
      if (event.detail === 0 && isOpen && toggleRef.current?.contains(event.target as Node)) {
        setTimeout(() => {
          const firstElement = menuRef?.current?.querySelector('li button:not(:disabled)');
          firstElement && (firstElement as HTMLElement).focus();
        }, 0);
      }

      // If the event is not on the toggle, close the menu
      if (
        isOpen &&
        !toggleRef?.current?.contains(event.target as Node) &&
        !menuRef.current?.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    };

    window.addEventListener('keydown', handleMenuKeys);
    window.addEventListener('click', handleClick);

    return () => {
      window.removeEventListener('keydown', handleMenuKeys);
      window.removeEventListener('click', handleClick);
    };
  }, [isOpen, menuRef]);

  const renderItems = () =>
    perPageOptions.map(({ value, title }) => (
       handleNewPerPage(event, value)}
      >
        {title}
        {` ${perPageSuffix}`}
      
    ));

  const toggle = (
    
      {toggleTemplate &&
        typeof toggleTemplate === 'string' &&
        fillTemplate(toggleTemplate, { firstIndex, lastIndex, ofWord, itemCount, itemsTitle })}
      {toggleTemplate &&
        typeof toggleTemplate !== 'string' &&
        (toggleTemplate as (props: PaginationToggleTemplateProps) => React.ReactElement)({
          firstIndex,
          lastIndex,
          ofWord,
          itemCount,
          itemsTitle
        })}
      {!toggleTemplate && (
        
      )}
    
  );

  const menu = (
    
      
        {renderItems()}
      
    
  );

  return (
    
); }; PaginationOptionsMenu.displayName = 'PaginationOptionsMenu';




© 2015 - 2024 Weber Informatics LLC | Privacy Policy