UNPKG

@mdxeditor/editor

Version:

React component for rich text markdown editing

215 lines (214 loc) 7.96 kB
import * as React from "react"; import { useRef } from "react"; import styles from "../../styles/ui.module.css.js"; import classNames from "classnames"; function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } const Direction = { east: 1 << 0, north: 1 << 3, south: 1 << 1, west: 1 << 2 }; function ImageResizer({ onResizeStart, onResizeEnd, imageRef, maxWidth, editor }) { const controlWrapperRef = useRef(null); const userSelect = useRef({ priority: "", value: "default" }); const positioningRef = useRef({ currentHeight: 0, currentWidth: 0, direction: 0, isResizing: false, ratio: 0, startHeight: 0, startWidth: 0, startX: 0, startY: 0 }); const editorRootElement = editor.getRootElement(); const maxWidthContainer = maxWidth ? maxWidth : editorRootElement !== null ? editorRootElement.getBoundingClientRect().width - 20 : 100; const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 : 100; const minWidth = 100; const minHeight = 100; const setStartCursor = (direction) => { const ew = direction === Direction.east || direction === Direction.west; const ns = direction === Direction.north || direction === Direction.south; const nwse = direction & Direction.north && direction & Direction.west || direction & Direction.south && direction & Direction.east; const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw"; if (editorRootElement !== null) { editorRootElement.style.setProperty("cursor", `${cursorDir}-resize`, "important"); } if (document.body !== null) { document.body.style.setProperty("cursor", `${cursorDir}-resize`, "important"); userSelect.current.value = document.body.style.getPropertyValue("-webkit-user-select"); userSelect.current.priority = document.body.style.getPropertyPriority("-webkit-user-select"); document.body.style.setProperty("-webkit-user-select", `none`, "important"); } }; const setEndCursor = () => { if (editorRootElement !== null) { editorRootElement.style.setProperty("cursor", "text"); } if (document.body !== null) { document.body.style.setProperty("cursor", "default"); document.body.style.setProperty("-webkit-user-select", userSelect.current.value, userSelect.current.priority); } }; const handlePointerDown = (event, direction) => { if (!editor.isEditable()) { return; } const image = imageRef.current; const controlWrapper = controlWrapperRef.current; if (image !== null && controlWrapper !== null) { event.preventDefault(); const { width, height } = image.getBoundingClientRect(); const positioning = positioningRef.current; positioning.startWidth = width; positioning.startHeight = height; positioning.ratio = width / height; positioning.currentWidth = width; positioning.currentHeight = height; positioning.startX = event.clientX; positioning.startY = event.clientY; positioning.isResizing = true; positioning.direction = direction; setStartCursor(direction); onResizeStart(); controlWrapper.classList.add(styles.imageControlWrapperResizing); image.style.height = `${height}px`; image.style.width = `${width}px`; document.addEventListener("pointermove", handlePointerMove); document.addEventListener("pointerup", handlePointerUp); } }; const handlePointerMove = (event) => { const image = imageRef.current; const positioning = positioningRef.current; const isHorizontal = positioning.direction & (Direction.east | Direction.west); const isVertical = positioning.direction & (Direction.south | Direction.north); if (image !== null && positioning.isResizing) { if (isHorizontal && isVertical) { let diff = Math.floor(positioning.startX - event.clientX); diff = positioning.direction & Direction.east ? -diff : diff; const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer); const height = width / positioning.ratio; image.style.width = `${width}px`; image.style.height = `${height}px`; positioning.currentHeight = height; positioning.currentWidth = width; } else if (isVertical) { let diff = Math.floor(positioning.startY - event.clientY); diff = positioning.direction & Direction.south ? -diff : diff; const height = clamp(positioning.startHeight + diff, minHeight, maxHeightContainer); image.style.height = `${height}px`; positioning.currentHeight = height; } else { let diff = Math.floor(positioning.startX - event.clientX); diff = positioning.direction & Direction.east ? -diff : diff; const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer); image.style.width = `${width}px`; positioning.currentWidth = width; } } }; const handlePointerUp = () => { const image = imageRef.current; const positioning = positioningRef.current; const controlWrapper = controlWrapperRef.current; if (image !== null && controlWrapper !== null && positioning.isResizing) { const width = positioning.currentWidth; const height = positioning.currentHeight; positioning.startWidth = 0; positioning.startHeight = 0; positioning.ratio = 0; positioning.startX = 0; positioning.startY = 0; positioning.currentWidth = 0; positioning.currentHeight = 0; positioning.isResizing = false; controlWrapper.classList.remove(styles.imageControlWrapperResizing); setEndCursor(); onResizeEnd(width, height); document.removeEventListener("pointermove", handlePointerMove); document.removeEventListener("pointerup", handlePointerUp); } }; return /* @__PURE__ */ React.createElement("div", { ref: controlWrapperRef }, /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerN), onPointerDown: (event) => { handlePointerDown(event, Direction.north); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerNe), onPointerDown: (event) => { handlePointerDown(event, Direction.north | Direction.east); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerE), onPointerDown: (event) => { handlePointerDown(event, Direction.east); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerSe), onPointerDown: (event) => { handlePointerDown(event, Direction.south | Direction.east); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerS), onPointerDown: (event) => { handlePointerDown(event, Direction.south); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerSw), onPointerDown: (event) => { handlePointerDown(event, Direction.south | Direction.west); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerW), onPointerDown: (event) => { handlePointerDown(event, Direction.west); } } ), /* @__PURE__ */ React.createElement( "div", { className: classNames(styles.imageResizer, styles.imageResizerNw), onPointerDown: (event) => { handlePointerDown(event, Direction.north | Direction.west); } } )); } export { ImageResizer as default };