UNPKG

@react-md/form

Version:

This package is for creating all the different form input types.

319 lines 13.2 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useFileUpload = void 0; var react_1 = require("react"); var nanoid_1 = require("nanoid"); var utils_1 = require("@react-md/utils"); var utils_2 = require("./utils"); /** @internal */ var EMPTY_LIST = []; /** @internal */ var EMPTY_OBJECT = {}; /** * This hook is generally used to upload files **to the browser** in different * formats to be previewed `<img>`, `<video>`, `<embed>`, etc tags. However, it * can also be used to upload the files as an `ArrayBuffer` and then uploaded to * a server. * * Note: If using the `aws-sdk` to upload files directly to S3, **do not use * this hook** since it uses its own upload process. * * @typeParam E - An optional HTMLElement type that is used for the * {@link FileUploadHandlers}. * @typeParam CustomError - An optional error type that gets returned from the * {@link FilesValidator}. * @param options - All the {@link FileUploadOptions} * @returns the {@link FileUploadHookReturnValue} * @remarks \@since 2.9.0 */ function useFileUpload(_a) { var _b = _a === void 0 ? {} : _a, _c = _b.maxFiles, maxFiles = _c === void 0 ? -1 : _c, _d = _b.extensions, extensions = _d === void 0 ? EMPTY_LIST : _d, _e = _b.minFileSize, minFileSize = _e === void 0 ? -1 : _e, _f = _b.maxFileSize, maxFileSize = _f === void 0 ? -1 : _f, _g = _b.totalFileSize, totalFileSize = _g === void 0 ? -1 : _g, _h = _b.concurrency, concurrency = _h === void 0 ? -1 : _h, propOnDrop = _b.onDrop, propOnChange = _b.onChange, _j = _b.validateFiles, validateFiles = _j === void 0 ? utils_2.validateFiles : _j, _k = _b.getFileParser, getFileParser = _k === void 0 ? utils_2.getFileParser : _k, _l = _b.isValidFileName, isValidFileName = _l === void 0 ? utils_2.isValidFileName : _l; var _m = __read((0, react_1.useReducer)(function reducer(state, action) { var _a, _b, _c, _d; switch (action.type) { case "reset": // need to reuse constants so that calling reset doesn't cause an // infinite loop in an effect return { stats: EMPTY_OBJECT, errors: EMPTY_LIST, readers: EMPTY_OBJECT, }; case "remove": return __assign(__assign({}, state), { stats: (0, utils_1.omit)(state.stats, action.files) }); case "queue": return __assign(__assign({}, state), { stats: __assign(__assign({}, state.stats), action.files.reduce(function (files, file) { var key = (0, nanoid_1.nanoid)(); files[key] = { key: key, file: file, progress: 0, status: "pending", }; return files; }, {})), errors: __spreadArray(__spreadArray([], __read(state.errors), false), __read(action.errors), false) }); case "start": { var key = action.key, reader = action.reader; /* istanbul ignore next */ if (!state.stats[key]) { throw new Error("Missing file with key \"".concat(key, "\"")); } var fileStats = { key: key, file: state.stats[key].file, progress: 0, status: "uploading", }; return __assign(__assign({}, state), { readers: __assign(__assign({}, state.readers), (_a = {}, _a[key] = reader, _a)), stats: __assign(__assign({}, state.stats), (_b = {}, _b[key] = fileStats, _b)) }); } case "progress": { var key = action.key, progress = action.progress; /* istanbul ignore next */ if (!state.stats[key]) { throw new Error("Missing file with key \"".concat(key, "\"")); } return __assign(__assign({}, state), { stats: __assign(__assign({}, state.stats), (_c = {}, _c[key] = __assign(__assign({}, state.stats[key]), { progress: progress }), _c)) }); } case "complete": { var key = action.key, result = action.result; /* istanbul ignore next */ if (!state.stats[key]) { throw new Error("Missing file with key \"".concat(key, "\"")); } var file = { key: key, file: state.stats[key].file, status: "complete", result: result, progress: 100, }; var _e = state.readers, _f = key, _reader = _e[_f], readers_1 = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]); return __assign(__assign({}, state), { readers: readers_1, stats: __assign(__assign({}, state.stats), (_d = {}, _d[key] = file, _d)) }); } case "clearErrors": return __assign(__assign({}, state), { errors: [] }); default: /* istanbul ignore next */ return state; } }, { stats: EMPTY_OBJECT, errors: EMPTY_LIST, readers: EMPTY_OBJECT, }), 2), state = _m[0], dispatch = _m[1]; var stats = state.stats, errors = state.errors, readers = state.readers; var statsList = Object.values(stats); var totalFiles = statsList.length; var totalBytes = statsList.reduce(function (result, _a) { var size = _a.file.size; return result + size; }, 0); var queueFiles = (0, react_1.useCallback)(function (files) { var _a = validateFiles(files, { maxFiles: maxFiles, extensions: extensions, minFileSize: minFileSize, maxFileSize: maxFileSize, totalBytes: totalBytes, totalFiles: totalFiles, totalFileSize: totalFileSize, isValidFileName: isValidFileName, }), pending = _a.pending, errors = _a.errors; dispatch({ type: "queue", errors: errors, files: pending }); }, [ validateFiles, maxFiles, extensions, minFileSize, maxFileSize, totalBytes, totalFiles, totalFileSize, isValidFileName, ]); var onDrop = (0, react_1.useCallback)(function (event) { propOnDrop === null || propOnDrop === void 0 ? void 0 : propOnDrop(event); event.preventDefault(); event.stopPropagation(); try { var files = event.dataTransfer.files; if (files) { queueFiles(Array.from(files)); } } catch (e) { dispatch({ type: "queue", files: [], errors: [ new utils_2.FileAccessError(e instanceof Error ? e.message : undefined), ], }); } }, [queueFiles, propOnDrop]); var onChange = (0, react_1.useCallback)(function (event) { propOnChange === null || propOnChange === void 0 ? void 0 : propOnChange(event); try { var files = event.currentTarget.files; if (files) { queueFiles(Array.from(files)); } else { throw new Error(); } } catch (e) { dispatch({ type: "queue", files: [], errors: [ new utils_2.FileAccessError(e instanceof Error ? e.message : undefined), ], }); } }, [queueFiles, propOnChange]); var remove = (0, react_1.useCallback)(function (keyOrKeys) { var files = typeof keyOrKeys === "string" ? [keyOrKeys] : keyOrKeys; files.forEach(function (fileKey) { var _a; (_a = readers[fileKey]) === null || _a === void 0 ? void 0 : _a.abort(); }); dispatch({ type: "remove", files: files }); }, [readers]); var reset = (0, react_1.useCallback)(function () { Object.values(readers).forEach(function (reader) { reader.abort(); }); dispatch({ type: "reset" }); }, [readers]); var clearErrors = (0, react_1.useCallback)(function () { dispatch({ type: "clearErrors" }); }, []); var start = (0, react_1.useCallback)(function (key, reader) { dispatch({ type: "start", key: key, reader: reader }); }, []); var complete = (0, react_1.useCallback)(function (key, result) { if (result === void 0) { result = null; } dispatch({ type: "complete", key: key, result: result }); }, []); var createProgressEventHandler = (0, react_1.useCallback)(function (key) { return function (event) { if (event.lengthComputable) { var percentage = Math.round((event.loaded * 100) / event.total); dispatch({ type: "progress", key: key, progress: percentage }); } }; }, []); (0, react_1.useEffect)(function () { var pending = []; var uploading = []; Object.values(stats).forEach(function (file) { if (file.status === "pending") { pending.push(file); } else if (file.status === "uploading") { uploading.push(file); } }); var lastIndex = concurrency === -1 ? pending.length : Math.max(0, concurrency - uploading.length); var queue = pending.slice(0, lastIndex); if (!queue.length) { return; } queue.forEach(function (stats) { var key = stats.key, file = stats.file; var reader = new FileReader(); // using `addEventListener` instead of directly setting to // `reader.progress`/`reader.load` so it's easier to test reader.addEventListener("progress", createProgressEventHandler(key)); reader.addEventListener("load", function () { complete(key, reader.result); }); start(key, reader); var parser = getFileParser(file); /* istanbul ignore next */ if (process.env.NODE_ENV !== "production" && ![ "readAsText", "readAsDataURL", "readAsArrayBuffer", "readAsBinaryString", ].includes(parser)) { throw new Error("Invalid file reader parser"); } reader[parser](file); }); }, [ concurrency, stats, getFileParser, createProgressEventHandler, start, complete, ]); var accept = ""; if (extensions.length) { accept = extensions.reduce(function (s, ext) { return "".concat(s ? "".concat(s, ",") : "", ".").concat(ext); }, ""); } return { stats: statsList, errors: errors, accept: accept, totalBytes: totalBytes, totalFiles: totalFiles, onDrop: onDrop, onChange: onChange, reset: reset, remove: remove, clearErrors: clearErrors, }; } exports.useFileUpload = useFileUpload; //# sourceMappingURL=useFileUpload.js.map