Fixes stray drag/drop on missed dropzones, allows removing of non uploaded files
This commit is contained in:
parent
2d1c58f725
commit
dd74fcb12e
186
dist/formulate.esm.js
vendored
186
dist/formulate.esm.js
vendored
@ -115,11 +115,12 @@ var library = {
|
|||||||
* The file upload class holds and represents a file’s upload state durring
|
* The file upload class holds and represents a file’s upload state durring
|
||||||
* the upload flow.
|
* the upload flow.
|
||||||
*/
|
*/
|
||||||
var FileUpload = function FileUpload (fileList, context, options) {
|
var FileUpload = function FileUpload (input, context, options) {
|
||||||
this.fileList = fileList;
|
this.input = input;
|
||||||
|
this.fileList = input.files;
|
||||||
this.files = [];
|
this.files = [];
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.setFileList(fileList);
|
this.addFileList(this.fileList);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -127,16 +128,28 @@ var FileUpload = function FileUpload (fileList, context, options) {
|
|||||||
* Produce an array of files and alert the callback.
|
* Produce an array of files and alert the callback.
|
||||||
* @param {FileList}
|
* @param {FileList}
|
||||||
*/
|
*/
|
||||||
FileUpload.prototype.setFileList = function setFileList (fileList) {
|
FileUpload.prototype.addFileList = function addFileList (fileList) {
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
var this$1 = this;
|
||||||
var file = fileList.item(i);
|
|
||||||
this.files.push({
|
var loop = function ( i ) {
|
||||||
progress: 0,
|
var file = fileList[i];
|
||||||
|
var uuid = nanoid();
|
||||||
|
var removeFile = function () {
|
||||||
|
this.removeFile(uuid);
|
||||||
|
};
|
||||||
|
this$1.files.push({
|
||||||
|
progress: false,
|
||||||
|
error: false,
|
||||||
|
complete: false,
|
||||||
|
justFinished: false,
|
||||||
name: file.name || 'file-upload',
|
name: file.name || 'file-upload',
|
||||||
file: file,
|
file: file,
|
||||||
uuid: nanoid()
|
uuid: uuid,
|
||||||
|
removeFile: removeFile.bind(this$1)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < fileList.length; i++) loop( i );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,8 +209,21 @@ FileUpload.prototype.upload = function upload () {
|
|||||||
Promise.all(this$1.files.map(function (file) {
|
Promise.all(this$1.files.map(function (file) {
|
||||||
return this$1.getUploader(
|
return this$1.getUploader(
|
||||||
file.file,
|
file.file,
|
||||||
function (progress) { file.progress = progress; },
|
function (progress) {
|
||||||
function (error) { return reject(new Error(error)); },
|
file.progress = progress;
|
||||||
|
if (progress >= 100) {
|
||||||
|
if (!file.complete) {
|
||||||
|
file.justFinished = true;
|
||||||
|
setTimeout(function () { file.justFinished = false; }, this$1.options.uploadJustCompleteDuration);
|
||||||
|
}
|
||||||
|
file.complete = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
file.progress = 0;
|
||||||
|
file.error = error;
|
||||||
|
file.complete = true;
|
||||||
|
},
|
||||||
this$1.options
|
this$1.options
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
@ -206,6 +232,20 @@ FileUpload.prototype.upload = function upload () {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file from the uploader (and the file list)
|
||||||
|
* @param {string} uuid
|
||||||
|
*/
|
||||||
|
FileUpload.prototype.removeFile = function removeFile (uuid) {
|
||||||
|
this.files = this.files.filter(function (file) { return file.uuid !== uuid; });
|
||||||
|
if (window) {
|
||||||
|
var transfer = new DataTransfer();
|
||||||
|
this.files.map(function (file) { return transfer.items.add(file.file); });
|
||||||
|
this.fileList = transfer.files;
|
||||||
|
this.input.files = this.fileList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the files.
|
* Get the files.
|
||||||
*/
|
*/
|
||||||
@ -538,14 +578,10 @@ var rules = {
|
|||||||
|
|
||||||
return Promise.resolve((function () {
|
return Promise.resolve((function () {
|
||||||
if (files instanceof FileUpload) {
|
if (files instanceof FileUpload) {
|
||||||
if (files.hasUploader()) {
|
var fileList = files.getFileList();
|
||||||
return false
|
for (var i = 0; i < fileList.length; i++) {
|
||||||
}
|
var file = fileList[i];
|
||||||
files = files.getFiles();
|
if (!types.includes(file.type)) {
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
|
||||||
for (var i in files) {
|
|
||||||
if (!types.includes(files[i].type)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -776,6 +812,16 @@ var en = {
|
|||||||
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (field-level) error message for mime errors.
|
||||||
|
*/
|
||||||
|
mime: function (ref) {
|
||||||
|
var name = ref.name;
|
||||||
|
var args = ref.args;
|
||||||
|
|
||||||
|
return ((sentence(name)) + " must of the the type: " + (args[0] || 'No file formats allowed.'))
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum value allowed.
|
* The maximum value allowed.
|
||||||
*/
|
*/
|
||||||
@ -842,17 +888,29 @@ var en = {
|
|||||||
*/
|
*/
|
||||||
function fauxUploader (file, progress, error, options) {
|
function fauxUploader (file, progress, error, options) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var totalTime = options.fauxUploaderDuration || 2000;
|
var totalTime = (options.fauxUploaderDuration || 2000) * (0.5 + Math.random());
|
||||||
var start = performance.now();
|
var start = performance.now();
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
var fail = (Math.random() > 0.5);
|
||||||
var advance = function () { return setTimeout(function () {
|
var advance = function () { return setTimeout(function () {
|
||||||
var elapsed = performance.now() - start;
|
var elapsed = performance.now() - start;
|
||||||
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
||||||
progress(currentProgress);
|
progress(currentProgress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
if (fail && currentProgress > 50) {
|
||||||
|
return error('There was an error uploading the file.')
|
||||||
|
}
|
||||||
|
|
||||||
if (currentProgress >= 100) {
|
if (currentProgress >= 100) {
|
||||||
resolve({
|
return resolve({
|
||||||
url: 'http://via.placeholder.com/350x150.png',
|
url: 'http://via.placeholder.com/350x150.png',
|
||||||
name: file.name
|
name: file.name
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
@ -881,7 +939,8 @@ var context = {
|
|||||||
showImage: this.showImage,
|
showImage: this.showImage,
|
||||||
uploadUrl: this.uploadUrl,
|
uploadUrl: this.uploadUrl,
|
||||||
uploader: this.uploader || this.$formulate.getUploader(),
|
uploader: this.uploader || this.$formulate.getUploader(),
|
||||||
immediateUpload: this.immediateUpload},
|
uploadBehavior: this.uploadBehavior,
|
||||||
|
preventWindowDrops: this.preventWindowDrops},
|
||||||
this.typeContext))
|
this.typeContext))
|
||||||
},
|
},
|
||||||
nameOrFallback: nameOrFallback,
|
nameOrFallback: nameOrFallback,
|
||||||
@ -1182,7 +1241,11 @@ var script = {
|
|||||||
type: [Function, Object, Boolean],
|
type: [Function, Object, Boolean],
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
immediateUpload: {
|
uploadBehavior: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
preventWindowDrops: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
@ -2200,18 +2263,52 @@ var __vue_render__$6 = function() {
|
|||||||
"ul",
|
"ul",
|
||||||
{ staticClass: "formulate-files" },
|
{ staticClass: "formulate-files" },
|
||||||
_vm._l(_vm.fileUploads, function(file) {
|
_vm._l(_vm.fileUploads, function(file) {
|
||||||
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
|
return _c(
|
||||||
_c("span", {
|
"li",
|
||||||
|
{ key: file.uuid, attrs: { "data-has-error": !!file.error } },
|
||||||
|
[
|
||||||
|
_c("div", { staticClass: "formulate-file" }, [
|
||||||
|
_c("div", {
|
||||||
staticClass: "formualte-file-name",
|
staticClass: "formualte-file-name",
|
||||||
domProps: { textContent: _vm._s(file.name) }
|
domProps: { textContent: _vm._s(file.name) }
|
||||||
}),
|
}),
|
||||||
_vm._v(" "),
|
_vm._v(" "),
|
||||||
file.progress > 0 && file.progress < 100
|
file.progress !== false
|
||||||
? _c("span", {
|
? _c(
|
||||||
domProps: { textContent: _vm._s(file.progress + "%") }
|
"div",
|
||||||
|
{
|
||||||
|
staticClass: "formulate-file-progress",
|
||||||
|
attrs: {
|
||||||
|
"data-just-finished": file.justFinished,
|
||||||
|
"data-is-finished":
|
||||||
|
!file.justFinished && file.complete
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
_c("div", {
|
||||||
|
staticClass: "formulate-file-progress-inner",
|
||||||
|
style: { width: file.progress + "%" }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: _vm._e(),
|
||||||
|
_vm._v(" "),
|
||||||
|
(file.complete && !file.justFinished) || file.progress === false
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-remove",
|
||||||
|
on: { click: file.removeFile }
|
||||||
})
|
})
|
||||||
: _vm._e()
|
: _vm._e()
|
||||||
])
|
]),
|
||||||
|
_vm._v(" "),
|
||||||
|
file.error
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-upload-error",
|
||||||
|
domProps: { textContent: _vm._s(file.error) }
|
||||||
|
})
|
||||||
|
: _vm._e()
|
||||||
|
]
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
@ -2264,16 +2361,36 @@ var script$7 = {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasFiles: function hasFiles () {
|
hasFiles: function hasFiles () {
|
||||||
return (this.context.model instanceof FileUpload && this.context.model.files.length)
|
return !!(this.context.model instanceof FileUpload && this.context.model.files.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function mounted () {
|
||||||
|
// Add a listener to the window to prevent drag/drops that miss the dropzone
|
||||||
|
// from opening the file and navigating the user away from the page.
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.addEventListener('dragover', this.preventDefault);
|
||||||
|
window.addEventListener('drop', this.preventDefault);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed: function destroyed () {
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.removeEventListener('dragover', this.preventDefault);
|
||||||
|
window.removeEventListener('drop', this.preventDefault);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
preventDefault: function preventDefault (e) {
|
||||||
|
if (e.target.tagName !== 'INPUT' && e.target.getAttribute('type') !== 'file') {
|
||||||
|
e = e || event;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
handleFile: function handleFile () {
|
handleFile: function handleFile () {
|
||||||
var input = this.$refs.file;
|
var input = this.$refs.file;
|
||||||
if (input.files.length) {
|
if (input.files.length) {
|
||||||
this.context.model = this.$formulate.createUpload(input.files, this.context);
|
this.context.model = this.$formulate.createUpload(input, this.context);
|
||||||
}
|
}
|
||||||
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
|
if (this.context.uploadBehavior === 'live' && this.context.model instanceof FileUpload) {
|
||||||
this.context.model.upload();
|
this.context.model.upload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2819,6 +2936,7 @@ var Formulate = function Formulate () {
|
|||||||
rules: rules,
|
rules: rules,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
uploader: fauxUploader,
|
uploader: fauxUploader,
|
||||||
|
uploadJustCompleteDuration: 1000,
|
||||||
locales: {
|
locales: {
|
||||||
en: en
|
en: en
|
||||||
}
|
}
|
||||||
|
186
dist/formulate.min.js
vendored
186
dist/formulate.min.js
vendored
@ -118,11 +118,12 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
* The file upload class holds and represents a file’s upload state durring
|
* The file upload class holds and represents a file’s upload state durring
|
||||||
* the upload flow.
|
* the upload flow.
|
||||||
*/
|
*/
|
||||||
var FileUpload = function FileUpload (fileList, context, options) {
|
var FileUpload = function FileUpload (input, context, options) {
|
||||||
this.fileList = fileList;
|
this.input = input;
|
||||||
|
this.fileList = input.files;
|
||||||
this.files = [];
|
this.files = [];
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.setFileList(fileList);
|
this.addFileList(this.fileList);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,16 +131,28 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
* Produce an array of files and alert the callback.
|
* Produce an array of files and alert the callback.
|
||||||
* @param {FileList}
|
* @param {FileList}
|
||||||
*/
|
*/
|
||||||
FileUpload.prototype.setFileList = function setFileList (fileList) {
|
FileUpload.prototype.addFileList = function addFileList (fileList) {
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
var this$1 = this;
|
||||||
var file = fileList.item(i);
|
|
||||||
this.files.push({
|
var loop = function ( i ) {
|
||||||
progress: 0,
|
var file = fileList[i];
|
||||||
|
var uuid = nanoid();
|
||||||
|
var removeFile = function () {
|
||||||
|
this.removeFile(uuid);
|
||||||
|
};
|
||||||
|
this$1.files.push({
|
||||||
|
progress: false,
|
||||||
|
error: false,
|
||||||
|
complete: false,
|
||||||
|
justFinished: false,
|
||||||
name: file.name || 'file-upload',
|
name: file.name || 'file-upload',
|
||||||
file: file,
|
file: file,
|
||||||
uuid: nanoid()
|
uuid: uuid,
|
||||||
|
removeFile: removeFile.bind(this$1)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < fileList.length; i++) loop( i );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,8 +212,21 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
Promise.all(this$1.files.map(function (file) {
|
Promise.all(this$1.files.map(function (file) {
|
||||||
return this$1.getUploader(
|
return this$1.getUploader(
|
||||||
file.file,
|
file.file,
|
||||||
function (progress) { file.progress = progress; },
|
function (progress) {
|
||||||
function (error) { return reject(new Error(error)); },
|
file.progress = progress;
|
||||||
|
if (progress >= 100) {
|
||||||
|
if (!file.complete) {
|
||||||
|
file.justFinished = true;
|
||||||
|
setTimeout(function () { file.justFinished = false; }, this$1.options.uploadJustCompleteDuration);
|
||||||
|
}
|
||||||
|
file.complete = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
file.progress = 0;
|
||||||
|
file.error = error;
|
||||||
|
file.complete = true;
|
||||||
|
},
|
||||||
this$1.options
|
this$1.options
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
@ -209,6 +235,20 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file from the uploader (and the file list)
|
||||||
|
* @param {string} uuid
|
||||||
|
*/
|
||||||
|
FileUpload.prototype.removeFile = function removeFile (uuid) {
|
||||||
|
this.files = this.files.filter(function (file) { return file.uuid !== uuid; });
|
||||||
|
if (window) {
|
||||||
|
var transfer = new DataTransfer();
|
||||||
|
this.files.map(function (file) { return transfer.items.add(file.file); });
|
||||||
|
this.fileList = transfer.files;
|
||||||
|
this.input.files = this.fileList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the files.
|
* Get the files.
|
||||||
*/
|
*/
|
||||||
@ -541,14 +581,10 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
|
|
||||||
return Promise.resolve((function () {
|
return Promise.resolve((function () {
|
||||||
if (files instanceof FileUpload) {
|
if (files instanceof FileUpload) {
|
||||||
if (files.hasUploader()) {
|
var fileList = files.getFileList();
|
||||||
return false
|
for (var i = 0; i < fileList.length; i++) {
|
||||||
}
|
var file = fileList[i];
|
||||||
files = files.getFiles();
|
if (!types.includes(file.type)) {
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
|
||||||
for (var i in files) {
|
|
||||||
if (!types.includes(files[i].type)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -779,6 +815,16 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (field-level) error message for mime errors.
|
||||||
|
*/
|
||||||
|
mime: function (ref) {
|
||||||
|
var name = ref.name;
|
||||||
|
var args = ref.args;
|
||||||
|
|
||||||
|
return ((sentence(name)) + " must of the the type: " + (args[0] || 'No file formats allowed.'))
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum value allowed.
|
* The maximum value allowed.
|
||||||
*/
|
*/
|
||||||
@ -845,17 +891,29 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
*/
|
*/
|
||||||
function fauxUploader (file, progress, error, options) {
|
function fauxUploader (file, progress, error, options) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var totalTime = options.fauxUploaderDuration || 2000;
|
var totalTime = (options.fauxUploaderDuration || 2000) * (0.5 + Math.random());
|
||||||
var start = performance.now();
|
var start = performance.now();
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
var fail = (Math.random() > 0.5);
|
||||||
var advance = function () { return setTimeout(function () {
|
var advance = function () { return setTimeout(function () {
|
||||||
var elapsed = performance.now() - start;
|
var elapsed = performance.now() - start;
|
||||||
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
||||||
progress(currentProgress);
|
progress(currentProgress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
if (fail && currentProgress > 50) {
|
||||||
|
return error('There was an error uploading the file.')
|
||||||
|
}
|
||||||
|
|
||||||
if (currentProgress >= 100) {
|
if (currentProgress >= 100) {
|
||||||
resolve({
|
return resolve({
|
||||||
url: 'http://via.placeholder.com/350x150.png',
|
url: 'http://via.placeholder.com/350x150.png',
|
||||||
name: file.name
|
name: file.name
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
@ -884,7 +942,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
showImage: this.showImage,
|
showImage: this.showImage,
|
||||||
uploadUrl: this.uploadUrl,
|
uploadUrl: this.uploadUrl,
|
||||||
uploader: this.uploader || this.$formulate.getUploader(),
|
uploader: this.uploader || this.$formulate.getUploader(),
|
||||||
immediateUpload: this.immediateUpload},
|
uploadBehavior: this.uploadBehavior,
|
||||||
|
preventWindowDrops: this.preventWindowDrops},
|
||||||
this.typeContext))
|
this.typeContext))
|
||||||
},
|
},
|
||||||
nameOrFallback: nameOrFallback,
|
nameOrFallback: nameOrFallback,
|
||||||
@ -1185,7 +1244,11 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
type: [Function, Object, Boolean],
|
type: [Function, Object, Boolean],
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
immediateUpload: {
|
uploadBehavior: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
preventWindowDrops: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
@ -2203,18 +2266,52 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
"ul",
|
"ul",
|
||||||
{ staticClass: "formulate-files" },
|
{ staticClass: "formulate-files" },
|
||||||
_vm._l(_vm.fileUploads, function(file) {
|
_vm._l(_vm.fileUploads, function(file) {
|
||||||
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
|
return _c(
|
||||||
_c("span", {
|
"li",
|
||||||
|
{ key: file.uuid, attrs: { "data-has-error": !!file.error } },
|
||||||
|
[
|
||||||
|
_c("div", { staticClass: "formulate-file" }, [
|
||||||
|
_c("div", {
|
||||||
staticClass: "formualte-file-name",
|
staticClass: "formualte-file-name",
|
||||||
domProps: { textContent: _vm._s(file.name) }
|
domProps: { textContent: _vm._s(file.name) }
|
||||||
}),
|
}),
|
||||||
_vm._v(" "),
|
_vm._v(" "),
|
||||||
file.progress > 0 && file.progress < 100
|
file.progress !== false
|
||||||
? _c("span", {
|
? _c(
|
||||||
domProps: { textContent: _vm._s(file.progress + "%") }
|
"div",
|
||||||
|
{
|
||||||
|
staticClass: "formulate-file-progress",
|
||||||
|
attrs: {
|
||||||
|
"data-just-finished": file.justFinished,
|
||||||
|
"data-is-finished":
|
||||||
|
!file.justFinished && file.complete
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
_c("div", {
|
||||||
|
staticClass: "formulate-file-progress-inner",
|
||||||
|
style: { width: file.progress + "%" }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: _vm._e(),
|
||||||
|
_vm._v(" "),
|
||||||
|
(file.complete && !file.justFinished) || file.progress === false
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-remove",
|
||||||
|
on: { click: file.removeFile }
|
||||||
})
|
})
|
||||||
: _vm._e()
|
: _vm._e()
|
||||||
])
|
]),
|
||||||
|
_vm._v(" "),
|
||||||
|
file.error
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-upload-error",
|
||||||
|
domProps: { textContent: _vm._s(file.error) }
|
||||||
|
})
|
||||||
|
: _vm._e()
|
||||||
|
]
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
@ -2267,16 +2364,36 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasFiles: function hasFiles () {
|
hasFiles: function hasFiles () {
|
||||||
return (this.context.model instanceof FileUpload && this.context.model.files.length)
|
return !!(this.context.model instanceof FileUpload && this.context.model.files.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function mounted () {
|
||||||
|
// Add a listener to the window to prevent drag/drops that miss the dropzone
|
||||||
|
// from opening the file and navigating the user away from the page.
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.addEventListener('dragover', this.preventDefault);
|
||||||
|
window.addEventListener('drop', this.preventDefault);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed: function destroyed () {
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.removeEventListener('dragover', this.preventDefault);
|
||||||
|
window.removeEventListener('drop', this.preventDefault);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
preventDefault: function preventDefault (e) {
|
||||||
|
if (e.target.tagName !== 'INPUT' && e.target.getAttribute('type') !== 'file') {
|
||||||
|
e = e || event;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
handleFile: function handleFile () {
|
handleFile: function handleFile () {
|
||||||
var input = this.$refs.file;
|
var input = this.$refs.file;
|
||||||
if (input.files.length) {
|
if (input.files.length) {
|
||||||
this.context.model = this.$formulate.createUpload(input.files, this.context);
|
this.context.model = this.$formulate.createUpload(input, this.context);
|
||||||
}
|
}
|
||||||
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
|
if (this.context.uploadBehavior === 'live' && this.context.model instanceof FileUpload) {
|
||||||
this.context.model.upload();
|
this.context.model.upload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2822,6 +2939,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
|||||||
rules: rules,
|
rules: rules,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
uploader: fauxUploader,
|
uploader: fauxUploader,
|
||||||
|
uploadJustCompleteDuration: 1000,
|
||||||
locales: {
|
locales: {
|
||||||
en: en
|
en: en
|
||||||
}
|
}
|
||||||
|
186
dist/formulate.umd.js
vendored
186
dist/formulate.umd.js
vendored
@ -121,11 +121,12 @@
|
|||||||
* The file upload class holds and represents a file’s upload state durring
|
* The file upload class holds and represents a file’s upload state durring
|
||||||
* the upload flow.
|
* the upload flow.
|
||||||
*/
|
*/
|
||||||
var FileUpload = function FileUpload (fileList, context, options) {
|
var FileUpload = function FileUpload (input, context, options) {
|
||||||
this.fileList = fileList;
|
this.input = input;
|
||||||
|
this.fileList = input.files;
|
||||||
this.files = [];
|
this.files = [];
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.setFileList(fileList);
|
this.addFileList(this.fileList);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,16 +134,28 @@
|
|||||||
* Produce an array of files and alert the callback.
|
* Produce an array of files and alert the callback.
|
||||||
* @param {FileList}
|
* @param {FileList}
|
||||||
*/
|
*/
|
||||||
FileUpload.prototype.setFileList = function setFileList (fileList) {
|
FileUpload.prototype.addFileList = function addFileList (fileList) {
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
var this$1 = this;
|
||||||
var file = fileList.item(i);
|
|
||||||
this.files.push({
|
var loop = function ( i ) {
|
||||||
progress: 0,
|
var file = fileList[i];
|
||||||
|
var uuid = nanoid();
|
||||||
|
var removeFile = function () {
|
||||||
|
this.removeFile(uuid);
|
||||||
|
};
|
||||||
|
this$1.files.push({
|
||||||
|
progress: false,
|
||||||
|
error: false,
|
||||||
|
complete: false,
|
||||||
|
justFinished: false,
|
||||||
name: file.name || 'file-upload',
|
name: file.name || 'file-upload',
|
||||||
file: file,
|
file: file,
|
||||||
uuid: nanoid()
|
uuid: uuid,
|
||||||
|
removeFile: removeFile.bind(this$1)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < fileList.length; i++) loop( i );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,8 +215,21 @@
|
|||||||
Promise.all(this$1.files.map(function (file) {
|
Promise.all(this$1.files.map(function (file) {
|
||||||
return this$1.getUploader(
|
return this$1.getUploader(
|
||||||
file.file,
|
file.file,
|
||||||
function (progress) { file.progress = progress; },
|
function (progress) {
|
||||||
function (error) { return reject(new Error(error)); },
|
file.progress = progress;
|
||||||
|
if (progress >= 100) {
|
||||||
|
if (!file.complete) {
|
||||||
|
file.justFinished = true;
|
||||||
|
setTimeout(function () { file.justFinished = false; }, this$1.options.uploadJustCompleteDuration);
|
||||||
|
}
|
||||||
|
file.complete = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
file.progress = 0;
|
||||||
|
file.error = error;
|
||||||
|
file.complete = true;
|
||||||
|
},
|
||||||
this$1.options
|
this$1.options
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
@ -212,6 +238,20 @@
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file from the uploader (and the file list)
|
||||||
|
* @param {string} uuid
|
||||||
|
*/
|
||||||
|
FileUpload.prototype.removeFile = function removeFile (uuid) {
|
||||||
|
this.files = this.files.filter(function (file) { return file.uuid !== uuid; });
|
||||||
|
if (window) {
|
||||||
|
var transfer = new DataTransfer();
|
||||||
|
this.files.map(function (file) { return transfer.items.add(file.file); });
|
||||||
|
this.fileList = transfer.files;
|
||||||
|
this.input.files = this.fileList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the files.
|
* Get the files.
|
||||||
*/
|
*/
|
||||||
@ -544,14 +584,10 @@
|
|||||||
|
|
||||||
return Promise.resolve((function () {
|
return Promise.resolve((function () {
|
||||||
if (files instanceof FileUpload) {
|
if (files instanceof FileUpload) {
|
||||||
if (files.hasUploader()) {
|
var fileList = files.getFileList();
|
||||||
return false
|
for (var i = 0; i < fileList.length; i++) {
|
||||||
}
|
var file = fileList[i];
|
||||||
files = files.getFiles();
|
if (!types.includes(file.type)) {
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
|
||||||
for (var i in files) {
|
|
||||||
if (!types.includes(files[i].type)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -782,6 +818,16 @@
|
|||||||
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (field-level) error message for mime errors.
|
||||||
|
*/
|
||||||
|
mime: function (ref) {
|
||||||
|
var name = ref.name;
|
||||||
|
var args = ref.args;
|
||||||
|
|
||||||
|
return ((sentence(name)) + " must of the the type: " + (args[0] || 'No file formats allowed.'))
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum value allowed.
|
* The maximum value allowed.
|
||||||
*/
|
*/
|
||||||
@ -848,17 +894,29 @@
|
|||||||
*/
|
*/
|
||||||
function fauxUploader (file, progress, error, options) {
|
function fauxUploader (file, progress, error, options) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var totalTime = options.fauxUploaderDuration || 2000;
|
var totalTime = (options.fauxUploaderDuration || 2000) * (0.5 + Math.random());
|
||||||
var start = performance.now();
|
var start = performance.now();
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
var fail = (Math.random() > 0.5);
|
||||||
var advance = function () { return setTimeout(function () {
|
var advance = function () { return setTimeout(function () {
|
||||||
var elapsed = performance.now() - start;
|
var elapsed = performance.now() - start;
|
||||||
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
|
||||||
progress(currentProgress);
|
progress(currentProgress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
if (fail && currentProgress > 50) {
|
||||||
|
return error('There was an error uploading the file.')
|
||||||
|
}
|
||||||
|
|
||||||
if (currentProgress >= 100) {
|
if (currentProgress >= 100) {
|
||||||
resolve({
|
return resolve({
|
||||||
url: 'http://via.placeholder.com/350x150.png',
|
url: 'http://via.placeholder.com/350x150.png',
|
||||||
name: file.name
|
name: file.name
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
@ -887,7 +945,8 @@
|
|||||||
showImage: this.showImage,
|
showImage: this.showImage,
|
||||||
uploadUrl: this.uploadUrl,
|
uploadUrl: this.uploadUrl,
|
||||||
uploader: this.uploader || this.$formulate.getUploader(),
|
uploader: this.uploader || this.$formulate.getUploader(),
|
||||||
immediateUpload: this.immediateUpload},
|
uploadBehavior: this.uploadBehavior,
|
||||||
|
preventWindowDrops: this.preventWindowDrops},
|
||||||
this.typeContext))
|
this.typeContext))
|
||||||
},
|
},
|
||||||
nameOrFallback: nameOrFallback,
|
nameOrFallback: nameOrFallback,
|
||||||
@ -1188,7 +1247,11 @@
|
|||||||
type: [Function, Object, Boolean],
|
type: [Function, Object, Boolean],
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
immediateUpload: {
|
uploadBehavior: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
preventWindowDrops: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
@ -2206,18 +2269,52 @@
|
|||||||
"ul",
|
"ul",
|
||||||
{ staticClass: "formulate-files" },
|
{ staticClass: "formulate-files" },
|
||||||
_vm._l(_vm.fileUploads, function(file) {
|
_vm._l(_vm.fileUploads, function(file) {
|
||||||
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
|
return _c(
|
||||||
_c("span", {
|
"li",
|
||||||
|
{ key: file.uuid, attrs: { "data-has-error": !!file.error } },
|
||||||
|
[
|
||||||
|
_c("div", { staticClass: "formulate-file" }, [
|
||||||
|
_c("div", {
|
||||||
staticClass: "formualte-file-name",
|
staticClass: "formualte-file-name",
|
||||||
domProps: { textContent: _vm._s(file.name) }
|
domProps: { textContent: _vm._s(file.name) }
|
||||||
}),
|
}),
|
||||||
_vm._v(" "),
|
_vm._v(" "),
|
||||||
file.progress > 0 && file.progress < 100
|
file.progress !== false
|
||||||
? _c("span", {
|
? _c(
|
||||||
domProps: { textContent: _vm._s(file.progress + "%") }
|
"div",
|
||||||
|
{
|
||||||
|
staticClass: "formulate-file-progress",
|
||||||
|
attrs: {
|
||||||
|
"data-just-finished": file.justFinished,
|
||||||
|
"data-is-finished":
|
||||||
|
!file.justFinished && file.complete
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
_c("div", {
|
||||||
|
staticClass: "formulate-file-progress-inner",
|
||||||
|
style: { width: file.progress + "%" }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: _vm._e(),
|
||||||
|
_vm._v(" "),
|
||||||
|
(file.complete && !file.justFinished) || file.progress === false
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-remove",
|
||||||
|
on: { click: file.removeFile }
|
||||||
})
|
})
|
||||||
: _vm._e()
|
: _vm._e()
|
||||||
])
|
]),
|
||||||
|
_vm._v(" "),
|
||||||
|
file.error
|
||||||
|
? _c("div", {
|
||||||
|
staticClass: "formulate-file-upload-error",
|
||||||
|
domProps: { textContent: _vm._s(file.error) }
|
||||||
|
})
|
||||||
|
: _vm._e()
|
||||||
|
]
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
@ -2270,16 +2367,36 @@
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasFiles: function hasFiles () {
|
hasFiles: function hasFiles () {
|
||||||
return (this.context.model instanceof FileUpload && this.context.model.files.length)
|
return !!(this.context.model instanceof FileUpload && this.context.model.files.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function mounted () {
|
||||||
|
// Add a listener to the window to prevent drag/drops that miss the dropzone
|
||||||
|
// from opening the file and navigating the user away from the page.
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.addEventListener('dragover', this.preventDefault);
|
||||||
|
window.addEventListener('drop', this.preventDefault);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed: function destroyed () {
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.removeEventListener('dragover', this.preventDefault);
|
||||||
|
window.removeEventListener('drop', this.preventDefault);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
preventDefault: function preventDefault (e) {
|
||||||
|
if (e.target.tagName !== 'INPUT' && e.target.getAttribute('type') !== 'file') {
|
||||||
|
e = e || event;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
handleFile: function handleFile () {
|
handleFile: function handleFile () {
|
||||||
var input = this.$refs.file;
|
var input = this.$refs.file;
|
||||||
if (input.files.length) {
|
if (input.files.length) {
|
||||||
this.context.model = this.$formulate.createUpload(input.files, this.context);
|
this.context.model = this.$formulate.createUpload(input, this.context);
|
||||||
}
|
}
|
||||||
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
|
if (this.context.uploadBehavior === 'live' && this.context.model instanceof FileUpload) {
|
||||||
this.context.model.upload();
|
this.context.model.upload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2825,6 +2942,7 @@
|
|||||||
rules: rules,
|
rules: rules,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
uploader: fauxUploader,
|
uploader: fauxUploader,
|
||||||
|
uploadJustCompleteDuration: 1000,
|
||||||
locales: {
|
locales: {
|
||||||
en: en
|
en: en
|
||||||
}
|
}
|
||||||
|
117
dist/snow.css
vendored
117
dist/snow.css
vendored
@ -19,7 +19,8 @@
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
.formulate-input .formulate-input-error {
|
.formulate-input .formulate-input-error,
|
||||||
|
.formulate-input .formulate-file-upload-error {
|
||||||
color: #960505;
|
color: #960505;
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@ -220,9 +221,7 @@
|
|||||||
.formulate-input[data-classification="file"] .formulate-input-upload-area {
|
.formulate-input[data-classification="file"] .formulate-input-upload-area {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 2em; }
|
padding: 2em 0; }
|
||||||
.formulate-input[data-classification="file"] .formulate-input-upload-area[data-has-files] {
|
|
||||||
padding: 0; }
|
|
||||||
.formulate-input[data-classification="file"] .formulate-input-upload-area input {
|
.formulate-input[data-classification="file"] .formulate-input-upload-area input {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
@ -235,6 +234,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 5; }
|
z-index: 5; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-input-upload-area[data-has-files] {
|
||||||
|
padding: 0; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-input-upload-area[data-has-files] input {
|
||||||
|
display: none; }
|
||||||
.formulate-input[data-classification="file"] .formulate-input-upload-area-mask {
|
.formulate-input[data-classification="file"] .formulate-input-upload-area-mask {
|
||||||
border-radius: .4em;
|
border-radius: .4em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -253,6 +256,8 @@
|
|||||||
content: '';
|
content: '';
|
||||||
background-color: #a8a8a8;
|
background-color: #a8a8a8;
|
||||||
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><path d="M29,58A29,29,0,1,0,0,29,29,29,0,0,0,29,58ZM29,4A25,25,0,1,1,4,29,25,25,0,0,1,29,4Z"/><polygon points="27 22 27 44.4 31 44.4 31 22 41.7 31.1 44.3 28.1 29 15 13.7 28.1 16.3 31.1 27 22"/></svg>');
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><path d="M29,58A29,29,0,1,0,0,29,29,29,0,0,0,29,58ZM29,4A25,25,0,1,1,4,29,25,25,0,0,1,29,4Z"/><polygon points="27 22 27 44.4 31 44.4 31 22 41.7 31.1 44.3 28.1 29 15 13.7 28.1 16.3 31.1 27 22"/></svg>');
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -269,7 +274,98 @@
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress {
|
||||||
|
background-color: #cecece;
|
||||||
|
height: .3em;
|
||||||
|
border-radius: 1.25em;
|
||||||
|
width: 5em;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
right: .75em;
|
||||||
|
transition: height .25s, width .25s;
|
||||||
|
z-index: 2; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.08);
|
||||||
|
background-color: #ffffff;
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M8.76,56.2c-6.38-6.34,3.26-16,9.64-9.69L38,65.88,80.56,23.29c6.38-6.38,16.07,3.32,9.69,9.69L42.84,80.37a6.83,6.83,0,0,1-9.65,0Z"/></svg>');
|
||||||
|
mask-size: 77%;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
z-index: 3; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress[data-just-finished] {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress[data-just-finished]::before {
|
||||||
|
transition: transform .25s .2s, opacity .25s .2s;
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress[data-is-finished] {
|
||||||
|
transition: height .25s, width .25s, left .25s, top, .25s, border-radius .25s;
|
||||||
|
width: .3em;
|
||||||
|
height: 100%;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress[data-is-finished]::before {
|
||||||
|
transition: opacity .1s;
|
||||||
|
opacity: 0; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-progress .formulate-file-progress-inner {
|
||||||
|
background-color: #41b883;
|
||||||
|
width: 1%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formualte-file-name {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 2em; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formualte-file-name::before {
|
||||||
|
position: absolute;
|
||||||
|
left: .7em;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -.7em;
|
||||||
|
background-color: #a8a8a8;
|
||||||
|
content: '';
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64.06 83.59"><path d="M55.94,83.59a8.16,8.16,0,0,0,8.12-8.16V19.12a1.77,1.77,0,0,0-.52-1.25L46.21.59A1.69,1.69,0,0,0,45.14.08L44.69,0l-.18,0H8.13A8.18,8.18,0,0,0,0,8.16V75.41a8.16,8.16,0,0,0,8.13,8.16H55.94ZM46.68,6,58.11,17.38H46.68ZM3.52,75.43V8.16A4.64,4.64,0,0,1,8.13,3.52h35V19.16a1.75,1.75,0,0,0,1.76,1.74H60.55V75.43a4.65,4.65,0,0,1-4.61,4.65H8.13A4.65,4.65,0,0,1,3.52,75.43Z"/></svg>');
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: .5em; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-remove {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
border-radius: 1em;
|
||||||
|
border: 1px solid #a8a8a8;
|
||||||
|
background-color: #a8a8a8;
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 59.1 59.2"><path d="M1.6,57.7a5,5,0,0,0,3.5,1.5,4.85,4.85,0,0,0,3.5-1.5l21-21,21,21a5,5,0,0,0,3.5,1.5,4.85,4.85,0,0,0,3.5-1.5,5,5,0,0,0,0-7.1l-21-21,21-21a5,5,0,0,0,0-7.1,5,5,0,0,0-7.1,0l-21,21L8.6,1.7a5,5,0,0,0-7.1,0,5,5,0,0,0,0,7.1l21,21L1.6,50.7A4.83,4.83,0,0,0,1.6,57.7Z"/></svg>');
|
||||||
|
mask-size: .6em;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: .75em;
|
||||||
|
z-index: 1;
|
||||||
|
transition: transform .25s; }
|
||||||
|
@media (pointer: fine) {
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file-remove:hover {
|
||||||
|
transform: scale(1.5); } }
|
||||||
.formulate-input[data-classification="file"] .formulate-files li {
|
.formulate-input[data-classification="file"] .formulate-files li {
|
||||||
|
display: block; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files li[data-has-error] .formulate-file-progress {
|
||||||
|
background-color: #dc2c2c; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files li + li {
|
||||||
|
margin-top: .5em; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
border: 1px solid #cecece;
|
border: 1px solid #cecece;
|
||||||
@ -286,14 +382,19 @@
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between; }
|
justify-content: space-between;
|
||||||
.formulate-input[data-classification="file"] .formulate-files li::placeholder {
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden; }
|
||||||
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file::placeholder {
|
||||||
color: #a8a8a8; }
|
color: #a8a8a8; }
|
||||||
.formulate-input[data-classification="file"] .formulate-files li:focus {
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border: 1px solid #41b883; }
|
border: 1px solid #41b883; }
|
||||||
.formulate-input[data-classification="file"] .formulate-files li ::-webkit-progress-bar {
|
.formulate-input[data-classification="file"] .formulate-files .formulate-file ::-webkit-progress-bar {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
height: .5em;
|
height: .5em;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
overflow: hidden; }
|
overflow: hidden; }
|
||||||
|
.formulate-input[data-classification="file"] [data-type="image"] .formulate-input-upload-area .formulate-input-upload-area-mask::before {
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 71.05"><path d="M82.89,0H7.1A7.12,7.12,0,0,0,0,7.11V64a7.11,7.11,0,0,0,7.1,7.1H82.9A7.11,7.11,0,0,0,90,64V7.11A7.12,7.12,0,0,0,82.89,0ZM69.28,39.35a5.44,5.44,0,0,0-8,0L50.58,50.74,32.38,30.88a5.31,5.31,0,0,0-7.92,0L4.74,52.4V7.11A2.37,2.37,0,0,1,7.11,4.74H82.9a2.37,2.37,0,0,1,2.36,2.37V56.3Z"/><circle cx="67.74" cy="22.26" r="8.53"/></svg>'); }
|
||||||
|
4
dist/snow.min.css
vendored
4
dist/snow.min.css
vendored
File diff suppressed because one or more lines are too long
@ -10,11 +10,12 @@ class FileUpload {
|
|||||||
* @param {FileList} fileList
|
* @param {FileList} fileList
|
||||||
* @param {object} context
|
* @param {object} context
|
||||||
*/
|
*/
|
||||||
constructor (fileList, context, options) {
|
constructor (input, context, options) {
|
||||||
this.fileList = fileList
|
this.input = input
|
||||||
|
this.fileList = input.files
|
||||||
this.files = []
|
this.files = []
|
||||||
this.options = options
|
this.options = options
|
||||||
this.setFileList(fileList)
|
this.addFileList(this.fileList)
|
||||||
this.context = context
|
this.context = context
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,14 +23,22 @@ class FileUpload {
|
|||||||
* Produce an array of files and alert the callback.
|
* Produce an array of files and alert the callback.
|
||||||
* @param {FileList}
|
* @param {FileList}
|
||||||
*/
|
*/
|
||||||
setFileList (fileList) {
|
addFileList (fileList) {
|
||||||
for (let i = 0; i < fileList.length; i++) {
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
const file = fileList.item(i)
|
const file = fileList[i]
|
||||||
|
const uuid = nanoid()
|
||||||
|
const removeFile = function () {
|
||||||
|
this.removeFile(uuid)
|
||||||
|
}
|
||||||
this.files.push({
|
this.files.push({
|
||||||
progress: 0,
|
progress: false,
|
||||||
|
error: false,
|
||||||
|
complete: false,
|
||||||
|
justFinished: false,
|
||||||
name: file.name || 'file-upload',
|
name: file.name || 'file-upload',
|
||||||
file: file,
|
file,
|
||||||
uuid: nanoid()
|
uuid,
|
||||||
|
removeFile: removeFile.bind(this)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +94,21 @@ class FileUpload {
|
|||||||
Promise.all(this.files.map(file => {
|
Promise.all(this.files.map(file => {
|
||||||
return this.getUploader(
|
return this.getUploader(
|
||||||
file.file,
|
file.file,
|
||||||
(progress) => { file.progress = progress },
|
(progress) => {
|
||||||
(error) => reject(new Error(error)),
|
file.progress = progress
|
||||||
|
if (progress >= 100) {
|
||||||
|
if (!file.complete) {
|
||||||
|
file.justFinished = true
|
||||||
|
setTimeout(() => { file.justFinished = false }, this.options.uploadJustCompleteDuration)
|
||||||
|
}
|
||||||
|
file.complete = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
file.progress = 0
|
||||||
|
file.error = error
|
||||||
|
file.complete = true
|
||||||
|
},
|
||||||
this.options
|
this.options
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
@ -95,6 +117,20 @@ class FileUpload {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file from the uploader (and the file list)
|
||||||
|
* @param {string} uuid
|
||||||
|
*/
|
||||||
|
removeFile (uuid) {
|
||||||
|
this.files = this.files.filter(file => file.uuid !== uuid)
|
||||||
|
if (window) {
|
||||||
|
const transfer = new DataTransfer()
|
||||||
|
this.files.map(file => transfer.items.add(file.file))
|
||||||
|
this.fileList = transfer.files
|
||||||
|
this.input.files = this.fileList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the files.
|
* Get the files.
|
||||||
*/
|
*/
|
||||||
|
@ -40,6 +40,7 @@ class Formulate {
|
|||||||
rules,
|
rules,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
uploader: fauxUploader,
|
uploader: fauxUploader,
|
||||||
|
uploadJustCompleteDuration: 1000,
|
||||||
locales: {
|
locales: {
|
||||||
en
|
en
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,34 @@
|
|||||||
<li
|
<li
|
||||||
v-for="file in fileUploads"
|
v-for="file in fileUploads"
|
||||||
:key="file.uuid"
|
:key="file.uuid"
|
||||||
class="formulate-file"
|
:data-has-error="!!file.error"
|
||||||
>
|
>
|
||||||
<span
|
<div class="formulate-file">
|
||||||
|
<div
|
||||||
class="formualte-file-name"
|
class="formualte-file-name"
|
||||||
v-text="file.name"
|
v-text="file.name"
|
||||||
/>
|
/>
|
||||||
<span
|
<div
|
||||||
v-if="file.progress > 0 && file.progress < 100"
|
v-if="file.progress !== false"
|
||||||
v-text="`${file.progress}%`"
|
:data-just-finished="file.justFinished"
|
||||||
|
:data-is-finished="!file.justFinished && file.complete"
|
||||||
|
class="formulate-file-progress"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="formulate-file-progress-inner"
|
||||||
|
:style="{width: file.progress + '%'}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="(file.complete && !file.justFinished) || file.progress === false"
|
||||||
|
class="formulate-file-remove"
|
||||||
|
@click="file.removeFile"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="file.error"
|
||||||
|
class="formulate-file-upload-error"
|
||||||
|
v-text="file.error"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -149,7 +149,11 @@ export default {
|
|||||||
type: [Function, Object, Boolean],
|
type: [Function, Object, Boolean],
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
immediateUpload: {
|
uploadBehavior: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
preventWindowDrops: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
|
@ -48,16 +48,36 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasFiles () {
|
hasFiles () {
|
||||||
return (this.context.model instanceof FileUpload && this.context.model.files.length)
|
return !!(this.context.model instanceof FileUpload && this.context.model.files.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// Add a listener to the window to prevent drag/drops that miss the dropzone
|
||||||
|
// from opening the file and navigating the user away from the page.
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.addEventListener('dragover', this.preventDefault)
|
||||||
|
window.addEventListener('drop', this.preventDefault)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
if (window && this.context.preventWindowDrops) {
|
||||||
|
window.removeEventListener('dragover', this.preventDefault)
|
||||||
|
window.removeEventListener('drop', this.preventDefault)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
preventDefault (e) {
|
||||||
|
if (e.target.tagName !== 'INPUT' && e.target.getAttribute('type') !== 'file') {
|
||||||
|
e = e || event
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
},
|
||||||
handleFile () {
|
handleFile () {
|
||||||
const input = this.$refs.file
|
const input = this.$refs.file
|
||||||
if (input.files.length) {
|
if (input.files.length) {
|
||||||
this.context.model = this.$formulate.createUpload(input.files, this.context)
|
this.context.model = this.$formulate.createUpload(input, this.context)
|
||||||
}
|
}
|
||||||
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
|
if (this.context.uploadBehavior === 'live' && this.context.model instanceof FileUpload) {
|
||||||
this.context.model.upload()
|
this.context.model.upload()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,8 @@ export default {
|
|||||||
showImage: this.showImage,
|
showImage: this.showImage,
|
||||||
uploadUrl: this.uploadUrl,
|
uploadUrl: this.uploadUrl,
|
||||||
uploader: this.uploader || this.$formulate.getUploader(),
|
uploader: this.uploader || this.$formulate.getUploader(),
|
||||||
immediateUpload: this.immediateUpload,
|
uploadBehavior: this.uploadBehavior,
|
||||||
|
preventWindowDrops: this.preventWindowDrops,
|
||||||
...this.typeContext
|
...this.typeContext
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -8,14 +8,26 @@
|
|||||||
*/
|
*/
|
||||||
export default function (file, progress, error, options) {
|
export default function (file, progress, error, options) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const totalTime = options.fauxUploaderDuration || 2000
|
const totalTime = (options.fauxUploaderDuration || 2000) * (0.5 + Math.random())
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
const fail = (Math.random() > 0.5)
|
||||||
const advance = () => setTimeout(() => {
|
const advance = () => setTimeout(() => {
|
||||||
const elapsed = performance.now() - start
|
const elapsed = performance.now() - start
|
||||||
const currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100))
|
const currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100))
|
||||||
progress(currentProgress)
|
progress(currentProgress)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo - remove, intentional failure
|
||||||
|
*/
|
||||||
|
if (fail && currentProgress > 50) {
|
||||||
|
return error('There was an error uploading the file.')
|
||||||
|
}
|
||||||
|
|
||||||
if (currentProgress >= 100) {
|
if (currentProgress >= 100) {
|
||||||
resolve({
|
return resolve({
|
||||||
url: 'http://via.placeholder.com/350x150.png',
|
url: 'http://via.placeholder.com/350x150.png',
|
||||||
name: file.name
|
name: file.name
|
||||||
})
|
})
|
||||||
|
@ -149,14 +149,10 @@ export default {
|
|||||||
mime: function (files, ...types) {
|
mime: function (files, ...types) {
|
||||||
return Promise.resolve((() => {
|
return Promise.resolve((() => {
|
||||||
if (files instanceof FileUpload) {
|
if (files instanceof FileUpload) {
|
||||||
if (files.hasUploader()) {
|
const fileList = files.getFileList()
|
||||||
return false
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
}
|
const file = fileList[i]
|
||||||
files = files.getFiles()
|
if (!types.includes(file.type)) {
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
|
||||||
for (const i in files) {
|
|
||||||
if (!types.includes(files[i].type)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,13 @@ export default {
|
|||||||
return `${s(name)} must be less than ${args[0]} characters long.`
|
return `${s(name)} must be less than ${args[0]} characters long.`
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (field-level) error message for mime errors.
|
||||||
|
*/
|
||||||
|
mime: function ({ name, args }) {
|
||||||
|
return `${s(name)} must of the the type: ${args[0] || 'No file formats allowed.'}`
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum value allowed.
|
* The maximum value allowed.
|
||||||
*/
|
*/
|
||||||
|
@ -32,7 +32,6 @@ test('type "radio" with options renders a group', () => {
|
|||||||
expect(wrapper.contains(FormulateInputGroup)).toBe(true)
|
expect(wrapper.contains(FormulateInputGroup)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
test('labelPosition of type "checkbox" defaults to after', () => {
|
test('labelPosition of type "checkbox" defaults to after', () => {
|
||||||
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } })
|
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } })
|
||||||
expect(wrapper.vm.context.labelPosition).toBe('after')
|
expect(wrapper.vm.context.labelPosition).toBe('after')
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import rules from '@/libs/rules'
|
import rules from '@/libs/rules'
|
||||||
|
import FileUpload from '../src/FileUpload'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required rule
|
* Required rule
|
||||||
@ -295,9 +296,26 @@ describe('email', () => {
|
|||||||
* Mime types.
|
* Mime types.
|
||||||
*/
|
*/
|
||||||
describe('mime', () => {
|
describe('mime', () => {
|
||||||
it('passes basic image/jpeg stack', async () => expect(await rules.mime([{type: 'image/jpeg'}], 'image/png', 'image/jpeg')).toBe(true))
|
it('passes basic image/jpeg stack', async () => {
|
||||||
|
const fileUpload = new FileUpload({
|
||||||
|
files: [ { type: 'image/jpeg' } ]
|
||||||
|
})
|
||||||
|
expect(await rules.mime(fileUpload, 'image/png', 'image/jpeg')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
it('fails when not in stack', async () => expect(await rules.mime([{type: 'application/json'}], 'image/png', 'image/jpeg')).toBe(false))
|
it('passes when match is at begining of stack', async () => {
|
||||||
|
const fileUpload = new FileUpload({
|
||||||
|
files: [ { type: 'document/pdf' } ]
|
||||||
|
})
|
||||||
|
expect(await rules.mime(fileUpload, 'document/pdf')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails when not in stack', async () => {
|
||||||
|
const fileUpload = new FileUpload({
|
||||||
|
files: [ { type: 'application/json' } ]
|
||||||
|
})
|
||||||
|
expect(await rules.mime(fileUpload, 'image/png', 'image/jpeg')).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,8 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.formulate-input-error {
|
.formulate-input-error,
|
||||||
|
.formulate-file-upload-error {
|
||||||
color: $formulate-error;
|
color: $formulate-error;
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@ -286,11 +287,7 @@
|
|||||||
.formulate-input-upload-area {
|
.formulate-input-upload-area {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 2em;
|
padding: 2em 0;
|
||||||
|
|
||||||
&[data-has-files] {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -306,6 +303,15 @@
|
|||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&[data-has-files] {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-mask {
|
&-mask {
|
||||||
border-radius: .4em;
|
border-radius: .4em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -325,6 +331,8 @@
|
|||||||
content: '';
|
content: '';
|
||||||
background-color: $formulate-gray-dd;
|
background-color: $formulate-gray-dd;
|
||||||
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><path d="M29,58A29,29,0,1,0,0,29,29,29,0,0,0,29,58ZM29,4A25,25,0,1,1,4,29,25,25,0,0,1,29,4Z"/><polygon points="27 22 27 44.4 31 44.4 31 22 41.7 31.1 44.3 28.1 29 15 13.7 28.1 16.3 31.1 27 22"/></svg>');
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><path d="M29,58A29,29,0,1,0,0,29,29,29,0,0,0,29,58ZM29,4A25,25,0,1,1,4,29,25,25,0,0,1,29,4Z"/><polygon points="27 22 27 44.4 31 44.4 31 22 41.7 31.1 44.3 28.1 29 15 13.7 28.1 16.3 31.1 27 22"/></svg>');
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -345,17 +353,145 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.formulate-files {
|
.formulate-files {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
.formulate-file-progress {
|
||||||
|
background-color: $formulate-gray-d;
|
||||||
|
height: .3em;
|
||||||
|
border-radius: 1.25em;
|
||||||
|
width: 5em;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
right: .75em;
|
||||||
|
transition: height .25s, width .25s;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(.08);
|
||||||
|
background-color: $formulate-white;
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M8.76,56.2c-6.38-6.34,3.26-16,9.64-9.69L38,65.88,80.56,23.29c6.38-6.38,16.07,3.32,9.69,9.69L42.84,80.37a6.83,6.83,0,0,1-9.65,0Z"/></svg>');
|
||||||
|
mask-size: 77%;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-just-finished] {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
transition: transform .25s .2s, opacity .25s .2s;
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-is-finished] {
|
||||||
|
transition: height .25s, width .25s, left .25s, top, .25s, border-radius .25s;
|
||||||
|
width: .3em;
|
||||||
|
height: 100%;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
transition: opacity .1s;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formulate-file-progress-inner {
|
||||||
|
background-color: $formulate-green;
|
||||||
|
width: 1%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formualte-file-name {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 2em;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
left: .7em;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -.7em;
|
||||||
|
background-color: $formulate-gray-dd;
|
||||||
|
content: '';
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64.06 83.59"><path d="M55.94,83.59a8.16,8.16,0,0,0,8.12-8.16V19.12a1.77,1.77,0,0,0-.52-1.25L46.21.59A1.69,1.69,0,0,0,45.14.08L44.69,0l-.18,0H8.13A8.18,8.18,0,0,0,0,8.16V75.41a8.16,8.16,0,0,0,8.13,8.16H55.94ZM46.68,6,58.11,17.38H46.68ZM3.52,75.43V8.16A4.64,4.64,0,0,1,8.13,3.52h35V19.16a1.75,1.75,0,0,0,1.76,1.74H60.55V75.43a4.65,4.65,0,0,1-4.61,4.65H8.13A4.65,4.65,0,0,1,3.52,75.43Z"/></svg>');
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formulate-file-remove {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
border-radius: 1em;
|
||||||
|
border: 1px solid $formulate-gray-dd;
|
||||||
|
background-color: $formulate-gray-dd;
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 59.1 59.2"><path d="M1.6,57.7a5,5,0,0,0,3.5,1.5,4.85,4.85,0,0,0,3.5-1.5l21-21,21,21a5,5,0,0,0,3.5,1.5,4.85,4.85,0,0,0,3.5-1.5,5,5,0,0,0,0-7.1l-21-21,21-21a5,5,0,0,0,0-7.1,5,5,0,0,0-7.1,0l-21,21L8.6,1.7a5,5,0,0,0-7.1,0,5,5,0,0,0,0,7.1l21,21L1.6,50.7A4.83,4.83,0,0,0,1.6,57.7Z"/></svg>');
|
||||||
|
mask-size: .6em;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: .75em;
|
||||||
|
z-index: 1;
|
||||||
|
transition: transform .25s;
|
||||||
|
|
||||||
|
@media (pointer: fine) {
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&[data-has-error] {
|
||||||
|
.formulate-file-progress {
|
||||||
|
background-color: $formulate-error-l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& + li {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formulate-file {
|
||||||
@include baseinput;
|
@include baseinput;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
@mixin progress {
|
@mixin progress {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
@ -369,5 +505,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Image uploads
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[data-type="image"] {
|
||||||
|
.formulate-input-upload-area {
|
||||||
|
.formulate-input-upload-area-mask {
|
||||||
|
&::before {
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 71.05"><path d="M82.89,0H7.1A7.12,7.12,0,0,0,0,7.11V64a7.11,7.11,0,0,0,7.1,7.1H82.9A7.11,7.11,0,0,0,90,64V7.11A7.12,7.12,0,0,0,82.89,0ZM69.28,39.35a5.44,5.44,0,0,0-8,0L50.58,50.74,32.38,30.88a5.31,5.31,0,0,0-7.92,0L4.74,52.4V7.11A2.37,2.37,0,0,1,7.11,4.74H82.9a2.37,2.37,0,0,1,2.36,2.37V56.3Z"/><circle cx="67.74" cy="22.26" r="8.53"/></svg>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,14 @@ $formulate-blue-l: #f3f4f4;
|
|||||||
$formulate-green: #41b883;
|
$formulate-green: #41b883;
|
||||||
|
|
||||||
$formulate-error: #960505;
|
$formulate-error: #960505;
|
||||||
|
$formulate-error-l: #dc2c2c;
|
||||||
|
|
||||||
$formulate-yellow-d: #6b5900;
|
$formulate-yellow-d: #6b5900;
|
||||||
$formulate-yellow: #e6c000;
|
$formulate-yellow: #e6c000;
|
||||||
$formulate-yellow-l: #fff8d2;
|
$formulate-yellow-l: #fff8d2;
|
||||||
|
|
||||||
|
$formulate-white: #ffffff;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Mixins
|
// Mixins
|
||||||
|
Loading…
Reference in New Issue
Block a user