1
0
mirror of synced 2025-02-16 20:53:13 +03:00

Adds faux uploader method and initial upload styling

This commit is contained in:
Justin Schroeder 2019-11-15 14:44:01 -05:00
parent cb9349a6cf
commit 2d1c58f725
20 changed files with 2016 additions and 210 deletions

535
dist/formulate.esm.js vendored
View File

@ -1,6 +1,6 @@
import isUrl from 'is-url';
import isPlainObject from 'is-plain-object';
import nanoid from 'nanoid';
import isPlainObject from 'is-plain-object';
var library = {
// === SINGLE LINE TEXT STYLE INPUTS
@ -97,9 +97,129 @@ var library = {
select: {
classification: 'select',
component: 'FormulateInputSelect'
},
// === FILE TYPE
file: {
classification: 'file',
component: 'FormulateInputFile'
},
image: {
classification: 'file',
component: 'FormulateInputFile'
}
};
/**
* The file upload class holds and represents a files upload state durring
* the upload flow.
*/
var FileUpload = function FileUpload (fileList, context, options) {
this.fileList = fileList;
this.files = [];
this.options = options;
this.setFileList(fileList);
this.context = context;
};
/**
* Produce an array of files and alert the callback.
* @param {FileList}
*/
FileUpload.prototype.setFileList = function setFileList (fileList) {
for (var i = 0; i < fileList.length; i++) {
var file = fileList.item(i);
this.files.push({
progress: 0,
name: file.name || 'file-upload',
file: file,
uuid: nanoid()
});
}
};
/**
* Check if the file has an.
*/
FileUpload.prototype.hasUploader = function hasUploader () {
return !!this.context.uploader
};
FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () {
if (
this.hasUploader &&
typeof this.hasUploader.request === 'function' &&
typeof this.hasUploader.get === 'function' &&
typeof this.hasUploader.delete === 'function' &&
typeof this.hasUploader.post === 'function'
) {
return true
}
return false
};
/**
* Get a new uploader function.
*/
FileUpload.prototype.getUploader = function getUploader () {
var ref;
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
if (this.uploaderIsAxios()) {
var formData = new FormData();
formData.append(this.context.name || 'file', args[0]);
return this.uploader.post(this.context.uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (progressEvent) {
args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total));
}
})
.catch(function (err) { return args[2](err); })
}
return (ref = this.context).uploader.apply(ref, args)
};
/**
* Perform the file upload.
*/
FileUpload.prototype.upload = function upload () {
var this$1 = this;
return new Promise(function (resolve, reject) {
if (!this$1.hasUploader) {
return reject(new Error('No uploader has been defined'))
}
Promise.all(this$1.files.map(function (file) {
return this$1.getUploader(
file.file,
function (progress) { file.progress = progress; },
function (error) { return reject(new Error(error)); },
this$1.options
)
}))
.then(function (results) { return resolve(results); })
.catch(function (err) { throw new Error(err) });
})
};
/**
* Get the files.
*/
FileUpload.prototype.getFileList = function getFileList () {
return this.fileList
};
/**
* Get the files.
*/
FileUpload.prototype.getFiles = function getFiles () {
return this.files
};
/**
* Function to map over an object.
* @param {Object} obj An object to map over
@ -389,19 +509,20 @@ var rules = {
/**
* Check the maximum value of a particular.
*/
max: function (value, minimum) {
max: function (value, minimum, force) {
if ( minimum === void 0 ) minimum = 10;
return Promise.resolve((function () {
minimum = Number(minimum);
if (!isNaN(value)) {
value = Number(value);
return value <= minimum
}
if (typeof value === 'string') {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum;
return value.length <= minimum
}
if (Array.isArray(value)) {
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value;
return value <= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value;
return value.length <= minimum
}
return false
@ -416,6 +537,12 @@ var rules = {
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
return Promise.resolve((function () {
if (files instanceof FileUpload) {
if (files.hasUploader()) {
return false
}
files = files.getFiles();
}
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
for (var i in files) {
if (!types.includes(files[i].type)) {
@ -639,10 +766,14 @@ var en = {
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be less than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You may only select " + (args[0]) + " " + name + ".")
}
return (name + " must be less than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be less than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
},
/**
@ -653,10 +784,14 @@ var en = {
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be more than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You must select at least " + (args[0]) + " " + name + ".")
}
return (name + " must be more than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be more than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.")
},
/**
@ -697,6 +832,35 @@ var en = {
}
};
/**
* A fake uploader used by default.
*
* @param {File} file
* @param {function} progress
* @param {function} error
* @param {object} options
*/
function fauxUploader (file, progress, error, options) {
return new Promise(function (resolve, reject) {
var totalTime = options.fauxUploaderDuration || 2000;
var start = performance.now();
var advance = function () { return setTimeout(function () {
var elapsed = performance.now() - start;
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
progress(currentProgress);
if (currentProgress >= 100) {
resolve({
url: 'http://via.placeholder.com/350x150.png',
name: file.name
});
} else {
advance();
}
}, 20); };
advance();
})
}
/**
* For a single instance of an input, export all of the context needed to fully
* render that element.
@ -713,7 +877,11 @@ var context = {
label: this.label,
labelPosition: this.logicalLabelPosition,
attributes: this.elementAttributes,
blurHandler: blurHandler.bind(this)},
blurHandler: blurHandler.bind(this),
showImage: this.showImage,
uploadUrl: this.uploadUrl,
uploader: this.uploader || this.$formulate.getUploader(),
immediateUpload: this.immediateUpload},
this.typeContext))
},
nameOrFallback: nameOrFallback,
@ -1001,6 +1169,22 @@ var script = {
showErrors: {
type: Boolean,
default: false
},
showImage: {
type: Boolean,
default: true
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
immediateUpload: {
type: Boolean,
default: true
}
},
data: function data () {
@ -1989,6 +2173,219 @@ __vue_render__$5._withStripped = true;
//
var script$6 = {
name: 'FormulateFiles',
props: {
files: {
type: FileUpload,
required: true
}
},
computed: {
fileUploads: function fileUploads () {
return this.files.files || []
}
}
};
/* script */
var __vue_script__$6 = script$6;
/* template */
var __vue_render__$6 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.fileUploads.length
? _c(
"ul",
{ staticClass: "formulate-files" },
_vm._l(_vm.fileUploads, function(file) {
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
_c("span", {
staticClass: "formualte-file-name",
domProps: { textContent: _vm._s(file.name) }
}),
_vm._v(" "),
file.progress > 0 && file.progress < 100
? _c("span", {
domProps: { textContent: _vm._s(file.progress + "%") }
})
: _vm._e()
])
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateFiles = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
false,
undefined,
undefined,
undefined
);
//
var script$7 = {
name: 'FormulateInputFile',
components: {
FormulateFiles: FormulateFiles
},
mixins: [FormulateInputMixin],
data: function data () {
return {
isOver: false
}
},
computed: {
hasFiles: function hasFiles () {
return (this.context.model instanceof FileUpload && this.context.model.files.length)
}
},
methods: {
handleFile: function handleFile () {
var input = this.$refs.file;
if (input.files.length) {
this.context.model = this.$formulate.createUpload(input.files, this.context);
}
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
this.context.model.upload();
}
},
handleDragOver: function handleDragOver (e) {
e.preventDefault();
this.isOver = true;
},
handleDragLeave: function handleDragLeave (e) {
e.preventDefault();
this.isOver = false;
}
}
};
/* script */
var __vue_script__$7 = script$7;
/* template */
var __vue_render__$7 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c(
"div",
{
class:
"formulate-input-element formulate-input-element--" + _vm.context.type,
attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles }
},
[
_c(
"div",
{
staticClass: "formulate-input-upload-area",
attrs: { "data-has-files": _vm.hasFiles }
},
[
_c(
"input",
_vm._b(
{
ref: "file",
attrs: { "data-is-drag-hover": _vm.isOver, type: "file" },
on: {
blur: _vm.context.blurHandler,
change: _vm.handleFile,
dragover: _vm.handleDragOver,
dragleave: _vm.handleDragLeave
}
},
"input",
_vm.attributes,
false
)
),
_vm._v(" "),
_c("div", {
directives: [
{
name: "show",
rawName: "v-show",
value: !_vm.hasFiles,
expression: "!hasFiles"
}
],
staticClass: "formulate-input-upload-area-mask"
}),
_vm._v(" "),
_vm.hasFiles
? _c("FormulateFiles", { attrs: { files: _vm.context.model } })
: _vm._e()
],
1
)
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputFile = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
false,
undefined,
undefined,
undefined
);
//
var script$8 = {
name: 'FormulateInputSelect',
mixins: [FormulateInputMixin],
computed: {
@ -2005,10 +2402,10 @@ var script$6 = {
};
/* script */
var __vue_script__$6 = script$6;
var __vue_script__$8 = script$8;
/* template */
var __vue_render__$6 = function() {
var __vue_render__$8 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2120,17 +2517,17 @@ var __vue_render__$6 = function() {
]
)
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
var __vue_inject_styles__$8 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
var __vue_scope_id__$8 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
var __vue_module_identifier__$8 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
var __vue_is_functional_template__$8 = false;
/* style inject */
/* style inject SSR */
@ -2140,12 +2537,12 @@ __vue_render__$6._withStripped = true;
var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
false,
undefined,
undefined,
@ -2154,16 +2551,16 @@ __vue_render__$6._withStripped = true;
//
var script$7 = {
name: 'FormulateInputText',
var script$9 = {
name: 'FormulateInputSlider',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$7 = script$7;
var __vue_script__$9 = script$9;
/* template */
var __vue_render__$7 = function() {
var __vue_render__$9 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2284,17 +2681,17 @@ var __vue_render__$7 = function() {
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
var __vue_staticRenderFns__$9 = [];
__vue_render__$9._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
var __vue_inject_styles__$9 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
var __vue_scope_id__$9 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
var __vue_module_identifier__$9 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
var __vue_is_functional_template__$9 = false;
/* style inject */
/* style inject SSR */
@ -2304,12 +2701,12 @@ __vue_render__$7._withStripped = true;
var FormulateInputSlider = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
__vue_inject_styles__$9,
__vue_script__$9,
__vue_scope_id__$9,
__vue_is_functional_template__$9,
__vue_module_identifier__$9,
false,
undefined,
undefined,
@ -2318,16 +2715,16 @@ __vue_render__$7._withStripped = true;
//
var script$8 = {
var script$a = {
name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$8 = script$8;
var __vue_script__$a = script$a;
/* template */
var __vue_render__$8 = function() {
var __vue_render__$a = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2369,17 +2766,17 @@ var __vue_render__$8 = function() {
]
)
};
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
var __vue_staticRenderFns__$a = [];
__vue_render__$a._withStripped = true;
/* style */
var __vue_inject_styles__$8 = undefined;
var __vue_inject_styles__$a = undefined;
/* scoped */
var __vue_scope_id__$8 = undefined;
var __vue_scope_id__$a = undefined;
/* module identifier */
var __vue_module_identifier__$8 = undefined;
var __vue_module_identifier__$a = undefined;
/* functional template */
var __vue_is_functional_template__$8 = false;
var __vue_is_functional_template__$a = false;
/* style inject */
/* style inject SSR */
@ -2389,12 +2786,12 @@ __vue_render__$8._withStripped = true;
var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
__vue_inject_styles__$a,
__vue_script__$a,
__vue_scope_id__$a,
__vue_is_functional_template__$a,
__vue_module_identifier__$a,
false,
undefined,
undefined,
@ -2412,6 +2809,7 @@ var Formulate = function Formulate () {
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText,
FormulateInputFile: FormulateInputFile,
FormulateInputGroup: FormulateInputGroup,
FormulateInputSelect: FormulateInputSelect,
FormulateInputSlider: FormulateInputSlider,
@ -2420,6 +2818,7 @@ var Formulate = function Formulate () {
library: library,
rules: rules,
locale: 'en',
uploader: fauxUploader,
locales: {
en: en
}
@ -2508,6 +2907,20 @@ Formulate.prototype.validationMessage = function validationMessage (rule, valida
return 'This field does not have a valid value'
};
/**
* Get the file uploader.
*/
Formulate.prototype.getUploader = function getUploader () {
return this.options.uploader || false
};
/**
* Create a new instance of an upload.
*/
Formulate.prototype.createUpload = function createUpload (fileList, context) {
return new FileUpload(fileList, context, this.options)
};
var Formulate$1 = new Formulate();
export default Formulate$1;

539
dist/formulate.min.js vendored
View File

@ -1,9 +1,9 @@
var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
'use strict';
isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl;
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid;
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
var library = {
// === SINGLE LINE TEXT STYLE INPUTS
@ -100,9 +100,129 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
select: {
classification: 'select',
component: 'FormulateInputSelect'
},
// === FILE TYPE
file: {
classification: 'file',
component: 'FormulateInputFile'
},
image: {
classification: 'file',
component: 'FormulateInputFile'
}
};
/**
* The file upload class holds and represents a files upload state durring
* the upload flow.
*/
var FileUpload = function FileUpload (fileList, context, options) {
this.fileList = fileList;
this.files = [];
this.options = options;
this.setFileList(fileList);
this.context = context;
};
/**
* Produce an array of files and alert the callback.
* @param {FileList}
*/
FileUpload.prototype.setFileList = function setFileList (fileList) {
for (var i = 0; i < fileList.length; i++) {
var file = fileList.item(i);
this.files.push({
progress: 0,
name: file.name || 'file-upload',
file: file,
uuid: nanoid()
});
}
};
/**
* Check if the file has an.
*/
FileUpload.prototype.hasUploader = function hasUploader () {
return !!this.context.uploader
};
FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () {
if (
this.hasUploader &&
typeof this.hasUploader.request === 'function' &&
typeof this.hasUploader.get === 'function' &&
typeof this.hasUploader.delete === 'function' &&
typeof this.hasUploader.post === 'function'
) {
return true
}
return false
};
/**
* Get a new uploader function.
*/
FileUpload.prototype.getUploader = function getUploader () {
var ref;
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
if (this.uploaderIsAxios()) {
var formData = new FormData();
formData.append(this.context.name || 'file', args[0]);
return this.uploader.post(this.context.uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (progressEvent) {
args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total));
}
})
.catch(function (err) { return args[2](err); })
}
return (ref = this.context).uploader.apply(ref, args)
};
/**
* Perform the file upload.
*/
FileUpload.prototype.upload = function upload () {
var this$1 = this;
return new Promise(function (resolve, reject) {
if (!this$1.hasUploader) {
return reject(new Error('No uploader has been defined'))
}
Promise.all(this$1.files.map(function (file) {
return this$1.getUploader(
file.file,
function (progress) { file.progress = progress; },
function (error) { return reject(new Error(error)); },
this$1.options
)
}))
.then(function (results) { return resolve(results); })
.catch(function (err) { throw new Error(err) });
})
};
/**
* Get the files.
*/
FileUpload.prototype.getFileList = function getFileList () {
return this.fileList
};
/**
* Get the files.
*/
FileUpload.prototype.getFiles = function getFiles () {
return this.files
};
/**
* Function to map over an object.
* @param {Object} obj An object to map over
@ -392,19 +512,20 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
/**
* Check the maximum value of a particular.
*/
max: function (value, minimum) {
max: function (value, minimum, force) {
if ( minimum === void 0 ) minimum = 10;
return Promise.resolve((function () {
minimum = Number(minimum);
if (!isNaN(value)) {
value = Number(value);
return value <= minimum
}
if (typeof value === 'string') {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum;
return value.length <= minimum
}
if (Array.isArray(value)) {
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value;
return value <= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value;
return value.length <= minimum
}
return false
@ -419,6 +540,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
return Promise.resolve((function () {
if (files instanceof FileUpload) {
if (files.hasUploader()) {
return false
}
files = files.getFiles();
}
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
for (var i in files) {
if (!types.includes(files[i].type)) {
@ -642,10 +769,14 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be less than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You may only select " + (args[0]) + " " + name + ".")
}
return (name + " must be less than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be less than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
},
/**
@ -656,10 +787,14 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be more than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You must select at least " + (args[0]) + " " + name + ".")
}
return (name + " must be more than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be more than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.")
},
/**
@ -700,6 +835,35 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
}
};
/**
* A fake uploader used by default.
*
* @param {File} file
* @param {function} progress
* @param {function} error
* @param {object} options
*/
function fauxUploader (file, progress, error, options) {
return new Promise(function (resolve, reject) {
var totalTime = options.fauxUploaderDuration || 2000;
var start = performance.now();
var advance = function () { return setTimeout(function () {
var elapsed = performance.now() - start;
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
progress(currentProgress);
if (currentProgress >= 100) {
resolve({
url: 'http://via.placeholder.com/350x150.png',
name: file.name
});
} else {
advance();
}
}, 20); };
advance();
})
}
/**
* For a single instance of an input, export all of the context needed to fully
* render that element.
@ -716,7 +880,11 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
label: this.label,
labelPosition: this.logicalLabelPosition,
attributes: this.elementAttributes,
blurHandler: blurHandler.bind(this)},
blurHandler: blurHandler.bind(this),
showImage: this.showImage,
uploadUrl: this.uploadUrl,
uploader: this.uploader || this.$formulate.getUploader(),
immediateUpload: this.immediateUpload},
this.typeContext))
},
nameOrFallback: nameOrFallback,
@ -1004,6 +1172,22 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
showErrors: {
type: Boolean,
default: false
},
showImage: {
type: Boolean,
default: true
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
immediateUpload: {
type: Boolean,
default: true
}
},
data: function data () {
@ -1992,6 +2176,219 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
//
var script$6 = {
name: 'FormulateFiles',
props: {
files: {
type: FileUpload,
required: true
}
},
computed: {
fileUploads: function fileUploads () {
return this.files.files || []
}
}
};
/* script */
var __vue_script__$6 = script$6;
/* template */
var __vue_render__$6 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.fileUploads.length
? _c(
"ul",
{ staticClass: "formulate-files" },
_vm._l(_vm.fileUploads, function(file) {
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
_c("span", {
staticClass: "formualte-file-name",
domProps: { textContent: _vm._s(file.name) }
}),
_vm._v(" "),
file.progress > 0 && file.progress < 100
? _c("span", {
domProps: { textContent: _vm._s(file.progress + "%") }
})
: _vm._e()
])
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateFiles = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
false,
undefined,
undefined,
undefined
);
//
var script$7 = {
name: 'FormulateInputFile',
components: {
FormulateFiles: FormulateFiles
},
mixins: [FormulateInputMixin],
data: function data () {
return {
isOver: false
}
},
computed: {
hasFiles: function hasFiles () {
return (this.context.model instanceof FileUpload && this.context.model.files.length)
}
},
methods: {
handleFile: function handleFile () {
var input = this.$refs.file;
if (input.files.length) {
this.context.model = this.$formulate.createUpload(input.files, this.context);
}
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
this.context.model.upload();
}
},
handleDragOver: function handleDragOver (e) {
e.preventDefault();
this.isOver = true;
},
handleDragLeave: function handleDragLeave (e) {
e.preventDefault();
this.isOver = false;
}
}
};
/* script */
var __vue_script__$7 = script$7;
/* template */
var __vue_render__$7 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c(
"div",
{
class:
"formulate-input-element formulate-input-element--" + _vm.context.type,
attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles }
},
[
_c(
"div",
{
staticClass: "formulate-input-upload-area",
attrs: { "data-has-files": _vm.hasFiles }
},
[
_c(
"input",
_vm._b(
{
ref: "file",
attrs: { "data-is-drag-hover": _vm.isOver, type: "file" },
on: {
blur: _vm.context.blurHandler,
change: _vm.handleFile,
dragover: _vm.handleDragOver,
dragleave: _vm.handleDragLeave
}
},
"input",
_vm.attributes,
false
)
),
_vm._v(" "),
_c("div", {
directives: [
{
name: "show",
rawName: "v-show",
value: !_vm.hasFiles,
expression: "!hasFiles"
}
],
staticClass: "formulate-input-upload-area-mask"
}),
_vm._v(" "),
_vm.hasFiles
? _c("FormulateFiles", { attrs: { files: _vm.context.model } })
: _vm._e()
],
1
)
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputFile = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
false,
undefined,
undefined,
undefined
);
//
var script$8 = {
name: 'FormulateInputSelect',
mixins: [FormulateInputMixin],
computed: {
@ -2008,10 +2405,10 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
};
/* script */
var __vue_script__$6 = script$6;
var __vue_script__$8 = script$8;
/* template */
var __vue_render__$6 = function() {
var __vue_render__$8 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2123,17 +2520,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
]
)
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
var __vue_inject_styles__$8 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
var __vue_scope_id__$8 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
var __vue_module_identifier__$8 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
var __vue_is_functional_template__$8 = false;
/* style inject */
/* style inject SSR */
@ -2143,12 +2540,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
false,
undefined,
undefined,
@ -2157,16 +2554,16 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
//
var script$7 = {
name: 'FormulateInputText',
var script$9 = {
name: 'FormulateInputSlider',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$7 = script$7;
var __vue_script__$9 = script$9;
/* template */
var __vue_render__$7 = function() {
var __vue_render__$9 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2287,17 +2684,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
var __vue_staticRenderFns__$9 = [];
__vue_render__$9._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
var __vue_inject_styles__$9 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
var __vue_scope_id__$9 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
var __vue_module_identifier__$9 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
var __vue_is_functional_template__$9 = false;
/* style inject */
/* style inject SSR */
@ -2307,12 +2704,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var FormulateInputSlider = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
__vue_inject_styles__$9,
__vue_script__$9,
__vue_scope_id__$9,
__vue_is_functional_template__$9,
__vue_module_identifier__$9,
false,
undefined,
undefined,
@ -2321,16 +2718,16 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
//
var script$8 = {
var script$a = {
name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$8 = script$8;
var __vue_script__$a = script$a;
/* template */
var __vue_render__$8 = function() {
var __vue_render__$a = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2372,17 +2769,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
]
)
};
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
var __vue_staticRenderFns__$a = [];
__vue_render__$a._withStripped = true;
/* style */
var __vue_inject_styles__$8 = undefined;
var __vue_inject_styles__$a = undefined;
/* scoped */
var __vue_scope_id__$8 = undefined;
var __vue_scope_id__$a = undefined;
/* module identifier */
var __vue_module_identifier__$8 = undefined;
var __vue_module_identifier__$a = undefined;
/* functional template */
var __vue_is_functional_template__$8 = false;
var __vue_is_functional_template__$a = false;
/* style inject */
/* style inject SSR */
@ -2392,12 +2789,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
__vue_inject_styles__$a,
__vue_script__$a,
__vue_scope_id__$a,
__vue_is_functional_template__$a,
__vue_module_identifier__$a,
false,
undefined,
undefined,
@ -2415,6 +2812,7 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText,
FormulateInputFile: FormulateInputFile,
FormulateInputGroup: FormulateInputGroup,
FormulateInputSelect: FormulateInputSelect,
FormulateInputSlider: FormulateInputSlider,
@ -2423,6 +2821,7 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
library: library,
rules: rules,
locale: 'en',
uploader: fauxUploader,
locales: {
en: en
}
@ -2511,10 +2910,24 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
return 'This field does not have a valid value'
};
/**
* Get the file uploader.
*/
Formulate.prototype.getUploader = function getUploader () {
return this.options.uploader || false
};
/**
* Create a new instance of an upload.
*/
Formulate.prototype.createUpload = function createUpload (fileList, context) {
return new FileUpload(fileList, context, this.options)
};
var Formulate$1 = new Formulate();
exports.default = Formulate$1;
return exports;
}({}, isUrl, isPlainObject, nanoid));
}({}, isUrl, nanoid, isPlainObject));

543
dist/formulate.umd.js vendored
View File

@ -1,12 +1,12 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-url'), require('is-plain-object'), require('nanoid')) :
typeof define === 'function' && define.amd ? define(['exports', 'is-url', 'is-plain-object', 'nanoid'], factory) :
(global = global || self, factory(global.Formulate = {}, global.isUrl, global.isPlainObject, global.nanoid));
}(this, (function (exports, isUrl, isPlainObject, nanoid) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-url'), require('nanoid'), require('is-plain-object')) :
typeof define === 'function' && define.amd ? define(['exports', 'is-url', 'nanoid', 'is-plain-object'], factory) :
(global = global || self, factory(global.Formulate = {}, global.isUrl, global.nanoid, global.isPlainObject));
}(this, (function (exports, isUrl, nanoid, isPlainObject) { 'use strict';
isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl;
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid;
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
var library = {
// === SINGLE LINE TEXT STYLE INPUTS
@ -103,9 +103,129 @@
select: {
classification: 'select',
component: 'FormulateInputSelect'
},
// === FILE TYPE
file: {
classification: 'file',
component: 'FormulateInputFile'
},
image: {
classification: 'file',
component: 'FormulateInputFile'
}
};
/**
* The file upload class holds and represents a files upload state durring
* the upload flow.
*/
var FileUpload = function FileUpload (fileList, context, options) {
this.fileList = fileList;
this.files = [];
this.options = options;
this.setFileList(fileList);
this.context = context;
};
/**
* Produce an array of files and alert the callback.
* @param {FileList}
*/
FileUpload.prototype.setFileList = function setFileList (fileList) {
for (var i = 0; i < fileList.length; i++) {
var file = fileList.item(i);
this.files.push({
progress: 0,
name: file.name || 'file-upload',
file: file,
uuid: nanoid()
});
}
};
/**
* Check if the file has an.
*/
FileUpload.prototype.hasUploader = function hasUploader () {
return !!this.context.uploader
};
FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () {
if (
this.hasUploader &&
typeof this.hasUploader.request === 'function' &&
typeof this.hasUploader.get === 'function' &&
typeof this.hasUploader.delete === 'function' &&
typeof this.hasUploader.post === 'function'
) {
return true
}
return false
};
/**
* Get a new uploader function.
*/
FileUpload.prototype.getUploader = function getUploader () {
var ref;
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
if (this.uploaderIsAxios()) {
var formData = new FormData();
formData.append(this.context.name || 'file', args[0]);
return this.uploader.post(this.context.uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (progressEvent) {
args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total));
}
})
.catch(function (err) { return args[2](err); })
}
return (ref = this.context).uploader.apply(ref, args)
};
/**
* Perform the file upload.
*/
FileUpload.prototype.upload = function upload () {
var this$1 = this;
return new Promise(function (resolve, reject) {
if (!this$1.hasUploader) {
return reject(new Error('No uploader has been defined'))
}
Promise.all(this$1.files.map(function (file) {
return this$1.getUploader(
file.file,
function (progress) { file.progress = progress; },
function (error) { return reject(new Error(error)); },
this$1.options
)
}))
.then(function (results) { return resolve(results); })
.catch(function (err) { throw new Error(err) });
})
};
/**
* Get the files.
*/
FileUpload.prototype.getFileList = function getFileList () {
return this.fileList
};
/**
* Get the files.
*/
FileUpload.prototype.getFiles = function getFiles () {
return this.files
};
/**
* Function to map over an object.
* @param {Object} obj An object to map over
@ -395,19 +515,20 @@
/**
* Check the maximum value of a particular.
*/
max: function (value, minimum) {
max: function (value, minimum, force) {
if ( minimum === void 0 ) minimum = 10;
return Promise.resolve((function () {
minimum = Number(minimum);
if (!isNaN(value)) {
value = Number(value);
return value <= minimum
}
if (typeof value === 'string') {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum;
return value.length <= minimum
}
if (Array.isArray(value)) {
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value;
return value <= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value;
return value.length <= minimum
}
return false
@ -422,6 +543,12 @@
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
return Promise.resolve((function () {
if (files instanceof FileUpload) {
if (files.hasUploader()) {
return false
}
files = files.getFiles();
}
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
for (var i in files) {
if (!types.includes(files[i].type)) {
@ -645,10 +772,14 @@
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be less than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You may only select " + (args[0]) + " " + name + ".")
}
return (name + " must be less than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be less than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.")
},
/**
@ -659,10 +790,14 @@
var value = ref.value;
var args = ref.args;
if (!isNaN(value)) {
return (name + " must be more than " + (args[0]) + ".")
if (Array.isArray(value)) {
return ("You must select at least " + (args[0]) + " " + name + ".")
}
return (name + " must be more than " + (args[0]) + " characters long.")
var force = Array.isArray(args) && args[1] ? args[1] : false;
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return ((sentence(name)) + " must be more than " + (args[0]) + ".")
}
return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.")
},
/**
@ -703,6 +838,35 @@
}
};
/**
* A fake uploader used by default.
*
* @param {File} file
* @param {function} progress
* @param {function} error
* @param {object} options
*/
function fauxUploader (file, progress, error, options) {
return new Promise(function (resolve, reject) {
var totalTime = options.fauxUploaderDuration || 2000;
var start = performance.now();
var advance = function () { return setTimeout(function () {
var elapsed = performance.now() - start;
var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100));
progress(currentProgress);
if (currentProgress >= 100) {
resolve({
url: 'http://via.placeholder.com/350x150.png',
name: file.name
});
} else {
advance();
}
}, 20); };
advance();
})
}
/**
* For a single instance of an input, export all of the context needed to fully
* render that element.
@ -719,7 +883,11 @@
label: this.label,
labelPosition: this.logicalLabelPosition,
attributes: this.elementAttributes,
blurHandler: blurHandler.bind(this)},
blurHandler: blurHandler.bind(this),
showImage: this.showImage,
uploadUrl: this.uploadUrl,
uploader: this.uploader || this.$formulate.getUploader(),
immediateUpload: this.immediateUpload},
this.typeContext))
},
nameOrFallback: nameOrFallback,
@ -1007,6 +1175,22 @@
showErrors: {
type: Boolean,
default: false
},
showImage: {
type: Boolean,
default: true
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
immediateUpload: {
type: Boolean,
default: true
}
},
data: function data () {
@ -1995,6 +2179,219 @@
//
var script$6 = {
name: 'FormulateFiles',
props: {
files: {
type: FileUpload,
required: true
}
},
computed: {
fileUploads: function fileUploads () {
return this.files.files || []
}
}
};
/* script */
var __vue_script__$6 = script$6;
/* template */
var __vue_render__$6 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.fileUploads.length
? _c(
"ul",
{ staticClass: "formulate-files" },
_vm._l(_vm.fileUploads, function(file) {
return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [
_c("span", {
staticClass: "formualte-file-name",
domProps: { textContent: _vm._s(file.name) }
}),
_vm._v(" "),
file.progress > 0 && file.progress < 100
? _c("span", {
domProps: { textContent: _vm._s(file.progress + "%") }
})
: _vm._e()
])
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateFiles = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
false,
undefined,
undefined,
undefined
);
//
var script$7 = {
name: 'FormulateInputFile',
components: {
FormulateFiles: FormulateFiles
},
mixins: [FormulateInputMixin],
data: function data () {
return {
isOver: false
}
},
computed: {
hasFiles: function hasFiles () {
return (this.context.model instanceof FileUpload && this.context.model.files.length)
}
},
methods: {
handleFile: function handleFile () {
var input = this.$refs.file;
if (input.files.length) {
this.context.model = this.$formulate.createUpload(input.files, this.context);
}
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
this.context.model.upload();
}
},
handleDragOver: function handleDragOver (e) {
e.preventDefault();
this.isOver = true;
},
handleDragLeave: function handleDragLeave (e) {
e.preventDefault();
this.isOver = false;
}
}
};
/* script */
var __vue_script__$7 = script$7;
/* template */
var __vue_render__$7 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c(
"div",
{
class:
"formulate-input-element formulate-input-element--" + _vm.context.type,
attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles }
},
[
_c(
"div",
{
staticClass: "formulate-input-upload-area",
attrs: { "data-has-files": _vm.hasFiles }
},
[
_c(
"input",
_vm._b(
{
ref: "file",
attrs: { "data-is-drag-hover": _vm.isOver, type: "file" },
on: {
blur: _vm.context.blurHandler,
change: _vm.handleFile,
dragover: _vm.handleDragOver,
dragleave: _vm.handleDragLeave
}
},
"input",
_vm.attributes,
false
)
),
_vm._v(" "),
_c("div", {
directives: [
{
name: "show",
rawName: "v-show",
value: !_vm.hasFiles,
expression: "!hasFiles"
}
],
staticClass: "formulate-input-upload-area-mask"
}),
_vm._v(" "),
_vm.hasFiles
? _c("FormulateFiles", { attrs: { files: _vm.context.model } })
: _vm._e()
],
1
)
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputFile = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
false,
undefined,
undefined,
undefined
);
//
var script$8 = {
name: 'FormulateInputSelect',
mixins: [FormulateInputMixin],
computed: {
@ -2011,10 +2408,10 @@
};
/* script */
var __vue_script__$6 = script$6;
var __vue_script__$8 = script$8;
/* template */
var __vue_render__$6 = function() {
var __vue_render__$8 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2126,17 +2523,17 @@
]
)
};
var __vue_staticRenderFns__$6 = [];
__vue_render__$6._withStripped = true;
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
/* style */
var __vue_inject_styles__$6 = undefined;
var __vue_inject_styles__$8 = undefined;
/* scoped */
var __vue_scope_id__$6 = undefined;
var __vue_scope_id__$8 = undefined;
/* module identifier */
var __vue_module_identifier__$6 = undefined;
var __vue_module_identifier__$8 = undefined;
/* functional template */
var __vue_is_functional_template__$6 = false;
var __vue_is_functional_template__$8 = false;
/* style inject */
/* style inject SSR */
@ -2146,12 +2543,12 @@
var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$6,
__vue_script__$6,
__vue_scope_id__$6,
__vue_is_functional_template__$6,
__vue_module_identifier__$6,
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
false,
undefined,
undefined,
@ -2160,16 +2557,16 @@
//
var script$7 = {
name: 'FormulateInputText',
var script$9 = {
name: 'FormulateInputSlider',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$7 = script$7;
var __vue_script__$9 = script$9;
/* template */
var __vue_render__$7 = function() {
var __vue_render__$9 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2290,17 +2687,17 @@
]
)
};
var __vue_staticRenderFns__$7 = [];
__vue_render__$7._withStripped = true;
var __vue_staticRenderFns__$9 = [];
__vue_render__$9._withStripped = true;
/* style */
var __vue_inject_styles__$7 = undefined;
var __vue_inject_styles__$9 = undefined;
/* scoped */
var __vue_scope_id__$7 = undefined;
var __vue_scope_id__$9 = undefined;
/* module identifier */
var __vue_module_identifier__$7 = undefined;
var __vue_module_identifier__$9 = undefined;
/* functional template */
var __vue_is_functional_template__$7 = false;
var __vue_is_functional_template__$9 = false;
/* style inject */
/* style inject SSR */
@ -2310,12 +2707,12 @@
var FormulateInputSlider = normalizeComponent(
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$7,
__vue_script__$7,
__vue_scope_id__$7,
__vue_is_functional_template__$7,
__vue_module_identifier__$7,
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
__vue_inject_styles__$9,
__vue_script__$9,
__vue_scope_id__$9,
__vue_is_functional_template__$9,
__vue_module_identifier__$9,
false,
undefined,
undefined,
@ -2324,16 +2721,16 @@
//
var script$8 = {
var script$a = {
name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin]
};
/* script */
var __vue_script__$8 = script$8;
var __vue_script__$a = script$a;
/* template */
var __vue_render__$8 = function() {
var __vue_render__$a = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
@ -2375,17 +2772,17 @@
]
)
};
var __vue_staticRenderFns__$8 = [];
__vue_render__$8._withStripped = true;
var __vue_staticRenderFns__$a = [];
__vue_render__$a._withStripped = true;
/* style */
var __vue_inject_styles__$8 = undefined;
var __vue_inject_styles__$a = undefined;
/* scoped */
var __vue_scope_id__$8 = undefined;
var __vue_scope_id__$a = undefined;
/* module identifier */
var __vue_module_identifier__$8 = undefined;
var __vue_module_identifier__$a = undefined;
/* functional template */
var __vue_is_functional_template__$8 = false;
var __vue_is_functional_template__$a = false;
/* style inject */
/* style inject SSR */
@ -2395,12 +2792,12 @@
var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
__vue_inject_styles__$8,
__vue_script__$8,
__vue_scope_id__$8,
__vue_is_functional_template__$8,
__vue_module_identifier__$8,
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
__vue_inject_styles__$a,
__vue_script__$a,
__vue_scope_id__$a,
__vue_is_functional_template__$a,
__vue_module_identifier__$a,
false,
undefined,
undefined,
@ -2418,6 +2815,7 @@
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText,
FormulateInputFile: FormulateInputFile,
FormulateInputGroup: FormulateInputGroup,
FormulateInputSelect: FormulateInputSelect,
FormulateInputSlider: FormulateInputSlider,
@ -2426,6 +2824,7 @@
library: library,
rules: rules,
locale: 'en',
uploader: fauxUploader,
locales: {
en: en
}
@ -2514,6 +2913,20 @@
return 'This field does not have a valid value'
};
/**
* Get the file uploader.
*/
Formulate.prototype.getUploader = function getUploader () {
return this.options.uploader || false
};
/**
* Create a new instance of an upload.
*/
Formulate.prototype.createUpload = function createUpload (fileList, context) {
return new FileUpload(fileList, context, this.options)
};
var Formulate$1 = new Formulate();
exports.default = Formulate$1;

116
dist/snow.css vendored
View File

@ -40,12 +40,37 @@
padding: .75em;
display: block;
width: 100%;
font-weight: 400; }
font-weight: 400;
line-height: 1.1em;
margin: 0; }
.formulate-input[data-classification='text'] input::placeholder {
color: #a8a8a8; }
.formulate-input[data-classification='text'] input:focus {
outline: 0;
border: 1px solid #41b883; }
.formulate-input[data-classification='text'] input[type="color"] {
height: 1.1em;
box-sizing: content-box; }
.formulate-input[data-classification='text'] input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0 0 0 1.5em;
display: flex;
align-items: center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 79.17 79.17"><path fill="%236d6d6d" d="M40.8,22.92c-3.4-3.4-4.76-8.44-1-12.24s8.84-2.44,12.24,1c5-5,10.69-13.33,18.81-11.31a11,11,0,0,1,7.62,14.34c-1.26,3.45-4.63,6.06-7.16,8.59-.92.93-3,2.26-3.46,3.46-.42,1,1.82,2.63,2.36,4a8,8,0,0,1-12.42,9.19c-.21-.16-1.35-1.51-1.59-1.51s-.83.83-1,1L49.71,44.9,32.43,62.18c-3.37,3.38-6.6,7.14-10.26,10.21a11,11,0,0,1-4.48,2.28c-1.25.3-3.11-.22-4.18.18-1.28.48-2.42,2.65-3.68,3.4-6.05,3.61-12.64-4-8.46-9.57.73-1,2.53-1.92,3-3a14.46,14.46,0,0,0-.09-2.52,10.75,10.75,0,0,1,3.14-6.77c.92-1,1.93-1.93,2.89-2.9Zm4.4-1.5c4.19,4,8.24,8.24,12.36,12.36,2.06,2.06,5,5.59,8,2.61,4.65-4.62-5-6.8-2.42-10.78C66.3,20.7,76.4,16.48,74.84,9.45,73.62,4,67.12,2.78,63.29,6.32c-2.55,2.36-4.93,4.94-7.39,7.4-.79.78-1.8,2.28-2.88,2.73-2.14.88-3.4-1.62-4.79-2.77-2.58-2.14-6.89-.82-6.53,3C41.89,18.68,43.87,20.09,45.2,21.42Zm-1.45,4.44L27.82,41.79C22,47.57,15.89,53.14,10.41,59.2a8.23,8.23,0,0,0-1.44,2c-.93,2,.25,4.14-.5,6S4.92,69.94,4.3,72a2.34,2.34,0,0,0,2.56,3c1.11-.17,2-1.33,2.71-2.07a11.17,11.17,0,0,1,2.08-2c1.68-.94,4,.17,5.93-.57C20,69.41,22,66.73,23.76,65L34.42,54.3,53.3,35.42Z"/></svg>');
background-repeat: no-repeat;
background-size: .9em .9em;
background-position: left .1em; }
.formulate-input[data-classification='text'] input[type="color"]::-webkit-color-swatch {
display: block;
height: 1em;
border-radius: .2em;
border: 0;
flex: auto; }
.formulate-input[data-classification='text'] input[type="color"]::-moz-color-swatch {
display: block;
height: 1em;
border-radius: .2em;
border: 0;
flex: auto; }
.formulate-input[data-classification='slider'] input {
appearance: none;
width: 100%;
@ -96,7 +121,9 @@
padding: .75em;
display: block;
width: 100%;
font-weight: 400; }
font-weight: 400;
line-height: 1.1em;
margin: 0; }
.formulate-input[data-classification='textarea'] textarea::placeholder {
color: #a8a8a8; }
.formulate-input[data-classification='textarea'] textarea:focus {
@ -127,6 +154,8 @@
display: block;
width: 100%;
font-weight: 400;
line-height: 1.1em;
margin: 0;
padding-right: 2em; }
.formulate-input[data-classification='select'] select::placeholder {
color: #a8a8a8; }
@ -174,7 +203,8 @@
.formulate-input[data-classification='box'] .formulate-input-element input[type="checkbox"]:checked ~ .formulate-input-element-decorator {
border-color: #41b883; }
.formulate-input[data-classification='box'] .formulate-input-element input[type="checkbox"]:checked ~ .formulate-input-element-decorator::before {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="%2341b883"><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>'); }
background-color: #41b883;
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>'); }
.formulate-input[data-classification='box'] .formulate-input-element input[type="radio"]:checked ~ .formulate-input-element-decorator {
border-color: #41b883; }
.formulate-input[data-classification='box'] .formulate-input-element input[type="radio"]:checked ~ .formulate-input-element-decorator::before {
@ -187,3 +217,83 @@
margin-right: .5em; }
.formulate-input[data-classification="group"] > .formulate-input-wrapper > .formulate-input-label {
margin-bottom: .5em; }
.formulate-input[data-classification="file"] .formulate-input-upload-area {
width: 100%;
position: relative;
padding: 2em; }
.formulate-input[data-classification="file"] .formulate-input-upload-area[data-has-files] {
padding: 0; }
.formulate-input[data-classification="file"] .formulate-input-upload-area input {
cursor: pointer;
appearance: none;
opacity: 0;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 5; }
.formulate-input[data-classification="file"] .formulate-input-upload-area-mask {
border-radius: .4em;
position: absolute;
pointer-events: none;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
left: 0;
right: 0;
top: 0;
bottom: 0;
border: 2px dashed #a8a8a8;
z-index: 2; }
.formulate-input[data-classification="file"] .formulate-input-upload-area-mask::before {
content: '';
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>');
width: 2em;
height: 2em;
position: absolute;
pointer-events: none; }
.formulate-input[data-classification="file"] .formulate-input-upload-area input:focus ~ .formulate-input-upload-area-mask,
.formulate-input[data-classification="file"] .formulate-input-upload-area input:hover ~ .formulate-input-upload-area-mask,
.formulate-input[data-classification="file"] .formulate-input-upload-area input[data-is-drag-hover] ~ .formulate-input-upload-area-mask {
border-color: #41b883; }
.formulate-input[data-classification="file"] .formulate-input-upload-area input:focus ~ .formulate-input-upload-area-mask::before,
.formulate-input[data-classification="file"] .formulate-input-upload-area input:hover ~ .formulate-input-upload-area-mask::before,
.formulate-input[data-classification="file"] .formulate-input-upload-area input[data-is-drag-hover] ~ .formulate-input-upload-area-mask::before {
background-color: #41b883; }
.formulate-input[data-classification="file"] .formulate-files {
list-style-type: none;
margin: 0;
padding: 0; }
.formulate-input[data-classification="file"] .formulate-files li {
appearance: none;
border-radius: .3em;
border: 1px solid #cecece;
box-sizing: border-box;
background-color: transparent;
font-size: .9em;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
padding: .75em;
display: block;
width: 100%;
font-weight: 400;
line-height: 1.1em;
margin: 0;
display: block;
width: 100%;
display: flex;
justify-content: space-between; }
.formulate-input[data-classification="file"] .formulate-files li::placeholder {
color: #a8a8a8; }
.formulate-input[data-classification="file"] .formulate-files li:focus {
outline: 0;
border: 1px solid #41b883; }
.formulate-input[data-classification="file"] .formulate-files li ::-webkit-progress-bar {
appearance: none;
height: .5em;
border-radius: .5em;
overflow: hidden; }

4
dist/snow.min.css vendored

File diff suppressed because one or more lines are too long

113
src/FileUpload.js Normal file
View File

@ -0,0 +1,113 @@
import nanoid from 'nanoid'
/**
* The file upload class holds and represents a files upload state durring
* the upload flow.
*/
class FileUpload {
/**
* Create a file upload object.
* @param {FileList} fileList
* @param {object} context
*/
constructor (fileList, context, options) {
this.fileList = fileList
this.files = []
this.options = options
this.setFileList(fileList)
this.context = context
}
/**
* Produce an array of files and alert the callback.
* @param {FileList}
*/
setFileList (fileList) {
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i)
this.files.push({
progress: 0,
name: file.name || 'file-upload',
file: file,
uuid: nanoid()
})
}
}
/**
* Check if the file has an.
*/
hasUploader () {
return !!this.context.uploader
}
uploaderIsAxios () {
if (
this.hasUploader &&
typeof this.hasUploader.request === 'function' &&
typeof this.hasUploader.get === 'function' &&
typeof this.hasUploader.delete === 'function' &&
typeof this.hasUploader.post === 'function'
) {
return true
}
return false
}
/**
* Get a new uploader function.
*/
getUploader (...args) {
if (this.uploaderIsAxios()) {
const formData = new FormData()
formData.append(this.context.name || 'file', args[0])
return this.uploader.post(this.context.uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: progressEvent => {
args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total))
}
})
.catch(err => args[2](err))
}
return this.context.uploader(...args)
}
/**
* Perform the file upload.
*/
upload () {
return new Promise((resolve, reject) => {
if (!this.hasUploader) {
return reject(new Error('No uploader has been defined'))
}
Promise.all(this.files.map(file => {
return this.getUploader(
file.file,
(progress) => { file.progress = progress },
(error) => reject(new Error(error)),
this.options
)
}))
.then(results => resolve(results))
.catch(err => { throw new Error(err) })
})
}
/**
* Get the files.
*/
getFileList () {
return this.fileList
}
/**
* Get the files.
*/
getFiles () {
return this.files
}
}
export default FileUpload

View File

@ -1,13 +1,16 @@
import library from './libs/library'
import rules from './libs/rules'
import en from './locales/en'
import FileUpload from './FileUpload'
import isPlainObject from 'is-plain-object'
import fauxUploader from './libs/faux-uploader'
import FormulateInput from './FormulateInput.vue'
import FormulateForm from './FormulateForm.vue'
import FormulateInputErrors from './FormulateInputErrors.vue'
import FormulateInputGroup from './FormulateInputGroup.vue'
import FormulateInputBox from './inputs/FormulateInputBox.vue'
import FormulateInputText from './inputs/FormulateInputText.vue'
import FormulateInputFile from './inputs/FormulateInputFile.vue'
import FormulateInputSelect from './inputs/FormulateInputSelect.vue'
import FormulateInputSlider from './inputs/FormulateInputSlider.vue'
import FormulateInputTextArea from './inputs/FormulateInputTextArea.vue'
@ -27,6 +30,7 @@ class Formulate {
FormulateInputErrors,
FormulateInputBox,
FormulateInputText,
FormulateInputFile,
FormulateInputGroup,
FormulateInputSelect,
FormulateInputSlider,
@ -35,6 +39,7 @@ class Formulate {
library,
rules,
locale: 'en',
uploader: fauxUploader,
locales: {
en
}
@ -122,6 +127,20 @@ class Formulate {
}
return 'This field does not have a valid value'
}
/**
* Get the file uploader.
*/
getUploader () {
return this.options.uploader || false
}
/**
* Create a new instance of an upload.
*/
createUpload (fileList, context) {
return new FileUpload(fileList, context, this.options)
}
}
export default new Formulate()

40
src/FormulateFiles.vue Normal file
View File

@ -0,0 +1,40 @@
<template>
<ul
v-if="fileUploads.length"
class="formulate-files"
>
<li
v-for="file in fileUploads"
:key="file.uuid"
class="formulate-file"
>
<span
class="formualte-file-name"
v-text="file.name"
/>
<span
v-if="file.progress > 0 && file.progress < 100"
v-text="`${file.progress}%`"
/>
</li>
</ul>
</template>
<script>
import FileUpload from './FileUpload'
export default {
name: 'FormulateFiles',
props: {
files: {
type: FileUpload,
required: true
}
},
computed: {
fileUploads () {
return this.files.files || []
}
}
}
</script>

View File

@ -136,6 +136,22 @@ export default {
showErrors: {
type: Boolean,
default: false
},
showImage: {
type: Boolean,
default: true
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
immediateUpload: {
type: Boolean,
default: true
}
},
data () {

View File

@ -0,0 +1,74 @@
<template>
<div
:class="`formulate-input-element formulate-input-element--${context.type}`"
:data-type="context.type"
:data-has-files="hasFiles"
>
<div
class="formulate-input-upload-area"
:data-has-files="hasFiles"
>
<input
ref="file"
:data-is-drag-hover="isOver"
type="file"
v-bind="attributes"
@blur="context.blurHandler"
@change="handleFile"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
>
<div
v-show="!hasFiles"
class="formulate-input-upload-area-mask"
/>
<FormulateFiles
v-if="hasFiles"
:files="context.model"
/>
</div>
</div>
</template>
<script>
import FormulateInputMixin from '../FormulateInputMixin'
import FileUpload from '../FileUpload'
import FormulateFiles from '../FormulateFiles.vue'
export default {
name: 'FormulateInputFile',
components: {
FormulateFiles
},
mixins: [FormulateInputMixin],
data () {
return {
isOver: false
}
},
computed: {
hasFiles () {
return (this.context.model instanceof FileUpload && this.context.model.files.length)
}
},
methods: {
handleFile () {
const input = this.$refs.file
if (input.files.length) {
this.context.model = this.$formulate.createUpload(input.files, this.context)
}
if (this.context.immediateUpload && this.context.model instanceof FileUpload) {
this.context.model.upload()
}
},
handleDragOver (e) {
e.preventDefault()
this.isOver = true
},
handleDragLeave (e) {
e.preventDefault()
this.isOver = false
}
}
}
</script>

View File

@ -16,7 +16,7 @@
import FormulateInputMixin from '../FormulateInputMixin'
export default {
name: 'FormulateInputText',
name: 'FormulateInputSlider',
mixins: [FormulateInputMixin]
}
</script>

View File

@ -19,6 +19,10 @@ export default {
labelPosition: this.logicalLabelPosition,
attributes: this.elementAttributes,
blurHandler: blurHandler.bind(this),
showImage: this.showImage,
uploadUrl: this.uploadUrl,
uploader: this.uploader || this.$formulate.getUploader(),
immediateUpload: this.immediateUpload,
...this.typeContext
})
},

28
src/libs/faux-uploader.js Normal file
View File

@ -0,0 +1,28 @@
/**
* A fake uploader used by default.
*
* @param {File} file
* @param {function} progress
* @param {function} error
* @param {object} options
*/
export default function (file, progress, error, options) {
return new Promise((resolve, reject) => {
const totalTime = options.fauxUploaderDuration || 2000
const start = performance.now()
const advance = () => setTimeout(() => {
const elapsed = performance.now() - start
const currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100))
progress(currentProgress)
if (currentProgress >= 100) {
resolve({
url: 'http://via.placeholder.com/350x150.png',
name: file.name
})
} else {
advance()
}
}, 20)
advance()
})
}

View File

@ -93,5 +93,16 @@ export default {
select: {
classification: 'select',
component: 'FormulateInputSelect'
},
// === FILE TYPE
file: {
classification: 'file',
component: 'FormulateInputFile'
},
image: {
classification: 'file',
component: 'FormulateInputFile'
}
}

View File

@ -1,4 +1,5 @@
import isUrl from 'is-url'
import FileUpload from '../FileUpload'
import { shallowEqualObjects, regexForFormat } from './utils'
/**
@ -124,17 +125,18 @@ export default {
/**
* Check the maximum value of a particular.
*/
max: function (value, minimum = 10) {
max: function (value, minimum = 10, force) {
return Promise.resolve((() => {
minimum = Number(minimum)
if (!isNaN(value)) {
value = Number(value)
return value <= minimum
}
if (typeof value === 'string') {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum
return value.length <= minimum
}
if (Array.isArray(value)) {
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value
return value <= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value
return value.length <= minimum
}
return false
@ -146,6 +148,12 @@ export default {
*/
mime: function (files, ...types) {
return Promise.resolve((() => {
if (files instanceof FileUpload) {
if (files.hasUploader()) {
return false
}
files = files.getFiles()
}
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
for (const i in files) {
if (!types.includes(files[i].type)) {

View File

@ -103,20 +103,28 @@ export default {
* The maximum value allowed.
*/
max: function ({ name, value, args }) {
if (!isNaN(value)) {
return `${name} must be less than ${args[0]}.`
if (Array.isArray(value)) {
return `You may only select ${args[0]} ${name}.`
}
return `${name} must be less than ${args[0]} characters long.`
const force = Array.isArray(args) && args[1] ? args[1] : false
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return `${s(name)} must be less than ${args[0]}.`
}
return `${s(name)} must be less than ${args[0]} characters long.`
},
/**
* The maximum value allowed.
*/
min: function ({ name, value, args }) {
if (!isNaN(value)) {
return `${name} must be more than ${args[0]}.`
if (Array.isArray(value)) {
return `You must select at least ${args[0]} ${name}.`
}
return `${name} must be more than ${args[0]} characters long.`
const force = Array.isArray(args) && args[1] ? args[1] : false
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return `${s(name)} must be more than ${args[0]}.`
}
return `${s(name)} must be more than ${args[0]} characters long.`
},
/**

View File

@ -53,6 +53,7 @@ test('installs on vue instance', () => {
'FormulateInputErrors',
'FormulateInputBox',
'FormulateInputText',
'FormulateInputFile',
'FormulateInputGroup',
'FormulateInputSelect',
'FormulateInputSlider',

View File

@ -331,11 +331,19 @@ describe('max', () => {
it('passes when a array length', async () => expect(await rules.max(Array(6), '6')).toBe(true))
it('passes when forced to validate on length', async () => expect(await rules.max(10, 3, 'length')).toBe(true))
it('passes when forced to validate string on value', async () => expect(await rules.max('b', 'e', 'value')).toBe(true))
it('fails when a array length', async () => expect(await rules.max(Array(6), '5')).toBe(false))
it('fails when a string length', async () => expect(await rules.max('bar', 2)).toBe(false))
it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false))
it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false))
it('fails when forced to validate on length', async () => expect(await rules.max(-10, '1', 'length')).toBe(false))
})
/**

View File

@ -55,6 +55,37 @@
input {
@include baseinput;
}
input[type="color"] {
height: 1.1em;
box-sizing: content-box;
@mixin color-swatch {
display: block;
height: 1em;
border-radius: .2em;
border: 0;
flex: auto;
}
&::-webkit-color-swatch-wrapper {
padding: 0 0 0 1.5em;
display: flex;
align-items: center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 79.17 79.17"><path fill="%23#{str-slice("#{$formulate-gray-ddd}", 2)}" d="M40.8,22.92c-3.4-3.4-4.76-8.44-1-12.24s8.84-2.44,12.24,1c5-5,10.69-13.33,18.81-11.31a11,11,0,0,1,7.62,14.34c-1.26,3.45-4.63,6.06-7.16,8.59-.92.93-3,2.26-3.46,3.46-.42,1,1.82,2.63,2.36,4a8,8,0,0,1-12.42,9.19c-.21-.16-1.35-1.51-1.59-1.51s-.83.83-1,1L49.71,44.9,32.43,62.18c-3.37,3.38-6.6,7.14-10.26,10.21a11,11,0,0,1-4.48,2.28c-1.25.3-3.11-.22-4.18.18-1.28.48-2.42,2.65-3.68,3.4-6.05,3.61-12.64-4-8.46-9.57.73-1,2.53-1.92,3-3a14.46,14.46,0,0,0-.09-2.52,10.75,10.75,0,0,1,3.14-6.77c.92-1,1.93-1.93,2.89-2.9Zm4.4-1.5c4.19,4,8.24,8.24,12.36,12.36,2.06,2.06,5,5.59,8,2.61,4.65-4.62-5-6.8-2.42-10.78C66.3,20.7,76.4,16.48,74.84,9.45,73.62,4,67.12,2.78,63.29,6.32c-2.55,2.36-4.93,4.94-7.39,7.4-.79.78-1.8,2.28-2.88,2.73-2.14.88-3.4-1.62-4.79-2.77-2.58-2.14-6.89-.82-6.53,3C41.89,18.68,43.87,20.09,45.2,21.42Zm-1.45,4.44L27.82,41.79C22,47.57,15.89,53.14,10.41,59.2a8.23,8.23,0,0,0-1.44,2c-.93,2,.25,4.14-.5,6S4.92,69.94,4.3,72a2.34,2.34,0,0,0,2.56,3c1.11-.17,2-1.33,2.71-2.07a11.17,11.17,0,0,1,2.08-2c1.68-.94,4,.17,5.93-.57C20,69.41,22,66.73,23.76,65L34.42,54.3,53.3,35.42Z"/></svg>');
background-repeat: no-repeat;
background-size: .9em .9em;
background-position: left .1em;
}
&::-webkit-color-swatch {
@include color-swatch;
}
&::-moz-color-swatch {
@include color-swatch;
}
}
}
// Slider inputs
@ -207,7 +238,8 @@
border-color: $formulate-green;
&::before {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="%23#{str-slice("#{$formulate-green}", 2)}"><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>');
background-color: $formulate-green;
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>');
}
}
}
@ -245,4 +277,97 @@
}
}
}
// File inputs
// -----------------------------------------------------------------------------
&[data-classification="file"] {
.formulate-input-upload-area {
width: 100%;
position: relative;
padding: 2em;
&[data-has-files] {
padding: 0;
}
input {
cursor: pointer;
appearance: none;
opacity: 0;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 5;
}
&-mask {
border-radius: .4em;
position: absolute;
pointer-events: none;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
left: 0;
right: 0;
top: 0;
bottom: 0;
border: 2px dashed $formulate-gray-dd;
z-index: 2;
&::before {
content: '';
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>');
width: 2em;
height: 2em;
position: absolute;
pointer-events: none;
}
}
input:focus,
input:hover,
input[data-is-drag-hover] {
& ~ .formulate-input-upload-area-mask {
border-color: $formulate-green;
&::before {
background-color: $formulate-green;
}
}
}
}
.formulate-files {
list-style-type: none;
margin: 0;
padding: 0;
li {
@include baseinput;
display: block;
width: 100%;
display: flex;
justify-content: space-between;
@mixin progress {
appearance: none;
height: .5em;
border-radius: .5em;
overflow: hidden;
}
::-webkit-progress-bar {
@include progress;
}
}
}
}
}

View File

@ -37,6 +37,8 @@ $formulate-yellow-l: #fff8d2;
display: block;
width: 100%;
font-weight: 400;
line-height: 1.1em;
margin: 0;
&::placeholder {
color: $formulate-gray-dd;