diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 8ab72d328..cbeb138f6 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -5,7 +5,6 @@ ### Breaking Changes - `n-layout-sider`'s `arrow-circle` trigger is changed into new style. -- `n-upload` add `abstract` prop, add `n-upload-trigger` 和 `n-upload-file-list` component, closes [#1102](https://github.com/TuSimple/naive-ui/issues/1102). ### Feats @@ -14,6 +13,7 @@ - `n-input-number` add `readonly` prop , closes [#1198](https://github.com/TuSimple/naive-ui/issues/1198). - `n-spin` add `description` prop and slot. - `n-anchor` add `variant` prop. +- `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 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 4349ea3e0..240d67da5 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -5,7 +5,6 @@ ### Breaking Changes - `n-layout-sider` 的 `arrow-circle` 类型触发按钮采用了新样式 -- `n-upload` 新增 `abstract` 属性,新增 `n-upload-trigger` 和 `n-upload-file-list` 组件,关闭 [#1102](https://github.com/TuSimple/naive-ui/issues/1102) ### Feats @@ -14,6 +13,7 @@ - `n-input-number` 新增 `readonly` 属性,关闭 [#1198](https://github.com/TuSimple/naive-ui/issues/1198) - `n-spin` 新增 `description` prop 和 slot - `n-anchor` 新增 `variant` 属性 +- `n-upload` 新增 `abstract` 属性,新增 `n-upload-trigger` 和 `n-upload-file-list` 组件,关闭 [#1102](https://github.com/TuSimple/naive-ui/issues/1102) ### Fixes diff --git a/src/upload/demos/enUS/abstract.demo.md b/src/upload/demos/enUS/abstract.demo.md index fc614155a..bbefb7344 100644 --- a/src/upload/demos/enUS/abstract.demo.md +++ b/src/upload/demos/enUS/abstract.demo.md @@ -1,29 +1,25 @@ -# No Wrapper DOM +# Split Trigger and List -`n-upload` set `abstract`. +Set `abstract` on `n-upload`. `n-upload-trigger` and `n-upload-file-list` need to be called from within `n-upload`. ```html -
- - - Eat - Sleep - - Upload - - - - - - - -
+ + + Useless + + Upload + + + + + + ``` ```js diff --git a/src/upload/demos/enUS/index.demo-entry.md b/src/upload/demos/enUS/index.demo-entry.md index d28ebcdca..977dcb1c8 100644 --- a/src/upload/demos/enUS/index.demo-entry.md +++ b/src/upload/demos/enUS/index.demo-entry.md @@ -23,7 +23,7 @@ abstract | Name | Type | Default | Description | | --- | --- | --- | --- | -| abstract | `boolean` | `false` | Whether or not DOM wrapping does not exist. | +| abstract | `boolean` | `false` | Whether or not DOM wrapping does not exist. Not supported for `image-card` type. | | 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. | @@ -64,6 +64,12 @@ abstract | type? | `string \| null` | MIME type. | | url? | `string \| null` | File URL. | +### UploadTrigger Props + +| Name | Type | Default | Description | +| -------- | --------- | ------- | ------------------------------------------- | +| abstract | `boolean` | `false` | Whether or not DOM wrapping does not exist. | + ### Upload Methods | Name | Type | Description | diff --git a/src/upload/demos/zhCN/abstract.demo.md b/src/upload/demos/zhCN/abstract.demo.md index 9c7a6468e..d7fe7e3ac 100644 --- a/src/upload/demos/zhCN/abstract.demo.md +++ b/src/upload/demos/zhCN/abstract.demo.md @@ -1,29 +1,25 @@ -# 不需要包裹 DOM +# 拆分触发器和列表 `n-upload` 设置 `abstract`。 `n-upload-trigger`和 `n-upload-file-list` 需在 `n-upload` 内调用。 ```html -
- - - Eat - Sleep - - Upload - - - - - - - -
+ + + 点我没用 + + 上传 + + + + + + ``` ```js diff --git a/src/upload/demos/zhCN/index.demo-entry.md b/src/upload/demos/zhCN/index.demo-entry.md index 77d430684..30ca2f403 100644 --- a/src/upload/demos/zhCN/index.demo-entry.md +++ b/src/upload/demos/zhCN/index.demo-entry.md @@ -23,7 +23,7 @@ abstract | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 | +| abstract | `boolean` | `false` | 是否不存在 DOM 包裹,不支持 `image-card` 类型的 Upload | | accept | `string` | `undefined` | 接受的文件类型,参考 accept | | action | `string` | `undefined` | 请求提交的地址 | | create-thumbnail-url | `(file: File) => Promise` | `undefined` | 自定义文件缩略图 | @@ -63,6 +63,12 @@ abstract | type? | `string \| null` | MIME 类型 | | url? | `string \| null` | 文件下载 URL | +### UploadTrigger Props + +| 名称 | 类型 | 默认值 | 说明 | +| -------- | --------- | ------- | ------------------- | +| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 | + ### Upload Methods | 名称 | 类型 | 说明 | diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index 47a42703a..b22059a1e 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -7,7 +7,6 @@ import { ref, PropType, CSSProperties, - renderSlot, Fragment, Teleport, nextTick @@ -18,7 +17,6 @@ import { useConfig, useTheme, useFormItem } from '../../_mixins' import type { ThemeProps } from '../../_mixins' import { ExtractPublicPropTypes, - getFirstSlotVNode, warn, MaybeArray, call, @@ -501,7 +499,8 @@ export default defineComponent({ openFileDialog, draggerInsideRef, handleFileAddition, - fileListStyle: props.fileListStyle, + mergedDisabledRef, + fileListStyleRef: toRef(props, 'fileListStyle'), abstractRef: toRef(props, 'abstract'), cssVarsRef }) @@ -509,7 +508,6 @@ export default defineComponent({ mergedClsPrefix: mergedClsPrefixRef, draggerInsideRef, inputElRef, - mergedDisabled: mergedDisabledRef, mergedTheme: themeRef, dragOver: dragOverRef, handleFileInputChange, @@ -519,53 +517,44 @@ export default defineComponent({ }, render () { const { draggerInsideRef, mergedClsPrefix, $slots } = this + if ($slots.default && !this.abstract) { - const firstChild = getFirstSlotVNode($slots, 'default') - // @ts-expect-error - if (firstChild?.type?.[uploadDraggerKey]) { + const firstChild = $slots.default()[0] + if ((firstChild as any)?.type?.[uploadDraggerKey]) { draggerInsideRef.value = true } } + const inputNode = ( + + ) + return this.abstract ? ( <> - {renderSlot(this.$slots, 'default')} - - - + {$slots.default?.()} + {inputNode} ) : (
- + {inputNode} {this.listType !== 'image-card' && ( - {this.$slots} + {$slots} )} - {this.showFileList && {this.$slots}} + {this.showFileList && {$slots}}
) } diff --git a/src/upload/src/UploadDragger.tsx b/src/upload/src/UploadDragger.tsx index 9e63fe865..c0a2da3bf 100644 --- a/src/upload/src/UploadDragger.tsx +++ b/src/upload/src/UploadDragger.tsx @@ -15,10 +15,21 @@ export default defineComponent({ '`n-upload-dragger` must be placed inside `n-upload`.' ) } - return () => ( -
- {slots} -
- ) + return () => { + const { + mergedClsPrefixRef: { value: mergedClsPrefix }, + mergedDisabledRef: { value: mergedDisabled } + } = NUpload + return ( +
+ {slots} +
+ ) + } } }) diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx index d936b89ee..61638091e 100644 --- a/src/upload/src/UploadFileList.tsx +++ b/src/upload/src/UploadFileList.tsx @@ -21,14 +21,16 @@ export default defineComponent({ mergedClsPrefixRef, listTypeRef, mergedFileListRef, - fileListStyle, - cssVarsRef + fileListStyleRef, + cssVarsRef, + mergedDisabledRef } = NUpload + const isImageCardTypeRef = computed( () => listTypeRef.value === 'image-card' ) - const createFileList = (): VNode[] => + const renderFileList = (): VNode[] => mergedFileListRef.value.map((file) => ( )) - const createUploadFileList = (): VNode => + const renderUploadFileList = (): VNode => isImageCardTypeRef.value ? ( - {{ default: createFileList }} + {{ default: renderFileList }} ) : ( {{ - default: createFileList + default: renderFileList }} ) - return () => ( -
- {createUploadFileList()} - {isImageCardTypeRef.value && {slots}} -
- ) + return () => { + const { value: mergedClsPrefix } = mergedClsPrefixRef + return ( +
+ {renderUploadFileList()} + {isImageCardTypeRef.value && {slots}} +
+ ) + } } }) diff --git a/src/upload/src/UploadTrigger.tsx b/src/upload/src/UploadTrigger.tsx index 164398c52..a3b370bab 100644 --- a/src/upload/src/UploadTrigger.tsx +++ b/src/upload/src/UploadTrigger.tsx @@ -1,11 +1,14 @@ -import { h, defineComponent, inject, renderSlot, computed } from 'vue' +import { h, defineComponent, inject, computed } from 'vue' import { throwError } from '../../_utils' import { uploadInjectionKey } from './interface' import NUploadDragger from './UploadDragger' export default defineComponent({ name: 'UploadTrigger', - setup (_, { slots }) { + props: { + abstract: Boolean + }, + setup (props, { slots }) { const NUpload = inject(uploadInjectionKey, null) if (!NUpload) { throwError( @@ -16,13 +19,13 @@ export default defineComponent({ const { mergedClsPrefixRef, + mergedDisabledRef, listTypeRef, disabledRef, dragOverRef, openFileDialog, draggerInsideRef, - handleFileAddition, - abstractRef + handleFileAddition } = NUpload const isImageCardTypeRef = computed( @@ -56,9 +59,10 @@ export default defineComponent({ dragOverRef.value = false } - return () => - abstractRef.value ? ( - renderSlot(slots, 'default', { + return () => { + const { value: mergedClsPrefix } = mergedClsPrefixRef + return props.abstract ? ( + slots.default?.({ handleClick: handleTriggerClick, handleDrop: handleTriggerDrop, handleDragOver: handleTriggerDragOver, @@ -68,9 +72,11 @@ export default defineComponent({ ) : (
) + } } }) diff --git a/src/upload/src/interface.ts b/src/upload/src/interface.ts index 4a564effa..b89d698f6 100644 --- a/src/upload/src/interface.ts +++ b/src/upload/src/interface.ts @@ -65,19 +65,20 @@ export interface UploadInjection { onRemoveRef: Ref onDownloadRef: Ref XhrMap: Map - submit: (fileId?: string) => void doChange: DoChange 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 + fileListStyleRef: Ref + mergedDisabledRef: Ref abstractRef: Ref cssVarsRef: Ref + submit: (fileId?: string) => void + getFileThumbnailUrl: (file: FileInfo) => Promise + handleFileAddition: (files: FileList | null, e?: Event) => void + openFileDialog: () => void } export const uploadInjectionKey: InjectionKey = diff --git a/src/upload/src/styles/index.cssr.ts b/src/upload/src/styles/index.cssr.ts index 6a8de0044..586645193 100644 --- a/src/upload/src/styles/index.cssr.ts +++ b/src/upload/src/styles/index.cssr.ts @@ -4,69 +4,73 @@ import createIconSwitchTransition from '../../../_styles/transitions/icon-switch export default c([ cB('upload', [ - 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', ` - cursor: not-allowed; - `) - ]), cM('drag-over', [ cB('upload-dragger', ` border: var(--dragger-border-hover); `) ]) ]), + cB('upload-dragger', ` + cursor: pointer; + box-sizing: border-box; + width: 100%; + text-align: center; + border-radius: var(--border-radius); + padding: 24px; + opacity: 1; + transition: + opacity: .3s var(--bezier), + 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); + cursor: not-allowed; + `) + ]), + cB('upload-trigger', ` + display: inline-block; + box-sizing: border-box; + opacity: 1; + transition: opacity .3s var(--bezier); + `, [ + cM('disabled', ` + opacity: var(--item-disabled-opacity); + cursor: not-allowed; + `), + cM('image-card', [ + cB('upload-dragger', ` + padding: 0; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + `) + ]) + ]), cB('upload-file-list', ` margin-top: 8px; line-height: var(--line-height); + opacity: 1; + transition: opacity .3s var(--bezier); `, [ + cM('disabled', ` + opacity: var(--item-disabled-opacity); + cursor: not-allowed; + `, [ + cB('upload-file', 'cursor: not-allowed;') + ]), cM('grid', ` display: grid; grid-template-columns: repeat(auto-fill, 96px); @@ -88,7 +92,7 @@ export default c([ }) ]), c('&:hover', ` - background-color: var(--item-color-hover); + background-color: var(--item-color-hover); `, [ cB('upload-file-info', [ cE('action', ` @@ -307,7 +311,7 @@ export default c([ ]) ]) ]), - cB('upload__file-input', ` + cB('upload-file-input', ` display: block; width: 0; height: 0; diff --git a/src/upload/tests/Upload.spec.ts b/src/upload/tests/Upload.spec.ts index 0333c1436..8e94bfd6a 100644 --- a/src/upload/tests/Upload.spec.ts +++ b/src/upload/tests/Upload.spec.ts @@ -31,10 +31,19 @@ describe('n-upload', () => { it('should work with `disabled` prop', async () => { const wrapper = mount(NUpload) - expect(wrapper.find('.n-upload--disabled').exists()).not.toBe(true) - + const disabledClasses = [ + 'n-upload-trigger--disabled', + 'n-upload-file-list--disabled' + ] + for (const disabledClass of disabledClasses) { + expect(wrapper.find(disabledClass).exists()).not.toBe(true) + } await wrapper.setProps({ disabled: true }) - expect(wrapper.find('.n-upload').classes()).toContain('n-upload--disabled') + for (const disabledClass of disabledClasses) { + expect( + wrapper.find('.' + disabledClass.split('--')[0]).classes() + ).toContain(disabledClass) + } }) it('should work with `on-before-upload` prop', async () => { @@ -347,7 +356,7 @@ describe('n-upload-trigger', () => { default: () => h(NButton, null, { default: () => 'button1' }) } }) - const triggerItem = wrapper.find('.n-upload__trigger') + const triggerItem = wrapper.find('.n-upload-trigger') await triggerItem.trigger('click') await triggerItem.trigger('drop')