UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

518 lines (510 loc) 22.6 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import { useRef, useState, useMemo, useCallback, forwardRef } from 'react'; import { StyledFileUploadWrapper } from './StyledFileUploadWrapper.js'; import { fileUploadColorTokens, getFileUploadInputHoverTokens, fileUploadLinkBorderTokens } from './fileUploadTokens.js'; import { FileUploadItem } from './FileUploadItem.js'; import { isFileAccepted } from './isFileAccepted.js'; import '../Box/BaseBox/index.js'; import '../Box/index.js'; import { SelectorLabel } from '../Form/Selector/SelectorLabel.web.js'; import { SelectorInput } from '../Form/Selector/SelectorInput.web.js'; import '../VisuallyHidden/index.web.js'; import '../Form/index.js'; import { useFormId } from '../Form/useFormId.js'; import '../../utils/assignWithoutSideEffects/index.js'; import '../../utils/metaAttribute/index.js'; import '../Box/styledProps/index.js'; import '../../utils/index.js'; import '../Typography/index.js'; import { getHintType } from '../Input/BaseInput/BaseInput.js'; import '../../utils/makeAccessible/index.js'; import { formHintLeftLabelMarginLeft } from '../Input/BaseInput/baseInputTokens.js'; import { useMergeRefs } from '../../utils/useMergeRefs.js'; import { useControllableState } from '../../utils/useControllable.js'; import { getOuterMotionRef, getInnerMotionRef } from '../../utils/getMotionRefs.js'; import '../../utils/makeAnalyticsAttribute/index.js'; import { jsxs, jsx } from 'react/jsx-runtime'; import useTheme from '../BladeProvider/useTheme.js'; import { makeAccessible } from '../../utils/makeAccessible/makeAccessible.web.js'; import { BaseBox } from '../Box/BaseBox/BaseBox.web.js'; import { metaAttribute } from '../../utils/metaAttribute/metaAttribute.web.js'; import { MetaConstants } from '../../utils/metaAttribute/metaConstants.js'; import { getStyledProps } from '../Box/styledProps/getStyledProps.js'; import { FormLabel } from '../Form/FormLabel.js'; import { screenReaderStyles } from '../VisuallyHidden/ScreenReaderStyles.js'; import { Box } from '../Box/Box.js'; import { makeSize } from '../../utils/makeSize/makeSize.js'; import { Text } from '../Typography/Text/Text.js'; import { makeAnalyticsAttribute } from '../../utils/makeAnalyticsAttribute/makeAnalyticsAttribute.js'; import { FormHint } from '../Form/FormHint.js'; import { assignWithoutSideEffects } from '../../utils/assignWithoutSideEffects/assignWithoutSideEffects.js'; var _excluded = ["name", "accept", "uploadType", "onChange", "onPreview", "onRemove", "onReupload", "onDismiss", "onDrop", "isDisabled", "isRequired", "necessityIndicator", "fileList", "testID", "label", "labelPosition", "accessibilityLabel", "validationState", "helpText", "errorText", "maxCount", "maxSize", "size", "_motionMeta"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } var _FileUpload = function _FileUpload(_ref, ref) { var name = _ref.name, accept = _ref.accept, _ref$uploadType = _ref.uploadType, uploadType = _ref$uploadType === void 0 ? 'single' : _ref$uploadType, onChange = _ref.onChange, onPreview = _ref.onPreview, _onRemove = _ref.onRemove, _onReupload = _ref.onReupload, _onDismiss = _ref.onDismiss, onDrop = _ref.onDrop, isDisabled = _ref.isDisabled, isRequired = _ref.isRequired, necessityIndicator = _ref.necessityIndicator, fileList = _ref.fileList, testID = _ref.testID, label = _ref.label, _ref$labelPosition = _ref.labelPosition, labelPosition = _ref$labelPosition === void 0 ? 'top' : _ref$labelPosition, accessibilityLabel = _ref.accessibilityLabel, validationState = _ref.validationState, helpText = _ref.helpText, errorText = _ref.errorText, maxCount = _ref.maxCount, maxSize = _ref.maxSize, _ref$size = _ref.size, size = _ref$size === void 0 ? 'medium' : _ref$size, _motionMeta = _ref._motionMeta, rest = _objectWithoutProperties(_ref, _excluded); var inputRef = useRef(null); var mergedRef = useMergeRefs(ref, inputRef); var _useTheme = useTheme(), platform = _useTheme.platform; var _useControllableState = useControllableState({ value: fileList, defaultValue: fileList !== null && fileList !== void 0 ? fileList : [] }), _useControllableState2 = _slicedToArray(_useControllableState, 2), selectedFiles = _useControllableState2[0], setSelectedFiles = _useControllableState2[1]; var _useState = useState(null), _useState2 = _slicedToArray(_useState, 2), errorMessage = _useState2[0], setErrorMessage = _useState2[1]; var _useState3 = useState('none'), _useState4 = _slicedToArray(_useState3, 2), internalValidationState = _useState4[0], setInternalValidationState = _useState4[1]; var _useState5 = useState(false), _useState6 = _slicedToArray(_useState5, 2), isActive = _useState6[0], setIsActive = _useState6[1]; var isMultiple = uploadType === 'multiple'; var isOneFileSelectedWithSingleUpload = !isMultiple && selectedFiles.length === 1; var inputLabelPosition = platform === 'onMobile' ? 'top' : labelPosition; var isLabelLeftPositioned = inputLabelPosition === 'left'; var willRenderHintText = Boolean(helpText) || Boolean(errorMessage); var showError = validationState === 'error' || internalValidationState === 'error'; var showHelpText = !showError && helpText; var accessibilityText = accessibilityLabel !== null && accessibilityLabel !== void 0 ? accessibilityLabel : ",".concat(showError ? errorMessage : '', " ").concat(showHelpText ? helpText : ''); var _useFormId = useFormId('fileuploadinput'), inputId = _useFormId.inputId, labelId = _useFormId.labelId, helpTextId = _useFormId.helpTextId, errorTextId = _useFormId.errorTextId; var accessibilityProps = makeAccessible({ required: Boolean(isRequired), invalid: Boolean(showError), disabled: Boolean(isDisabled), describedBy: labelId }); // In control mode attach a unique id to each file if not provided useMemo(function () { var _iterator = _createForOfIteratorHelper(selectedFiles), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var file = _step.value; if (!file.id) { file.id = "".concat(new Date().getTime().toString()).concat(Math.floor(Math.random() * 1000000)); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } }, [selectedFiles]); var handleFilesChange = useCallback(function (inputFiles) { setSelectedFiles(function (prevFiles) { if (prevFiles.length > 0) { var allFiles = [].concat(_toConsumableArray(prevFiles), _toConsumableArray(inputFiles)); return allFiles; } return inputFiles; }); }, []); var validateFiles = function validateFiles(inputFiles, allFiles) { if (accept && inputFiles.some(function (file) { return !isFileAccepted(file, accept); })) { setErrorMessage("You provided an unsupported file type. Supported file types are: ".concat(accept)); setInternalValidationState('error'); return true; } if (uploadType === 'single' && inputFiles.length > 1) { setErrorMessage('You can upload only one file.'); setInternalValidationState('error'); return true; } if (maxCount && allFiles.length > maxCount) { setErrorMessage("You can't upload more than ".concat(maxCount, " files.")); setInternalValidationState('error'); return true; } if (maxSize && inputFiles.some(function (file) { return file.size > maxSize; })) { setErrorMessage('File size exceeded.'); setInternalValidationState('error'); return true; } setInternalValidationState('none'); setErrorMessage(null); return false; }; var handleDragOver = function handleDragOver(event) { event.preventDefault(); setIsActive(true); }; var handleDragLeave = function handleDragLeave(event) { event.preventDefault(); setIsActive(false); }; var handleDrop = function handleDrop(event) { event.preventDefault(); setIsActive(false); var droppedFiles = Array.from(event.dataTransfer.files); var allFiles = selectedFiles.length > 0 ? [].concat(_toConsumableArray(selectedFiles), droppedFiles) : droppedFiles; var hasValidationErrors = validateFiles(droppedFiles, allFiles); if (!hasValidationErrors) { handleFilesChange(droppedFiles); onDrop === null || onDrop === void 0 ? void 0 : onDrop({ name: name, fileList: allFiles }); } }; var handleInputChange = function handleInputChange(event) { var _event$target$files; var inputFiles = Array.from((_event$target$files = event.target.files) !== null && _event$target$files !== void 0 ? _event$target$files : []); var allFiles = selectedFiles.length > 0 ? [].concat(_toConsumableArray(selectedFiles), inputFiles) : inputFiles; var hasValidationErrors = validateFiles(inputFiles, allFiles); if (!hasValidationErrors) { handleFilesChange(inputFiles); onChange === null || onChange === void 0 ? void 0 : onChange({ name: name, fileList: allFiles }); } // Reset the input value to allow re-selecting the same file event.target.value = ''; }; return /*#__PURE__*/jsxs(BaseBox, _objectSpread(_objectSpread(_objectSpread({ ref: getOuterMotionRef({ _motionMeta: _motionMeta, ref: ref }), display: "flex", flexDirection: "column", width: "100%" }, metaAttribute({ name: MetaConstants.FileUpload, testID: testID })), getStyledProps(rest)), {}, { children: [/*#__PURE__*/jsxs(BaseBox, { display: "flex", flexDirection: isLabelLeftPositioned ? 'row' : 'column', alignItems: isLabelLeftPositioned ? 'center' : undefined, position: "relative", width: "100%", children: [label ? /*#__PURE__*/jsx(FormLabel, { size: size, as: "span", necessityIndicator: necessityIndicator, position: labelPosition, id: labelId, accessibilityText: accessibilityText, children: label }) : null, /*#__PURE__*/jsx(SelectorLabel, { componentName: MetaConstants.FileUploadLabel, inputProps: {}, style: { cursor: isDisabled ? 'not-allowed' : 'pointer', width: '100%' }, children: /*#__PURE__*/jsx(BaseBox, { display: "flex", flexDirection: "column", width: "100%", children: /*#__PURE__*/jsx(StyledFileUploadWrapper, { size: size, isDisabled: isDisabled, isActive: isActive, display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center", borderRadius: "medium", borderWidth: "thin", onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: function onClick() { return setIsActive(true); }, "data-comp": "f", style: _objectSpread({}, isOneFileSelectedWithSingleUpload ? screenReaderStyles : {}), children: /*#__PURE__*/jsxs(Box, { display: "flex", justifyContent: "center", alignItems: "center", flexDirection: { base: 'column', s: 'row' }, gap: makeSize(6), padding: "spacing.3", children: [/*#__PURE__*/jsxs(Text, { color: isDisabled ? fileUploadColorTokens.text.disabled : fileUploadColorTokens.text["default"], children: ["Drag files here or", ' '] }), /*#__PURE__*/jsx(SelectorInput, _objectSpread({ id: inputId, hoverTokens: getFileUploadInputHoverTokens(), isChecked: false, isDisabled: isDisabled, inputProps: _objectSpread({ name: name, type: 'file', onChange: handleInputChange, multiple: isMultiple, required: isRequired, disabled: isDisabled, accept: accept, onBlur: function onBlur() { return setIsActive(false); } }, accessibilityProps), ref: getInnerMotionRef({ _motionMeta: _motionMeta, ref: mergedRef }) }, makeAnalyticsAttribute(rest))), /*#__PURE__*/jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", flexDirection: { base: 'column', s: 'row' }, borderRadius: "small", children: /*#__PURE__*/jsx(Box, { display: "flex", flexDirection: "row", alignItems: "center", borderBottomColor: fileUploadLinkBorderTokens.color[isDisabled ? 'disabled' : 'default'], borderBottomWidth: fileUploadLinkBorderTokens.width["default"], children: /*#__PURE__*/jsx(Text, { color: isDisabled ? fileUploadColorTokens.link.disabled : fileUploadColorTokens.link["default"], children: "Upload" }) }) })] }) }) }) }), isOneFileSelectedWithSingleUpload && /*#__PURE__*/jsx(FileUploadItem, { file: selectedFiles[0], size: size, onRemove: function onRemove() { var newFiles = selectedFiles.filter(function (_ref2) { var id = _ref2.id; return id !== selectedFiles[0].id; }); setSelectedFiles(function () { return newFiles; }); _onRemove === null || _onRemove === void 0 ? void 0 : _onRemove({ file: selectedFiles[0] }); }, onReupload: function onReupload() { var _inputRef$current; var newFiles = selectedFiles.filter(function (_ref3) { var id = _ref3.id; return id !== selectedFiles[0].id; }); setSelectedFiles(function () { return newFiles; }); (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.click(); // TODO - Remove this in the next major release // Fallback to onRemove if onReupload isn't provided to avoid breaking changes in the API if (_onReupload) { _onReupload({ file: selectedFiles[0] }); } else { _onRemove === null || _onRemove === void 0 ? void 0 : _onRemove({ file: selectedFiles[0] }); } setIsActive(false); }, onDismiss: function onDismiss() { var newFiles = selectedFiles.filter(function (_ref4) { var id = _ref4.id; return id !== selectedFiles[0].id; }); setSelectedFiles(function () { return newFiles; }); _onDismiss === null || _onDismiss === void 0 ? void 0 : _onDismiss({ file: selectedFiles[0] }); }, onPreview: onPreview })] }), willRenderHintText && /*#__PURE__*/jsx(BaseBox, { marginLeft: makeSize(label && isLabelLeftPositioned ? formHintLeftLabelMarginLeft[size] : 0), children: /*#__PURE__*/jsx(BaseBox, { display: "flex", flexDirection: "row", justifyContent: "'space-between", children: /*#__PURE__*/jsx(FormHint, { size: size, type: getHintType({ validationState: showError ? 'error' : validationState, hasHelpText: Boolean(helpText) }), helpText: helpText, errorText: errorMessage !== null && errorMessage !== void 0 ? errorMessage : errorText, helpTextId: helpTextId, errorTextId: errorTextId }) }) }), !isOneFileSelectedWithSingleUpload && selectedFiles.map(function (file, index) { return /*#__PURE__*/jsx(BaseBox, { marginLeft: makeSize(label && isLabelLeftPositioned ? formHintLeftLabelMarginLeft[size] : 0), marginTop: index === 0 ? 'spacing.5' : 'spacing.3', children: /*#__PURE__*/jsx(FileUploadItem, { file: file, size: size, onRemove: function onRemove() { var newFiles = selectedFiles.filter(function (_ref5) { var id = _ref5.id; return id !== file.id; }); setSelectedFiles(function () { return newFiles; }); _onRemove === null || _onRemove === void 0 ? void 0 : _onRemove({ file: file }); }, onReupload: function onReupload() { var _inputRef$current2; var newFiles = selectedFiles.filter(function (_ref6) { var id = _ref6.id; return id !== file.id; }); setSelectedFiles(function () { return newFiles; }); (_inputRef$current2 = inputRef.current) === null || _inputRef$current2 === void 0 ? void 0 : _inputRef$current2.click(); // TODO - Remove this in the next major release // Fallback to onRemove if onReupload isn't provided to avoid breaking changes in the API if (_onReupload) { _onReupload({ file: file }); } else { _onRemove === null || _onRemove === void 0 ? void 0 : _onRemove({ file: file }); } setIsActive(false); }, onDismiss: function onDismiss() { var newFiles = selectedFiles.filter(function (_ref7) { var id = _ref7.id; return id !== file.id; }); setSelectedFiles(function () { return newFiles; }); _onDismiss === null || _onDismiss === void 0 ? void 0 : _onDismiss({ file: file }); }, onPreview: onPreview }) }, file.id); })] })); }; /** * ### FileUpload Component * * The FileUpload component is used to handle file attachments, including the drag-and-drop interaction. * Primarily, it is used to upload files to a server or to display a list of uploaded files. * * --- * * #### Usage * * ```jsx const GSTForm = () => { const [selectedFile, setSelectedFile] = useState<BladeFile>(); const [isLoading, setIsLoading] = useState(false); return ( <Box> <Heading marginBottom="spacing.4">Add GST Details</Heading> <form encType="multipart/form-data" onSubmit={handleSubmit}> <FileUpload uploadType="single" label="Upload GST" helpText="Upload .jpg, .jpeg, or .png file only" accept=".jpg, .jpeg, .png" onChange={({ fileList }) => { setSelectedFile(fileList[0]); }} onDrop={({ fileList }) => { setSelectedFile(fileList[0]); }} isRequired necessityIndicator="required" /> <Button type="submit" variant="primary"> Submit </Button> {isLoading && ( <ProgressBar isIndeterminate label="Uploading your GST Certificate..." /> )} </form> </Box> ); } * ``` * * --- * * Checkout {@link https://blade.razorpay.com/?path=/docs/components-fileupload FileUpload Documentation} * */ var FileUpload = /*#__PURE__*/assignWithoutSideEffects( /*#__PURE__*/forwardRef(_FileUpload), { displayName: 'FileUpload', componentId: 'FileUpload' }); export { FileUpload }; //# sourceMappingURL=FileUpload.web.js.map