@itwin/itwinui-react
Version:
A react component library for iTwinUI
256 lines (255 loc) • 8.23 kB
JavaScript
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;
}`;