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

package.src.vaadin-grid-active-item-mixin.js Maven / Gradle / Ivy

/**
 * @license
 * Copyright (c) 2016 - 2023 Vaadin Ltd.
 * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
 */

/**
 * @param {!Element} target
 * @return {boolean}
 * @protected
 */
export const isFocusable = (target) => {
  if (!target.parentNode) {
    return false;
  }
  const focusables = Array.from(
    target.parentNode.querySelectorAll(
      '[tabindex], button, input, select, textarea, object, iframe, a[href], area[href]',
    ),
  ).filter((element) => {
    const part = element.getAttribute('part');
    return !(part && part.includes('body-cell'));
  });

  const isFocusableElement = focusables.includes(target);
  return (
    !target.disabled && isFocusableElement && target.offsetParent && getComputedStyle(target).visibility !== 'hidden'
  );
};

/**
 * @polymerMixin
 */
export const ActiveItemMixin = (superClass) =>
  class ActiveItemMixin extends superClass {
    static get properties() {
      return {
        /**
         * The item user has last interacted with. Turns to `null` after user deactivates
         * the item by re-interacting with the currently active item.
         * @type {GridItem}
         */
        activeItem: {
          type: Object,
          notify: true,
          value: null,
          sync: true,
        },
      };
    }

    /** @protected */
    ready() {
      super.ready();

      this.$.scroller.addEventListener('click', this._onClick.bind(this));
      this.addEventListener('cell-activate', this._activateItem.bind(this));
      this.addEventListener('row-activate', this._activateItem.bind(this));
    }

    /** @private */
    _activateItem(e) {
      const model = e.detail.model;
      const clickedItem = model ? model.item : null;

      if (clickedItem) {
        this.activeItem = !this._itemsEqual(this.activeItem, clickedItem) ? clickedItem : null;
      }
    }

    /**
     * We need to listen to click instead of tap because on mobile safari, the
     * document.activeElement has not been updated (focus has not been shifted)
     * yet at the point when tap event is being executed.
     * @param {!MouseEvent} e
     * @protected
     */
    _onClick(e) {
      if (e.defaultPrevented) {
        // Something has handled this click already, e. g., 
        return;
      }

      const path = e.composedPath();
      const cell = path[path.indexOf(this.$.table) - 3];
      if (!cell || cell.getAttribute('part').indexOf('details-cell') > -1) {
        return;
      }
      const cellContent = cell._content;

      const activeElement = this.getRootNode().activeElement;
      const cellContentHasFocus = cellContent.contains(activeElement);
      if (!cellContentHasFocus && !this._isFocusable(e.target) && !(e.target instanceof HTMLLabelElement)) {
        this.dispatchEvent(
          new CustomEvent('cell-activate', {
            detail: {
              model: this.__getRowModel(cell.parentElement),
            },
          }),
        );
      }
    }

    /**
     * @param {!Element} target
     * @return {boolean}
     * @protected
     */
    _isFocusable(target) {
      return isFocusable(target);
    }

    /**
     * Fired when the `activeItem` property changes.
     *
     * @event active-item-changed
     */

    /**
     * Fired when the cell is activated with click or keyboard.
     *
     * @event cell-activate
     */
  };




© 2015 - 2024 Weber Informatics LLC | Privacy Policy