UNPKG

vui-design

Version:

A high quality UI Toolkit based on Vue.js

481 lines (384 loc) 11.4 kB
const env = process && process.env && process.env.NODE_ENV; const funProto = Function.prototype; const objProto = Object.prototype; const getPrototypeOf = Object.getPrototypeOf; const objToString = objProto.toString; const hasOwnProperty = objProto.hasOwnProperty; const funToString = funProto.toString; const noop = () => {}; const has = (object, property) => hasOwnProperty.call(object, property); const isInteger = Number.isInteger || (value => typeof value === "number" && isFinite(value) && Math.floor(value) === value); const isFunction = value => objToString.call(value) === "[object Function]" || typeof value === "function"; const isElement = value => value && (value instanceof window.Node) && (value.nodeType === 1); const isDate = value => objToString.call(value) === "[object Date]"; const isArray = Array.isArray || (value => objToString.call(value) === "[object Array]"); const isPlainObject = value => { if (!value || objToString.call(value) !== "[object Object]") { return false; } const prototype = getPrototypeOf(value); if (prototype === null) { return true; } const constructor = has(prototype, "constructor") && prototype.constructor; return typeof constructor === "function" && funToString.call(constructor) === funToString.call(Object); }; let warn; if (env === "production") { warn = noop; } else { const hasConsole = typeof console !== "undefined"; warn = typeof console === "undefined" ? noop : message => console.warn("[VuePropTypes warn]: " + message); } const getType = fn => { const type = fn !== null && fn !== undefined ? (fn.type ? fn.type : fn) : null; const match = type && type.toString().match(/^\s*function (\w+)/); return match && match[1]; }; const getNativeType = value => { if (value === null || value === undefined) { return null; } const match = value.constructor.toString().match(/^\s*function (\w+)/); return match && match[1]; }; const withRequired = type => { Object.defineProperty(type, "isRequired", { enumerable: false, get() { this.required = true; return this; } }); }; const withDefault = type => { Object.defineProperty(type, "def", { enumerable: false, writable: false, value(def) { if (def === undefined && this.default === undefined) { this.default = undefined; return this; } if (!isFunction(def) && !validateType(this, def)) { warn(this.vuePropTypesName + " - invalid default value: " + def, def); return this; } this.default = isArray(def) || isPlainObject(def) ? () => def : def; return this; } }); }; const toType = (name, object) => { Object.defineProperty(object, "vuePropTypesName", { enumerable: false, writable: false, value: name }); withRequired(object); withDefault(object); if (isFunction(object.validator)) { object.validator = object.validator.bind(object); } return object; }; const validateType = (type, value, silent = false) => { let typeToCheck = type; let boolean = true; let expectedType; if (!isPlainObject(type)) { typeToCheck = { type }; } const namePrefix = typeToCheck.vuePropTypesName ? typeToCheck.vuePropTypesName + " - " : ""; if (has(typeToCheck, "type") && typeToCheck.type !== null) { if (isArray(typeToCheck.type)) { boolean = typeToCheck.type.some(type => validateType(type, value, true)); expectedType = typeToCheck.type.map(type => getType(type)).join(" or "); } else { expectedType = getType(typeToCheck); if (expectedType === "element") { boolean = isElement(value); } else if (expectedType === "Date") { boolean = isDate(value); } else if (expectedType === "Array") { boolean = isArray(value); } else if (expectedType === "Object") { boolean = isPlainObject(value); } else if (expectedType === "Boolean" || expectedType === "String" || expectedType === "Number" || expectedType === "Function") { boolean = getNativeType(value) === expectedType; } else { boolean = value instanceof typeToCheck.type; } } } if (!boolean) { silent === false && warn(namePrefix + "value " + value + " should be of type " + expectedType); return false; } if (has(typeToCheck, "validator") && isFunction(typeToCheck.validator)) { boolean = typeToCheck.validator(value); if (!boolean && silent === false) { warn(namePrefix + "custom validation failed"); } return boolean; } return boolean; }; const VuePropTypes = { get any() { return toType("any", { type: null }); }, get bool() { return toType("boolean", { type: Boolean, }).def(defaults.bool); }, get string() { return toType("string", { type: String, }).def(defaults.string); }, get number() { return toType("number", { type: Number, }).def(defaults.number); }, get integer() { return toType("integer", { type: Number, validator(value) { return isInteger(value); } }).def(defaults.integer); }, get func() { return toType("function", { type: Function, }).def(defaults.func); }, get element() { return toType("element", { type: HTMLElement, }).def(defaults.element); }, get date() { return toType("date", { type: Date, }).def(defaults.date); }, get array() { return toType("array", { type: Array, }).def(defaults.array); }, get object() { return toType("object", { type: Object, }).def(defaults.object); }, get symbol() { return toType("symbol", { type: null, validator(value) { return typeof value === "symbol"; }, }); }, custom(validator, message = "custom validation failed") { if (!isFunction(validator)) { throw new TypeError("[VuePropTypes error]: You must provide a function as argument"); } return toType(validator.name || "<<anonymous function>>", { validator(...args) { const boolean = validator(...args); if (!boolean) { warn(this.vuePropTypesName + " - " + message); } return boolean; } }); }, oneOf(array) { if (!isArray(array)) { throw new TypeError("[VuePropTypes error]: You must provide an array as argument"); } const message = "oneOf - value should be one of [" + array.join(", ") + "]"; const allowedTypes = array.reduce((result, value) => { if (value !== null && value !== undefined) { result.indexOf(value.constructor) === -1 && result.push(value.constructor); } return result; }, []); return toType("oneOf", { type: allowedTypes.length > 0 ? allowedTypes : null, validator(value) { const boolean = array.indexOf(value) !== -1; if (!boolean) { warn(message); } return boolean; } }); }, instanceOf(Constructor) { return toType("instanceOf", { type: Constructor }); }, oneOfType(array) { if (!isArray(array)) { throw new TypeError("[VuePropTypes error]: You must provide an array as argument"); } let hasCustomValidators = false; const checks = array.reduce((result, type) => { if (isPlainObject(type)) { if (type.vuePropTypesName === "oneOf") { return result.concat(type.type || []); } if (type.type && !isFunction(type.validator)) { if (isArray(type.type)) { return result.concat(type.type); } result.push(type.type); } else if (isFunction(type.validator)) { hasCustomValidators = true; } return result; } result.push(type); return result; }, []); if (!hasCustomValidators) { return toType("oneOfType", { type: checks, }).def(undefined); } const class2types = array.map(type => { if (type && isArray(type.type)) { return type.type.map(getType); } return getType(type); }).reduce((result, type) => result.concat(isArray(type) ? type : [type]), []).join(", "); return this.custom(function oneOfType(value) { const boolean = array.some(type => { if (type.vuePropTypesName === "oneOf") { return type.type ? validateType(type.type, value, true) : true; } return validateType(type, value, true); }); if (!boolean) { warn("oneOfType - value type should be one of [" + class2types + "]"); } return boolean; }).def(undefined); }, arrayOf(type) { return toType("arrayOf", { type: Array, validator(array) { const boolean = array.every(value => validateType(type, value)); if (!boolean) { warn("arrayOf - value must be an array of " + getType(type)); } return boolean; } }); }, objectOf(type) { return toType("objectOf", { type: Object, validator(object) { const boolean = Object.keys(object).every(key => validateType(type, object[key])); if (!boolean) { warn("objectOf - value must be an object of " + getType(type)); } return boolean; } }); }, shape(object) { const keys = Object.keys(object); const requiredKeys = keys.filter(key => object[key] && object[key].required === true); const type = toType("shape", { type: Object, validator(value) { if (!isPlainObject(value)) { return false; } const valueKeys = Object.keys(value); if (requiredKeys.length > 0 && requiredKeys.some(requiredKey => valueKeys.indexOf(requiredKey) === -1)) { warn("shape - at least one of required properties [" + requiredKeys.join(", ") + "] is not present"); return false; } return valueKeys.every(key => { if (keys.indexOf(key) === -1) { if (this.vuePropTypesIsLoose === true) { return true; } warn("shape - object is missing " + key + " property"); return false; } const type = object[key]; return validateType(type, value[key]); }); } }); Object.defineProperty(type, "vuePropTypesIsLoose", { enumerable: false, writable: true, value: false }); Object.defineProperty(type, "loose", { enumerable: false, get() { this.vuePropTypesIsLoose = true; return this; } }); return type; } }; const createDefaultTypes = () => { return { bool: undefined, string: undefined, number: undefined, integer: undefined, func: undefined, element: undefined, date: undefined, array: undefined, object: undefined }; }; let defaults = createDefaultTypes(); Object.defineProperty(VuePropTypes, "sensibleDefaults", { enumerable: false, set(value) { if (value === false) { defaults = {}; } else if (value === true) { defaults = createDefaultTypes(); } else if (isPlainObject(value)) { defaults = value; } }, get() { return defaults; } }); export default VuePropTypes;