wtetsu/mouse-dictionary

View on GitHub
src/main/lib/snap.js

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * Mouse Dictionary (https://github.com/wtetsu/mouse-dictionary/)
 * Copyright 2018-present wtetsu, suiheilibe
 * Licensed under MIT
 */

import dom from "./dom";

const DEFAULT_STYLE = "position:fixed;background-color:#4169e1;opacity:0.20;z-index:2147483646;";

class Snap {
  constructor() {
    this.initialized = false;
    this.activated = false;
    this.minArea = 300 * 300;
    this.minSide = 90;
    this.isElementPresent = false;
  }

  initialize() {
    if (this.initialized) {
      return;
    }
    this.snapElement = dom.create(`<div style="${DEFAULT_STYLE};"></div>`);
    this.initialized = true;
  }

  appendElement() {
    if (this.isElementPresent) {
      return;
    }
    document.body.appendChild(this.snapElement);
    this.isElementPresent = true;
  }
  removeElement() {
    if (!this.isElementPresent) {
      return;
    }
    this.snapElement.remove();
    this.isElementPresent = false;
  }

  update(pointX, pointY, square, clientWidth) {
    const range = this.fetchSnapRange(pointX, pointY, square, clientWidth);
    if (!range) {
      return;
    }
    this.lastRange = range;

    if (this.initialized) {
      this.transform(range);
    }
  }

  transform(range) {
    const style = {
      left: `${range.left}px`,
      top: `${range.top}px`,
      width: `${range.width}px`,
      height: `${range.height}px`,
    };
    dom.applyStyles(this.snapElement, style);
  }

  activate() {
    this.initialize();
    this.appendElement();
    this.activated = true;
  }

  deactivate() {
    this.removeElement();
    this.activated = false;
  }

  getRange() {
    if (!this.lastRange) {
      return null;
    }
    return { ...this.lastRange };
  }

  fetchSnapRange(pointX, pointY, square, clientWidth) {
    if (square.left < 0) {
      const width = Math.min(clientWidth, window.innerWidth / 2);
      const height = window.innerHeight - 6;
      return { left: 0, top: 0, width, height };
    }

    if (square.left + square.width > window.innerWidth) {
      const width = Math.min(clientWidth, window.innerWidth / 2);
      const height = window.innerHeight - 6;
      const left = document.documentElement.clientWidth - width;
      return { left, top: 0, width, height };
    }

    const candidates = document.elementsFromPoint(pointX, pointY);
    return this.selectSnapElementRange(candidates);
  }

  selectSnapElementRange(elements) {
    for (let i = 1; i < elements.length; i++) {
      const e = elements[i];
      if (this.isExceptionalElement(e)) {
        continue;
      }
      const range = adjustRange(e.getBoundingClientRect());
      if (this.isEligibleElement(range)) {
        return range;
      }
    }
    return null;
  }

  isExceptionalElement(element) {
    if (element === this.snapElement) {
      return true;
    }
    const tagName = element.tagName;
    if (tagName === "BODY" || tagName === "HTML") {
      return true;
    }
    return false;
  }

  isEligibleElement(range) {
    if (range.width < this.minSide || range.height < this.minSide) {
      return false;
    }
    const rangeArea = range.width * range.height;
    const windowArea = window.innerWidth * window.innerHeight;
    if (rangeArea < this.minArea || rangeArea > windowArea / 2) {
      return false;
    }
    return true;
  }

  isActivated() {
    return this.activated;
  }
}

const adjustRange = (range) => {
  return {
    left: range.left,
    top: range.top,
    width: Math.min(range.width, window.innerWidth),
    height: Math.min(range.height, window.innerHeight),
  };
};

const build = () => {
  return new Snap();
};

export default {
  build,
};