UNPKG

@carbon/react

Version:

React components for the Carbon Design System

214 lines (210 loc) 6.31 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import cx from 'classnames'; import PropTypes from 'prop-types'; import React, { useState, useImperativeHandle } from 'react'; import Filename from './Filename.js'; import FileUploaderButton from './FileUploaderButton.js'; import { ButtonKinds } from '../Button/Button.js'; import { Enter, Space } from '../../internal/keyboard/keys.js'; import { matches } from '../../internal/keyboard/match.js'; import { usePrefix } from '../../internal/usePrefix.js'; import '../Text/index.js'; import { useId } from '../../internal/useId.js'; import { Text } from '../Text/Text.js'; const FileUploader = /*#__PURE__*/React.forwardRef(({ accept, buttonKind, buttonLabel, className, disabled, filenameStatus, iconDescription, labelDescription, labelTitle, multiple, name, onChange, onClick, onDelete, size, ...other }, ref) => { const fileUploaderInstanceId = useId('file-uploader'); const [state, updateState] = useState({ fileNames: [] }); const nodes = []; const prefix = usePrefix(); const handleChange = evt => { evt.stopPropagation(); const filenames = Array.prototype.map.call(evt.target.files, file => file.name); updateState(prevState => ({ fileNames: multiple ? [...new Set([...prevState.fileNames, ...filenames])] : filenames })); if (onChange) { onChange(evt); } }; const handleClick = (evt, { index, filenameStatus }) => { if (filenameStatus === 'edit') { evt.stopPropagation(); const filteredArray = state.fileNames.filter(filename => filename !== nodes[index]?.innerText?.trim()); updateState({ fileNames: filteredArray }); if (onDelete) { onDelete(evt); uploaderButton.current?.focus?.(); } onClick?.(evt); } }; useImperativeHandle(ref, () => ({ clearFiles() { updateState({ fileNames: [] }); } })); const uploaderButton = /*#__PURE__*/React.createRef(); const classes = cx({ [`${prefix}--form-item`]: true, [className]: className }); const getHelperLabelClasses = baseClass => cx(baseClass, { [`${prefix}--label-description--disabled`]: disabled }); const selectedFileClasses = cx(`${prefix}--file__selected-file`, { [`${prefix}--file__selected-file--md`]: size === 'field' || size === 'md', [`${prefix}--file__selected-file--sm`]: size === 'small' || size === 'sm' }); return /*#__PURE__*/React.createElement("div", _extends({ className: classes }, other), !labelTitle ? null : /*#__PURE__*/React.createElement(Text, { as: "h3", className: getHelperLabelClasses(`${prefix}--file--label`) }, labelTitle), /*#__PURE__*/React.createElement(Text, { as: "p", className: getHelperLabelClasses(`${prefix}--label-description`), id: fileUploaderInstanceId }, labelDescription), /*#__PURE__*/React.createElement(FileUploaderButton, { innerRef: uploaderButton, disabled: disabled, labelText: buttonLabel, multiple: multiple, buttonKind: buttonKind, onChange: handleChange, disableLabelChanges: true, accept: accept, name: name, size: size, "aria-describedby": fileUploaderInstanceId }), /*#__PURE__*/React.createElement("div", { className: `${prefix}--file-container` }, state.fileNames.length === 0 ? null : state.fileNames.map((name, index) => /*#__PURE__*/React.createElement("span", _extends({ key: index, className: selectedFileClasses, ref: node => { nodes[index] = node; } // eslint-disable-line }, other), /*#__PURE__*/React.createElement(Text, { as: "p", className: `${prefix}--file-filename`, id: name }, name), /*#__PURE__*/React.createElement("span", { className: `${prefix}--file__state-container` }, /*#__PURE__*/React.createElement(Filename, { name: name, iconDescription: iconDescription, status: filenameStatus, onKeyDown: evt => { if (matches(evt, [Enter, Space])) { handleClick(evt, { index, filenameStatus }); } }, onClick: evt => handleClick(evt, { index, filenameStatus }) })))))); }); FileUploader.propTypes = { /** * Specify the types of files that this input should be able to receive */ accept: PropTypes.arrayOf(PropTypes.string), /** * Specify the type of the `<FileUploaderButton>` */ buttonKind: PropTypes.oneOf(ButtonKinds), /** * Provide the label text to be read by screen readers when interacting with * the `<FileUploaderButton>` */ buttonLabel: PropTypes.string, /** * Provide a custom className to be applied to the container node */ className: PropTypes.string, /** * Specify whether file input is disabled */ disabled: PropTypes.bool, /** * Specify the status of the File Upload */ filenameStatus: PropTypes.oneOf(['edit', 'complete', 'uploading']).isRequired, /** * Provide a description for the complete/close icon that can be read by screen readers */ iconDescription: PropTypes.string, /** * Specify the description text of this `<FileUploader>` */ labelDescription: PropTypes.string, /** * Specify the title text of this `<FileUploader>` */ labelTitle: PropTypes.string, /** * Specify if the component should accept multiple files to upload */ multiple: PropTypes.bool, /** * Provide a name for the underlying `<input>` node */ name: PropTypes.string, /** * Provide an optional `onChange` hook that is called each time the input is * changed */ onChange: PropTypes.func, /** * Provide an optional `onClick` hook that is called each time the * FileUploader is clicked */ onClick: PropTypes.func, /** * Provide an optional `onDelete` hook that is called when an uploaded item * is removed */ onDelete: PropTypes.func, /** * Specify the size of the FileUploaderButton, from a list of available * sizes. */ size: PropTypes.oneOf(['sm', 'md', 'lg']) }; export { FileUploader as default };