From 1233d8a8548d666b4b529ba1281f6ba09596ddf6 Mon Sep 17 00:00:00 2001 From: kev1nzh Date: Fri, 17 Sep 2021 15:44:23 +0800 Subject: [PATCH 1/7] feat(upload): add `abstract` prop, separate `upload-file-list` and `upload-trigger` components (#1102) --- src/upload/index.ts | 2 + src/upload/src/Upload.tsx | 227 ++++++-------- src/upload/src/UploadFileList.tsx | 62 ++++ src/upload/src/UploadTrigger.tsx | 97 ++++++ src/upload/src/interface.ts | 10 +- src/upload/src/styles/index.cssr.ts | 446 ++++++++++++++-------------- src/upload/tests/Upload.spec.ts | 39 ++- 7 files changed, 524 insertions(+), 359 deletions(-) create mode 100644 src/upload/src/UploadFileList.tsx create mode 100644 src/upload/src/UploadTrigger.tsx diff --git a/src/upload/index.ts b/src/upload/index.ts index 79899aafd0d..dd12c9afbe5 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -1,5 +1,7 @@ export { default as NUpload } from './src/Upload' export { default as NUploadDragger } from './src/UploadDragger' +export { default as NUploadTrigger } from './src/UploadTrigger' +export { default as NUploadFileList } from './src/UploadFileList' export type { UploadProps } from './src/Upload' export type { uploadDraggerKey } from './src/UploadDragger' export type { UploadInst, FileInfo as UploadFileInfo } from './src/interface' diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index f0bd60103f3..ee327d3bbba 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -7,7 +7,9 @@ import { ref, PropType, CSSProperties, - VNode + renderSlot, + Fragment, + Teleport } from 'vue' import { createId } from 'seemly' import { useMergedState } from 'vooks' @@ -18,12 +20,11 @@ import { getFirstSlotVNode, warn, MaybeArray, - call + call, + throwError } from '../../_utils' -import { NFadeInExpandTransition } from '../../_internal' import { uploadLight, UploadTheme } from '../styles' -import NUploadFile from './UploadFile' -import NUploadDragger, { uploadDraggerKey } from './UploadDragger' +import { uploadDraggerKey } from './UploadDragger' import style from './styles/index.cssr' import { XhrHandlers, @@ -43,8 +44,8 @@ import { CreateThumbnailUrl } from './interface' import { createImageDataUrl } from './utils' -import { NImageGroup } from '../../image' - +import NUploadTrigger from './UploadTrigger' +import NUploadFileList from './UploadFileList' /** * fils status ['pending', 'uploading', 'finished', 'removed', 'error'] */ @@ -256,7 +257,11 @@ const uploadProps = { default: 'text' }, onPreview: Function as PropType, - createThumbnailUrl: Function as PropType + createThumbnailUrl: Function as PropType, + abstract: { + type: Boolean, + default: false + } } as const export type UploadProps = ExtractPublicPropTypes @@ -265,6 +270,12 @@ export default defineComponent({ name: 'Upload', props: uploadProps, setup (props) { + if (props.abstract && props.listType === 'image-card') { + throwError( + 'upload', + 'when the list-type is image-card, abstract is not supported.' + ) + } const { mergedClsPrefixRef } = useConfig(props) const themeRef = useTheme( 'Upload', @@ -291,32 +302,6 @@ export default defineComponent({ function openFileDialog (): void { inputElRef.value?.click() } - function handleTriggerClick (): void { - if (mergedDisabledRef.value) return - openFileDialog() - } - function handleTriggerDragOver (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = true - } - function handleTriggerDragEnter (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = true - } - function handleTriggerDragLeave (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = false - } - function handleTriggerDrop (e: DragEvent): void { - e.preventDefault() - if (!draggerInsideRef.value || mergedDisabledRef.value) return - const dataTransfer = e.dataTransfer - const files = dataTransfer?.files - if (files) { - handleFileAddition(files) - } - dragOverRef.value = false - } function handleFileInputChange (e: Event): void { const target = e.target as HTMLInputElement handleFileAddition(target.files, e) @@ -442,6 +427,46 @@ export default defineComponent({ ? await createThumbnailUrl(file.file as File) : await createImageDataUrl(file.file as File) } + const cssVarsRef = computed(() => { + const { + common: { cubicBezierEaseInOut }, + self: { + draggerColor, + draggerBorder, + draggerBorderHover, + itemColorHover, + itemColorHoverError, + itemTextColorError, + itemTextColorSuccess, + itemTextColor, + itemIconColor, + itemDisabledOpacity, + lineHeight, + borderRadius, + fontSize, + itemBorderImageCardError, + itemBorderImageCard + } + } = themeRef.value + return { + '--bezier': cubicBezierEaseInOut, + '--border-radius': borderRadius, + '--dragger-border': draggerBorder, + '--dragger-border-hover': draggerBorderHover, + '--dragger-color': draggerColor, + '--font-size': fontSize, + '--item-color-hover': itemColorHover, + '--item-color-hover-error': itemColorHoverError, + '--item-disabled-opacity': itemDisabledOpacity, + '--item-icon-color': itemIconColor, + '--item-text-color': itemTextColor, + '--item-text-color-error': itemTextColorError, + '--item-text-color-success': itemTextColorSuccess, + '--line-height': lineHeight, + '--item-border-image-card-error': itemBorderImageCardError, + '--item-border-image-card': itemBorderImageCard + } as any + }) provide(uploadInjectionKey, { mergedClsPrefixRef, @@ -459,114 +484,53 @@ export default defineComponent({ doChange, showPreivewButtonRef: toRef(props, 'showPreivewButton'), onPreviewRef: toRef(props, 'onPreview'), - getFileThumbnailUrl + getFileThumbnailUrl, + listTypeRef: toRef(props, 'listType'), + dragOverRef, + openFileDialog, + draggerInsideRef, + handleFileAddition, + fileListStyle: props.fileListStyle, + abstractRef: toRef(props, 'abstract'), + cssVarsRef }) return { mergedClsPrefix: mergedClsPrefixRef, draggerInsideRef, inputElRef, - mergedFileList: mergedFileListRef, mergedDisabled: mergedDisabledRef, mergedTheme: themeRef, dragOver: dragOverRef, - handleTriggerDrop, - handleTriggerDragLeave, - handleTriggerDragEnter, - handleTriggerDragOver, - handleTriggerClick, handleFileInputChange, submit, - openFileDialog, - cssVars: computed(() => { - const { - common: { cubicBezierEaseInOut }, - self: { - draggerColor, - draggerBorder, - draggerBorderHover, - itemColorHover, - itemColorHoverError, - itemTextColorError, - itemTextColorSuccess, - itemTextColor, - itemIconColor, - itemDisabledOpacity, - lineHeight, - borderRadius, - fontSize, - itemBorderImageCardError, - itemBorderImageCard - } - } = themeRef.value - return { - '--bezier': cubicBezierEaseInOut, - '--border-radius': borderRadius, - '--dragger-border': draggerBorder, - '--dragger-border-hover': draggerBorderHover, - '--dragger-color': draggerColor, - '--font-size': fontSize, - '--item-color-hover': itemColorHover, - '--item-color-hover-error': itemColorHoverError, - '--item-disabled-opacity': itemDisabledOpacity, - '--item-icon-color': itemIconColor, - '--item-text-color': itemTextColor, - '--item-text-color-error': itemTextColorError, - '--item-text-color-success': itemTextColorSuccess, - '--line-height': lineHeight, - '--item-border-image-card-error': itemBorderImageCardError, - '--item-border-image-card': itemBorderImageCard - } - }) + cssVars: cssVarsRef } }, render () { - const { draggerInsideRef, mergedClsPrefix, listType, $slots } = this - if ($slots.default) { + const { draggerInsideRef, mergedClsPrefix, $slots } = this + if ($slots.default && !this.abstract) { const firstChild = getFirstSlotVNode($slots, 'default') // @ts-expect-error if (firstChild?.type?.[uploadDraggerKey]) { draggerInsideRef.value = true } } - const isImageCardType = listType === 'image-card' - const uploadTrigger = ( -
- {isImageCardType ? ( - {this.$slots} - ) : ( - this.$slots - )} -
- ) - const createFileList = (): VNode[] => - this.mergedFileList.map((file) => ( - - )) - const uploadFileList = isImageCardType ? ( - {{ default: createFileList }} + + return this.abstract ? ( + <> + {renderSlot(this.$slots, 'default')} + + + + ) : ( - - {{ - default: createFileList - }} - - ) - return (
- {this.listType !== 'image-card' && uploadTrigger} - {this.showFileList && ( -
- {uploadFileList} - {isImageCardType && uploadTrigger} -
- )} + {this.listType !== 'image-card' ? ( + {this.$slots} + ) : null} + {this.showFileList ? ( + {this.$slots} + ) : null}
) } diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx new file mode 100644 index 00000000000..ef4370fda04 --- /dev/null +++ b/src/upload/src/UploadFileList.tsx @@ -0,0 +1,62 @@ +import { h, defineComponent, inject, VNode, CSSProperties } from 'vue' +import { throwError } from '../../_utils' +import { uploadInjectionKey } from './interface' +import NUploadFile from './UploadFile' +import { NImageGroup } from '../../image' +import { NFadeInExpandTransition } from '../../_internal' +import NUploadTrigger from './UploadTrigger' +export default defineComponent({ + name: 'UploadFileList', + setup (_, { slots }) { + const NUpload = inject(uploadInjectionKey, null) + if (!NUpload) { + throwError( + 'upload-file-list', + '`n-upload-file-list` must be placed inside `n-upload`.' + ) + } + + const { + mergedClsPrefixRef, + listTypeRef, + mergedFileListRef, + fileListStyle, + cssVarsRef + } = NUpload + const isImageCardType = listTypeRef.value === 'image-card' + + const createFileList = (): VNode[] => + mergedFileListRef.value.map((file) => ( + + )) + + const uploadFileList = isImageCardType ? ( + {{ default: createFileList }} + ) : ( + + {{ + default: createFileList + }} + + ) + + return () => ( +
+ {uploadFileList} + {isImageCardType && {slots}} +
+ ) + } +}) diff --git a/src/upload/src/UploadTrigger.tsx b/src/upload/src/UploadTrigger.tsx new file mode 100644 index 00000000000..6806fa15d67 --- /dev/null +++ b/src/upload/src/UploadTrigger.tsx @@ -0,0 +1,97 @@ +import { h, defineComponent, inject, computed, renderSlot } from 'vue' +import { ExtractPublicPropTypes, throwError } from '../../_utils' +import { uploadInjectionKey } from './interface' +import NUploadDragger from './UploadDragger' + +const uploadTriggerProps = { + abstract: { + type: Boolean, + default: false + } +} as const +export type UploadTriggerProps = ExtractPublicPropTypes< + typeof uploadTriggerProps +> + +export default defineComponent({ + name: 'UploadTrigger', + props: uploadTriggerProps, + setup (props, { slots }) { + const NUpload = inject(uploadInjectionKey, null) + if (!NUpload) { + throwError( + 'upload-trigger', + '`n-upload-trigger` must be placed inside `n-upload`.' + ) + } + + const { + mergedClsPrefixRef, + listTypeRef, + disabledRef, + dragOverRef, + openFileDialog, + draggerInsideRef, + handleFileAddition, + abstractRef: parentAbstract + } = NUpload + + const isImageCardType = listTypeRef.value === 'image-card' + const mergedAbstractRef = computed( + () => parentAbstract.value && props.abstract + ) + + function handleTriggerClick (): void { + if (disabledRef.value) return + openFileDialog() + } + function handleTriggerDragOver (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = true + } + function handleTriggerDragEnter (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = true + } + function handleTriggerDragLeave (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = false + } + function handleTriggerDrop (e: DragEvent): void { + e.preventDefault() + if (!draggerInsideRef.value || disabledRef.value) return + const dataTransfer = e.dataTransfer + const files = dataTransfer?.files + if (files) { + handleFileAddition(files) + } + dragOverRef.value = false + } + + return () => + mergedAbstractRef.value ? ( + renderSlot(slots, 'default', { + handleTriggerClick, + handleTriggerDrop, + handleTriggerDragOver, + handleTriggerDragEnter, + handleTriggerDragLeave + }) + ) : ( +
+ {isImageCardType ? {slots} : slots} +
+ ) + } +}) diff --git a/src/upload/src/interface.ts b/src/upload/src/interface.ts index 42c11440495..4a564effa4d 100644 --- a/src/upload/src/interface.ts +++ b/src/upload/src/interface.ts @@ -1,4 +1,4 @@ -import { Ref, InjectionKey } from 'vue' +import { Ref, InjectionKey, CSSProperties } from 'vue' import type { MergedTheme } from '../../_mixins' import type { UploadTheme } from '../styles' @@ -70,6 +70,14 @@ export interface UploadInjection { showPreivewButtonRef: Ref onPreviewRef: Ref getFileThumbnailUrl: (file: FileInfo) => Promise + listTypeRef: Ref + dragOverRef: Ref + draggerInsideRef: { value: boolean } + handleFileAddition: (files: FileList | null, e?: Event) => void + fileListStyle: string | CSSProperties | undefined + openFileDialog: () => void + abstractRef: Ref + cssVarsRef: Ref } export const uploadInjectionKey: InjectionKey = diff --git a/src/upload/src/styles/index.cssr.ts b/src/upload/src/styles/index.cssr.ts index 366c15584f8..e97eebca4c0 100644 --- a/src/upload/src/styles/index.cssr.ts +++ b/src/upload/src/styles/index.cssr.ts @@ -2,68 +2,91 @@ import { c, cM, cB, cE } from '../../../_utils/cssr' import fadeInHeightExpand from '../../../_styles/transitions/fade-in-height-expand.cssr' import createIconSwitchTransition from '../../../_styles/transitions/icon-switch.cssr' -export default cB('upload', [ - cE('file-input', ` - display: block; - width: 0; - height: 0; - opacity: 0; - `), - cE('trigger', ` - display: inline-block; - box-sizing: border-box; - `, [ - cM('image-card', [ +export default c([ + cB('upload', [ + cE('file-input', ` + display: block; + width: 0; + height: 0; + opacity: 0; + `), + cE('trigger', ` + display: inline-block; + box-sizing: border-box; + `, [ + cM('image-card', [ + cB('upload-dragger', ` + padding: 0; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + `) + ]) + ]), + cM('dragger-inside', [ + cE('trigger', ` + display: block; + `) + ]), + cB('upload-dragger', ` + cursor: pointer; + box-sizing: border-box; + width: 100%; + text-align: center; + border-radius: var(--border-radius); + padding: 24px; + transition: + border-color .3s var(--bezier), + background-color .3s var(--bezier); + background-color: var(--dragger-color); + border: var(--dragger-border); + `, [ + c('&:hover', ` + border: var(--dragger-border-hover); + `) + ]), + cM('disabled', ` + opacity: var(--item-disabled-opacity); + `, [ + cE('trigger', ` + cursor: not-allowed; + `), + cB('upload-file', ` + cursor: not-allowed; + `), + cB('upload-file-list', ` + cursor: not-allowed; + `), cB('upload-dragger', ` - padding: 0; - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; + cursor: not-allowed; + `) + ]), + cM('drag-over', [ + cB('upload-dragger', ` + border: var(--dragger-border-hover); `) ]) ]), - cM('dragger-inside', [ - cE('trigger', ` - display: block; - `) - ]), - cB('upload-dragger', ` - cursor: pointer; - box-sizing: border-box; - width: 100%; - text-align: center; - border-radius: var(--border-radius); - padding: 24px; - transition: - border-color .3s var(--bezier), - background-color .3s var(--bezier); - background-color: var(--dragger-color); - border: var(--dragger-border); - `, [ - c('&:hover', ` - border: var(--dragger-border-hover); - `) - ]), cB('upload-file-list', ` - margin-top: 8px; - line-height: var(--line-height); + margin-top: 8px; + line-height: var(--line-height); `, [ cM('grid', ` - display: grid; - grid-template-columns: repeat(auto-fill, 96px); - grid-gap: 8px; - margin-top: 0; - `), + display: grid; + grid-template-columns: repeat(auto-fill, 96px); + grid-gap: 8px; + margin-top: 0; + `), cB('upload-file', ` - display: block; - box-sizing: border-box; - cursor: default; - padding: 0px 12px 0 6px; - transition: background-color .3s var(--bezier); - border-radius: var(--border-radius); - `, [ + display: block; + box-sizing: border-box; + cursor: default; + padding: 0px 12px 0 6px; + transition: background-color .3s var(--bezier); + border-radius: var(--border-radius); + `, [ fadeInHeightExpand(), cB('progress', [ fadeInHeightExpand({ @@ -71,108 +94,108 @@ export default cB('upload', [ }) ]), c('&:hover', ` - background-color: var(--item-color-hover); - `, [ + background-color: var(--item-color-hover); + `, [ cB('upload-file-info', [ cE('action', ` - opacity: 1; - `) + opacity: 1; + `) ]) ]), cM('image-type', ` - border-radius: var(--border-radius); - text-decoration: underline; - text-decoration-color: #0000; - `, [ + border-radius: var(--border-radius); + text-decoration: underline; + text-decoration-color: #0000; + `, [ cB('upload-file-info', ` - padding-top: 0px; - padding-bottom: 0px; - width: 100%; - height: 100%; - display: flex; - justify-content: space-between; - align-items: center; - padding: 6px 0; - `, [ + padding-top: 0px; + padding-bottom: 0px; + width: 100%; + height: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 0; + `, [ cB('progress', ` - padding: 2px 0; - margin-bottom: 0; - `), + padding: 2px 0; + margin-bottom: 0; + `), cE('name', ` - padding: 0 8px; - `), + padding: 0 8px; + `), cE('thumbnail', ` - width: 32px; - height: 32px; - font-size: 28px; - display: flex; - justify-content: center; - align-items: center; - `, [ + width: 32px; + height: 32px; + font-size: 28px; + display: flex; + justify-content: center; + align-items: center; + `, [ c('img', ` - width: 100%; - `) + width: 100%; + `) ]) ]) ]), cM('text-type', [ cB('progress', ` - box-sizing: border-box; - padding-bottom: 6px; - margin-bottom: 6px; - `) + box-sizing: border-box; + padding-bottom: 6px; + margin-bottom: 6px; + `) ]), cM('image-card-type', ` - position: relative; - width: 96px; - height: 96px; - border: var(--item-border-image-card); - border-radius: var(--border-radius); - padding: 0; - display: flex; - align-items: center; - justify-content: center; - transition: border-color .3s var(--bezier), background-color .3s var(--bezier); - border-radius: var(--border-radius); - `, [ + position: relative; + width: 96px; + height: 96px; + border: var(--item-border-image-card); + border-radius: var(--border-radius); + padding: 0; + display: flex; + align-items: center; + justify-content: center; + transition: border-color .3s var(--bezier), background-color .3s var(--bezier); + border-radius: var(--border-radius); + `, [ cB('progress', ` - position: absolute; - left: 8px; - bottom: 8px; - right: 8px; - width: unset; - `), + position: absolute; + left: 8px; + bottom: 8px; + right: 8px; + width: unset; + `), cB('upload-file-info', ` - padding: 0; + padding: 0; + width: 100%; + height: 100%; + `, [ + cE('thumbnail', ` width: 100%; height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 36px; `, [ - cE('thumbnail', ` - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: 36px; - `, [ c('img', ` - width: 100%; - `) + width: 100%; + `) ]) ]), c('&::before', ` - position: absolute; - z-index: 1; - left: 0; - right: 0; - top: 0; - bottom: 0; - border-radius: inherit; - opacity: 0; - transition: opacity .2s var(--bezier); - content: ""; - `), + position: absolute; + z-index: 1; + left: 0; + right: 0; + top: 0; + bottom: 0; + border-radius: inherit; + opacity: 0; + transition: opacity .2s var(--bezier); + content: ""; + `), c('&:hover', [ c('&::before', 'opacity: 1;'), cB('upload-file-info', [ @@ -182,63 +205,63 @@ export default cB('upload', [ ]), cM('error-status', [ c('&:hover', ` - background-color: var(--item-color-hover-error); - `), + background-color: var(--item-color-hover-error); + `), cB('upload-file-info', [ cE('name', 'color: var(--item-text-color-error);'), cE('thumbnail', 'color: var(--item-text-color-error);') ]), cM('image-card-type', ` - border: var(--item-border-image-card-error); - `) + border: var(--item-border-image-card-error); + `) ]), cM('with-url', ` - cursor: pointer; - `, [ + cursor: pointer; + `, [ cB('upload-file-info', [ cE('name', ` - color: var(--item-text-color-success); - text-decoration-color: var(--item-text-color-success); - `, [ + color: var(--item-text-color-success); + text-decoration-color: var(--item-text-color-success); + `, [ c('a', ` - text-decoration: underline; - `) + text-decoration: underline; + `) ]) ]) ]), cB('upload-file-info', ` - position: relative; - padding-top: 6px; - padding-bottom: 6px; - display: flex; - flex-wrap: nowrap; - `, [ + position: relative; + padding-top: 6px; + padding-bottom: 6px; + display: flex; + flex-wrap: nowrap; + `, [ cE('thumbnail', ` - font-size: 18px; - opacity: 1; - transition: opacity .2s var(--bezier); - color: var(--item-icon-color); - `, [ + font-size: 18px; + opacity: 1; + transition: opacity .2s var(--bezier); + color: var(--item-icon-color); + `, [ cB('base-icon', ` - margin-right: 2px; - vertical-align: middle; - transition: color .3s var(--bezier); - `) + margin-right: 2px; + vertical-align: middle; + transition: color .3s var(--bezier); + `) ]), cE('action', ` - padding-top: inherit; - padding-bottom: inherit; - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 80px; - display: flex; - align-items: center; - transition: opacity .2s var(--bezier); - justify-content: flex-end; - opacity: 0; - `, [ + padding-top: inherit; + padding-bottom: inherit; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 80px; + display: flex; + align-items: center; + transition: opacity .2s var(--bezier); + justify-content: flex-end; + opacity: 0; + `, [ cB('button', [ c('&:not(:last-child)', { marginRight: '4px' @@ -250,65 +273,44 @@ export default cB('upload', [ ]) ]), cM('image-type', ` - position: relative; - max-width: 80px; - width: auto; - `), + position: relative; + max-width: 80px; + width: auto; + `), cM('image-card-type', ` - z-index: 2; - position: absolute; - width: 100%; - height: 100%; - left: 0; - right: 0; - bottom: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - `) - ]), - cE('name', ` - color: var(--item-text-color); - flex: 1; + z-index: 2; + position: absolute; + width: 100%; + height: 100%; + left: 0; + right: 0; + bottom: 0; + top: 0; display: flex; justify-content: center; - text-overflow: ellipsis; - overflow: hidden; - flex-direction: column; - text-decoration-color: #0000; - font-size: var(--font-size); - transition: - color .3s var(--bezier), - text-decoration-color .3s var(--bezier); - `, [ + align-items: center; + `) + ]), + cE('name', ` + color: var(--item-text-color); + flex: 1; + display: flex; + justify-content: center; + text-overflow: ellipsis; + overflow: hidden; + flex-direction: column; + text-decoration-color: #0000; + font-size: var(--font-size); + transition: + color .3s var(--bezier), + text-decoration-color .3s var(--bezier); + `, [ c('a', ` - color: inherit; - text-decoration: underline; - `) + color: inherit; + text-decoration: underline; + `) ]) ]) ]) - ]), - cM('disabled', ` - opacity: var(--item-disabled-opacity); - `, [ - cE('trigger', ` - cursor: not-allowed; - `), - cB('upload-file', ` - cursor: not-allowed; - `), - cB('upload-file-list', ` - cursor: not-allowed; - `), - cB('upload-dragger', ` - cursor: not-allowed; - `) - ]), - cM('drag-over', [ - cB('upload-dragger', ` - border: var(--dragger-border-hover); - `) ]) ]) diff --git a/src/upload/tests/Upload.spec.ts b/src/upload/tests/Upload.spec.ts index dfc56740631..dc0c58bdd1d 100644 --- a/src/upload/tests/Upload.spec.ts +++ b/src/upload/tests/Upload.spec.ts @@ -1,6 +1,9 @@ import { mount } from '@vue/test-utils' -import { NUpload } from '../index' +import { NUpload, NUploadFileList, NUploadTrigger } from '../index' import { sleep } from 'seemly' +import { NButtonGroup, NButton } from '../../button' +import { h } from 'vue' +import { NCard } from '../../card' const getMockFile = (element: Element, files: File[]): void => { Object.defineProperty(element, 'files', { @@ -183,4 +186,38 @@ describe('n-upload', () => { await button[0].trigger('click') expect(onRemove).toHaveBeenCalled() }) + it('should work with `abstract` prop', async () => { + const wrapper = mount(NUpload, { + props: { abstract: true }, + slots: { + default: () => [ + h(NButtonGroup, null, { + default: () => [ + h(NButton, null, { default: () => 'button1' }), + h( + NUploadTrigger, + { abstract: true }, + { + default: () => + h( + NButton, + { class: 'upload-button' }, + { default: () => 'upload button' } + ) + } + ) + ] + }), + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + const uploadWrapperDom = wrapper.find('.n-upload') + const uploadTriggerDom = wrapper.find('.upload-button') + const uploadFileLIstDom = wrapper.find('.n-upload-file-list') + + expect(uploadWrapperDom.exists()).toBe(false) + expect(uploadTriggerDom.exists()).toBe(true) + expect(uploadFileLIstDom.exists()).toBe(true) + }) }) From 1ab45306b7e2a92b7deb23820233729e23452d1d Mon Sep 17 00:00:00 2001 From: kev1nzh Date: Fri, 17 Sep 2021 15:51:06 +0800 Subject: [PATCH 2/7] docs(upload): add `abstract` props docs and CHANGELOG (#1102) --- CHANGELOG.en-US.md | 4 ++ CHANGELOG.zh-CN.md | 4 ++ src/upload/demos/enUS/abstract.demo.md | 45 +++++++++++++++++++++++ src/upload/demos/enUS/index.demo-entry.md | 14 +++++++ src/upload/demos/zhCN/abstract.demo.md | 45 +++++++++++++++++++++++ src/upload/demos/zhCN/index.demo-entry.md | 14 +++++++ 6 files changed, 126 insertions(+) create mode 100644 src/upload/demos/enUS/abstract.demo.md create mode 100644 src/upload/demos/zhCN/abstract.demo.md diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 83855ca600d..643e3fdc63a 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -1,5 +1,9 @@ # CHANGELOG +### Feats + +- `n-upload` add `abstract` prop, add `n-upload-trigger` 和 `n-upload-file-list` component, closes [#1102](https://github.com/TuSimple/naive-ui/issues/1102). + ### Fixes - Fix `n-select` focus input when closing tag with `filterable` , closes [#1170](https://github.com/TuSimple/naive-ui/issues/1170). diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 61bde385b3c..c4ab6dfe008 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -1,5 +1,9 @@ # CHANGELOG +### Feats + +- `n-upload` 新增 `abstract` 属性,新增 `n-upload-trigger` 和 `n-upload-file-list` 组件,关闭 [#1102](https://github.com/TuSimple/naive-ui/issues/1102) + ### Fixes - 修复 `n-select` `filterable` 下关闭标签 input 光标聚焦问题,关闭 [#1170](https://github.com/TuSimple/naive-ui/issues/1170) diff --git a/src/upload/demos/enUS/abstract.demo.md b/src/upload/demos/enUS/abstract.demo.md new file mode 100644 index 00000000000..a19ae4fc3bd --- /dev/null +++ b/src/upload/demos/enUS/abstract.demo.md @@ -0,0 +1,45 @@ +# No Wrapper DOM + +`n-upload` and `n-upload-trigger` set `abstract`. + +`n-upload-trigger` and `n-upload-file-list` need to be called from within `n-upload`. + +```html +
+ + + Eat + Sleep + + Upload + + + + + + + +
+``` + +```js +import { defineComponent, ref } from 'vue' +export default defineComponent({ + setup () { + const fileListRef = ref([ + { + id: 'b', + name: 'file.doc', + status: 'finished', + type: 'text/plain' + } + ]) + + return { fileList: fileListRef } + } +}) +``` diff --git a/src/upload/demos/enUS/index.demo-entry.md b/src/upload/demos/enUS/index.demo-entry.md index 95f8833432f..75d4ddc2347 100644 --- a/src/upload/demos/enUS/index.demo-entry.md +++ b/src/upload/demos/enUS/index.demo-entry.md @@ -14,6 +14,7 @@ default-files before-upload image-style image-card-style +abstract ``` ## API @@ -22,6 +23,7 @@ image-card-style | Name | Type | Default | Description | | --- | --- | --- | --- | +| abstract | `boolean` | `false` | Whether or not DOM wrapping does not exist. | | accept | `string` | `undefined` | The accept type of upload. See accept. | | action | `string` | `undefined` | The URL to submit data to. | | create-thumbnail-url | `(file: File) => Promise` | `undefined` | Customize file thumbnails. | @@ -49,6 +51,12 @@ image-card-style | on-before-upload | `(options: { file: UploadFileInfo, fileList: Array }) => (Promise \| boolean \| void)` | `true` | Callback before file is uploaded, return false or a Promise that resolve false or reject will cancel this upload. | | on-preview | `(file: FileInfo) => void` | `undefined` | Callback functions for clicking on file links or preview buttons. | +### UploadTrigger Props + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| abstract | `boolean` | `false` | Whether there is no DOM wrapper, to be used together with the parent component `Upload.abstract`. | + #### UploadFileInfo Type | Property | Type | Description | @@ -80,3 +88,9 @@ image-card-style | Name | Parameters | Description | | --- | --- | --- | | default | `()` | The content of the upload dragger, use can refer to [Drag to Upload](#drag). | + +### UploadTrigger Slots + +| 名称 | 参数 | 说明 | +| --- | --- | --- | +| default | `(options: { handleTriggerClick: () => void, handleTriggerDragOver: (e: DragEvent) => void, handleTriggerDragEnter: (e: DragEvent) => void, handleTriggerDragLeave: (e: DragEvent) => void, handleTriggerDrop: (e: DragEvent) => void})` | `handleTriggerClick` is the click upload function, `handleTriggerDrop` is the drag and drop upload function, `handleTriggerDragEnter`, `handleTriggerDragOver` and `handleTriggerDragLeave` are the drag and drop event functions. | diff --git a/src/upload/demos/zhCN/abstract.demo.md b/src/upload/demos/zhCN/abstract.demo.md new file mode 100644 index 00000000000..1c9315c090d --- /dev/null +++ b/src/upload/demos/zhCN/abstract.demo.md @@ -0,0 +1,45 @@ +# 不需要包裹 DOM + +`n-upload` 和 `n-upload-trigger` 设置 `abstract`。 + +`n-upload-trigger`和 `n-upload-file-list` 需在 `n-upload` 内调用。 + +```html +
+ + + Eat + Sleep + + Upload + + + + + + + +
+``` + +```js +import { defineComponent, ref } from 'vue' +export default defineComponent({ + setup () { + const fileListRef = ref([ + { + id: 'b', + name: 'file.doc', + status: 'finished', + type: 'text/plain' + } + ]) + + return { fileList: fileListRef } + } +}) +``` diff --git a/src/upload/demos/zhCN/index.demo-entry.md b/src/upload/demos/zhCN/index.demo-entry.md index b1ae3a58c3e..722d102a566 100644 --- a/src/upload/demos/zhCN/index.demo-entry.md +++ b/src/upload/demos/zhCN/index.demo-entry.md @@ -14,6 +14,7 @@ default-files before-upload image-style image-card-style +abstract ``` ## API @@ -22,6 +23,7 @@ image-card-style | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 | | accept | `string` | `undefined` | 接受的文件类型,参考 accept | | action | `string` | `undefined` | 请求提交的地址 | | create-thumbnail-url | `(file: File) => Promise` | `undefined` | 自定义文件缩略图 | @@ -48,6 +50,12 @@ image-card-style | on-before-upload | `(options: { file: UploadFileInfo, fileList: UploadFileInfo[] }) => (Promise \| boolean \| void)` | `undefined` | 文件上传之前的回调,返回 `false`、`Promise resolve false`、`Promise rejected` 时会取消本次上传 | | on-preview | `(file: FileInfo) => void` | `undefined` | 点击文件链接或预览按钮的回调函数 | +### UploadTrigger Props + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| abstract | `boolean` | `false` | 是否不存在 DOM 包裹,需与父级 `Upload.abstract` 同时使用 | + #### UploadFileInfo Type | 属性 | 类型 | 说明 | @@ -79,3 +87,9 @@ image-card-style | 名称 | 参数 | 说明 | | ------- | ---- | --------------------------------------------- | | default | `()` | 上传拖动器的内容,使用可参考[拖拽上传](#drag) | + +### UploadTrigger Slots + +| 名称 | 参数 | 说明 | +| --- | --- | --- | +| default | `(options: { handleTriggerClick: () => void, handleTriggerDragOver: (e: DragEvent) => void, handleTriggerDragEnter: (e: DragEvent) => void, handleTriggerDragLeave: (e: DragEvent) => void, handleTriggerDrop: (e: DragEvent) => void})` | `handleTriggerClick` 为点击上传函数,`handleTriggerDrop` 为拖拽上传函数,`handleTriggerDragEnter`、`handleTriggerDragOver` 和 `handleTriggerDragLeave` 为拖拽事件函数 | From f8fe3b3751107ef51809308985343c3bd3b54528 Mon Sep 17 00:00:00 2001 From: kev1nzh Date: Fri, 17 Sep 2021 17:31:10 +0800 Subject: [PATCH 3/7] feat(upload): add upload tests (#1102) --- src/upload/tests/Upload.spec.ts | 121 ++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/upload/tests/Upload.spec.ts b/src/upload/tests/Upload.spec.ts index 7c1eb473c96..f385b325552 100644 --- a/src/upload/tests/Upload.spec.ts +++ b/src/upload/tests/Upload.spec.ts @@ -4,6 +4,7 @@ import { sleep } from 'seemly' import { NButtonGroup, NButton } from '../../button' import { h } from 'vue' import { NCard } from '../../card' +import { NImageGroup } from '../../image' const getMockFile = (element: Element, files: File[]): void => { Object.defineProperty(element, 'files', { @@ -241,3 +242,123 @@ describe('n-upload', () => { expect(wrapper.find('input').attributes('multiple')).toBe('') }) }) + +describe('n-upload-file-list', () => { + it('should work', async () => { + const wrapper = mount(NUpload, { + props: { + abstract: true, + defaultFileList: [ + { + name: 'test.png', + url: '/testUrl.png', + status: 'finished', + id: 'test', + percentage: 100, + file: null + } + ] + }, + slots: { + default: () => [ + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + + const fileList = wrapper.findAll('.n-upload-file') + expect(fileList.length).toEqual(1) + }) + it('should work with `list-type` prop', async () => { + const wrapper = mount(NUpload, { + props: { + listType: 'image-card', + defaultFileList: [ + { + name: 'test.png', + url: '/testUrl.png', + status: 'finished', + id: 'test', + percentage: 100, + file: null + } + ] + } + }) + expect(wrapper.findAll('.n-upload-file--image-card-type').length).toBe(1) + expect(wrapper.findComponent(NImageGroup).exists()).toBe(true) + }) + it('should work inside `n-upload`', async () => { + try { + mount(NUploadFileList) + } catch (error) { + expect(String(error)).toBe( + 'Error: [naive/upload-file-list]: `n-upload-file-list` must be placed inside `n-upload`.' + ) + } + }) +}) + +describe('n-upload-trigger', () => { + it('should work', async () => { + const wrapper = mount(NUpload, { + props: { abstract: true }, + slots: { + default: () => [ + h(NButtonGroup, null, { + default: () => [ + h(NButton, null, { default: () => 'button1' }), + h( + NUploadTrigger, + { abstract: true }, + { + default: () => + h( + NButton, + { class: 'upload-button' }, + { default: () => 'upload button' } + ) + } + ) + ] + }), + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + + const uploadButton = wrapper.findAll('.upload-button') + expect(uploadButton.length).toEqual(1) + }) + it('should work inside `n-upload`', async () => { + try { + mount(NUploadTrigger) + } catch (error) { + expect(String(error)).toBe( + 'Error: [naive/upload-trigger]: `n-upload-trigger` must be placed inside `n-upload`.' + ) + } + }) + + it('should work with drag and drop', async () => { + const wrapper = mount(NUpload, { + slots: { + default: () => h(NButton, null, { default: () => 'button1' }) + } + }) + const triggerItem = wrapper.find('.n-upload__trigger') + await triggerItem.trigger('click') + await triggerItem.trigger('drop') + + expect(wrapper.vm.dragOver).toBe(false) + + await triggerItem.trigger('dragenter') + expect(wrapper.vm.dragOver).toBe(true) + + await triggerItem.trigger('dragleave') + expect(wrapper.vm.dragOver).toBe(false) + + await triggerItem.trigger('dragover') + expect(wrapper.vm.dragOver).toBe(true) + }) +}) From 02a49b9ade21155ea7a02bc1bf65cf6bba54136d Mon Sep 17 00:00:00 2001 From: kev1nzh Date: Sat, 18 Sep 2021 17:05:30 +0800 Subject: [PATCH 4/7] feat(upload): fix `upload-trigger` and `upload-file-list` bug (#1102) --- src/upload/src/Upload.tsx | 13 +- src/upload/src/UploadFileList.tsx | 42 ++-- src/upload/src/UploadTrigger.tsx | 44 ++-- src/upload/src/styles/index.cssr.ts | 336 ++++++++++++++-------------- 4 files changed, 218 insertions(+), 217 deletions(-) diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index ee327d3bbba..54dfb092a47 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -258,10 +258,7 @@ const uploadProps = { }, onPreview: Function as PropType, createThumbnailUrl: Function as PropType, - abstract: { - type: Boolean, - default: false - } + abstract: Boolean } as const export type UploadProps = ExtractPublicPropTypes @@ -551,12 +548,10 @@ export default defineComponent({ multiple={this.multiple} onChange={this.handleFileInputChange} /> - {this.listType !== 'image-card' ? ( + {this.listType !== 'image-card' && ( {this.$slots} - ) : null} - {this.showFileList ? ( - {this.$slots} - ) : null} + )} + {this.showFileList && {this.$slots}} ) } diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx index ef4370fda04..0c8ec5590ea 100644 --- a/src/upload/src/UploadFileList.tsx +++ b/src/upload/src/UploadFileList.tsx @@ -1,10 +1,19 @@ -import { h, defineComponent, inject, VNode, CSSProperties } from 'vue' +import { + h, + defineComponent, + inject, + VNode, + CSSProperties, + computed, + ComputedRef +} from 'vue' import { throwError } from '../../_utils' import { uploadInjectionKey } from './interface' import NUploadFile from './UploadFile' import { NImageGroup } from '../../image' import { NFadeInExpandTransition } from '../../_internal' import NUploadTrigger from './UploadTrigger' + export default defineComponent({ name: 'UploadFileList', setup (_, { slots }) { @@ -23,7 +32,9 @@ export default defineComponent({ fileListStyle, cssVarsRef } = NUpload - const isImageCardType = listTypeRef.value === 'image-card' + const isImageCardTypeRef = computed( + () => listTypeRef.value === 'image-card' + ) const createFileList = (): VNode[] => mergedFileListRef.value.map((file) => ( @@ -35,27 +46,30 @@ export default defineComponent({ /> )) - const uploadFileList = isImageCardType ? ( - {{ default: createFileList }} - ) : ( - - {{ - default: createFileList - }} - - ) + const createUploadFileList = ( + isImageCardTypeRef: ComputedRef + ): VNode => + isImageCardTypeRef.value ? ( + {{ default: createFileList }} + ) : ( + + {{ + default: createFileList + }} + + ) return () => (
- {uploadFileList} - {isImageCardType && {slots}} + {createUploadFileList(isImageCardTypeRef)} + {isImageCardTypeRef.value && {slots}}
) } diff --git a/src/upload/src/UploadTrigger.tsx b/src/upload/src/UploadTrigger.tsx index 6806fa15d67..164398c52b8 100644 --- a/src/upload/src/UploadTrigger.tsx +++ b/src/upload/src/UploadTrigger.tsx @@ -1,22 +1,11 @@ -import { h, defineComponent, inject, computed, renderSlot } from 'vue' -import { ExtractPublicPropTypes, throwError } from '../../_utils' +import { h, defineComponent, inject, renderSlot, computed } from 'vue' +import { throwError } from '../../_utils' import { uploadInjectionKey } from './interface' import NUploadDragger from './UploadDragger' -const uploadTriggerProps = { - abstract: { - type: Boolean, - default: false - } -} as const -export type UploadTriggerProps = ExtractPublicPropTypes< - typeof uploadTriggerProps -> - export default defineComponent({ name: 'UploadTrigger', - props: uploadTriggerProps, - setup (props, { slots }) { + setup (_, { slots }) { const NUpload = inject(uploadInjectionKey, null) if (!NUpload) { throwError( @@ -33,12 +22,11 @@ export default defineComponent({ openFileDialog, draggerInsideRef, handleFileAddition, - abstractRef: parentAbstract + abstractRef } = NUpload - const isImageCardType = listTypeRef.value === 'image-card' - const mergedAbstractRef = computed( - () => parentAbstract.value && props.abstract + const isImageCardTypeRef = computed( + () => listTypeRef.value === 'image-card' ) function handleTriggerClick (): void { @@ -69,19 +57,19 @@ export default defineComponent({ } return () => - mergedAbstractRef.value ? ( + abstractRef.value ? ( renderSlot(slots, 'default', { - handleTriggerClick, - handleTriggerDrop, - handleTriggerDragOver, - handleTriggerDragEnter, - handleTriggerDragLeave + handleClick: handleTriggerClick, + handleDrop: handleTriggerDrop, + handleDragOver: handleTriggerDragOver, + handleDragEnter: handleTriggerDragEnter, + handleDragLeave: handleTriggerDragLeave }) ) : (
- {isImageCardType ? {slots} : slots} + {isImageCardTypeRef.value ? ( + {slots} + ) : ( + slots + )}
) } diff --git a/src/upload/src/styles/index.cssr.ts b/src/upload/src/styles/index.cssr.ts index e97eebca4c0..6a8de004419 100644 --- a/src/upload/src/styles/index.cssr.ts +++ b/src/upload/src/styles/index.cssr.ts @@ -4,12 +4,6 @@ import createIconSwitchTransition from '../../../_styles/transitions/icon-switch export default c([ cB('upload', [ - cE('file-input', ` - display: block; - width: 0; - height: 0; - opacity: 0; - `), cE('trigger', ` display: inline-block; box-sizing: border-box; @@ -70,23 +64,23 @@ export default c([ ]) ]), cB('upload-file-list', ` - margin-top: 8px; - line-height: var(--line-height); + margin-top: 8px; + line-height: var(--line-height); `, [ cM('grid', ` - display: grid; - grid-template-columns: repeat(auto-fill, 96px); - grid-gap: 8px; - margin-top: 0; - `), + display: grid; + grid-template-columns: repeat(auto-fill, 96px); + grid-gap: 8px; + margin-top: 0; + `), cB('upload-file', ` - display: block; - box-sizing: border-box; - cursor: default; - padding: 0px 12px 0 6px; - transition: background-color .3s var(--bezier); - border-radius: var(--border-radius); - `, [ + display: block; + box-sizing: border-box; + cursor: default; + padding: 0px 12px 0 6px; + transition: background-color .3s var(--bezier); + border-radius: var(--border-radius); + `, [ fadeInHeightExpand(), cB('progress', [ fadeInHeightExpand({ @@ -94,108 +88,108 @@ export default c([ }) ]), c('&:hover', ` - background-color: var(--item-color-hover); - `, [ + background-color: var(--item-color-hover); + `, [ cB('upload-file-info', [ cE('action', ` - opacity: 1; - `) + opacity: 1; + `) ]) ]), cM('image-type', ` - border-radius: var(--border-radius); - text-decoration: underline; - text-decoration-color: #0000; - `, [ - cB('upload-file-info', ` - padding-top: 0px; - padding-bottom: 0px; - width: 100%; - height: 100%; - display: flex; - justify-content: space-between; - align-items: center; - padding: 6px 0; + border-radius: var(--border-radius); + text-decoration: underline; + text-decoration-color: #0000; `, [ - cB('progress', ` - padding: 2px 0; - margin-bottom: 0; - `), - cE('name', ` - padding: 0 8px; - `), - cE('thumbnail', ` - width: 32px; - height: 32px; - font-size: 28px; + cB('upload-file-info', ` + padding-top: 0px; + padding-bottom: 0px; + width: 100%; + height: 100%; display: flex; - justify-content: center; + justify-content: space-between; align-items: center; + padding: 6px 0; `, [ + cB('progress', ` + padding: 2px 0; + margin-bottom: 0; + `), + cE('name', ` + padding: 0 8px; + `), + cE('thumbnail', ` + width: 32px; + height: 32px; + font-size: 28px; + display: flex; + justify-content: center; + align-items: center; + `, [ c('img', ` - width: 100%; - `) + width: 100%; + `) ]) ]) ]), cM('text-type', [ cB('progress', ` - box-sizing: border-box; - padding-bottom: 6px; - margin-bottom: 6px; - `) + box-sizing: border-box; + padding-bottom: 6px; + margin-bottom: 6px; + `) ]), cM('image-card-type', ` - position: relative; - width: 96px; - height: 96px; - border: var(--item-border-image-card); - border-radius: var(--border-radius); - padding: 0; - display: flex; - align-items: center; - justify-content: center; - transition: border-color .3s var(--bezier), background-color .3s var(--bezier); - border-radius: var(--border-radius); - `, [ - cB('progress', ` - position: absolute; - left: 8px; - bottom: 8px; - right: 8px; - width: unset; - `), - cB('upload-file-info', ` + position: relative; + width: 96px; + height: 96px; + border: var(--item-border-image-card); + border-radius: var(--border-radius); padding: 0; - width: 100%; - height: 100%; + display: flex; + align-items: center; + justify-content: center; + transition: border-color .3s var(--bezier), background-color .3s var(--bezier); + border-radius: var(--border-radius); `, [ - cE('thumbnail', ` + cB('progress', ` + position: absolute; + left: 8px; + bottom: 8px; + right: 8px; + width: unset; + `), + cB('upload-file-info', ` + padding: 0; width: 100%; height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: 36px; `, [ - c('img', ` + cE('thumbnail', ` width: 100%; - `) + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 36px; + `, [ + c('img', ` + width: 100%; + `) ]) ]), c('&::before', ` - position: absolute; - z-index: 1; - left: 0; - right: 0; - top: 0; - bottom: 0; - border-radius: inherit; - opacity: 0; - transition: opacity .2s var(--bezier); - content: ""; - `), + position: absolute; + z-index: 1; + left: 0; + right: 0; + top: 0; + bottom: 0; + border-radius: inherit; + opacity: 0; + transition: opacity .2s var(--bezier); + content: ""; + `), c('&:hover', [ c('&::before', 'opacity: 1;'), cB('upload-file-info', [ @@ -205,63 +199,63 @@ export default c([ ]), cM('error-status', [ c('&:hover', ` - background-color: var(--item-color-hover-error); - `), + background-color: var(--item-color-hover-error); + `), cB('upload-file-info', [ cE('name', 'color: var(--item-text-color-error);'), cE('thumbnail', 'color: var(--item-text-color-error);') ]), cM('image-card-type', ` - border: var(--item-border-image-card-error); - `) + border: var(--item-border-image-card-error); + `) ]), cM('with-url', ` - cursor: pointer; - `, [ + cursor: pointer; + `, [ cB('upload-file-info', [ cE('name', ` - color: var(--item-text-color-success); - text-decoration-color: var(--item-text-color-success); - `, [ + color: var(--item-text-color-success); + text-decoration-color: var(--item-text-color-success); + `, [ c('a', ` - text-decoration: underline; - `) + text-decoration: underline; + `) ]) ]) ]), cB('upload-file-info', ` - position: relative; - padding-top: 6px; - padding-bottom: 6px; - display: flex; - flex-wrap: nowrap; - `, [ - cE('thumbnail', ` - font-size: 18px; - opacity: 1; - transition: opacity .2s var(--bezier); - color: var(--item-icon-color); + position: relative; + padding-top: 6px; + padding-bottom: 6px; + display: flex; + flex-wrap: nowrap; `, [ + cE('thumbnail', ` + font-size: 18px; + opacity: 1; + transition: opacity .2s var(--bezier); + color: var(--item-icon-color); + `, [ cB('base-icon', ` - margin-right: 2px; - vertical-align: middle; - transition: color .3s var(--bezier); - `) + margin-right: 2px; + vertical-align: middle; + transition: color .3s var(--bezier); + `) ]), cE('action', ` - padding-top: inherit; - padding-bottom: inherit; - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 80px; - display: flex; - align-items: center; - transition: opacity .2s var(--bezier); - justify-content: flex-end; - opacity: 0; - `, [ + padding-top: inherit; + padding-bottom: inherit; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 80px; + display: flex; + align-items: center; + transition: opacity .2s var(--bezier); + justify-content: flex-end; + opacity: 0; + `, [ cB('button', [ c('&:not(:last-child)', { marginRight: '4px' @@ -273,44 +267,50 @@ export default c([ ]) ]), cM('image-type', ` - position: relative; - max-width: 80px; - width: auto; - `), + position: relative; + max-width: 80px; + width: auto; + `), cM('image-card-type', ` - z-index: 2; - position: absolute; - width: 100%; - height: 100%; - left: 0; - right: 0; - bottom: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - `) + z-index: 2; + position: absolute; + width: 100%; + height: 100%; + left: 0; + right: 0; + bottom: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + `) ]), cE('name', ` - color: var(--item-text-color); - flex: 1; - display: flex; - justify-content: center; - text-overflow: ellipsis; - overflow: hidden; - flex-direction: column; - text-decoration-color: #0000; - font-size: var(--font-size); - transition: - color .3s var(--bezier), - text-decoration-color .3s var(--bezier); - `, [ + color: var(--item-text-color); + flex: 1; + display: flex; + justify-content: center; + text-overflow: ellipsis; + overflow: hidden; + flex-direction: column; + text-decoration-color: #0000; + font-size: var(--font-size); + transition: + color .3s var(--bezier), + text-decoration-color .3s var(--bezier); + `, [ c('a', ` - color: inherit; - text-decoration: underline; - `) + color: inherit; + text-decoration: underline; + `) ]) ]) ]) - ]) + ]), + cB('upload__file-input', ` + display: block; + width: 0; + height: 0; + opacity: 0; + `) ]) From 6d68dafeb65f222e1e6101c74175155b93b1c4bc Mon Sep 17 00:00:00 2001 From: kev1nzh Date: Sat, 18 Sep 2021 17:07:20 +0800 Subject: [PATCH 5/7] docs(upload): remove `upload-trigger` abstract prop (#1102) --- src/upload/demos/enUS/abstract.demo.md | 6 +++--- src/upload/demos/enUS/index.demo-entry.md | 8 +------- src/upload/demos/zhCN/abstract.demo.md | 6 +++--- src/upload/demos/zhCN/index.demo-entry.md | 8 +------- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/upload/demos/enUS/abstract.demo.md b/src/upload/demos/enUS/abstract.demo.md index a19ae4fc3bd..fc614155ae6 100644 --- a/src/upload/demos/enUS/abstract.demo.md +++ b/src/upload/demos/enUS/abstract.demo.md @@ -1,6 +1,6 @@ # No Wrapper DOM -`n-upload` and `n-upload-trigger` set `abstract`. +`n-upload` set `abstract`. `n-upload-trigger` and `n-upload-file-list` need to be called from within `n-upload`. @@ -14,8 +14,8 @@ Eat Sleep - - Upload + + Upload diff --git a/src/upload/demos/enUS/index.demo-entry.md b/src/upload/demos/enUS/index.demo-entry.md index 75d4ddc2347..d28ebcdcabd 100644 --- a/src/upload/demos/enUS/index.demo-entry.md +++ b/src/upload/demos/enUS/index.demo-entry.md @@ -51,12 +51,6 @@ abstract | on-before-upload | `(options: { file: UploadFileInfo, fileList: Array }) => (Promise \| boolean \| void)` | `true` | Callback before file is uploaded, return false or a Promise that resolve false or reject will cancel this upload. | | on-preview | `(file: FileInfo) => void` | `undefined` | Callback functions for clicking on file links or preview buttons. | -### UploadTrigger Props - -| 名称 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| abstract | `boolean` | `false` | Whether there is no DOM wrapper, to be used together with the parent component `Upload.abstract`. | - #### UploadFileInfo Type | Property | Type | Description | @@ -93,4 +87,4 @@ abstract | 名称 | 参数 | 说明 | | --- | --- | --- | -| default | `(options: { handleTriggerClick: () => void, handleTriggerDragOver: (e: DragEvent) => void, handleTriggerDragEnter: (e: DragEvent) => void, handleTriggerDragLeave: (e: DragEvent) => void, handleTriggerDrop: (e: DragEvent) => void})` | `handleTriggerClick` is the click upload function, `handleTriggerDrop` is the drag and drop upload function, `handleTriggerDragEnter`, `handleTriggerDragOver` and `handleTriggerDragLeave` are the drag and drop event functions. | +| default | `(options: { handleClick: () => void, handleDragOver: (e: DragEvent) => void, handleDragEnter: (e: DragEvent) => void, handleDragLeave: (e: DragEvent) => void, handleDrop: (e: DragEvent) => void})` | `handleClick` is the click upload function, `handleDrop` is the drag and drop upload function, `handleDragEnter`, `handleDragOver` and `handleDragLeave` are the drag and drop event functions. | diff --git a/src/upload/demos/zhCN/abstract.demo.md b/src/upload/demos/zhCN/abstract.demo.md index 1c9315c090d..9c7a6468e93 100644 --- a/src/upload/demos/zhCN/abstract.demo.md +++ b/src/upload/demos/zhCN/abstract.demo.md @@ -1,6 +1,6 @@ # 不需要包裹 DOM -`n-upload` 和 `n-upload-trigger` 设置 `abstract`。 +`n-upload` 设置 `abstract`。 `n-upload-trigger`和 `n-upload-file-list` 需在 `n-upload` 内调用。 @@ -14,8 +14,8 @@ Eat Sleep - - Upload + + Upload diff --git a/src/upload/demos/zhCN/index.demo-entry.md b/src/upload/demos/zhCN/index.demo-entry.md index 722d102a566..77d43068401 100644 --- a/src/upload/demos/zhCN/index.demo-entry.md +++ b/src/upload/demos/zhCN/index.demo-entry.md @@ -50,12 +50,6 @@ abstract | on-before-upload | `(options: { file: UploadFileInfo, fileList: UploadFileInfo[] }) => (Promise \| boolean \| void)` | `undefined` | 文件上传之前的回调,返回 `false`、`Promise resolve false`、`Promise rejected` 时会取消本次上传 | | on-preview | `(file: FileInfo) => void` | `undefined` | 点击文件链接或预览按钮的回调函数 | -### UploadTrigger Props - -| 名称 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| abstract | `boolean` | `false` | 是否不存在 DOM 包裹,需与父级 `Upload.abstract` 同时使用 | - #### UploadFileInfo Type | 属性 | 类型 | 说明 | @@ -92,4 +86,4 @@ abstract | 名称 | 参数 | 说明 | | --- | --- | --- | -| default | `(options: { handleTriggerClick: () => void, handleTriggerDragOver: (e: DragEvent) => void, handleTriggerDragEnter: (e: DragEvent) => void, handleTriggerDragLeave: (e: DragEvent) => void, handleTriggerDrop: (e: DragEvent) => void})` | `handleTriggerClick` 为点击上传函数,`handleTriggerDrop` 为拖拽上传函数,`handleTriggerDragEnter`、`handleTriggerDragOver` 和 `handleTriggerDragLeave` 为拖拽事件函数 | +| default | `(options: { handleClick: () => void, handleDragOver: (e: DragEvent) => void, handleDragEnter: (e: DragEvent) => void, handleDragLeave: (e: DragEvent) => void, handleDrop: (e: DragEvent) => void})` | `handleClick` 为点击上传函数,`handleDrop` 为拖拽上传函数,`handleDragEnter`、`handleDragOver` 和 `handleDragLeave` 为拖拽事件函数 | From ae0761a444067ac39c69ea0ed6548bffde4e524d Mon Sep 17 00:00:00 2001 From: kev1nzh_ark Date: Sun, 19 Sep 2021 13:02:40 +0800 Subject: [PATCH 6/7] feat(upload) fix upload-file-list method (#1102) --- src/upload/src/UploadFileList.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx index 0c8ec5590ea..ad2d7816fe5 100644 --- a/src/upload/src/UploadFileList.tsx +++ b/src/upload/src/UploadFileList.tsx @@ -46,9 +46,7 @@ export default defineComponent({ /> )) - const createUploadFileList = ( - isImageCardTypeRef: ComputedRef - ): VNode => + const createUploadFileList = (): VNode => isImageCardTypeRef.value ? ( {{ default: createFileList }} ) : ( @@ -68,7 +66,7 @@ export default defineComponent({ ]} style={[cssVarsRef.value, fileListStyle as CSSProperties]} > - {createUploadFileList(isImageCardTypeRef)} + {createUploadFileList()} {isImageCardTypeRef.value && {slots}} ) From cac57c425dceb9328aaac8c9aef5cd0753e6db34 Mon Sep 17 00:00:00 2001 From: kev1nzh_ark Date: Sun, 19 Sep 2021 13:15:31 +0800 Subject: [PATCH 7/7] feat(upload): fix lint (#1102) --- src/upload/src/UploadFileList.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx index ad2d7816fe5..d936b89ee45 100644 --- a/src/upload/src/UploadFileList.tsx +++ b/src/upload/src/UploadFileList.tsx @@ -1,12 +1,4 @@ -import { - h, - defineComponent, - inject, - VNode, - CSSProperties, - computed, - ComputedRef -} from 'vue' +import { h, defineComponent, inject, VNode, CSSProperties, computed } from 'vue' import { throwError } from '../../_utils' import { uploadInjectionKey } from './interface' import NUploadFile from './UploadFile'