UNPKG

@aokiapp/rjsf-mantine-theme

Version:

Mantine theme, fields and widgets for react-jsonschema-form

171 lines 6.98 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { createContext, useCallback, useContext, useMemo } from 'react'; import { dataURItoBlob, getTemplate } from '@rjsf/utils'; import { Badge, Card, Group, Text, Image, Box, AspectRatio, CloseButton } from '@mantine/core'; import { IconCode, IconFile, IconFileDigit, IconFileTypeTxt, IconFileZip, IconPdf, IconPhoto, } from '@tabler/icons-react'; function addNameToDataURL(dataURL, name) { if (dataURL === null) { return null; } return dataURL.replace(';base64', `;name=${encodeURIComponent(name)};base64`); } function processFile(file) { const { name, size, type } = file; return new Promise((resolve, reject) => { const reader = new window.FileReader(); reader.onerror = reject; reader.onload = (event) => { var _a; if (typeof ((_a = event.target) === null || _a === void 0 ? void 0 : _a.result) === 'string') { resolve({ dataURL: addNameToDataURL(event.target.result, name), name, size, type, }); } else { resolve({ dataURL: null, name, size, type, }); } }; reader.readAsDataURL(file); }); } function processFiles(files) { return Promise.all(Array.from(files).map(processFile)); } const fileInfoCtx = createContext(null); function FileInfoPreview({ fileInfo }) { const { preview } = useContext(fileInfoCtx); const { dataURL, type, name } = fileInfo; if (!dataURL) { return null; } if (preview && type.indexOf('image') !== -1) { return _jsx(Image, { src: dataURL, alt: name }); } let IconComponent; switch (type) { case 'application/pdf': case 'application/x-pdf': IconComponent = IconPdf; break; case 'image/svg+xml': case 'image/svg': IconComponent = IconFile; break; case 'image/png': case 'image/jpeg': case 'image/gif': case 'image/bmp': IconComponent = IconPhoto; break; case 'application/zip': case 'application/x-zip-compressed': case 'application/x-gzip': case 'application/x-tar': case 'application/x-bzip': case 'application/x-bzip2': case 'application/x-7z-compressed': case 'application/x-rar-compressed': IconComponent = IconFileZip; break; case 'text/plain': IconComponent = IconFileTypeTxt; break; case 'text/html': case 'application/xhtml+xml': case 'application/xml': case 'application/json': case 'application/javascript': IconComponent = IconCode; break; case 'application/octet-stream': IconComponent = IconFileDigit; break; default: IconComponent = IconFile; break; } return (_jsxs(Box, { style: { width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', }, children: [_jsx(IconComponent, { size: 30 }), _jsx(Badge, { variant: 'outline', children: type })] })); } function convertUnitPrefix(size) { const prefixes = ['B', 'KB', 'MB', 'GB', 'TB']; const index = Math.floor(Math.log(size) / Math.log(1024)); return `${(size / Math.pow(1024, index)).toFixed(2)} ${prefixes[index]}`; } function FilesInfo() { const { filesInfo, onRemove } = useContext(fileInfoCtx); if (filesInfo.length === 0) { return null; } return (_jsx(Group, { m: 'sm', children: filesInfo.map((fileInfo, key) => { const { name, size } = fileInfo; const rmFile = () => onRemove(key); return (_jsxs(Card, { shadow: 'sm', padding: 'xs', radius: 'md', w: 200, withBorder: true, children: [_jsx(Card.Section, { children: _jsx(AspectRatio, { ratio: 2, maw: 240, mx: 'auto', children: _jsx(FileInfoPreview, { fileInfo: fileInfo }) }) }), _jsx(Text, { fw: 600, truncate: 'end', children: name }), _jsxs(Group, { gap: 'xs', justify: 'space-between', children: [_jsx(Badge, { color: 'blue', variant: 'light', children: convertUnitPrefix(size) }), _jsx(CloseButton, { onClick: rmFile })] })] }, key)); }) })); } function extractFileInfo(dataURLs) { return dataURLs .filter((dataURL) => dataURL) .map((dataURL) => { const { blob, name } = dataURItoBlob(dataURL); return { dataURL, name: name, size: blob.size, type: blob.type, }; }); } /** * The `FileWidget` is a widget for rendering file upload fields. * It is typically used with a string property with data-url format. */ function FileWidget(props) { var _a; const { disabled, readonly, required, multiple, onChange, value, options, registry } = props; const BaseInputTemplate = getTemplate('BaseInputTemplate', registry, options); const handleChange = useCallback((event) => { if (!event.target.files) { return; } // Due to variances in themes, dealing with multiple files for the array case now happens one file at a time. // This is because we don't pass `multiple` into the `BaseInputTemplate` anymore. Instead, we deal with the single // file in each event and concatenate them together ourselves processFiles(event.target.files).then((filesInfoEvent) => { const newValue = filesInfoEvent.map((fileInfo) => fileInfo.dataURL); if (multiple) { onChange(value.concat(newValue[0])); } else { onChange(newValue[0]); } }); }, [multiple, value, onChange]); const filesInfo = useMemo(() => extractFileInfo(Array.isArray(value) ? value : [value]), [value]); const rmFile = useCallback((index) => { if (multiple) { const newValue = value.filter((_, i) => i !== index); onChange(newValue); } else { onChange(undefined); } }, [multiple, value, onChange]); return (_jsx("div", { className: 'armt-widget-file', children: _jsxs(fileInfoCtx.Provider, { value: { filesInfo, onRemove: rmFile, preview: (_a = options.filePreview) !== null && _a !== void 0 ? _a : false }, children: [_jsx(BaseInputTemplate, { ...props, disabled: disabled || readonly, type: 'file', required: value ? false : required, onChangeOverride: handleChange, value: '', accept: options.accept ? String(options.accept) : undefined }), _jsx(FilesInfo, {})] }) })); } export default FileWidget; //# sourceMappingURL=FileWidget.js.map