```
class DndPlugin extends RectLifecycleInterface {
  constructor() {
    super();
    this.currentRect = null;
    this.action = null;
    this.host = null;
    this.startX = 0;
    this.startY = 0;
    this.startRect = {};
    this.boundOnMouseMove = this.onMouseMove.bind(this);
    this.boundOnMouseUp = this.onMouseUp.bind(this);
    this.listeners = new WeakMap();
  }

  attach(host) {
    if (host) {
      this.host = host;
    }
    return null;
  }

  onRectCreate(rect, element) {
    const callback = this.onMouseDown.bind(this, rect);
    this.listeners.set(element, callback);
    element.addEventListener('mousedown', callback);
  }

  onRectRemove(rect, element) {
    element.removeEventListener('mousedown', this.listeners.get(element));
    this.listeners.delete(element);
  }

  onMouseDown(rect, e) {
    const rectElement = e.target;
    if (!rectElement) return;
    const rectStyle = rectElement.style;

    const x = parseInt(rectStyle.left, 10);
    const y = parseInt(rectStyle.top, 10);
    const width = parseInt(rectStyle.getPropertyValue('--width'), 10);
    const height = parseInt(rectStyle.getPropertyValue('--height'), 10);
    const borderSize = parseInt(rectStyle.getPropertyValue('--border-size') || getComputedStyle(this.host).getPropertyValue('--border-size'), 10) || 6;
    const vertexSize = parseInt(rectStyle.getPropertyValue('--vertex-size') || getComputedStyle(this.host).getPropertyValue('--vertex-size'), 10) || 0;

    const bbox = rectElement.getBoundingClientRect();
    const cursorOffsetX = e.clientX - bbox.left;
    const cursorOffsetY = e.clientY - bbox.top;

    const xOffset = width - borderSize;
    const yOffset = height - borderSize;
    const x1 = -(vertexSize-borderSize)/2;
    const x2 = (vertexSize+borderSize)/2;
    const x3 = x1 + xOffset;
    const x4 = x2 + xOffset;
    const y1 = x1;
    const y2 = x2;
    const y3 = y1 + yOffset;
    const y4 = y2 + yOffset;
    const heightSubBorder = height - borderSize;
    const widthSubBorder = width - borderSize;

    if (cursorOffsetX >= x1 && cursorOffsetX <= x2 && cursorOffsetY >= y1 && cursorOffsetY <= y2) {
      this.action = 'resize-nw';
    } else if (cursorOffsetX >= x3 && cursorOffsetX <= x4 && cursorOffsetY >= y1 && cursorOffsetY <= y2) {
      this.action = 'resize-ne';
    } else if (cursorOffsetX >= x1 && cursorOffsetX <= x2 && cursorOffsetY >= y3 && cursorOffsetY <= y4) {
      this.action = 'resize-sw';
    } else if (cursorOffsetX >= x3 && cursorOffsetX <= x4 && cursorOffsetY >= y3 && cursorOffsetY <= y4) {
      this.action = 'resize-se';
    } else if (cursorOffsetX >= 0 && cursorOffsetX <= width && cursorOffsetY >= 0 && cursorOffsetY <= borderSize) {
      this.action = 'resize-n';
    } else if (cursorOffsetX >= 0 && cursorOffsetX <= width && cursorOffsetY >= heightSubBorder && cursorOffsetY <= height) {
      this.action = 'resize-s';
    } else if (cursorOffsetX >= 0 && cursorOffsetX <= borderSize && cursorOffsetY >= 0 && cursorOffsetY <= height) {
      this.action = 'resize-w';
    } else if (cursorOffsetX >= widthSubBorder && cursorOffsetX <= width && cursorOffsetY >= 0 && cursorOffsetY <= height) {
      this.action = 'resize-e';
    } else if (
      cursorOffsetX > borderSize && cursorOffsetX < widthSubBorder &&
      cursorOffsetY > borderSize && cursorOffsetY < heightSubBorder
    ) {
      this.action = 'move';
    } else {
      return;
    }

    this.startX = e.clientX;
    this.startY = e.clientY;

    this.currentRect = rect;
    this.startRect = {x, y, width, height};

    e.preventDefault();

    document.addEventListener('mousemove', this.boundOnMouseMove);
    document.addEventListener('mouseup', this.boundOnMouseUp);
  }

  onMouseMove(e) {
    if (!this.currentRect) return;

    const dx = e.clientX - this.startX;
    const dy = e.clientY - this.startY;
    let {x: newX, y: newY, width: newWidth, height: newHeight} = this.startRect;

    switch (this.action) {
      case 'move':
        newX += dx;
        newY += dy;
        break;
      case 'resize-nw':
        newX += dx;
        newY += dy;
        newWidth -= dx;
        newHeight -= dy;
        break;
      case 'resize-ne':
        newY += dy;
        newWidth += dx;
        newHeight -= dy;
        break;
      case 'resize-sw':
        newX += dx;
        newWidth -= dx;
        newHeight += dy;
        break;
      case 'resize-se':
        newWidth += dx;
        newHeight += dy;
        break;
      case 'resize-n':
        newY += dy;
        newHeight -= dy;
        break;
      case 'resize-s':
        newHeight += dy;
        break;
      case 'resize-w':
        newX += dx;
        newWidth -= dx;
        break;
      case 'resize-e':
        newWidth += dx;
        break;
    }

    this.updateRect({x: newX, y: newY, width: newWidth, height: newHeight});
  }

  updateRect(newRect) {
    const updates = {};
    for (const key in newRect) {
      if (newRect[key] !== this.startRect[key]) {
        updates[key] = newRect[key];
      }
    }
    if (Object.keys(updates).length > 0) {
      this.currentRect.update(updates);
    }
  }

  onMouseUp() {
    document.removeEventListener('mousemove', this.boundOnMouseMove);
    document.removeEventListener('mouseup', this.boundOnMouseUp);
    this.currentRect = null;
    this.action = null;
  }
}
```

Above is original version. I made some changes to it. Are they good? I want to document the change (compare original vs new approach) so that I can review in the future for some similar problems.
```
const DndPlugin = (() => {

  const RESIZE_NW = 0;  
  const RESIZE_NE = 1;  
  const RESIZE_SW = 2;  
  const RESIZE_SE = 3;  
  const RESIZE_N = 4;   
  const RESIZE_S = 5;   
  const RESIZE_W = 6;   
  const RESIZE_E = 7;   
  const MOVE = 8;       
  const NULL = 9;       

  const ACTION_TABLE = [
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  NULL,     RESIZE_NE,  RESIZE_NE, RESIZE_NE], 
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  RESIZE_N, RESIZE_NE,  RESIZE_NE, RESIZE_NE], 
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  MOVE,     RESIZE_NE,  RESIZE_NE, RESIZE_NE], 
    [NULL,      RESIZE_W,   MOVE,       MOVE,     MOVE,       RESIZE_E,  NULL     ], 
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  MOVE,     RESIZE_SE,  RESIZE_SE, RESIZE_SE], 
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  RESIZE_S, RESIZE_SE,  RESIZE_SE, RESIZE_SE], 
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  NULL,     RESIZE_SE,  RESIZE_SE, RESIZE_SE], 
  ];

  const WIDTH_CHANGE_TABLE  = [-1,  1, -1,  1,  0, 0, -1,  1];
  const HEIGHT_CHANGE_TABLE = [-1, -1,  1,  1, -1, 1,  0,  0];
  const X_CHANGE_TABLE      = [ 1,  0,  1,  0,  0, 0,  1,  0];
  const Y_CHANGE_TABLE      = [ 1,  1,  0,  0,  1, 0,  0,  0];

  return class DndPlugin extends RectLifecycleInterface {

    constructor() {
      super(); 

      this.currentRect = null;

      this.currentAction = NULL;

      this.host = null;

      this.initialMouseX = 0;
      this.initialMouseY = 0;

      this.initialRectState = null;

      this.mouseMoveHandler = this.onMouseMove.bind(this);
      this.mouseUpHandler = this.onMouseUp.bind(this);

      this.listeners = new WeakMap();

      this.isAnimating = false;  

      this.MIN_WIDTH = 50;  
      this.MIN_HEIGHT = 50; 
    }

    attach(host) {
      if (host) {
        this.host = host;
      }
      return null;
    }

    onRectCreate(rect, element) {

      const callback = this.onMouseDown.bind(this, rect);

      this.listeners.set(element, callback);

      element.addEventListener('mousedown', callback);
    }

    onRectRemove(rect, element) {
      const callback = this.listeners.get(element);
      if (callback) {

        element.removeEventListener('mousedown', callback);

        this.listeners.delete(element);
      }
    }

    onMouseDown(rect, e) {
      const rectElement = e.target;
      if (!rectElement) return;
      const rectStyle = rectElement.style;
      const hostStyle = this.host.style;
      const width = parseFloat(rectStyle.getPropertyValue('--width'));
      const height = parseFloat(rectStyle.getPropertyValue('--height'));
      const borderSize = parseFloat(rectStyle.getPropertyValue('--border-size') || hostStyle.getPropertyValue('--border-size')) || 6;
      const vertexSize = parseFloat(rectStyle.getPropertyValue('--vertex-size') || hostStyle.getPropertyValue('--vertex-size')) || 0;

      const bbox = rectElement.getBoundingClientRect();

      const cursorOffsetX = e.clientX - bbox.left;
      const cursorOffsetY = e.clientY - bbox.top;

      const xOffset = width - borderSize;
      const yOffset = height - borderSize;

      const halfVertexMinusBorder = (vertexSize - borderSize) / 2;
      const halfVertexPlusBorder = (vertexSize + borderSize) / 2;

      const minX = -halfVertexMinusBorder;
      const maxX = halfVertexPlusBorder + xOffset;

      const minY = minX;
      const maxY = halfVertexPlusBorder + yOffset;

      if (cursorOffsetX < minX || cursorOffsetX > maxX || cursorOffsetY < minY || cursorOffsetY > maxY) {
        return;
      }

      const xSorted = [

        0,
        borderSize,
        halfVertexPlusBorder,
        -halfVertexMinusBorder + xOffset,
        width - borderSize,
        width

      ];

      const ySorted = [

        0,
        borderSize,
        halfVertexPlusBorder,
        -halfVertexMinusBorder + yOffset,
        height - borderSize,
        height

      ];

      let xi = 0;
      let yi = 0;

      for (; xi < 6 && cursorOffsetX >= xSorted[xi]; xi++) { }

      for (; yi < 6 && cursorOffsetY >= ySorted[yi]; yi++) { }

      this.currentAction = ACTION_TABLE[yi][xi];

      if (this.currentAction === NULL) {
        return;
      }

      this.initialMouseX = e.clientX;
      this.initialMouseY = e.clientY;

      this.currentRect = rect;
      this.initialRectState = { x: parseInt(rectStyle.left, 10), y: parseInt(rectStyle.top, 10), width, height };

      e.preventDefault();

      document.addEventListener('mousemove', this.mouseMoveHandler);
      document.addEventListener('mouseup', this.mouseUpHandler);
    }

    onMouseMove(e) {
      if (!this.currentRect || this.isAnimating) return;
      this.isAnimating = true;

      const dx = e.clientX - this.initialMouseX;
      const dy = e.clientY - this.initialMouseY;

      if (this.currentAction === MOVE) {
        this.moveRect(this.currentRect, this.initialRectState, dx, dy);
      } else {
        this.resizeRect(this.currentRect, this.initialRectState, dx, dy);
      }
    }

    moveRect(rect, initialRectState, dx, dy) {
      let {x, y} = initialRectState;
      x += dx;
      y += dy;

      requestAnimationFrame(() => {
        rect.update({ x, y });

        this.isAnimating = false;
      });
    }

    resizeRect(rect, initialRectState, dx, dy) {
      const action = this.currentAction;

      const {x: initialX, y: initialY, width: initialWidth, height: initialHeight} = initialRectState;

      const width = Math.max(initialWidth + WIDTH_CHANGE_TABLE[action] * dx, this.MIN_WIDTH);
      const height = Math.max(initialHeight + HEIGHT_CHANGE_TABLE[action] * dy, this.MIN_HEIGHT);

      const x = initialX + X_CHANGE_TABLE[action] * (initialWidth - width);
      const y = initialY + Y_CHANGE_TABLE[action] * (initialHeight - height);

      requestAnimationFrame(() => {
        this.updateRect(rect, initialRectState, { x, y, width, height });

        this.isAnimating = false;
      });
    }

    updateRect(rect, initialRectState, newRect) {
      const updates = {};

      for (const key in newRect) {
        if (newRect[key] !== initialRectState[key]) {
          updates[key] = newRect[key];
        }
      }

      rect.update(updates);
    }

    onMouseUp() {

      document.removeEventListener('mousemove', this.mouseMoveHandler);
      document.removeEventListener('mouseup', this.mouseUpHandler);

      this.currentRect = null;
      this.currentAction = NULL;

      this.isAnimating = false;
    }
  }
})();
```