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

package.src.components.TreeView.TreeViewRoot.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 styles from '@patternfly/react-styles/css/components/TreeView/tree-view';
import { canUseDOM } from '../../helpers/util';
import { handleArrows } from '../../helpers';
import { KeyTypes } from '../../helpers/constants';

export interface TreeViewRootProps {
  /** Child nodes of the tree view */
  children: React.ReactNode;
  /** Flag indicating if the tree view has checkboxes. */
  hasCheckboxes?: boolean;
  /** Flag indicating if tree view has guide lines. */
  hasGuides?: boolean;
  /** Variant presentation styles for the tree view. */
  variant?: 'default' | 'compact' | 'compactNoBackground';
  /** Class to add to add if not passed a parentItem */
  className?: string;
  /** Flag indicating that tree nodes should be independently selectable, even when having children */
  hasSelectableNodes?: boolean;
}

class TreeViewRoot extends React.Component {
  displayName = 'TreeViewRoot';
  private treeRef = React.createRef();

  componentDidMount() {
    if (canUseDOM) {
      window.addEventListener(
        'keydown',
        this.props.hasCheckboxes || this.props.hasSelectableNodes ? this.handleKeysCheckbox : this.handleKeys
      );
    }
    if (this.props.hasCheckboxes || this.props.hasSelectableNodes) {
      const firstToggle = this.treeRef.current.getElementsByClassName(styles.treeViewNodeToggle)[0] as HTMLElement;
      if (firstToggle) {
        firstToggle.tabIndex = 0;
      }
      if (this.props.hasCheckboxes) {
        const firstInput = this.treeRef.current.getElementsByTagName('INPUT')[0] as HTMLElement;
        if (firstInput) {
          firstInput.tabIndex = 0;
        }
      }
      if (this.props.hasSelectableNodes) {
        const firstTextButton = this.treeRef.current.getElementsByClassName(styles.treeViewNodeText)[0] as HTMLElement;
        if (firstTextButton) {
          firstTextButton.tabIndex = 0;
        }
      }
    } else {
      (this.treeRef.current?.getElementsByClassName(styles.treeViewNode)[0] as HTMLElement).tabIndex = 0;
    }
  }

  componentWillUnmount() {
    if (canUseDOM) {
      window.removeEventListener(
        'keydown',
        this.props.hasCheckboxes || this.props.hasSelectableNodes ? this.handleKeysCheckbox : this.handleKeys
      );
    }
  }

  handleKeys = (event: KeyboardEvent) => {
    if (
      !this.treeRef.current.contains(event.target as HTMLElement) ||
      !(event.target as HTMLElement).classList.contains(styles.treeViewNode)
    ) {
      return;
    }
    const activeElement = document.activeElement;
    const key = event.key;
    const treeItems = Array.from(this.treeRef.current?.getElementsByClassName(styles.treeViewNode)).filter(
      (el) => !el.classList.contains('pf-m-disabled')
    );

    if (key === KeyTypes.Space) {
      (activeElement as HTMLElement).click();
      event.preventDefault();
    }

    handleArrows(
      event,
      treeItems,
      (element: Element) => activeElement === element,
      undefined,
      [],
      undefined,
      true,
      true
    );

    if (['ArrowLeft', 'ArrowRight'].includes(key)) {
      const isExpandable = activeElement?.firstElementChild?.firstElementChild?.classList.contains(
        styles.treeViewNodeToggle
      );
      const isExpanded = activeElement?.closest('li')?.classList.contains('pf-m-expanded');
      if (key === 'ArrowLeft') {
        if (isExpandable && isExpanded) {
          (activeElement as HTMLElement).click();
        } else {
          const parentList = activeElement?.closest('ul')?.parentElement;
          if (parentList?.tagName !== 'DIV') {
            const parentButton = parentList?.querySelector('button');
            (activeElement as HTMLElement).tabIndex = -1;
            if (parentButton) {
              parentButton.tabIndex = 0;
              parentButton.focus();
            }
          }
        }
      } else {
        if (isExpandable && !isExpanded) {
          (activeElement as HTMLElement).tabIndex = -1;
          (activeElement as HTMLElement).click();
          const childElement = activeElement?.closest('li')?.querySelector('ul > li')?.querySelector('button');
          if (childElement) {
            childElement.tabIndex = 0;
            childElement.focus();
          }
        }
      }
      event.preventDefault();
    }
  };

  handleKeysCheckbox = (event: KeyboardEvent) => {
    if (!this.treeRef.current.contains(event.target as HTMLElement)) {
      return;
    }

    const activeElement = document.activeElement;
    const key = event.key;

    if (key === KeyTypes.Space) {
      (activeElement as HTMLElement).click();
      event.preventDefault();
    }

    const treeNodes = Array.from(this.treeRef.current?.getElementsByClassName(styles.treeViewNode));

    handleArrows(
      event,
      treeNodes as HTMLElement[],
      (element: Element) => element.contains(activeElement),
      (element: Element) => element.querySelector('button,input'),
      [],
      undefined,
      true,
      true
    );

    if (['ArrowLeft', 'ArrowRight'].includes(key)) {
      if (key === 'ArrowLeft') {
        if (activeElement?.tagName === 'INPUT') {
          activeElement?.parentElement?.previousSibling &&
            (activeElement.parentElement.previousSibling as HTMLElement).focus();
        } else if (activeElement?.previousSibling) {
          if (activeElement.previousElementSibling?.tagName === 'SPAN') {
            (activeElement.previousSibling.firstChild as HTMLElement).focus();
          } else {
            (activeElement.previousSibling as HTMLElement).focus();
          }
        }
      } else {
        if (activeElement?.tagName === 'INPUT') {
          activeElement.parentElement?.nextSibling && (activeElement.parentElement.nextSibling as HTMLElement).focus();
        } else if (activeElement?.nextSibling) {
          if (activeElement.nextElementSibling?.tagName === 'SPAN') {
            (activeElement.nextSibling.firstChild as HTMLElement).focus();
          } else {
            (activeElement.nextSibling as HTMLElement).focus();
          }
        }
      }
      event.preventDefault();
    }
  };

  variantStyleModifiers: { [key in TreeViewRootProps['variant']]: string | string[] } = {
    default: '',
    compact: styles.modifiers.compact,
    compactNoBackground: [styles.modifiers.compact, styles.modifiers.noBackground]
  };

  render() {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { children, hasCheckboxes, hasGuides, variant, className, hasSelectableNodes, ...props } = this.props;
    return (
      
{children}
); } } export { TreeViewRoot };




© 2015 - 2024 Weber Informatics LLC | Privacy Policy