Home Manual Reference Source Repository

src/behaviors/base-behavior.js

/**
 * Is an abstract class or interface to be overriden in order to define the way
 * a given shape should behave when selected or edited by a user. Instances of
 * `BaseBehavior` are internally used by `Layer` instances to modify the data
 * according to a user interaction and a given shape. A single instance of
 * `Behavior` is created in one given shape.
 *
 * By default, the only method to override to define a new behavior for a
 * shape is the `edit` method. However, if needed in special cases, all the
 * selection handling can be overriden too.
 *
 * The flow is the following:
 * `Event`  - (forwarded to) -> `Layer` - (command) -> `Behavior` - (modify) -> `data` - (upates) -> `Shape`
 *
 * The behavior responsability is then to modify the data according to the
 * user interactions, while shapes are always a view of the current state of the
 * data.
 */
export default class BaseBehavior {
  constructor() {
    this._selectedItems = new Set(); // no duplicate in Set
    this._selectedClass = null;
    this._layer = null;
  }

  initialize(layer) {
    this._layer = layer;
    this._selectedClass = layer.params.selectedClassName;
  }

  /**
   * Destroy the references to the selected items.
   *
   * @type {String}
   * @todo - rename to `clearSelection` (removing the class) ?
   */
  destroy() {
    this._selectedItems.clear();
  }

  /**
   * The class to add to the shapes when selected.
   *
   * @type {String}
   */
  set selectedClass(value) {
    this._selectedClass = value;
  }

  /**
   * The class to add to the shapes when selected.
   *
   * @type {String}
   */
  get selectedClass() {
    return this._selectedClass;
  }

  /**
   * An array containing all the selected items of the layer.
   *
   * @type {Array}
   */
  get selectedItems() {
    return [...this._selectedItems];
  }

  /**
   * @param {Element} $item - The item to select.
   * @param {Object} datum - Not used in this implementation. Could be
   *    used to mark the data as selected.
   * @todo - Pass the shape object to get the accessors ?
   */
  select($item, datum) {
    $item.classList.add(this.selectedClass);
    this._selectedItems.add($item);
  }

  /**
   * @param {Element} $item - The item to unselect.
   * @param {Object} datum - Not used in this implementation. Could be
   *    used to mark the data as selected.
   * @todo - Pass the shape object to get the accessors ?
   */
  unselect($item, datum) {
    $item.classList.remove(this.selectedClass);
    this._selectedItems.delete($item);
  }

  /**
   * @param {Element} $item - The item to toggle selection.
   * @param {Object} datum - Not used in this implementation. Could be
   *    used to mark the data as selected.
   * @todo - Pass the shape object to get the accessors ?
   */
  toggleSelection($item, datum) {
    const method = this._selectedItems.has($item) ? 'unselect' : 'select';
    this[method]($item);
  }

  /**
   * Interface method to override in order to define its particular behavior when
   * interacted with.
   *
   * @param {Object} renderingContext - The layer rendering context.
   * @param {BaseShape} shape - The shape object to be edited.
   * @param {Object|Array} datum - The related datum to modify.
   * @param {Number} dx - The value of the interaction in the x axis (in pixels).
   * @param {Number} dy - The value of the interaction in the y axis (in pixels).
   * @param {Element} $target - The target DOM element of the interaction.
   */
  edit(renderingContext, shape, datum, dx, dy, $target) {
    // must be implemented in children
  }
}