UNPKG

@iobroker/adapter-react-v5

Version:

React components to develop ioBroker interfaces with react.

516 lines (475 loc) 13.9 kB
import React, { Component, createRef } from 'react'; import Dropzone from 'react-dropzone'; import { Cropper } from 'react-cropper'; import { Menu, MenuItem, Tooltip, IconButton } from '@mui/material'; import { Close as IconClose, Crop as CropIcon, UploadFileOutlined as UploadIcon } from '@mui/icons-material'; import { I18n } from '../i18n'; import { Icon } from './Icon'; // import 'cropperjs/dist/cropper.css'; const cropperStyles = ` /*! * Cropper.js v1.5.12 * https://fengyuanchen.github.io/cropperjs * * Copyright 2015-present Chen Fengyuan * Released under the MIT license * * Date: 2021-06-12T08:00:11.623Z */ .cropper-container { direction: ltr; font-size: 0; line-height: 0; position: relative; -ms-touch-action: none; touch-action: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .cropper-container img { display: block; height: 100%; image-orientation: 0deg; max-height: none !important; max-width: none !important; min-height: 0 !important; min-width: 0 !important; width: 100%; } .cropper-wrap-box, .cropper-canvas, .cropper-drag-box, .cropper-crop-box, .cropper-modal { bottom: 0; left: 0; position: absolute; right: 0; top: 0; } .cropper-wrap-box, .cropper-canvas { overflow: hidden; } .cropper-drag-box { background-color: #fff; opacity: 0; } .cropper-modal { background-color: #000; opacity: 0.5; } .cropper-view-box { display: block; height: 100%; outline: 1px solid #39f; outline-color: rgba(51, 153, 255, 0.75); overflow: hidden; width: 100%; } .cropper-dashed { border: 0 dashed #eee; display: block; opacity: 0.5; position: absolute; } .cropper-dashed.dashed-h { border-bottom-width: 1px; border-top-width: 1px; height: calc(100% / 3); left: 0; top: calc(100% / 3); width: 100%; } .cropper-dashed.dashed-v { border-left-width: 1px; border-right-width: 1px; height: 100%; left: calc(100% / 3); top: 0; width: calc(100% / 3); } .cropper-center { display: block; height: 0; left: 50%; opacity: 0.75; position: absolute; top: 50%; width: 0; } .cropper-center::before, .cropper-center::after { background-color: #eee; content: ' '; display: block; position: absolute; } .cropper-center::before { height: 1px; left: -3px; top: 0; width: 7px; } .cropper-center::after { height: 7px; left: 0; top: -3px; width: 1px; } .cropper-face, .cropper-line, .cropper-point { display: block; height: 100%; opacity: 0.1; position: absolute; width: 100%; } .cropper-face { background-color: #fff; left: 0; top: 0; } .cropper-line { background-color: #39f; } .cropper-line.line-e { cursor: ew-resize; right: -3px; top: 0; width: 5px; } .cropper-line.line-n { cursor: ns-resize; height: 5px; left: 0; top: -3px; } .cropper-line.line-w { cursor: ew-resize; left: -3px; top: 0; width: 5px; } .cropper-line.line-s { bottom: -3px; cursor: ns-resize; height: 5px; left: 0; } .cropper-point { background-color: #39f; height: 5px; opacity: 0.75; width: 5px; } .cropper-point.point-e { cursor: ew-resize; margin-top: -3px; right: -3px; top: 50%; } .cropper-point.point-n { cursor: ns-resize; left: 50%; margin-left: -3px; top: -3px; } .cropper-point.point-w { cursor: ew-resize; left: -3px; margin-top: -3px; top: 50%; } .cropper-point.point-s { bottom: -3px; cursor: s-resize; left: 50%; margin-left: -3px; } .cropper-point.point-ne { cursor: nesw-resize; right: -3px; top: -3px; } .cropper-point.point-nw { cursor: nwse-resize; left: -3px; top: -3px; } .cropper-point.point-sw { bottom: -3px; cursor: nesw-resize; left: -3px; } .cropper-point.point-se { bottom: -3px; cursor: nwse-resize; height: 20px; opacity: 1; right: -3px; width: 20px; } @media (min-width: 768px) { .cropper-point.point-se { height: 15px; width: 15px; } } @media (min-width: 992px) { .cropper-point.point-se { height: 10px; width: 10px; } } @media (min-width: 1200px) { .cropper-point.point-se { height: 5px; opacity: 0.75; width: 5px; } } .cropper-point.point-se::before { background-color: #39f; bottom: -50%; content: ' '; display: block; height: 200%; opacity: 0; position: absolute; right: -50%; width: 200%; } .cropper-invisible { opacity: 0; } .cropper-bg { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC'); } .cropper-hide { display: block; height: 0; position: absolute; width: 0; } .cropper-hidden { display: none !important; } .cropper-move { cursor: move; } .cropper-crop { cursor: crosshair; } .cropper-disabled .cropper-drag-box, .cropper-disabled .cropper-face, .cropper-disabled .cropper-line, .cropper-disabled .cropper-point { cursor: not-allowed; } `; const styles = { dropZone: { width: '100%', height: 100, position: 'relative', }, dropZoneEmpty: {}, image: { objectFit: 'contain', margin: 'auto', display: 'flex', width: '100%', height: '100%', }, uploadDiv: { position: 'relative', width: '100%', height: 300, opacity: 0.9, marginTop: 30, cursor: 'pointer', outline: 'none', }, uploadDivDragging: { opacity: 1, background: 'rgba(128,255,128,0.1)', }, uploadCenterDiv: { margin: 5, border: '3px dashed grey', borderRadius: 5, width: 'calc(100% - 10px)', height: 'calc(100% - 10px)', position: 'relative', display: 'flex', }, uploadCenterIcon: { paddingTop: 10, width: 48, height: 48, }, uploadCenterText: { fontSize: 16, }, uploadCenterTextAndIcon: { textAlign: 'center', position: 'absolute', top: 0, bottom: 0, left: 0, right: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', }, disabledOpacity: { opacity: 0.3, cursor: 'default', }, buttonRemoveWrapper: { position: 'absolute', zIndex: 222, right: 0, }, buttonCropWrapper: { position: 'absolute', zIndex: 222, right: 0, top: 50, }, error: { border: '2px solid red', boxSizing: 'border-box', }, }; export class UploadImage extends Component { cropperRef; constructor(props) { super(props); this.state = { uploadFile: false, anchorEl: null, cropHandler: false, }; this.cropperRef = createRef(); if (!window.document.getElementById('cropper-style-json-component')) { const style = window.document.createElement('style'); style.setAttribute('id', 'cropper-style-json-component'); style.innerHTML = cropperStyles; window.document.head.appendChild(style); } } onDrop(acceptedFiles) { const onChange = this.props.onChange; const maxSize = this.props.maxSize || 10 * 1024; const file = acceptedFiles[0]; const reader = new FileReader(); reader.onabort = () => console.log('file reading was aborted'); reader.onerror = () => console.log('file reading has failed'); reader.onload = () => { if (!file || !file.name) { return; } const parts = file.name?.split('.'); let ext = parts?.length ? `image/${parts.pop()?.toLowerCase()}` : 'image/jpeg'; if (ext === 'image/jpg') { ext = 'image/jpeg'; } else if (ext.includes('svg')) { ext = 'image/svg+xml'; } if (file.size > maxSize) { window.alert(I18n.t('ra_File is too big. Max %sk allowed. Try use SVG.', Math.round(maxSize / 1024))); } else { const base64 = `data:${ext};base64,${btoa(new Uint8Array(reader.result).reduce((data, byte) => data + String.fromCharCode(byte), ''))}`; if (onChange) { onChange(base64); } else { console.log(base64); } } }; reader.readAsArrayBuffer(file); } render() { const { disabled, icon, removeIconFunc, error, crop, onChange } = this.props; const maxSize = this.props.maxSize || 10 * 1024; let accept = this.props.accept || { 'image/*': [] }; const { uploadFile, anchorEl, cropHandler } = this.state; // covert '"image/png"' to { 'image/*': [] } if (typeof accept === 'string') { accept = { [accept]: [] }; } else if (Array.isArray(accept)) { const result = {}; accept.forEach(item => { result[item] = []; }); accept = result; } return (React.createElement(Dropzone, { disabled: !!disabled || cropHandler, key: "dropzone", multiple: false, accept: accept, maxSize: maxSize, onDragEnter: () => this.setState({ uploadFile: 'dragging' }), onDragLeave: () => this.setState({ uploadFile: true }), onDrop: (acceptedFiles, errors) => { this.setState({ uploadFile: false }); if (!acceptedFiles.length) { window.alert(errors?.[0]?.errors?.[0]?.message || I18n.t('ra_Cannot upload')); } else { this.onDrop(acceptedFiles); } } }, ({ getRootProps, getInputProps }) => (React.createElement("div", { style: { ...styles.uploadDiv, ...(uploadFile === 'dragging' ? styles.uploadDivDragging : undefined), ...styles.dropZone, ...(disabled ? styles.disabledOpacity : undefined), ...(!icon ? styles.dropZoneEmpty : undefined), }, ...getRootProps() }, React.createElement("input", { ...getInputProps() }), React.createElement("div", { style: { ...styles.uploadCenterDiv, ...(error ? styles.error : undefined) } }, !icon ? (React.createElement("div", { style: styles.uploadCenterTextAndIcon }, React.createElement(UploadIcon, { style: styles.uploadCenterIcon }), React.createElement("div", { style: styles.uploadCenterText }, uploadFile === 'dragging' ? I18n.t('ra_Drop file here') : I18n.t('ra_Place your files here or click here to open the browse dialog')))) : (removeIconFunc && !cropHandler && (React.createElement("div", { style: styles.buttonRemoveWrapper }, React.createElement(Tooltip, { title: I18n.t('ra_Clear'), slotProps: { popper: { sx: { pointerEvents: 'none' } } } }, React.createElement(IconButton, { size: "large", onClick: e => { removeIconFunc && removeIconFunc(); e.stopPropagation(); } }, React.createElement(IconClose, null)))))), icon && crop && (React.createElement("div", { style: styles.buttonCropWrapper }, React.createElement(Tooltip, { title: I18n.t('ra_Crop'), slotProps: { popper: { sx: { pointerEvents: 'none' } } } }, React.createElement(IconButton, { size: "large", onClick: e => { if (!cropHandler) { this.setState({ cropHandler: true }); } else { this.setState({ anchorEl: e.currentTarget }); } e.stopPropagation(); } }, React.createElement(CropIcon, { color: cropHandler ? 'primary' : 'inherit' }))), React.createElement(Menu, { anchorEl: anchorEl, keepMounted: true, open: Boolean(anchorEl), onClose: () => this.setState({ anchorEl: null }) }, React.createElement(MenuItem, { onClick: () => this.setState({ anchorEl: null, cropHandler: false }, () => { const imageElement = this.cropperRef?.current?.cropper; if (imageElement) { if (onChange) { onChange(imageElement.getCroppedCanvas().toDataURL()); } else { console.log(imageElement.getCroppedCanvas().toDataURL()); } } }) }, I18n.t('ra_Save')), React.createElement(MenuItem, { onClick: () => this.setState({ anchorEl: null, cropHandler: false }) }, I18n.t('ra_Close'))))), icon && !cropHandler ? (React.createElement(Icon, { src: icon, style: styles.image, alt: "icon" })) : null, icon && crop && cropHandler ? (React.createElement(Cropper, { ref: this.cropperRef, style: styles.image, src: icon, initialAspectRatio: 1, viewMode: 1, guides: false, minCropBoxHeight: 10, minCropBoxWidth: 10, background: false, checkOrientation: false })) : null))))); } } //# sourceMappingURL=UploadImage.js.map