components_moveables.jsx

import { useState } from "react";
import Moveable from "react-moveable";

/**
 * Component to be resized, which can be a div or an image.
 * @param {string} img - Url of the image to render
 * @param {string} color - If the element is a div, it will have a random color.
 * @param {number} id - Id of the element to render
 * @param {number} top - Position of the top element
 * @param {number} left - Poicion of the element left
 * @param {number} width - Width of the element to be rendered
 * @param {number} height - Height of the element to be rendered
 * @param {boolean} error - Indicates if there was an error in the image request
 * @param {number} limitBottom - Lower limit to which the element can be displaced
 * @param {number} limitRight - Horizontal limit to where the element can be displaced
 * @returns - Component to resize moveable
 */
export function MoveableComponent({
  img,
  color,
  id,
  top,
  left,
  width,
  height,
  error,
  limitBottom,
  limitRight,
}) {
  /** State controlling the element the selection of the element to be used */
  const [target, setTarget] = useState();
  /** State controlling the size and position of the element */
  const [nodoReferencia, setNodoReferencia] = useState({
    width: 0,
    height: 0,
    translate: [10, 10],
  });

  /**
   * Function that selects the element to be modified
   * @param {Element} target - Element to be modified
   * @returns {void} - Function returns nothing
   */
  const selectMoveable = (target) => setTarget(target);

  /**
   * Updates the position of the element, shifts the element
   * @param {Element} target - Element to be modified
   * @param {number[]} beforeTranslate - New position of the element
   * @returns {void} - Function return nothing
   */
  const updateMoveable = ({ target, beforeTranslate }) => {
    let frames = Object.assign(nodoReferencia);
    setNodoReferencia(frames);
    frames.translate = [...beforeTranslate];
    target.style.left = `${beforeTranslate[0]}px`;
    target.style.top = `${beforeTranslate[1]}px`;
  };

  /**
   * Function that resizes the element
   * @param {Element} target - Element to be modified
   * @param {number} width - New element width
   * @param {number} height - New element height
   * @return {void} - Function return nothing
   */
  const onResize = ({ target, width, height }) => {
    let frames = Object.assign(nodoReferencia);
    frames.width = width;
    frames.height = height;
    setNodoReferencia(frames);
    target.style.width = `${width}px`;
    target.style.height = `${height}px`;
  };

  /**
   * Function that handles the start of a resize event.
   * @param {Object} e - The event object that contains information about the event.
   * @param {Function} e.setOrigin - A function that sets the origin of the resize event.
   * @param {Object} e.dragStart - An object that contains information about the starting position of the drag event.
   * @param {Array} e.dragStart.set - A function that sets the starting position of the drag event as an array in the form of [x, y].
   * @return {void} - Function return nothing
   */
  const onResizeStart = (e) => {
    let setOrigin = e.setOrigin;
    let dragStart = e.dragStart;
    setOrigin(["%", "%"]);
    dragStart && dragStart.set(nodoReferencia.translate);
  };

  return (
    <>
      {error ? (
        <div
          onClick={(e) => selectMoveable(e.target)}
          id={`component-${id}`}
          className="moveable__component"
          style={{
            position: "absolute",
            borderRadius: "12px",
            width: width,
            height: height,
            top: top,
            left: left,
            background: color,
          }}
        ></div>
      ) : (
        <img
          onClick={(e) => selectMoveable(e.target)}
          className="moveable__component"
          id={`component-${id}`}
          alt={`component-${id}`}
          src={img}
          style={{
            position: "absolute",
            borderRadius: "12px",
            width: width,
            height: height,
            top: top,
            left: left,
          }}
        />
      )}
      <Moveable
        target={target}
        dragTarget={target}
        draggable={true}
        resizable={true}
        throttleResize={0}
        throttleDrag={0}
        snappable={true}
        bounds={{ left: 0, top: 0, right: limitRight, bottom: limitBottom }}
        zoom={1}
        origin={false}
        padding={{ left: 0, top: 0, right: 0, bottom: 0 }}
        onDragStart={({ set }) => set(nodoReferencia.translate)}
        onDrag={updateMoveable}
        onResizeStart={onResizeStart}
        onResize={onResize}
      />
    </>
  );
}