Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/client/shared/components/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 9 kB image not shown  

Quelle  List.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */


"use strict";

const {
  createFactory,
  createRef,
  Component,
  cloneElement,
} = require("resource://devtools/client/shared/vendor/react.js");
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
const {
  ul,
  li,
  div,
} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");

const {
  scrollIntoView,
} = require("resource://devtools/client/shared/scroll.js");
const {
  preventDefaultAndStopPropagation,
} = require("resource://devtools/client/shared/events.js");

loader.lazyRequireGetter(
  this,
  ["getFocusableElements""wrapMoveFocus"],
  "resource://devtools/client/shared/focus.js",
  true
);

class ListItemClass extends Component {
  static get propTypes() {
    return {
      active: PropTypes.bool,
      current: PropTypes.bool,
      onClick: PropTypes.func,
      item: PropTypes.shape({
        key: PropTypes.string,
        component: PropTypes.object,
        componentProps: PropTypes.object,
        className: PropTypes.string,
      }).isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.contentRef = createRef();

    this._setTabbableState = this._setTabbableState.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
  }

  componentDidMount() {
    this._setTabbableState();
  }

  componentDidUpdate() {
    this._setTabbableState();
  }

  _onKeyDown(event) {
    const { target, key, shiftKey } = event;

    if (key !== "Tab") {
      return;
    }

    const focusMoved = !!wrapMoveFocus(
      getFocusableElements(this.contentRef.current),
      target,
      shiftKey
    );
    if (focusMoved) {
      // Focus was moved to the begining/end of the list, so we need to prevent the
      // default focus change that would happen here.
      event.preventDefault();
    }

    event.stopPropagation();
  }

  /**
   * Makes sure that none of the focusable elements inside the list item container are
   * tabbable if the list item is not active. If the list item is active and focus is
   * outside its container, focus on the first focusable element inside.
   */

  _setTabbableState() {
    const elms = getFocusableElements(this.contentRef.current);
    if (elms.length === 0) {
      return;
    }

    if (!this.props.active) {
      elms.forEach(elm => elm.setAttribute("tabindex""-1"));
      return;
    }

    if (!elms.includes(document.activeElement)) {
      elms[0].focus();
    }
  }

  render() {
    const { active, item, current, onClick } = this.props;
    const { className, component, componentProps } = item;

    return li(
      {
        className: `${className}${current ? " current" : ""}${
          active ? " active" : ""
        }`,
        id: item.key,
        onClick,
        onKeyDownCapture: active ? this._onKeyDown : null,
      },
      div(
        {
          className: "list-item-content",
          role: "presentation",
          ref: this.contentRef,
        },
        cloneElement(component, componentProps || {})
      )
    );
  }
}

const ListItem = createFactory(ListItemClass);

class List extends Component {
  static get propTypes() {
    return {
      // A list of all items to be rendered using a List component.
      items: PropTypes.arrayOf(
        PropTypes.shape({
          component: PropTypes.object,
          componentProps: PropTypes.object,
          className: PropTypes.string,
          key: PropTypes.string.isRequired,
        })
      ).isRequired,

      // Note: the two properties below are mutually exclusive. Only one of the
      // label properties is necessary.
      // ID of an element whose textual content serves as an accessible label for
      // a list.
      labelledBy: PropTypes.string,

      // Accessibility label for a list widget.
      label: PropTypes.string,
    };
  }

  constructor(props) {
    super(props);

    this.listRef = createRef();

    this.state = {
      active: null,
      current: null,
      mouseDown: false,
    };

    this._setCurrentItem = this._setCurrentItem.bind(this);
    this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const { active, current, mouseDown } = this.state;

    return (
      current !== nextState.current ||
      active !== nextState.active ||
      mouseDown === nextState.mouseDown
    );
  }

  _preventArrowKeyScrolling(e) {
    switch (e.key) {
      case "ArrowUp":
      case "ArrowDown":
      case "ArrowLeft":
      case "ArrowRight":
        preventDefaultAndStopPropagation(e);
        break;
    }
  }

  /**
   * Sets the passed in item to be the current item.
   *
   * @param {null|Number} index
   *        The index of the item in to be set as current, or undefined to unset the
   *        current item.
   */

  _setCurrentItem(index = -1, options = {}) {
    const item = this.props.items[index];
    if (item !== undefined && !options.preventAutoScroll) {
      const element = document.getElementById(item.key);
      scrollIntoView(element, {
        ...options,
        container: this.listRef.current,
      });
    }

    const state = {};
    if (this.state.active != undefined) {
      state.active = null;
      if (this.listRef.current !== document.activeElement) {
        this.listRef.current.focus();
      }
    }

    if (this.state.current !== index) {
      this.setState({
        ...state,
        current: index,
      });
    }
  }

  /**
   * Handles key down events in the list's container.
   *
   * @param {Event} e
   */

  _onKeyDown(e) {
    const { active, current } = this.state;
    if (current == null) {
      return;
    }

    if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
      return;
    }

    this._preventArrowKeyScrolling(e);

    const { length } = this.props.items;
    switch (e.key) {
      case "ArrowUp":
        current > 0 && this._setCurrentItem(current - 1, { alignTo: "top" });
        break;

      case "ArrowDown":
        current < length - 1 &&
          this._setCurrentItem(current + 1, { alignTo: "bottom" });
        break;

      case "Home":
        this._setCurrentItem(0, { alignTo: "top" });
        break;

      case "End":
        this._setCurrentItem(length - 1, { alignTo: "bottom" });
        break;

      case "Enter":
      case " ":
        // On space or enter make current list item active. This means keyboard focus
        // handling is passed on to the component within the list item.
        if (document.activeElement === this.listRef.current) {
          preventDefaultAndStopPropagation(e);
          if (active !== current) {
            this.setState({ active: current });
          }
        }
        break;

      case "Escape":
        // If current list item is active, make it inactive and let keyboard focusing be
        // handled normally.
        preventDefaultAndStopPropagation(e);
        if (active != null) {
          this.setState({ active: null });
        }

        this.listRef.current.focus();
        break;
    }
  }

  render() {
    const { active, current } = this.state;
    const { items } = this.props;

    return ul(
      {
        ref: this.listRef,
        className: "list",
        tabIndex: 0,
        onKeyDown: this._onKeyDown,
        onKeyPress: this._preventArrowKeyScrolling,
        onKeyUp: this._preventArrowKeyScrolling,
        onMouseDown: () => this.setState({ mouseDown: true }),
        onMouseUp: () => this.setState({ mouseDown: false }),
        onFocus: () => {
          if (current != null || this.state.mouseDown) {
            return;
          }

          // Only set default current to the first list item if current item is
          // not yet set and the focus event is not the result of a mouse
          // interarction.
          this._setCurrentItem(0);
        },
        onClick: () => {
          // Focus should always remain on the list container itself.
          this.listRef.current.focus();
        },
        onBlur: e => {
          if (active != null) {
            const { relatedTarget } = e;
            if (!this.listRef.current.contains(relatedTarget)) {
              this.setState({ active: null });
            }
          }
        },
        "aria-label"this.props.label,
        "aria-labelledby"this.props.labelledBy,
        "aria-activedescendant": current != null ? items[current].key : null,
      },
      items.map((item, index) => {
        return ListItem({
          item,
          current: index === current,
          active: index === active,
          // We make a key unique depending on whether the list item is in active or
          // inactive state to make sure that it is actually replaced and the tabbable
          // state is reset.
          key: `${item.key}-${index === active ? "active" : "inactive"}`,
          // Since the user just clicked the item, there's no need to check if it should
          // be scrolled into view.
          onClick: () =>
            this._setCurrentItem(index, { preventAutoScroll: true }),
        });
      })
    );
  }
}

module.exports = {
  ListItem: ListItemClass,
  List,
};

92%


¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.