UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

256 lines (255 loc) 8.23 kB
import * as React from 'react'; import { getBoundedValue, getTranslateValuesFromElement, } from '../functions/index.js'; export const Resizer = (props) => React.createElement( 'div', { style: { position: 'absolute', inset: -6, display: 'grid', pointerEvents: 'none', }, }, React.createElement(ResizerStyles, null), React.createElement(Resizers, props), ); let Resizers = (props) => { let { elementRef, containerRef, onResizeStart, onResizeEnd } = props; let isResizing = React.useRef(false); let onResizePointerDown = (event) => { if (!elementRef.current || 0 !== event.button) return; let initialPointerX = event.clientX; let initialPointerY = event.clientY; let [initialTranslateX, initialTranslateY] = getTranslateValuesFromElement( elementRef.current, ); let { width: initialWidth, height: initialHeight } = elementRef.current.getBoundingClientRect(); let width = `${initialWidth}px`; let height = `${initialHeight}px`; let translateX = initialTranslateX; let translateY = initialTranslateY; let minWidth = parseFloat(getComputedStyle(elementRef.current).minWidth); if (Number.isNaN(minWidth)) minWidth = 380; let minHeight = parseFloat(getComputedStyle(elementRef.current).minHeight); let resizer = event.currentTarget.dataset.iuiResizer; let ownerDocument = elementRef.current.ownerDocument || document; let originalUserSelect = ownerDocument.body.style.userSelect; ownerDocument.body.style.userSelect = 'none'; let onResizePointerMove = (event) => { if (!elementRef.current) return; if (!isResizing.current) { isResizing.current = true; onResizeStart?.(); } let containerRect = containerRef?.current?.getBoundingClientRect(); let clientX = getBoundedValue( event.clientX, containerRect?.left ?? 0, containerRect?.right ?? ownerDocument.documentElement.clientWidth ?? 0, ); let clientY = getBoundedValue( event.clientY, containerRect?.top ?? 0, containerRect?.bottom ?? ownerDocument.documentElement.clientHeight ?? 0, ); let diffX = initialPointerX - clientX; let diffY = initialPointerY - clientY; switch (resizer) { case 'top-left': { let newHeight = initialHeight + diffY; if (newHeight >= minHeight) { height = elementRef.current.style.height = `${newHeight}px`; translateY = initialTranslateY - diffY; } let newWidth = initialWidth + diffX; if (newWidth >= minWidth) { width = elementRef.current.style.width = `${newWidth}px`; translateX = initialTranslateX - diffX; } elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; } case 'top': { let newHeight = initialHeight + diffY; if (newHeight < minHeight) break; height = elementRef.current.style.height = `${newHeight}px`; translateY = initialTranslateY - diffY; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; } case 'top-right': { let newHeight = initialHeight + diffY; if (newHeight >= minHeight) { height = elementRef.current.style.height = `${newHeight}px`; translateY = initialTranslateY - diffY; } width = elementRef.current.style.width = `${initialWidth - diffX}px`; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; } case 'right': width = elementRef.current.style.width = `${initialWidth - diffX}px`; height = elementRef.current.style.height = `${initialHeight}px`; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; case 'bottom-right': width = elementRef.current.style.width = `${initialWidth - diffX}px`; height = elementRef.current.style.height = `${ initialHeight - diffY }px`; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; case 'bottom': height = elementRef.current.style.height = `${ initialHeight - diffY }px`; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; case 'bottom-left': { let newWidth = initialWidth + diffX; if (newWidth >= minWidth) { width = elementRef.current.style.width = `${newWidth}px`; translateX = initialTranslateX - diffX; } height = elementRef.current.style.height = `${ initialHeight - diffY }px`; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; } case 'left': { let newWidth = initialWidth + diffX; if (newWidth < minWidth) break; width = elementRef.current.style.width = `${newWidth}px`; height = elementRef.current.style.height = `${initialHeight}px`; translateX = initialTranslateX - diffX; elementRef.current.style.transform = `translate(${translateX}px, ${translateY}px)`; break; } default: break; } }; ownerDocument.addEventListener('pointermove', onResizePointerMove); ownerDocument.addEventListener( 'pointerup', () => { ownerDocument.removeEventListener('pointermove', onResizePointerMove); if (elementRef.current) { ownerDocument.body.style.userSelect = originalUserSelect; isResizing.current = false; onResizeEnd?.({ width, height, transform: `translate(${translateX}px, ${translateY}px)`, }); } }, { once: true, }, ); }; return React.createElement( React.Fragment, null, React.createElement('div', { 'data-iui-resizer': 'top-left', onPointerDown: onResizePointerDown, style: { cursor: 'nw-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'top', onPointerDown: onResizePointerDown, style: { cursor: 'n-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'top-right', onPointerDown: onResizePointerDown, style: { cursor: 'ne-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'right', onPointerDown: onResizePointerDown, style: { cursor: 'e-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'bottom-right', onPointerDown: onResizePointerDown, style: { cursor: 'se-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'bottom', onPointerDown: onResizePointerDown, style: { cursor: 's-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'bottom-left', onPointerDown: onResizePointerDown, style: { cursor: 'sw-resize', }, }), React.createElement('div', { 'data-iui-resizer': 'left', onPointerDown: onResizePointerDown, style: { cursor: 'w-resize', }, }), ); }; let ResizerStyles = React.memo(() => React.createElement('style', null, resizerStyles), ); let resizerStyles = ` [data-iui-resizer] { pointer-events: auto; grid-area: 1 / 1 / -1 / -1; width: 12px; height: 12px; z-index: 1; } [data-iui-resizer='top'], [data-iui-resizer='bottom'] { height: 8px; width: auto; z-index: 0; } [data-iui-resizer='left'], [data-iui-resizer='right'] { height: auto; width: 8px; z-index: 0; } [data-iui-resizer^='top'] { align-self: start; } [data-iui-resizer^='bottom'] { align-self: end; } [data-iui-resizer$='left'] { justify-self: start; } [data-iui-resizer$='right'] { justify-self: end; }`;