Commit d2942fca authored by Pavel Mashliakovskiy's avatar Pavel Mashliakovskiy 🤹🏻

Merge branch 'featute/UFileInputImprovements' into 'master'

`UFileInput` improvement allows to use a control as independent file selection

See merge request !815
parents 5fe1969e 67da22ab
Pipeline #21721 skipped with stage
......@@ -44,6 +44,10 @@
<u-form-row label="<u-file-input/>">
<u-file-input />
</u-form-row>
<u-form-row label="<u-file-input accept='.dbf'/>">
<u-file-input accept=".dbf" placeholder="DBF upload" v-model="selectedFiles"/>
<div> {{ selectedFiles.length }} files selected </div>
</u-form-row>
<u-auto-field
v-model="doc_file"
label="<u-file/> Auto field"
......@@ -259,6 +263,7 @@ module.exports.default = {
data () {
return {
multipleFileExample: [],
selectedFiles: [],
iconSize: 'medium',
sizes: ['small', 'medium', 'large'],
icons: [
......
......@@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- `UTable`: new slot `appendTable` - rendered below the table and can be used as a table footer
- `UFile`: additional param `file` to the `beforeSetDocument` hook
- `UFileInput` improvement allows to use a control as independent file selection (for example to select files for import, select PKI key etc.):
- new properties `placeholder` & `selectedPlaceholder` allows change text what displaying in case file(s) is not selected / selected
- in case some files are selected it names are displayed behind `selectedPlaceholder` instead of `placeholder`
- in case `accept` prop is not empty then allowed file types displayed after `placeholder`
- `v-model` support
- `UToolbar`: handler for `showAccessRights` action added for entites with aclRls mixin
### Changed
### Deprecated
......@@ -17,7 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed
### Fixed
- `UTableEntity`: hidding of bottom pagination button for tables with many columns.
- `UTableEntity`: bottom pagination button for tables is centered on a current viewport, so visible even in case of many columns in table
- `UTableEntity`: bottom pagination button is hidden in case next page is unavailable
- `UNavbar` collapse button background color is bound to `USidebar` background.
## [1.12.1] - 2020-07-19
......
......@@ -207,6 +207,7 @@
</template>
<div
v-if="!isLastPageIndex && !loading"
slot="appendTable"
class="u-table-entity__next-page-button-wrap"
>
......
......@@ -42,7 +42,7 @@
:accept="accept"
:disabled="disabled"
:border="false"
@upload="upload"
@input="upload"
/>
</template>
</u-file-container>
......
......@@ -18,38 +18,64 @@
size="large"
class="u-file__dropzone-icon"
/>
{{ $ut('fileInput.dropZone.caption') }}
<div v-if="value.length">
{{ $ut(selectedPlaceholder) }}: {{ selectedFileNames }}
</div>
<div
v-else
class="u-file__dropzone-placeholder"
>
{{ $ut(placeholder) }}
<div v-if="accept">
({{ accept }})
</div>
</div>
<input
type="file"
:disabled="disabled"
:multiple="multiple"
:accept="accept"
v-bind="$attrs"
@change="fileChanged"
@change="fileInputChanged"
>
</label>
</template>
<script>
/**
* Input file with drag and drop, but without display value - just upload
* Input file with drag and drop
*/
export default {
name: 'UFileInput',
props: {
/**
* Like native attribute multiple in input[type=file].
* Allow to select multiple files. Like native attribute `multiple` in input[type=file]
*/
multiple: Boolean,
/**
* Dropzone in non-selected state placeholder
*/
placeholder: {
type: String,
default: 'fileInput.dropZone.caption'
},
/**
* Dropzone in selected state placeholder
*/
selectedPlaceholder: {
type: String,
default: 'fileInput.dropZone.selectedFiles'
},
/**
* Sets disabled state
*/
disabled: Boolean,
/**
* File extensions to bind into `accept` input property
* File types the file input should accept. This string is a comma-separated list of unique file type specifiers.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept
*
* Example: ".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
*/
accept: {
type: String,
......@@ -57,11 +83,16 @@ export default {
},
/**
* Display border
* Show border
*/
border: {
type: Boolean,
default: true
},
value: {
type: Array,
default: () => []
}
},
......@@ -71,33 +102,34 @@ export default {
}
},
computed: {
acceptableFileTypes: function () {
return this.accept.split(',').map(a => a.trim())
},
selectedFileNames: function () {
return this.value.map(f => f.name).join(', ')
}
},
methods: {
fileChanged (e) {
const files = e.target.files
if (files && files.length) {
this.$emit('upload', [...files])
e.target.value = null
}
fileInputChanged (e) {
this.upload(e.target.files)
e.target.value = null
},
drop (e) {
this.dragging = false
if (this.disabled) return
const { valid, invalid } = [...e.dataTransfer.files].reduce((files, file) => {
const isValid = this.isAccept(file)
if (isValid) {
files.valid.push(file)
} else {
files.invalid.push(file)
}
return files
}, {
valid: [],
invalid: []
})
this.upload(e.dataTransfer.files)
},
upload (files) {
const { valid, invalid } = this.validateFiles(files)
this.$emit('upload', valid)
if (valid.length) {
this.$emit('input', valid)
}
if (invalid.length) {
const invalidFilesNames = invalid.map(f => f.name).join(', ')
......@@ -109,34 +141,34 @@ export default {
}
},
dragover (e) {
if (this.disabled) {
return false
} else {
e.preventDefault()
e.stopPropagation()
this.dragging = true
validateFiles (files) {
const valid = []
const invalid = []
for (const file of Array.from(files)) {
if (this.isAcceptable(file)) {
valid.push(file)
} else {
invalid.push(file)
}
}
return { valid, invalid }
},
dragover (e) {
if (this.disabled) return false
e.preventDefault()
e.stopPropagation()
this.dragging = true
},
isAccept (file) {
isAcceptable (file) {
if (this.accept === '') {
return true
}
const accepts = this.accept.split(',')
.map(a => a.trim())
return accepts.some(accept => {
const [type, extension] = accept.split(/\/|\./)
if (type === '') {
const fileExtention = file.name.split('.').pop()
return fileExtention.includes(extension)
} else {
if (extension === '*') {
return file.type.includes(type)
} else {
return file.type === accept
}
}
return this.acceptableFileTypes.some(ft => {
// file type specifier can be either extension or mime
return file.name.endsWith(ft) || file.type.includes(ft)
})
}
}
......@@ -151,7 +183,6 @@ export default {
.u-file__dropzone {
padding: 20px 12px;
border-radius: var(--border-radius);
color: hsl(var(--hs-text), var(--l-text-description));
display: flex;
flex-direction: column;
align-items: center;
......@@ -162,6 +193,10 @@ export default {
width: 100%;
}
.u-file__dropzone-placeholder {
color: hsl(var(--hs-text), var(--l-text-description));
}
.u-file__dropzone-border{
border: 1px solid hsl(var(--hs-border), var(--l-layout-border-default));
}
......@@ -189,19 +224,30 @@ export default {
```vue
<template>
<div>
<el-button @click="previewMode = !previewMode">Toggle "previewMode"</el-button>
<el-button @click="disabled = !disabled">Toggle "disabled"</el-button>
<el-button @click="multiple = !multiple">Toggle "multiple"</el-button>
<pre>
previewMode: {{previewMode}}
disabled: {{disabled}}
multiple: {{multiple}}
</pre>
<u-file-input
:disabled="disabled"
:multiple="multiple"
@upload="upload"
@input="upload"
/>
Accept only PDF and txt and bind to model selectedFiles property:
<u-file-input
:disabled="disabled"
:multiple="multiple"
placeholder="Select file for import"
selected-placeholder="Will import"
accept=".pdf,.txt"
v-model="selectedFiles"
/>
<u-button @click="doImport" :disabled="!selectedFiles.length">Import</u-button>
<div>
{{selectedFiles.length}} files selected
</div>
</div>
</template>
<script>
......@@ -209,13 +255,17 @@ export default {
data () {
return {
disabled: false,
previewMode: false,
multiple: true
multiple: true,
selectedFiles: []
}
},
methods: {
upload (files) {
console.log(files)
},
doImport () {
const fileNames = selectedFiles.map(f => f.name).join(', ')
this.$dialogYesNo(`Import ${fileNames} into database?`)
}
}
}
......
......@@ -46,7 +46,7 @@
multiple
:accept="accept"
:border="false"
@upload="upload"
@input="upload"
/>
<component
:is="view"
......
......@@ -182,7 +182,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Faylı buraya sürün və ya seçmək üçün vurun',
acceptError: 'Yanlış fayl uzadılması'
acceptError: 'Yanlış fayl uzadılması',
selectedFiles: 'Seçilib'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Drop file(s) here or click to upload',
acceptError: 'Invalid file extension'
acceptError: 'Invalid file(s) type',
selectedFiles: 'Selected'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Drop file(s) here or click to upload',
acceptError: 'Invalid file extension'
acceptError: 'Invalid file(s) type',
selectedFiles: 'Selected'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Drop file(s) here or click to upload',
acceptError: 'Invalid file extension'
acceptError: 'Invalid file(s) type',
selectedFiles: 'Selected'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Перетащите файл сюда или нажмите для выбора',
acceptError: 'Неверное расширение файла'
acceptError: 'Неверный тип файла(ов)',
selectedFiles: 'Выбрано'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Перетащите файл сюда или нажмите для выбора',
acceptError: 'Неверное расширение файла'
acceptError: 'Неверный тип файла(ов)',
selectedFiles: 'Выбрано'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Перетащите файл сюда или нажмите для выбора',
acceptError: 'Неверное расширение файла'
acceptError: 'Неверный тип файла(ов)',
selectedFiles: 'Выбрано'
}
},
......
......@@ -181,7 +181,8 @@ UB.i18nExtend({
},
dropZone: {
caption: 'Перетягніть файл сюди або натисніть для вибору',
acceptError: 'Невірне розширення файлу'
acceptError: 'Невірний тип файлу(ів)',
selectedFiles: 'Обрано'
}
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment