diff --git a/components/icon/index.tsx b/components/icon/index.tsx index b4a6d27a71e1..57985b3a90b5 100755 --- a/components/icon/index.tsx +++ b/components/icon/index.tsx @@ -14,9 +14,11 @@ export interface SWIconProps { antDesignIcon?: AntIconType; weight?: IconWeight; iconColor?: string; + className?: string; } const Icon: React.FC = ({ + className, type = 'phosphor', size, phosphorIcon: PhosphorIcon, @@ -31,19 +33,21 @@ const Icon: React.FC = ({ } if (size === 'xs') { - return 12; + return 16; } if (size === 'sm') { - return 16; + return 24; } - return 24; + return 32; }; + const wrapperClass = className ? `anticon ${className}` : 'anticon'; + if (type === 'fontAwesome' && fontawesomeIcon) { return ( = ({ if (type === 'phosphor' && PhosphorIcon) { return ( = ({ if (type === 'antDesignIcon' && AntDesignIcon) { return ( ; + +const SingleFileDragger = React.forwardRef( + ({ ...restProps }, ref) => ( + + ), +); + +if (process.env.NODE_ENV !== 'production') { + SingleFileDragger.displayName = 'SingleFileDragger'; +} + +export default SingleFileDragger; diff --git a/components/upload/Upload.tsx b/components/upload/Upload.tsx index c71d49b657c9..85caf808fec0 100644 --- a/components/upload/Upload.tsx +++ b/components/upload/Upload.tsx @@ -4,6 +4,7 @@ import RcUpload from 'rc-upload'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import * as React from 'react'; import { flushSync } from 'react-dom'; +import { Eraser, UploadSimple } from 'phosphor-react'; import { ConfigContext } from '../config-provider'; import DisabledContext from '../config-provider/DisabledContext'; import LocaleReceiver from '../locale/LocaleReceiver'; @@ -13,6 +14,7 @@ import type { RcFile, ShowUploadListInterface, UploadChangeParam, UploadFile } f import { UploadProps } from './interface'; import UploadList from './UploadList'; import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils'; +import Icon from '../icon'; import useStyle from './style'; @@ -20,6 +22,9 @@ export const LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`; export { UploadProps }; +// todo: i18n this +const ClickOrDragToReplaceFile = 'Click or Drag to replace file'; + const InternalUpload: React.ForwardRefRenderFunction = (props, ref) => { const { fileList, @@ -49,6 +54,8 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr action = '', accept = '', supportServerRender = true, + title = 'Drag title', + hint = 'Drag hint', } = props; // ===================== Disabled ===================== @@ -416,6 +423,53 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr ); } + if (type === 'single-file-drag') { + const dragCls = classNames( + prefixCls, + `${prefixCls}-drag-single`, + { + '-drag-uploading': mergedFileList.some((file) => file.status === 'uploading'), + '-drag-hover': dragState === 'dragover', + '-disabled': mergedDisabled, + '-uploaded': mergedFileList.length, + }, + hashId, + ); + + return wrapSSR( +
+
+ +
+ {!mergedFileList.length ? ( + <> + +
{title}
+
{hint}
+ + ) : ( + <> + +
{ClickOrDragToReplaceFile}
+
{mergedFileList[0].name}
+ + )} +
+
+
+
, + ); + } + const uploadButtonCls = classNames(prefixCls, `${prefixCls}-select`, { [`${prefixCls}-disabled`]: mergedDisabled, }); diff --git a/components/upload/index.tsx b/components/upload/index.tsx index 98a5a8b20095..98e75f792e81 100644 --- a/components/upload/index.tsx +++ b/components/upload/index.tsx @@ -1,4 +1,5 @@ import Dragger from './Dragger'; +import SingleFileDragger from './SingleFileDragger'; import type { UploadProps } from './Upload'; import InternalUpload, { LIST_IGNORE } from './Upload'; @@ -17,11 +18,13 @@ type CompoundedComponent = InternalUploadType & { props: React.PropsWithChildren> & React.RefAttributes, ): React.ReactElement; Dragger: typeof Dragger; + SingleFileDragger: typeof SingleFileDragger; LIST_IGNORE: string; }; const Upload = InternalUpload as CompoundedComponent; Upload.Dragger = Dragger; +Upload.SingleFileDragger = SingleFileDragger; Upload.LIST_IGNORE = LIST_IGNORE; export default Upload; diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx index 709e0e45d9ab..f7476da07c20 100755 --- a/components/upload/interface.tsx +++ b/components/upload/interface.tsx @@ -65,7 +65,7 @@ export interface UploadLocale { previewFile?: string; } -export type UploadType = 'drag' | 'select'; +export type UploadType = 'single-file-drag' | 'drag' | 'select'; export type UploadListType = 'text' | 'picture' | 'picture-card'; export type UploadListProgressProps = Omit; @@ -89,6 +89,8 @@ type BeforeUploadValueType = void | boolean | string | Blob | File; export interface UploadProps extends Pick { type?: UploadType; name?: string; + title?: string; + hint?: string; defaultFileList?: Array>; fileList?: Array>; action?: string | ((file: RcFile) => string) | ((file: RcFile) => PromiseLike); diff --git a/components/upload/stories/singleFileDragger.stories.tsx b/components/upload/stories/singleFileDragger.stories.tsx new file mode 100644 index 000000000000..c444c8554730 --- /dev/null +++ b/components/upload/stories/singleFileDragger.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import Upload from '..'; + +const { SingleFileDragger } = Upload; + +export default { + title: 'Core/SingleFileDragger', + component: SingleFileDragger, + // More on argTypes: https://storybook.js.org/docs/react/api/argtypes + argTypes: {}, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +export const Default = Template.bind({}); +Default.args = { + title: 'Import from Polkadot.js', + hint: 'Please drag an drop the .json file you exported from Polkadot.js', +}; diff --git a/components/upload/style/dragger.ts b/components/upload/style/dragger.ts index b04358ee4470..59531946467d 100644 --- a/components/upload/style/dragger.ts +++ b/components/upload/style/dragger.ts @@ -70,6 +70,48 @@ const genDraggerStyle: GenerateStyle = (token) => { }, }, }, + [`${componentCls}-drag-single`]: { + textAlign: 'center', + cursor: 'pointer', + background: token.colorBgSecondary, + border: `2px dotted ${token.colorBgDivider}`, + transition: `border-color ${token.motionDurationSlow}`, + borderRadius: token.borderRadiusLG, + padding: '32px 16px 10px 16px', + + [`${componentCls}-btn`]: { + display: 'block', + outline: 'none', + }, + [`${componentCls}-drag__icon`]: { + fontSize: 32, + marginBottom: 8, + }, + [`${componentCls}-drag__title`]: { + fontWeight: '600', + marginBottom: 8, + }, + [`${componentCls}-drag__hint`]: { + fontWeight: '500', + color: token.colorTextLight4, + wordBreak: 'break-word', + }, + '&.-drag-hover': { + paddingBottom: 32, + borderColor: token['geekblue-6'], + }, + '&.-uploaded': { + paddingBottom: 32, + + [`${componentCls}-drag__icon`]: { + color: token.colorWarning, + }, + }, + + '&:hover': { + borderColor: token['geekblue-4'], + }, + }, }; };