```
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;

    // action    x-range  y-range
    // resize-nw: (x1, x2) (y1, y2)
    // resize-ne: (x3, x4) (y1, y2)
    // resize-sw: (x1, x2) (y3, y4)
    // resize-se: (x3, x4) (y3, y4)
    // resize-n: (0, width), (0, borderSize);
    // resize-s: (0, width), (heightSubBorder, height)
    // resize-w: (0, borderSize), (0, height)
    // resize-e: (widthSubBorder, width), (0, height)
    // move: (borderSize, widthSubBorder), (borderSize, heightSubBorder)

    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;
  }
}
```
i moved away from above implementation to the one below. is it an improvement? i want to create an article that details the change and improvement so later on i can re-read what happens when i need to implement drag and drop.
```

const DndPlugin = (() => {
  // ------------------------
  // Action Type Constants
  // ------------------------
  const NULL = 0;       // No action
  const MOVE = 1;       // Move the rectangle
  const RESIZE_NW = 2;  // Resize from the North-West corner
  const RESIZE_NE = 3;  // Resize from the North-East corner
  const RESIZE_SW = 4;  // Resize from the South-West corner
  const RESIZE_SE = 5;  // Resize from the South-East corner
  const RESIZE_N = 6;   // Resize from the North edge
  const RESIZE_S = 7;   // Resize from the South edge
  const RESIZE_W = 8;   // Resize from the West edge
  const RESIZE_E = 9;   // Resize from the East edge

  // ------------------------
  // Action Lookup Table
  // ------------------------
  // This table maps cursor positions to specific actions based on X and Y indices.
  // Both X and Y indices range from 0 to 6, corresponding to seven distinct regions.
  // Row: Y, Col: X
  const ACTION_TABLE = [
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  NULL,     RESIZE_NE,  RESIZE_NE, RESIZE_NE], // Y=0
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  RESIZE_N, RESIZE_NE,  RESIZE_NE, RESIZE_NE], // Y=1
    [RESIZE_NW, RESIZE_NW,  RESIZE_NW,  MOVE,     RESIZE_NE,  RESIZE_NE, RESIZE_NE], // Y=2
    [NULL,      RESIZE_W,   MOVE,       MOVE,     MOVE,       RESIZE_E,  NULL     ], // Y=3
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  MOVE,     RESIZE_SE,  RESIZE_SE, RESIZE_SE], // Y=4
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  RESIZE_S, RESIZE_SE,  RESIZE_SE, RESIZE_SE], // Y=5
    [RESIZE_SW, RESIZE_SW,  RESIZE_SW,  NULL,     RESIZE_SE,  RESIZE_SE, RESIZE_SE], // Y=6
  ];

  // ------------------------
  // DndPlugin Class Definition
  // ------------------------
  return class DndPlugin extends RectLifecycleInterface {
    /**
     * Constructor initializes the plugin's state and binds event handlers.
     */
    constructor() {
      super(); // Call the parent class constructor

      // Current rectangle being interacted with
      this.currentRect = null;

      // Current action being performed (e.g., MOVE, RESIZE_NW)
      this.action = NULL;

      // Host element to which the plugin is attached
      this.host = null;

      // Starting mouse coordinates when interaction begins
      this.startX = 0;
      this.startY = 0;

      // Initial state of the rectangle before interaction
      this.startRect = null;

      // Bound event handlers for mouse move and mouse up events
      this.boundOnMouseMove = this.onMouseMove.bind(this);
      this.boundOnMouseUp = this.onMouseUp.bind(this);

      // WeakMap to store event listeners associated with each element
      this.listeners = new WeakMap();

      // Flags and storage for requestAnimationFrame optimization
      this.isAnimating = false;  // Indicates if an animation frame is already scheduled
      this.latestRect = null;    // Stores the latest rectangle state to be applied

      // Minimum size constraints to prevent the rectangle from becoming too small
      this.MIN_WIDTH = 50;  // Minimum width in pixels
      this.MIN_HEIGHT = 50; // Minimum height in pixels
    }

    /**
     * Attaches the plugin to a host element.
     * @param {HTMLElement} host - The host element.
     * @returns {null} Always returns null.
     */
    attach(host) {
      if (host) {
        this.host = host;
      }
      return null;
    }

    /**
     * Called when a rectangle is created. Sets up the mousedown event listener.
     * @param {Object} rect - The rectangle object.
     * @param {HTMLElement} element - The DOM element representing the rectangle.
     */
    onRectCreate(rect, element) {
      // Bind the onMouseDown handler with the current rectangle
      const callback = this.onMouseDown.bind(this, rect);

      // Store the callback in the listeners WeakMap
      this.listeners.set(element, callback);

      // Add the mousedown event listener to the rectangle element
      element.addEventListener('mousedown', callback);
    }

    /**
     * Called when a rectangle is removed. Cleans up the mousedown event listener.
     * @param {Object} rect - The rectangle object.
     * @param {HTMLElement} element - The DOM element representing the rectangle.
     */
    onRectRemove(rect, element) {
      const callback = this.listeners.get(element);
      if (callback) {
        // Remove the previously added mousedown event listener
        element.removeEventListener('mousedown', callback);

        // Remove the callback from the listeners WeakMap
        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;

      // Check if cursorOffsetX and cursorOffsetY are outside the boundaries
      if (cursorOffsetX < minX || cursorOffsetX > maxX || cursorOffsetY < minY || cursorOffsetY > maxY) {
        return;
      }

      // Sorted array excluding the first and last elements
      const xSorted = [
        // segment 0: minX to x[0]
        0,
        borderSize,
        halfVertexPlusBorder,
        -halfVertexMinusBorder + xOffset,
        width - borderSize,
        width
        // segment 6: x[5] to maxX
      ];

      const ySorted = [
        // segment 0: minY to y[0]
        0,
        borderSize,
        halfVertexPlusBorder,
        -halfVertexMinusBorder + yOffset,
        height - borderSize,
        height
        // segment 6: y[5] to maxY
      ];

      let xi = 0;
      let yi = 0;

      // Find the segment index for cursorOffsetX
      for (; xi < 6 && cursorOffsetX >= xSorted[xi]; xi++) { }

      // Find the segment index for cursorOffsetY
      for (; yi < 6 && cursorOffsetY >= ySorted[yi]; yi++) { }

      // Lookup the action based on the position of cursor in particular [y, x] segment
      this.action = ACTION_TABLE[yi][xi];

      // If the action is NULL, no operation should be performed
      if (this.action === NULL) {
        return;
      }

      // Store starting mouse coordinates
      this.startX = e.clientX;
      this.startY = e.clientY;

      // Store the current rectangle and its initial state
      this.currentRect = rect;
      this.startRect = { x: parseInt(rectStyle.left, 10), y: parseInt(rectStyle.top, 10), width, height };

      // Prevent default browser behavior (e.g., text selection)
      e.preventDefault();

      // Add global event listeners for mousemove and mouseup
      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;

      const action = this.action;

      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;
        default:
          return;
      }

      // Apply minimum size constraints
      newWidth = Math.max(newWidth, this.MIN_WIDTH);
      newHeight = Math.max(newHeight, this.MIN_HEIGHT);

      // For certain resize actions, adjusting x or y when width/height is clamped
      if (this.action === RESIZE_NW || this.action === RESIZE_SW || this.action === RESIZE_W) {
        newX = this.startRect.x + (this.startRect.width - newWidth);
      }
      if (this.action === RESIZE_NW || this.action === RESIZE_NE || this.action === RESIZE_N) {
        newY = this.startRect.y + (this.startRect.height - newHeight);
      }

      // Store the latest desired rectangle state
      this.latestRect = { x: newX, y: newY, width: newWidth, height: newHeight };

      // Schedule an update via requestAnimationFrame if not already scheduled
      if (!this.isAnimating) {
        this.isAnimating = true;
        requestAnimationFrame(() => {
          this.updateRect(this.latestRect);

          // Reset the animation flag and latestRect storage
          this.isAnimating = false;
          this.latestRect = null;
        });
      }
    }


    /**
     * Updates the rectangle's properties based on the new state.
     * @param {Object} newRect - The new state of the rectangle.
     */
    updateRect(newRect) {
      const updates = {};

      // Compare each property and collect updates that have changed
      for (const key in newRect) {
        if (newRect[key] !== this.startRect[key]) {
          updates[key] = newRect[key];
        }
      }

      // If there are any updates, apply them to the current rectangle
      if (Object.keys(updates).length > 0) {
        this.currentRect.update(updates);
      }
    }

    /**
     * Handles the mouseup event. Cleans up event listeners and resets state.
     */
    onMouseUp() {
      // Remove global event listeners for mousemove and mouseup
      document.removeEventListener('mousemove', this.boundOnMouseMove);
      document.removeEventListener('mouseup', this.boundOnMouseUp);

      // Reset the current rectangle and action
      this.currentRect = null;
      this.action = NULL;

      // Reset animation flags and storage
      this.isAnimating = false;
      this.latestRect = null;
    }
  }
})();
```