UNPKG

@geneui/components

Version:

The Gene UI components library designed for BI tools

220 lines (214 loc) 18.6 kB
import { _ as __rest } from '../tslib.es6-f211516f.js'; import React__default, { forwardRef, useRef, useState, useImperativeHandle, useCallback, useEffect } from 'react'; import Portal from '../Portal/index.js'; import { c as classnames } from '../index-031ff73c.js'; import { f as fileSizeDisplay, n as noop } from '../index-a0e4e333.js'; import '../configs-00612ce0.js'; import useImgDownload from '../hooks/useImgDownload.js'; import { u as useEllipsisDetection } from '../useEllipsisDetection-4d997d5d.js'; import Icon from '../Icon/index.js'; import Switcher from '../Switcher/index.js'; import { T as Tooltip } from '../index-6d7e99cd.js'; import { s as styleInject } from '../style-inject.es-746bb8ed.js'; import 'react-dom'; import 'prop-types'; import '../GeneUIProvider/index.js'; import '../dateValidation-67caec66.js'; import '../_commonjsHelpers-24198af3.js'; import '../hooks/useDebounce.js'; import '../_rollupPluginBabelHelpers-e8fb2e5c.js'; import '../hooks/useKeyDown.js'; import '../checkboxRadioSwitcher-5b69d7bd.js'; import '../guid-8ddf77b3.js'; import '../hooks/useDeviceType.js'; import '../hooks/useWindowSize.js'; const bufferSize = 40; const borderWidth = 2; const Magnifier = forwardRef(({ imgUrl, className, name = '', withRotation = false, withMagnifier = false, showMagnifier = false, zoom = 1.5, magnifierAppearance = 'square' }, ref) => { const imgRef = useRef(null); const glassRef = useRef(null); const [rotationDeg, setRotationDeg] = useState(0); const [isCursorInScopeOfImage, setIsCursorInScopeOfImage] = useState(false); const [glassPositionStyles, setGlassPositionStyles] = useState({}); const rotate = (deg = 90) => { if (!withRotation) return; const newDeg = rotationDeg + deg; setRotationDeg(Math.abs(newDeg) >= 360 ? 0 : newDeg); }; useImperativeHandle(ref, () => ({ rotate })); const onMouseMoveHandler = useCallback((e) => { var _a, _b; // Prevent any calculation in case of magnifier is turned off if (!withMagnifier || !imgRef.current || !glassRef.current) return; e.preventDefault(); const img = imgRef.current; const cnt = img.offsetParent; if (!cnt) return; const glassWidth = ((_a = glassRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) / 2; const glassHeight = ((_b = glassRef.current) === null || _b === void 0 ? void 0 : _b.offsetHeight) / 2; // Positive value of rotation deg const absRotationDeg = Math.abs(rotationDeg); const { clientX, clientY } = e; const stylesProps = {}; // @TODO need move to useMemo hooks for better performance const { bottom, height, left, right, top, width } = cnt.getBoundingClientRect(); let x = clientX - left - glassWidth; let y = clientY - top - glassHeight; // Calculate horizontal thresholds for img if (clientX < left + bufferSize) x = -glassWidth + bufferSize; if (clientX > right - bufferSize) x = width - bufferSize - glassWidth; // Calculate vertical thresholds for img if (clientY < top + bufferSize) y = -glassHeight + bufferSize; if (clientY > bottom - bufferSize) y = height - glassHeight - bufferSize; // Handle vertical rotation cases if (rotationDeg === 0 || absRotationDeg === 180) { if (rotationDeg === 0) { // Adoption coordinates values to img sides stylesProps.left = `${x}px`; stylesProps.top = `${y}px`; // Calculate zoomed image positions const glassX = x * zoom + bufferSize * zoom + borderWidth * 2; const glassY = y * zoom + bufferSize * zoom + borderWidth * 2; stylesProps.backgroundPosition = `-${glassX}px -${glassY}px`; } if (absRotationDeg === 180) { // Adoption coordinates values to img sides stylesProps.right = `${x}px`; stylesProps.bottom = `${y}px`; // Calculate zoomed image positions const glassX = width * zoom - glassWidth - 40 * zoom - (x * zoom + 40 * zoom); const glassY = height * zoom - glassHeight - 40 * zoom - (y * zoom + 40 * zoom); stylesProps.backgroundPosition = `-${glassX}px -${glassY}px`; } } // Handle horizontal rotation cases if (absRotationDeg === 90 || absRotationDeg === 270) { if (rotationDeg === 90 || rotationDeg === -270) { // Adoption coordinates values to img sides stylesProps.bottom = `${x}px`; // x stylesProps.left = `${y}px`; // y // Calculate zoomed image positions const glassX = img.height * zoom - glassHeight - 40 * zoom - (x * zoom + 40 * zoom); const glassY = y * zoom + 40 * zoom; stylesProps.backgroundPosition = `-${glassY}px -${glassX}px`; } if (rotationDeg === 270 || rotationDeg === -90) { // Adoption coordinates values to img sides stylesProps.top = `${x}px`; // x stylesProps.right = `${y}px`; // y // Calculate zoomed image positions const glassX = x * zoom + 40 * zoom; const glassY = img.width * zoom - glassWidth - 40 * zoom - (y * zoom + 40 * zoom); stylesProps.backgroundPosition = `-${glassY}px -${glassX}px`; } } setGlassPositionStyles(stylesProps); }, [rotationDeg]); const onMouseEnterHandler = () => showMagnifier && setIsCursorInScopeOfImage(true); const onMouseLeaveHandler = (e) => { if (e.relatedTarget !== glassRef.current) { setIsCursorInScopeOfImage(false); } }; return (React__default.createElement(React__default.Fragment, null, React__default.createElement("div", { className: "imgMagnifier", style: { transform: `rotateZ(${rotationDeg}deg)` } }, withMagnifier && showMagnifier && isCursorInScopeOfImage && (React__default.createElement("div", { ref: glassRef, className: "imgMagnifier__glass", style: Object.assign({ borderRadius: `${magnifierAppearance === 'circle' ? 50 : 0}%`, backgroundImage: `url(${imgUrl})`, backgroundRepeat: 'no-repeat', backgroundSize: imgRef.current ? `${imgRef.current.clientWidth * zoom}px ${imgRef.current.clientHeight * zoom}px` : `0 0` }, glassPositionStyles), onMouseLeave: onMouseLeaveHandler, onMouseMove: onMouseMoveHandler })), React__default.createElement("img", { ref: imgRef, src: imgUrl, alt: name, onMouseEnter: onMouseEnterHandler, onMouseMove: (e) => showMagnifier && onMouseMoveHandler(e), className: `imgMagnifier__img ${className}` })))); }); var css_248z = "[data-gene-ui-version=\"2.16.5\"] .imagePreview{display:flex;flex-direction:column;max-height:100%;max-width:100%;min-width:320px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__close{display:flex}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__close{margin-left:auto}[data-gene-ui-version=\"2.16.5\"] [dir=rtl] .imagePreview.mobile-view .imagePreview__close{margin-left:0;margin-right:auto}[data-gene-ui-version=\"2.16.5\"] .imagePreview__header{align-items:center;display:flex;justify-content:space-between;padding:16px;width:100%}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__header{align-items:flex-start;flex-direction:column-reverse;padding:8px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__infoWrapper{align-items:center;align-self:stretch;display:flex;max-width:calc(100% - 250px)}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__infoWrapper{max-width:100%}[data-gene-ui-version=\"2.16.5\"] .imagePreview__info{display:flex;flex-direction:column;font-size:14px;font-weight:600;height:100%;justify-content:center;max-width:calc(100% - 28px);padding:4px 0}[data-gene-ui-version=\"2.16.5\"] .imagePreview__info-center{justify-content:center}[data-gene-ui-version=\"2.16.5\"] .imagePreview__imgIcon{opacity:.8}[data-gene-ui-version=\"2.16.5\"] .imagePreview__sizes{display:flex}[data-gene-ui-version=\"2.16.5\"] .imagePreview__weight{opacity:.8;padding-right:8px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__resolution{opacity:.8;padding-left:8px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__resolution--divider{opacity:.8;padding-left:0}[data-gene-ui-version=\"2.16.5\"] .imagePreview__resolution--separate{margin:0 5px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__options{align-items:center;display:flex}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__options{width:100%}[data-gene-ui-version=\"2.16.5\"] .imagePreview__magnifier{min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;padding-right:8px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__rotate{display:flex}[data-gene-ui-version=\"2.16.5\"] .imagePreview__divider,[data-gene-ui-version=\"2.16.5\"] .imagePreview__divider-small{position:relative}[data-gene-ui-version=\"2.16.5\"] .imagePreview__divider-small:after{background:rgba(var(--background-sc-rgb),.3);content:\"\";height:12px;position:absolute;right:0;top:50%;transform:translateY(-50%);width:1px}[data-gene-ui-version=\"2.16.5\"] [dir=rtl] .imagePreview__divider-small{margin:0 5px}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__divider{margin-right:6px}[data-gene-ui-version=\"2.16.5\"] .imagePreview__icon{cursor:pointer;font-size:28px;font-weight:100;margin:6px}[data-gene-ui-version=\"2.16.5\"] .imagePreview.mobile-view .imagePreview__icon{margin-left:0}[data-gene-ui-version=\"2.16.5\"] .imagePreview__content{align-items:center;aspect-ratio:1;display:flex;justify-content:center}[data-gene-ui-version=\"2.16.5\"] .imagePreview__img{max-height:100%;max-width:100%}[data-gene-ui-version=\"2.16.5\"] [dir=rtl] .imagePreview__name{padding-right:8px}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view{height:100%;left:0;position:fixed;top:0;width:100%;z-index:500}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__header{background:#000}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__infoWrapper,[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__name{color:#fff}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__sizes{color:#fff9}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__options{color:#fff}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__magnifier .switcher-element{background:#ffffff61}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__magnifier .switcher-element.active{background:var(--hero)}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__divider:after{background:#ffffff4d;content:\"\";height:24px;position:absolute;right:0;top:50%;transform:translateY(-50%);width:1px}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__divider-small:after{background:#ffffff4d}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__content{height:100%;transform:translateZ(0);width:100%}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__background{background-color:#000c;height:100%;position:absolute;width:100%}[data-gene-ui-version=\"2.16.5\"] .imagePreview.modal-view .imagePreview__img{margin:0 auto;max-height:calc(100vh - 10rem);max-width:calc(100vh - 10rem)}[data-gene-ui-version=\"2.16.5\"] .imgMagnifier{position:relative}[data-gene-ui-version=\"2.16.5\"] .imgMagnifier__glass{border:2px solid #000;cursor:none;height:150px;position:absolute;width:150px;z-index:999999}[data-gene-ui-version=\"2.16.5\"] .imgMagnifier__img{width:100%}"; styleInject(css_248z); const ImagePreview = (_a) => { var { name, path, onClose = noop, showSize = true, isMobile = false, withModal = true, showRotate = true, showDownload = true, customHeaders, showDimensions = true, withMagnifier = false, magnifierDefaultValue = true } = _a, rest = __rest(_a, ["name", "path", "onClose", "showSize", "isMobile", "withModal", "showRotate", "showDownload", "customHeaders", "showDimensions", "withMagnifier", "magnifierDefaultValue"]); const [isMagnifierOn, setIsMagnifierOn] = useState(magnifierDefaultValue); const [imageData, setImageData] = useState(''); const [meta, setMeta] = useState({ size: 0, width: 0, height: 0 }); const nameRef = useRef(null); const magnifierRef = useRef(null); const isTruncated = useEllipsisDetection(nameRef); const downloadImg = useImgDownload(); useEffect(() => { !!path && fetch(path, { headers: Object.assign({}, customHeaders) }) .then((r) => r.arrayBuffer()) .then((buffer) => { const img = new Image(); const blob = new Blob([buffer], { type: 'image/jpeg' }); img.src = URL.createObjectURL(blob); img.onload = () => setMeta((val) => (Object.assign(Object.assign({}, val), { width: img.naturalWidth, height: img.naturalHeight }))); setMeta((val) => (Object.assign(Object.assign({}, val), { size: buffer.byteLength }))); setImageData(img.src); }); }, [path]); const MagnifierWrapper = imageData && (React__default.createElement(Magnifier, { ref: magnifierRef, imgUrl: imageData, name: name, zoom: 2, showMagnifier: isMagnifierOn, className: "imagePreview__img", withRotation: true, withMagnifier: true })); return (React__default.createElement("div", Object.assign({ className: classnames('imagePreview', { 'modal-view': withModal, 'mobile-view': isMobile }) }, rest), React__default.createElement("div", { className: "imagePreview__header" }, React__default.createElement("div", { className: "imagePreview__infoWrapper" }, React__default.createElement(Icon, { type: "bc-icon-Image", className: "imagePreview__icon imagePreview__imgIcon" }), React__default.createElement("div", { className: classnames('imagePreview__info', { 'imagePreview__info-center': !showSize && !showDimensions }) }, name && (React__default.createElement(Tooltip, { text: name, isVisible: isTruncated }, React__default.createElement("span", { className: "imagePreview__name ellipsis-text", ref: nameRef }, name))), React__default.createElement("div", { className: "imagePreview__sizes" }, showSize && React__default.createElement("span", { className: "imagePreview__weight" }, fileSizeDisplay(meta.size)), showSize && showDimensions && React__default.createElement("div", { className: "imagePreview__divider-small" }), showDimensions && (React__default.createElement("span", { className: classnames('imagePreview__resolution', { 'imagePreview__resolution--divider': !showSize }) }, React__default.createElement("span", null, meta.width), React__default.createElement("span", { className: "imagePreview__resolution--separate" }, "x"), React__default.createElement("span", null, meta.height)))))), React__default.createElement("div", { className: "imagePreview__options" }, withMagnifier && (React__default.createElement("div", { className: "imagePreview__magnifier" }, React__default.createElement(Switcher /*@ts-ignore*/ , { /*@ts-ignore*/ defaultChecked: magnifierDefaultValue, value: isMagnifierOn, onChange: (e) => setIsMagnifierOn(e.currentTarget.checked), labelPosition: "left", label: "Magnifier", className: "imagePreview__switcher" }))), showRotate && (React__default.createElement("div", { className: "imagePreview__rotate" }, React__default.createElement(Icon, { type: "bc-icon-rotate-left", className: "imagePreview__icon", onClick: () => { var _a; if (!((_a = magnifierRef.current) === null || _a === void 0 ? void 0 : _a.rotate)) return; magnifierRef.current.rotate(-90); } }), React__default.createElement(Icon, { type: "bc-icon-rotate-right", className: "imagePreview__icon", onClick: () => { var _a; if (!((_a = magnifierRef.current) === null || _a === void 0 ? void 0 : _a.rotate)) return; magnifierRef.current.rotate(90); } }))), showDownload && (React__default.createElement("div", { className: "imagePreview__download" }, React__default.createElement(Icon, { type: "bc-icon-download", className: "imagePreview__icon", onClick: () => downloadImg(path, name, customHeaders) }))), withModal && (React__default.createElement("div", { className: "imagePreview__close" }, React__default.createElement("div", { className: "imagePreview__divider" }), React__default.createElement(Icon, { type: "bc-icon-close", className: "imagePreview__icon", onClick: onClose }))))), React__default.createElement("div", { className: "imagePreview__content" }, withModal ? (React__default.createElement(React__default.Fragment, null, React__default.createElement("div", { className: "imagePreview__background", onClick: onClose }), MagnifierWrapper)) : (MagnifierWrapper)))); }; const ImagePreviewHOC = (_a) => { var { withModal } = _a, restProps = __rest(_a, ["withModal"]); return withModal ? ( // @ts-ignore React__default.createElement(Portal, { isOpen: true }, React__default.createElement(ImagePreview, Object.assign({}, restProps, { withModal: withModal })))) : (React__default.createElement(ImagePreview, Object.assign({}, restProps, { withModal: withModal }))); }; export { ImagePreviewHOC as default };