<commit_before>import './Menu.js';
import { indexOfItemContainingTarget, elementsFromPoint, ownEvent } from './utilities.js';
import { merge } from './updates.js';
import * as symbols from './symbols.js';
import PopupSource from './PopupSource.js';

const menuTagKey = Symbol('menuTag');
const documentMouseupListenerKey = Symbol('documentMouseupListener');


/**
 * A button that invokes a menu.
 * 
 * @inherits PopupSource
 * @elementtag {Menu} menu
 */
class MenuButton extends PopupSource {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }

    // If we're doing a drag-select, and the user hovers over an item, select
    // it.
    this.addEventListener('mousemove', event => {
      const target = event.target;
      if (target && this.state.dragSelect) {
        /** @type {any} */
        const cast = target;
        const hoverIndex = indexOfItemContainingTarget(this, cast);
        if (hoverIndex !== this.state.menuSelectedIndex) {
          this[symbols.raiseChangeEvents] = true;
          this.setState({
            menuSelectedIndex: hoverIndex
          });
          this[symbols.raiseChangeEvents] = false;
        }
      }
    });

    this.$.source.addEventListener('mouseup', event => {
     if (this.state.dragSelect && this.state.opened) {
        // Since we got a mouse up, we're no longer doing a drag-select.
        this[symbols.raiseChangeEvents] = true;
        this.setState({
          dragSelect: false
        });
        this[symbols.raiseChangeEvents] = false;
        event.stopPropagation();
      }
    });

    // If the popup is open and user releases the mouse over the backdrop, close
    // the popup. We need to listen to mouseup on the document, not this
    // element. If the user mouses down on the source, then moves the mouse off
    // the document before releasing the mouse, the element itself won't get the
    // mouseup. The document will, however, so it's a more reliable source of
    // mouse state.
    this[documentMouseupListenerKey] = async (event) => {
      if (this.opened) {
        // If the user mouses up over the menu, the menu mouseup handler will
        // handle that case. So if we get to this point and the popup is still
        // open, the user either released over the popup source or the backdrop.
        // Hit test to see if the event is over the source. If not, they were
        // over the backdrop.
        const hitTargets = elementsFromPoint(this, event.clientX, event.clientY);
        const overSource = hitTargets.indexOf(this.$.source) >= 0;
        if (!overSource) {
          // Mouse is likely over the backdrop, so close.
          this[symbols.raiseChangeEvents] = true;
          await this.close();
          this[symbols.raiseChangeEvents] = false;
        }
      }
    };

    if (this.state.opened) {<commit_msg>Fix a regression. If the user clicks a menu button to open the menu (rather than drag-selecting into it), then turn off our drag-select flag. This ensures that subsequent clicks on static items like menu separators don't close the menu.<commit_after>import './Menu.js';
import { indexOfItemContainingTarget, elementsFromPoint, ownEvent } from './utilities.js';
import { merge } from './updates.js';
import * as symbols from './symbols.js';
import PopupSource from './PopupSource.js';

const menuTagKey = Symbol('menuTag');
const documentMouseupListenerKey = Symbol('documentMouseupListener');


/**
 * A button that invokes a menu.
 * 
 * @inherits PopupSource
 * @elementtag {Menu} menu
 */
class MenuButton extends PopupSource {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }

    // If we're doing a drag-select, and the user hovers over an item, select
    // it.
    this.addEventListener('mousemove', event => {
      const target = event.target;
      if (target && this.state.dragSelect) {
        /** @type {any} */
        const cast = target;
        const hoverIndex = indexOfItemContainingTarget(this, cast);
        if (hoverIndex !== this.state.menuSelectedIndex) {
          this[symbols.raiseChangeEvents] = true;
          this.setState({
            menuSelectedIndex: hoverIndex
          });
          this[symbols.raiseChangeEvents] = false;
        }
      }
    });

    // If the popup is open and user releases the mouse over the backdrop, close
    // the popup. We need to listen to mouseup on the document, not this
    // element. If the user mouses down on the source, then moves the mouse off
    // the document before releasing the mouse, the element itself won't get the
    // mouseup. The document will, however, so it's a more reliable source of
    // mouse state.
    //
    // Coincidentally, we *also* need to listen to mouseup on the document to
    // tell whether the user released the mouse over the source button. When the
    // user mouses down, the backdrop will appear and cover the source, so from
    // that point on the source won't receive a mouseup event. Again, we can
    // listen to mouseup on the document and do our own hit-testing to see if
    // the user released the mouse over the source.
    this[documentMouseupListenerKey] = async (event) => {
      const hitTargets = elementsFromPoint(this, event.clientX, event.clientY);
      const overSource = hitTargets.indexOf(this.$.source) >= 0;
      if (this.opened) {
        if (overSource) {
          // User released the mouse over the source button (behind the
          // backdrop), so we're no longer doing a drag-select.
          if (this.state.dragSelect) {
            this[symbols.raiseChangeEvents] = true;
            this.setState({
              dragSelect: false
            });
            this[symbols.raiseChangeEvents] = false;
          }
        } else {
          // If we get to this point, the user released over the backdrop with
          // the popup open, so close.
          this[symbols.raiseChangeEvents] = true;
          await this.close();
          this[symbols.raiseChangeEvents] = false;
        }
      }
    };

    if (this.state.opened) {
{'fraction_non_alphanumeric': 0.10003274394237066, 'fraction_numerical': 0.0003274394237066143, 'mean_word_length': 2.5089029293509477, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1631012', 'n_tokens_mistral': 1671, 'n_tokens_neox': 1628, 'n_words': 605}