refactor(upload): clean codes

This commit is contained in:
07akioni 2021-09-19 15:13:07 +08:00
parent 01bd890b73
commit 78f5db7620
13 changed files with 203 additions and 171 deletions

View File

@ -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

View File

@ -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

View File

@ -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
<div>
<n-upload
<n-upload
abstract
:default-file-list="fileList"
action="http://www.mocky.io/v2/5e4bafc63100007100d8b70f"
>
>
<n-button-group>
<n-button> Eat </n-button>
<n-button> Sleep </n-button>
<n-upload-trigger #="{handleClick}">
<n-button>Useless</n-button>
<n-upload-trigger #="{handleClick}" abstract>
<n-button @click="handleClick">Upload</n-button>
</n-upload-trigger>
</n-button-group>
<n-card style="margin-top: 12px;" title="File List">
<n-upload-file-list />
</n-card>
</n-upload>
</div>
</n-upload>
```
```js

View File

@ -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 <n-a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept">accept</n-a>. |
| action | `string` | `undefined` | The URL to submit data to. |
| create-thumbnail-url | `(file: File) => Promise<string>` | `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 |

View File

@ -1,29 +1,25 @@
# 不需要包裹 DOM
# 拆分触发器和列表
`n-upload` 设置 `abstract`
`n-upload-trigger``n-upload-file-list` 需在 `n-upload` 内调用。
```html
<div>
<n-upload
<n-upload
abstract
:default-file-list="fileList"
action="http://www.mocky.io/v2/5e4bafc63100007100d8b70f"
>
>
<n-button-group>
<n-button> Eat </n-button>
<n-button> Sleep </n-button>
<n-upload-trigger #="{handleClick}">
<n-button @click="handleClick">Upload</n-button>
<n-button>点我没用</n-button>
<n-upload-trigger #="{handleClick}" abstract>
<n-button @click="handleClick">上传</n-button>
</n-upload-trigger>
</n-button-group>
<n-card style="margin-top: 12px;" title="File List">
<n-card style="margin-top: 12px;" title="文件列表">
<n-upload-file-list />
</n-card>
</n-upload>
</div>
</n-upload>
```
```js

View File

@ -23,7 +23,7 @@ abstract
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 |
| abstract | `boolean` | `false` | 是否不存在 DOM 包裹,不支持 `image-card` 类型的 Upload |
| accept | `string` | `undefined` | 接受的文件类型,参考 <n-a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept">accept</n-a> |
| action | `string` | `undefined` | 请求提交的地址 |
| create-thumbnail-url | `(file: File) => Promise<string>` | `undefined` | 自定义文件缩略图 |
@ -63,6 +63,12 @@ abstract
| type? | `string \| null` | MIME 类型 |
| url? | `string \| null` | 文件下载 URL |
### UploadTrigger Props
| 名称 | 类型 | 默认值 | 说明 |
| -------- | --------- | ------- | ------------------- |
| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 |
### Upload Methods
| 名称 | 类型 | 说明 |

View File

@ -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
}
}
return this.abstract ? (
<>
{renderSlot(this.$slots, 'default')}
<Teleport to="body">
const inputNode = (
<input
ref="inputElRef"
type="file"
class={`${mergedClsPrefix}-upload__file-input`}
class={`${mergedClsPrefix}-upload-file-input`}
accept={this.accept}
multiple={this.multiple}
onChange={this.handleFileInputChange}
/>
</Teleport>
)
return this.abstract ? (
<>
{$slots.default?.()}
<Teleport to="body">{inputNode}</Teleport>
</>
) : (
<div
class={[
`${mergedClsPrefix}-upload`,
{
[`${mergedClsPrefix}-upload--dragger-inside`]:
draggerInsideRef.value,
[`${mergedClsPrefix}-upload--drag-over`]: this.dragOver,
[`${mergedClsPrefix}-upload--disabled`]: this.mergedDisabled
}
draggerInsideRef.value && `${mergedClsPrefix}-upload--dragger-inside`,
this.dragOver && `${mergedClsPrefix}-upload--drag-over`
]}
style={this.cssVars as CSSProperties}
>
<input
ref="inputElRef"
type="file"
class={`${mergedClsPrefix}-upload__file-input`}
accept={this.accept}
multiple={this.multiple}
onChange={this.handleFileInputChange}
/>
{inputNode}
{this.listType !== 'image-card' && (
<NUploadTrigger>{this.$slots}</NUploadTrigger>
<NUploadTrigger>{$slots}</NUploadTrigger>
)}
{this.showFileList && <NUploadFileList>{this.$slots}</NUploadFileList>}
{this.showFileList && <NUploadFileList>{$slots}</NUploadFileList>}
</div>
)
}

View File

@ -15,10 +15,21 @@ export default defineComponent({
'`n-upload-dragger` must be placed inside `n-upload`.'
)
}
return () => (
<div class={`${NUpload.mergedClsPrefixRef.value}-upload-dragger`}>
return () => {
const {
mergedClsPrefixRef: { value: mergedClsPrefix },
mergedDisabledRef: { value: mergedDisabled }
} = NUpload
return (
<div
class={[
`${mergedClsPrefix}-upload-dragger`,
mergedDisabled && `${mergedClsPrefix}-upload-dragger--disabled`
]}
>
{slots}
</div>
)
}
}
})

View File

@ -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) => (
<NUploadFile
clsPrefix={mergedClsPrefixRef.value}
@ -38,29 +40,34 @@ export default defineComponent({
/>
))
const createUploadFileList = (): VNode =>
const renderUploadFileList = (): VNode =>
isImageCardTypeRef.value ? (
<NImageGroup>{{ default: createFileList }}</NImageGroup>
<NImageGroup>{{ default: renderFileList }}</NImageGroup>
) : (
<NFadeInExpandTransition group>
{{
default: createFileList
default: renderFileList
}}
</NFadeInExpandTransition>
)
return () => (
return () => {
const { value: mergedClsPrefix } = mergedClsPrefixRef
return (
<div
class={[
`${mergedClsPrefixRef.value}-upload-file-list`,
`${mergedClsPrefix}-upload-file-list`,
mergedDisabledRef.value &&
`${mergedClsPrefix}-upload-file-list--disabled`,
isImageCardTypeRef.value &&
`${mergedClsPrefixRef.value}-upload-file-list--grid`
`${mergedClsPrefix}-upload-file-list--grid`
]}
style={[cssVarsRef.value, fileListStyle as CSSProperties]}
style={[cssVarsRef.value, fileListStyleRef.value as CSSProperties]}
>
{createUploadFileList()}
{renderUploadFileList()}
{isImageCardTypeRef.value && <NUploadTrigger>{slots}</NUploadTrigger>}
</div>
)
}
}
})

View File

@ -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({
) : (
<div
class={[
`${mergedClsPrefixRef.value}-upload__trigger`,
`${mergedClsPrefix}-upload-trigger`,
mergedDisabledRef.value &&
`${mergedClsPrefix}-upload-trigger--disabled`,
isImageCardTypeRef.value &&
`${mergedClsPrefixRef.value}-upload__trigger--image-card`
`${mergedClsPrefix}-upload-trigger--image-card`
]}
onClick={handleTriggerClick}
onDrop={handleTriggerDrop}
@ -86,4 +92,5 @@ export default defineComponent({
</div>
)
}
}
})

View File

@ -65,19 +65,20 @@ export interface UploadInjection {
onRemoveRef: Ref<OnRemove | undefined>
onDownloadRef: Ref<OnDownload | undefined>
XhrMap: Map<string, XMLHttpRequest>
submit: (fileId?: string) => void
doChange: DoChange
showPreivewButtonRef: Ref<boolean>
onPreviewRef: Ref<OnPreview | undefined>
getFileThumbnailUrl: (file: FileInfo) => Promise<string>
listTypeRef: Ref<listType>
dragOverRef: Ref<boolean>
draggerInsideRef: { value: boolean }
handleFileAddition: (files: FileList | null, e?: Event) => void
fileListStyle: string | CSSProperties | undefined
openFileDialog: () => void
fileListStyleRef: Ref<string | CSSProperties | undefined>
mergedDisabledRef: Ref<boolean>
abstractRef: Ref<boolean>
cssVarsRef: Ref<CSSProperties>
submit: (fileId?: string) => void
getFileThumbnailUrl: (file: FileInfo) => Promise<string>
handleFileAddition: (files: FileList | null, e?: Event) => void
openFileDialog: () => void
}
export const uploadInjectionKey: InjectionKey<UploadInjection> =

View File

@ -4,10 +4,50 @@ import createIconSwitchTransition from '../../../_styles/transitions/icon-switch
export default c([
cB('upload', [
cM('dragger-inside', [
cE('trigger', `
display: block;
`)
]),
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;
@ -19,54 +59,18 @@ export default c([
`)
])
]),
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-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);
@ -307,7 +311,7 @@ export default c([
])
])
]),
cB('upload__file-input', `
cB('upload-file-input', `
display: block;
width: 0;
height: 0;

View File

@ -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')