himanshu-react-jsonschema-form
Version:
A simple React component capable of building HTML forms out of a JSON schema.
604 lines (529 loc) • 19.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.getDefaultRegistry = getDefaultRegistry;
exports.defaultFieldValue = defaultFieldValue;
exports.getWidget = getWidget;
exports.getDefaultFormState = getDefaultFormState;
exports.getUiOptions = getUiOptions;
exports.isObject = isObject;
exports.mergeObjects = mergeObjects;
exports.asNumber = asNumber;
exports.orderProperties = orderProperties;
exports.isMultiSelect = isMultiSelect;
exports.isFilesArray = isFilesArray;
exports.isFixedItems = isFixedItems;
exports.allowAdditionalItems = allowAdditionalItems;
exports.optionsList = optionsList;
exports.retrieveSchema = retrieveSchema;
exports.deepEquals = deepEquals;
exports.shouldRender = shouldRender;
exports.toIdSchema = toIdSchema;
exports.parseDateString = parseDateString;
exports.toDateString = toDateString;
exports.pad = pad;
exports.setState = setState;
exports.dataURItoBlob = dataURItoBlob;
exports.rangeSpec = rangeSpec;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
require("setimmediate");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var widgetMap = {
boolean: {
checkbox: "CheckboxWidget",
radio: "RadioWidget",
select: "SelectWidget",
hidden: "HiddenWidget"
},
string: {
text: "TextWidget",
password: "PasswordWidget",
email: "EmailWidget",
hostname: "TextWidget",
ipv4: "TextWidget",
ipv6: "TextWidget",
uri: "URLWidget",
"data-url": "FileWidget",
radio: "RadioWidget",
select: "SelectWidget",
textarea: "TextareaWidget",
hidden: "HiddenWidget",
date: "DateWidget",
datetime: "DateTimeWidget",
"date-time": "DateTimeWidget",
"alt-date": "AltDateWidget",
"alt-datetime": "AltDateTimeWidget",
color: "ColorWidget",
file: "FileWidget"
},
number: {
text: "TextWidget",
select: "SelectWidget",
updown: "UpDownWidget",
range: "RangeWidget",
radio: "RadioWidget",
hidden: "HiddenWidget"
},
integer: {
text: "TextWidget",
select: "SelectWidget",
updown: "UpDownWidget",
range: "RangeWidget",
radio: "RadioWidget",
hidden: "HiddenWidget"
},
array: {
select: "SelectWidget",
checkboxes: "CheckboxesWidget",
files: "FileWidget"
}
};
var defaultRegistry = {
fields: require("./components/fields").default,
widgets: require("./components/widgets").default,
definitions: {},
formContext: {}
};
function getDefaultRegistry() {
return defaultRegistry;
}
function defaultFieldValue(formData, schema) {
return typeof formData === "undefined" ? schema.default : formData;
}
function getWidget(schema, widget) {
var registeredWidgets = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var type = schema.type;
function mergeOptions(Widget) {
// cache return value as property of widget for proper react reconciliation
if (!Widget.MergedWidget) {
(function () {
var defaultOptions = Widget.defaultProps && Widget.defaultProps.options || {};
Widget.MergedWidget = function (_ref) {
var _ref$options = _ref.options,
options = _ref$options === undefined ? {} : _ref$options,
props = _objectWithoutProperties(_ref, ["options"]);
return _react2.default.createElement(Widget, _extends({ options: _extends({}, defaultOptions, options) }, props));
};
})();
}
return Widget.MergedWidget;
}
if (typeof widget === "function") {
return mergeOptions(widget);
}
if (typeof widget !== "string") {
throw new Error("Unsupported widget definition: " + (typeof widget === "undefined" ? "undefined" : _typeof(widget)));
}
if (registeredWidgets.hasOwnProperty(widget)) {
var registeredWidget = registeredWidgets[widget];
return getWidget(schema, registeredWidget, registeredWidgets);
}
if (!widgetMap.hasOwnProperty(type)) {
throw new Error("No widget for type \"" + type + "\"");
}
if (widgetMap[type].hasOwnProperty(widget)) {
var _registeredWidget = registeredWidgets[widgetMap[type][widget]];
return getWidget(schema, _registeredWidget, registeredWidgets);
}
throw new Error("No widget \"" + widget + "\" for type \"" + type + "\"");
}
function computeDefaults(schema, parentDefaults) {
var definitions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
// Compute the defaults recursively: give highest priority to deepest nodes.
var defaults = parentDefaults;
if (isObject(defaults) && isObject(schema.default)) {
// For object defaults, only override parent defaults that are defined in
// schema.default.
defaults = mergeObjects(defaults, schema.default);
} else if ("default" in schema) {
// Use schema defaults for this node.
defaults = schema.default;
} else if ("enum" in schema && Array.isArray(schema.enum)) {
// For enum with no defined default, select the first entry.
defaults = schema.enum[0];
} else if ("$ref" in schema) {
// Use referenced schema defaults for this node.
var refSchema = findSchemaDefinition(schema.$ref, definitions);
return computeDefaults(refSchema, defaults, definitions);
} else if (isFixedItems(schema)) {
defaults = schema.items.map(function (itemSchema) {
return computeDefaults(itemSchema, undefined, definitions);
});
}
// Not defaults defined for this node, fallback to generic typed ones.
if (typeof defaults === "undefined") {
defaults = schema.default;
}
// We need to recur for object schema inner default values.
if (schema.type === "object") {
return Object.keys(schema.properties).reduce(function (acc, key) {
// Compute the defaults for this node, with the parent defaults we might
// have from a previous run: defaults[key].
acc[key] = computeDefaults(schema.properties[key], (defaults || {})[key], definitions);
return acc;
}, {});
}
return defaults;
}
function getDefaultFormState(_schema, formData) {
var definitions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!isObject(_schema)) {
throw new Error("Invalid schema: " + _schema);
}
var schema = retrieveSchema(_schema, definitions);
var defaults = computeDefaults(schema, _schema.default, definitions);
if (typeof formData === "undefined") {
// No form data? Use schema defaults.
return defaults;
}
if (isObject(formData)) {
// Override schema defaults with form data.
return mergeObjects(defaults, formData);
}
return formData || defaults;
}
function getUiOptions(uiSchema) {
// get all passed options from ui:widget, ui:options, and ui:<optionName>
return Object.keys(uiSchema).filter(function (key) {
return key.indexOf("ui:") === 0;
}).reduce(function (options, key) {
var value = uiSchema[key];
if (key === "ui:widget" && isObject(value)) {
console.warn("Setting options via ui:widget object is deprecated, use ui:options instead");
return _extends({}, options, value.options || {}, { widget: value.component });
}
if (key === "ui:options" && isObject(value)) {
return _extends({}, options, value);
}
return _extends({}, options, _defineProperty({}, key.substring(3), value));
}, {});
}
function isObject(thing) {
return (typeof thing === "undefined" ? "undefined" : _typeof(thing)) === "object" && thing !== null && !Array.isArray(thing);
}
function mergeObjects(obj1, obj2) {
var concatArrays = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
// Recursively merge deeply nested objects.
var acc = Object.assign({}, obj1); // Prevent mutation of source object.
return Object.keys(obj2).reduce(function (acc, key) {
var left = obj1[key],
right = obj2[key];
if (obj1.hasOwnProperty(key) && isObject(right)) {
acc[key] = mergeObjects(left, right, concatArrays);
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) {
acc[key] = left.concat(right);
} else {
acc[key] = right;
}
return acc;
}, acc);
}
function asNumber(value) {
if (value === "") {
return undefined;
}
if (/\.$/.test(value)) {
// "3." can't really be considered a number even if it parses in js. The
// user is most likely entering a float.
return value;
}
if (/\.0$/.test(value)) {
// we need to return this as a string here, to allow for input like 3.07
return value;
}
var n = Number(value);
var valid = typeof n === "number" && !Number.isNaN(n);
if (/\.\d*0$/.test(value)) {
// It's a number, that's cool - but we need it as a string so it doesn't screw
// with the user when entering dollar amounts or other values (such as those with
// specific precision or number of significant digits)
return value;
}
return valid ? n : value;
}
function orderProperties(properties, order) {
if (!Array.isArray(order)) {
return properties;
}
var arrayToHash = function arrayToHash(arr) {
return arr.reduce(function (prev, curr) {
prev[curr] = true;
return prev;
}, {});
};
var errorPropList = function errorPropList(arr) {
return arr.length > 1 ? "properties '" + arr.join("', '") + "'" : "property '" + arr[0] + "'";
};
var propertyHash = arrayToHash(properties);
var orderHash = arrayToHash(order);
var extraneous = order.filter(function (prop) {
return prop !== "*" && !propertyHash[prop];
});
if (extraneous.length) {
throw new Error("uiSchema order list contains extraneous " + errorPropList(extraneous));
}
var rest = properties.filter(function (prop) {
return !orderHash[prop];
});
var restIndex = order.indexOf("*");
if (restIndex === -1) {
if (rest.length) {
throw new Error("uiSchema order list does not contain " + errorPropList(rest));
}
return order;
}
if (restIndex !== order.lastIndexOf("*")) {
throw new Error("uiSchema order list contains more than one wildcard item");
}
var complete = [].concat(_toConsumableArray(order));
complete.splice.apply(complete, [restIndex, 1].concat(_toConsumableArray(rest)));
return complete;
}
function isMultiSelect(schema) {
return Array.isArray(schema.items.enum) && schema.uniqueItems;
}
function isFilesArray(schema, uiSchema) {
return schema.items.type === "string" && schema.items.format === "data-url" || uiSchema["ui:widget"] === "files";
}
function isFixedItems(schema) {
return Array.isArray(schema.items) && schema.items.length > 0 && schema.items.every(function (item) {
return isObject(item);
});
}
function allowAdditionalItems(schema) {
if (schema.additionalItems === true) {
console.warn("additionalItems=true is currently not supported");
}
return isObject(schema.additionalItems);
}
function optionsList(schema) {
return schema.enum.map(function (value, i) {
var label = schema.enumNames && schema.enumNames[i] || String(value);
return { label: label, value: value };
});
}
function findSchemaDefinition($ref) {
var definitions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Extract and use the referenced definition if we have it.
var match = /#\/definitions\/(.*)$/.exec($ref);
if (match && match[1] && definitions.hasOwnProperty(match[1])) {
return definitions[match[1]];
}
// No matching definition found, that's an error (bogus schema?)
throw new Error("Could not find a definition for " + $ref + ".");
}
function retrieveSchema(schema) {
var definitions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// No $ref attribute found, returning the original schema.
if (!schema.hasOwnProperty("$ref")) {
return schema;
}
// Retrieve the referenced schema definition.
var $refSchema = findSchemaDefinition(schema.$ref, definitions);
// Drop the $ref property of the source schema.
var $ref = schema.$ref,
localSchema = _objectWithoutProperties(schema, ["$ref"]); // eslint-disable-line no-unused-vars
// Update referenced schema definition with local schema properties.
return _extends({}, $refSchema, localSchema);
}
function isArguments(object) {
return Object.prototype.toString.call(object) === "[object Arguments]";
}
function deepEquals(a, b) {
var ca = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var cb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
// Partially extracted from node-deeper and adapted to exclude comparison
// checks for functions.
// https://github.com/othiym23/node-deeper
if (a === b) {
return true;
} else if (typeof a === "function" || typeof b === "function") {
// Assume all functions are equivalent
// see https://github.com/mozilla-services/react-jsonschema-form/issues/255
return true;
} else if ((typeof a === "undefined" ? "undefined" : _typeof(a)) !== "object" || (typeof b === "undefined" ? "undefined" : _typeof(b)) !== "object") {
return false;
} else if (a === null || b === null) {
return false;
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
} else if (a instanceof RegExp && b instanceof RegExp) {
return a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.lastIndex === b.lastIndex && a.ignoreCase === b.ignoreCase;
} else if (isArguments(a) || isArguments(b)) {
if (!(isArguments(a) && isArguments(b))) {
return false;
}
var slice = Array.prototype.slice;
return deepEquals(slice.call(a), slice.call(b), ca, cb);
} else {
if (a.constructor !== b.constructor) {
return false;
}
var ka = Object.keys(a);
var kb = Object.keys(b);
// don't bother with stack acrobatics if there's nothing there
if (ka.length === 0 && kb.length === 0) {
return true;
}
if (ka.length !== kb.length) {
return false;
}
var cal = ca.length;
while (cal--) {
if (ca[cal] === a) {
return cb[cal] === b;
}
}
ca.push(a);
cb.push(b);
ka.sort();
kb.sort();
for (var j = ka.length - 1; j >= 0; j--) {
if (ka[j] !== kb[j]) {
return false;
}
}
var key = void 0;
for (var k = ka.length - 1; k >= 0; k--) {
key = ka[k];
if (!deepEquals(a[key], b[key], ca, cb)) {
return false;
}
}
ca.pop();
cb.pop();
return true;
}
}
function shouldRender(comp, nextProps, nextState) {
var props = comp.props,
state = comp.state;
return !deepEquals(props, nextProps) || !deepEquals(state, nextState);
}
function toIdSchema(schema, id, definitions) {
var idSchema = {
$id: id || "root"
};
if ("$ref" in schema) {
var _schema = retrieveSchema(schema, definitions);
return toIdSchema(_schema, id, definitions);
}
if ("items" in schema) {
return toIdSchema(schema.items, id, definitions);
}
if (schema.type !== "object") {
return idSchema;
}
for (var name in schema.properties || {}) {
var field = schema.properties[name];
var fieldId = idSchema.$id + "_" + name;
idSchema[name] = toIdSchema(field, fieldId, definitions);
}
return idSchema;
}
function parseDateString(dateString) {
var includeTime = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (!dateString) {
return {
year: -1,
month: -1,
day: -1,
hour: includeTime ? -1 : 0,
minute: includeTime ? -1 : 0,
second: includeTime ? -1 : 0
};
}
var date = new Date(dateString);
if (Number.isNaN(date.getTime())) {
throw new Error("Unable to parse date " + dateString);
}
return {
year: date.getUTCFullYear(),
month: date.getUTCMonth() + 1, // oh you, javascript.
day: date.getUTCDate(),
hour: includeTime ? date.getUTCHours() : 0,
minute: includeTime ? date.getUTCMinutes() : 0,
second: includeTime ? date.getUTCSeconds() : 0
};
}
function toDateString(_ref2) {
var year = _ref2.year,
month = _ref2.month,
day = _ref2.day,
_ref2$hour = _ref2.hour,
hour = _ref2$hour === undefined ? 0 : _ref2$hour,
_ref2$minute = _ref2.minute,
minute = _ref2$minute === undefined ? 0 : _ref2$minute,
_ref2$second = _ref2.second,
second = _ref2$second === undefined ? 0 : _ref2$second;
var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var utcTime = Date.UTC(year, month - 1, day, hour, minute, second);
var datetime = new Date(utcTime).toJSON();
return time ? datetime : datetime.slice(0, 10);
}
function pad(num, size) {
var s = String(num);
while (s.length < size) {
s = "0" + s;
}
return s;
}
function setState(instance, state, callback) {
var safeRenderCompletion = instance.props.safeRenderCompletion;
if (safeRenderCompletion) {
instance.setState(state, callback);
} else {
instance.setState(state);
setImmediate(callback);
}
}
function dataURItoBlob(dataURI) {
// Split metadata from data
var splitted = dataURI.split(",");
// Split params
var params = splitted[0].split(";");
// Get mime-type from params
var type = params[0].replace("data:", "");
// Filter the name property from params
var properties = params.filter(function (param) {
return param.split("=")[0] === "name";
});
// Look for the name and use unknown if no name property.
var name = void 0;
if (properties.length !== 1) {
name = "unknown";
} else {
// Because we filtered out the other property,
// we only have the name case here.
name = properties[0].split("=")[1];
}
// Built the Uint8Array Blob parameter from the base64 string.
var binary = atob(splitted[1]);
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
// Create the blob object
var blob = new window.Blob([new Uint8Array(array)], { type: type });
return { blob: blob, name: name };
}
function rangeSpec(schema) {
var spec = {};
if (schema.multipleOf) {
spec.step = schema.multipleOf;
}
if (schema.minimum || schema.minimum === 0) {
spec.min = schema.minimum;
}
if (schema.maximum || schema.maximum === 0) {
spec.max = schema.maximum;
}
return spec;
}