@aokiapp/rjsf-mantine-theme
Version:
Mantine theme, fields and widgets for react-jsonschema-form
171 lines • 6.98 kB
JavaScript
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