From c67ac4690cfa2ebc2a0860ac9dfd7b88b6150a73 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Sun, 18 Apr 2021 01:13:59 +0800 Subject: [PATCH] refactor(upload): clsPrefix --- src/upload/index.ts | 4 +- src/upload/src/Upload.tsx | 220 ++++++++++++++++-------------- src/upload/src/UploadDragger.tsx | 28 ++-- src/upload/src/UploadFile.tsx | 74 ++++++---- src/upload/src/UploadProgress.tsx | 29 +--- src/upload/src/interface.ts | 28 ++-- 6 files changed, 211 insertions(+), 172 deletions(-) diff --git a/src/upload/index.ts b/src/upload/index.ts index 4bb06018f..978a3ca1b 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -1,3 +1,5 @@ export { default as NUpload } from './src/Upload' export { default as NUploadDragger } from './src/UploadDragger' -export type { UploadRef } from './src/interface' +export type { UploadProps } from './src/Upload' +export type { uploadDraggerKey } from './src/UploadDragger' +export type { UploadInst } from './src/interface' diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index 06c3ae4e9..a8568d99b 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -5,37 +5,37 @@ import { provide, toRef, ref, - reactive, PropType, CSSProperties } from 'vue' import { createId } from 'seemly' -import { useTheme } from '../../_mixins' +import { useConfig, useTheme } from '../../_mixins' import type { ThemeProps } from '../../_mixins' -import { warn } from '../../_utils' +import { ExtractPublicPropTypes, getFirstSlotVNode, warn } from '../../_utils' import { NFadeInExpandTransition } from '../../_internal' import { uploadLight, UploadTheme } from '../styles' import NUploadFile from './UploadFile' import style from './styles/index.cssr' -import type { +import { XhrHandlers, FileInfo, DoChange, - UploadInst, + UploadInternalInst, FuncOrRecordOrUndef, OnFinish, - UploadInjection, OnRemove, OnDownload, - OnChange + OnChange, + uploadInjectionKey } from './interface' import { useMergedState } from 'vooks' +import { uploadDraggerKey } from './UploadDragger' /** * fils status ['pending', 'uploading', 'finished', 'removed', 'error'] */ function createXhrHandlers ( - inst: UploadInst, + inst: UploadInternalInst, file: FileInfo, XHR: XMLHttpRequest ): XhrHandlers { @@ -86,7 +86,7 @@ function createXhrHandlers ( } function registerHandler ( - inst: UploadInst, + inst: UploadInternalInst, file: FileInfo, request: XMLHttpRequest ): void { @@ -135,7 +135,7 @@ function appendData ( } function submitImpl ( - inst: UploadInst, + inst: UploadInternalInst, file: FileInfo, formData: FormData, { @@ -168,76 +168,90 @@ function submitImpl ( } } +const uploadProps = { + ...(useTheme.props as ThemeProps), + name: { + type: String, + default: 'file' + }, + accept: String, + action: String, + // to be impl + // directory: { + // type: Boolean, + // default: false + // }, + method: { + type: String, + default: 'POST' + }, + multiple: { + type: Boolean, + default: false + }, + data: [Object, Function] as PropType, + headers: [Object, Function] as PropType, + withCredentials: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + }, + onChange: Function as PropType, + onRemove: Function as PropType, + onFinish: Function as PropType, + /** currently of no usage */ + onDownload: Function as PropType, + defaultUpload: { + type: Boolean, + default: true + }, + fileList: Array as PropType, + fileListStyle: [String, Object] as PropType, + defaultFileList: { + type: Array as PropType, + default: () => [] + }, + showCancelButton: { + type: Boolean, + default: true + }, + showRemoveButton: { + type: Boolean, + default: true + }, + showDownloadButton: { + type: Boolean, + default: false + }, + showRetryButton: { + type: Boolean, + default: true + } +} as const + +export type UploadProps = ExtractPublicPropTypes + export default defineComponent({ name: 'Upload', - props: { - ...(useTheme.props as ThemeProps), - name: { - type: String, - default: 'file' - }, - accept: String, - action: String, - // to be impl - // directory: { - // type: Boolean, - // default: false - // }, - method: { - type: String, - default: 'POST' - }, - multiple: { - type: Boolean, - default: false - }, - data: [Object, Function] as PropType, - headers: [Object, Function] as PropType, - withCredentials: { - type: Boolean, - default: false - }, - disabled: { - type: Boolean, - default: false - }, - onChange: Function as PropType, - onRemove: Function as PropType, - onFinish: Function as PropType, - /** currently of no usage */ - onDownload: Function as PropType, - defaultUpload: { - type: Boolean, - default: true - }, - fileList: Array as PropType, - fileListStyle: [String, Object] as PropType, - defaultFileList: { - type: Array as PropType, - default: () => [] - }, - showCancelButton: { - type: Boolean, - default: true - }, - showRemoveButton: { - type: Boolean, - default: true - }, - showDownloadButton: { - type: Boolean, - default: false - }, - showRetryButton: { - type: Boolean, - default: true - } - }, + props: uploadProps, setup (props) { - const themeRef = useTheme('Upload', 'Upload', style, uploadLight, props) + const { mergedClsPrefix } = useConfig(props) + const themeRef = useTheme( + 'Upload', + 'Upload', + style, + uploadLight, + props, + mergedClsPrefix + ) const uncontrolledFileListRef = ref(props.defaultFileList) const inputElRef = ref(null) - const draggerInsideRef = ref(false) + const draggerInsideRef = { + value: false + } const dragOverRef = ref(false) const XhrMap = new Map() const mergedFileListRef = useMergedState( @@ -369,28 +383,26 @@ export default defineComponent({ warn('upload', 'File has no corresponding id in current file list.') } } - provide( - 'NUpload', - reactive({ - mergedTheme: themeRef, - showCancelButton: toRef(props, 'showCancelButton'), - showDownloadButton: toRef(props, 'showDownloadButton'), - showRemoveButton: toRef(props, 'showRemoveButton'), - showRetryButton: toRef(props, 'showRetryButton'), - draggerInside: draggerInsideRef, - mergedFileList: mergedFileListRef, - XhrMap, - onRemove: toRef(props, 'onRemove'), - onDownload: toRef(props, 'onDownload'), - submit, - doChange - }) - ) + provide(uploadInjectionKey, { + cPrefixRef: mergedClsPrefix, + mergedThemeRef: themeRef, + showCancelButtonRef: toRef(props, 'showCancelButton'), + showDownloadButtonRef: toRef(props, 'showDownloadButton'), + showRemoveButtonRef: toRef(props, 'showRemoveButton'), + showRetryButtonRef: toRef(props, 'showRetryButton'), + onRemoveRef: toRef(props, 'onRemove'), + onDownloadRef: toRef(props, 'onDownload'), + mergedFileListRef: mergedFileListRef, + XhrMap, + submit, + doChange + }) return { + cPrefix: mergedClsPrefix, + draggerInsideRef, inputElRef, mergedFileList: mergedFileListRef, mergedTheme: themeRef, - draggerInside: draggerInsideRef, dragOver: dragOverRef, handleTriggerDrop, handleTriggerDragLeave, @@ -439,14 +451,20 @@ export default defineComponent({ } }, render () { + const { draggerInsideRef, cPrefix } = this + const firstChild = getFirstSlotVNode(this.$slots, 'default') + // @ts-expect-error + if (firstChild?.type?.[uploadDraggerKey]) { + draggerInsideRef.value = true + } return (
{this.$slots}
-
+
{{ default: () => this.mergedFileList.map((file) => ( - + )) }} diff --git a/src/upload/src/UploadDragger.tsx b/src/upload/src/UploadDragger.tsx index 4c3266d90..622fac0b9 100644 --- a/src/upload/src/UploadDragger.tsx +++ b/src/upload/src/UploadDragger.tsx @@ -1,16 +1,22 @@ -import { h, inject, defineComponent, onBeforeUnmount } from 'vue' -import { UploadInjection } from './interface' +import { h, defineComponent, inject } from 'vue' +import { throwError } from '../../_utils' +import { uploadInjectionKey } from './interface' + +export const uploadDraggerKey = '__UPLOAD_DRAGGER__' export default defineComponent({ name: 'UploadDragger', - setup () { - const NUpload = inject('NUpload') - NUpload && (NUpload.draggerInside = true) - onBeforeUnmount(() => { - NUpload && (NUpload.draggerInside = false) - }) - }, - render () { - return
{this.$slots}
+ [uploadDraggerKey]: true, + setup (_, { slots }) { + const NUpload = inject(uploadInjectionKey, null) + if (!NUpload) { + throwError( + 'upload-dragger', + '`n-upload-dragger` must be placed inside `n-upload`.' + ) + } + return () => ( +
{slots}
+ ) } }) diff --git a/src/upload/src/UploadFile.tsx b/src/upload/src/UploadFile.tsx index 8ef144e51..3a50e263a 100644 --- a/src/upload/src/UploadFile.tsx +++ b/src/upload/src/UploadFile.tsx @@ -10,18 +10,23 @@ import { NButton } from '../../button' import { NIconSwitchTransition, NBaseIcon } from '../../_internal' import { warn } from '../../_utils' import NUploadProgress from './UploadProgress' -import type { FileInfo, UploadInjection } from './interface' +import { FileInfo, uploadInjectionKey } from './interface' export default defineComponent({ name: 'UploadFile', props: { + clsPrefix: { + type: String, + required: true + }, file: { type: Object as PropType, required: true } }, setup (props) { - const NUpload = inject('NUpload') as UploadInjection + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const NUpload = inject(uploadInjectionKey)! const progressStatusRef = computed(() => { const { file } = props if (file.status === 'finished') return 'success' @@ -38,22 +43,22 @@ export default defineComponent({ return file.status === 'uploading' }) const showCancelButtonRef = computed(() => { - if (!NUpload.showCancelButton) return false + if (!NUpload.showCancelButtonRef.value) return false const { file } = props return ['uploading', 'pending', 'error'].includes(file.status) }) const showRemoveButtonRef = computed(() => { - if (!NUpload.showRemoveButton) return false + if (!NUpload.showRemoveButtonRef.value) return false const { file } = props return ['finished'].includes(file.status) }) const showDownloadButtonRef = computed(() => { - if (!NUpload.showDownloadButton) return false + if (!NUpload.showDownloadButtonRef.value) return false const { file } = props return ['finished'].includes(file.status) }) const showRetryButtonRef = computed(() => { - if (!NUpload.showRetryButton) return false + if (!NUpload.showRetryButtonRef.value) return false const { file } = props return ['error'].includes(file.status) }) @@ -76,12 +81,17 @@ export default defineComponent({ handleDownload(props.file) } function handleRemove (file: FileInfo): void { - const { XhrMap, doChange } = NUpload + const { + XhrMap, + doChange, + onRemoveRef: { value: onRemove }, + mergedFileListRef: { value: mergedFileList } + } = NUpload void Promise.resolve( - NUpload.onRemove - ? NUpload.onRemove({ + onRemove + ? onRemove({ file: Object.assign({}, file), - fileList: NUpload.mergedFileList + fileList: mergedFileList }) : true ).then((result) => { @@ -96,8 +106,11 @@ export default defineComponent({ }) } function handleDownload (file: FileInfo): void { + const { + onDownloadRef: { value: onDownload } + } = NUpload void Promise.resolve( - NUpload.onDownload ? NUpload.onDownload(Object.assign({}, file)) : true + onDownload ? onDownload(Object.assign({}, file)) : true ).then((res) => { /** I haven't figure out its usage, so just leave it here */ }) @@ -109,7 +122,7 @@ export default defineComponent({ handleRemove(Object.assign({}, file)) } return { - NUpload, + mergedTheme: NUpload.mergedThemeRef, progressStatus: progressStatusRef, buttonType: buttonTypeRef, showProgress: showProgressRef, @@ -123,28 +136,31 @@ export default defineComponent({ } }, render () { + const { clsPrefix, mergedTheme } = this return ( -
-
- {{ default: () => }} +
+
+ + {{ default: () => }} + {this.file.name}
-
+
{this.showRemoveButton || this.showCancelButton ? ( this.showRemoveButton ? ( - + {{ default: () => }} ) : ( - + {{ default: () => }} ) @@ -175,10 +191,14 @@ export default defineComponent({ text type={this.buttonType} onClick={this.handleRetryClick} + theme={mergedTheme.peers.Button} + themeOverrides={mergedTheme.peerOverrides.Button} > {{ icon: () => ( - {{ default: () => }} + + {{ default: () => }} + ) }} @@ -189,10 +209,14 @@ export default defineComponent({ text type={this.buttonType} onClick={this.handleDownloadClick} + theme={mergedTheme.peers.Button} + themeOverrides={mergedTheme.peerOverrides.Button} > {{ icon: () => ( - {{ default: () => }} + + {{ default: () => }} + ) }} diff --git a/src/upload/src/UploadProgress.tsx b/src/upload/src/UploadProgress.tsx index d4dad1587..4c3d50444 100644 --- a/src/upload/src/UploadProgress.tsx +++ b/src/upload/src/UploadProgress.tsx @@ -1,7 +1,7 @@ import { h, defineComponent, inject, PropType } from 'vue' import { NFadeInExpandTransition } from '../../_internal' import { NProgress } from '../../progress' -import type { UploadInjection } from './interface' +import { uploadInjectionKey } from './interface' export default defineComponent({ name: 'UploadProgress', @@ -23,28 +23,11 @@ export default defineComponent({ default: 900 } }, - // data () { - // return { - // deferredShow: false - // } - // }, - // watch: { - // show (value) { - // if (value) this.deferredShow = true - // else { - // window.setTimeout(() => { - // this.deferredShow = false - // }, this.delay) - // } - // } - // }, - // created () { - // this.deferredShow = this.show - // }, setup (props) { - const NUpload = inject('NUpload') as UploadInjection + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const NUpload = inject(uploadInjectionKey)! return { - NUpload + mergedTheme: NUpload.mergedThemeRef } }, render () { @@ -59,8 +42,8 @@ export default defineComponent({ percentage={this.percentage} status={this.status} height={2} - theme={this.NUpload.mergedTheme.peers.Progress} - themeOverrides={this.NUpload.mergedTheme.peerOverrides.Progress} + theme={this.mergedTheme.peers.Progress} + themeOverrides={this.mergedTheme.peerOverrides.Progress} /> ) : null }} diff --git a/src/upload/src/interface.ts b/src/upload/src/interface.ts index db09573ce..3495f8166 100644 --- a/src/upload/src/interface.ts +++ b/src/upload/src/interface.ts @@ -1,3 +1,5 @@ +import { Ref } from '@vue/reactivity' +import { InjectionKey } from '@vue/runtime-core' import type { MergedTheme } from '../../_mixins' import type { UploadTheme } from '../styles' @@ -27,7 +29,7 @@ export type OnRemove = (data: { }) => Promise | boolean | any export type OnDownload = (file: FileInfo) => Promise | boolean | any -export interface UploadInst { +export interface UploadInternalInst { doChange: DoChange XhrMap: Map onFinish?: OnFinish @@ -43,20 +45,24 @@ export type DoChange = ( ) => void export interface UploadInjection { - readonly mergedTheme: MergedTheme - draggerInside: boolean - showCancelButton: boolean - showRemoveButton: boolean - showDownloadButton: boolean - showRetryButton: boolean - mergedFileList: FileInfo[] + cPrefixRef: Ref + mergedThemeRef: Ref> + showCancelButtonRef: Ref + showRemoveButtonRef: Ref + showDownloadButtonRef: Ref + showRetryButtonRef: Ref + mergedFileListRef: Ref + onRemoveRef: Ref + onDownloadRef: Ref XhrMap: Map submit: (fileId?: string) => void doChange: DoChange - onRemove: OnRemove | undefined - onDownload: OnDownload | undefined } +export const uploadInjectionKey: InjectionKey = Symbol( + 'upload' +) + export interface XhrHandlers { handleXHRLoad: (e: ProgressEvent) => void handleXHRAbort: (e: ProgressEvent) => void @@ -64,6 +70,6 @@ export interface XhrHandlers { handleXHRError: (e: ProgressEvent) => void } -export interface UploadRef { +export interface UploadInst { submit: () => void }