UNPKG

@bhch/react-json-form

Version:
1,484 lines (1,283 loc) 151 kB
import React$1 from 'react'; import ReactModal from 'react-modal'; import ReactDOM from 'react-dom'; function _extends() { _extends = Object.assign ? Object.assign.bind() : 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; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } /* Symbol for joining coordinates. * Earlier, a hyphen (-) was used. But that caused problems when * object keys had hyphen in them. So, we're switching to a less * commonly used symbol. */ const JOIN_SYMBOL = '§'; /* HTML field name prefix */ const FIELD_NAME_PREFIX = 'rjf'; /* Filler item for arrays to make them at least minItems long */ const FILLER = '__RJF_FILLER__'; const EditorContext = /*#__PURE__*/React$1.createContext(); function capitalize$1(string) { if (!string) return ''; return string.charAt(0).toUpperCase() + string.substr(1).toLowerCase(); } function convertType(value, to) { if (typeof value === to) return value; if (to === 'number' || to === 'integer') { if (typeof value === 'string') { value = value.trim(); if (value === '') value = null;else if (!isNaN(Number(value))) value = Number(value); } else if (typeof value === 'boolean') { value = value === true ? 1 : 0; } } else if (to === 'boolean') { if (value === 'false' || value === false) value = false;else value = true; } return value; } function actualType(value) { /* Returns the "actual" type of the given value. - array -> 'array' - null -> 'null' */ let type = typeof value; if (type === 'object') { if (Array.isArray(value)) type = 'array';else if (value === null) type = 'null'; } return type; } function getSchemaType(schema) { /* Returns type of the given schema. If schema.type is not present, it tries to guess the type. If data is given, it will try to use that to guess the type. */ let type; if (schema.hasOwnProperty('const')) type = actualType(schema.const);else type = normalizeKeyword(schema.type); if (!type) { if (schema.hasOwnProperty('properties') || schema.hasOwnProperty('keys')) type = 'object';else if (schema.hasOwnProperty('items')) type = 'array';else if (schema.hasOwnProperty('allOf')) type = 'allOf';else if (schema.hasOwnProperty('oneOf')) type = 'oneOf';else if (schema.hasOwnProperty('anyOf')) type = 'anyOf';else type = 'string'; } return type; } function getVerboseName(name) { if (name === undefined || name === null) return ''; name = name.replace(/_/g, ' '); return capitalize$1(name); } function getCsrfCookie() { let csrfCookies = document.cookie.split(';').filter(item => item.trim().indexOf('csrftoken=') === 0); if (csrfCookies.length) { return csrfCookies[0].split('=')[1]; } else { // if no cookie found, get the value from the csrf form input let input = document.querySelector('input[name="csrfmiddlewaretoken"]'); if (input) return input.value; } return null; } function joinCoords() { /* Generates coordinates from given arguments */ return Array.from(arguments).join(JOIN_SYMBOL); } function splitCoords(coords) { /* Generates coordinates */ return coords.split(JOIN_SYMBOL); } function getCoordsFromName(name) { /* Returns coordinates of a field in the data from * the given name of the input. * Field names have FIELD_NAME_PREFIX prepended but the coordinates don't. * e.g.: * name: rjf-0-field (where rjf- is the FIELD_NAME_PREFIX) * coords: 0-field */ return name.slice((FIELD_NAME_PREFIX + JOIN_SYMBOL).length); } function debounce(func, wait) { let timeout; return function () { clearTimeout(timeout); let args = arguments; let context = this; timeout = setTimeout(function () { func.apply(context, args); }, wait || 1); }; } function normalizeKeyword(kw) { /* Converts custom supported keywords to standard JSON schema keywords */ if (Array.isArray(kw)) kw = kw.find(k => k !== 'null') || 'null'; switch (kw) { case 'list': return 'array'; case 'dict': return 'object'; case 'keys': return 'properties'; case 'choices': return 'enum'; case 'datetime': return 'date-time'; default: return kw; } } function getKeyword(obj, keyword, alias, default_value) { /* Function useful for getting value from schema if a * keyword has an alias. */ return getKey(obj, keyword, getKey(obj, alias, default_value)); } function getKey(obj, key, default_value) { /* Approximation of Python's dict.get() function. */ let val = obj[key]; return typeof val !== 'undefined' ? val : default_value; } function choicesValueTitleMap(choices) { /* Returns a mapping of {value: title} for the given choices. * E.g.: * Input: [{'title': 'One', 'value': 1}, 2] * Output: {1: 'One', 2: 2} */ let map = {}; for (let i = 0; i < choices.length; i++) { let choice = choices[i]; let value, title; if (actualType(choice) === 'object') { value = choice.value; title = choice.title; } else { value = choice; title = choice; } map[value] = title; } return map; } function valueInChoices(schema, value) { /* Checks whether the given value is in schema choices or not. If schema doesn't have choices, returns true. */ let choices = getKeyword(schema, 'choices', 'enum'); if (!choices) return true; let found = choices.find(choice => { if (typeof choice == 'object') choice = choice.value; return value == choice; }); return found !== undefined ? true : false; } /* Set operations */ function isEqualset(a, b) { return a.size === b.size && Array.from(a).every(i => b.has(i)); } function isSubset(set, superset) { for (const elem of set) { if (!superset.has(elem)) { return false; } } return true; } function getBlankObject(schema, getRef) { let keys = {}; let schema_keys = getKeyword(schema, 'keys', 'properties', {}); for (let key in schema_keys) { let value = schema_keys[key]; let isRef = value.hasOwnProperty('$ref'); let isConst = value.hasOwnProperty('const'); if (isRef) { value = _extends({}, getRef(value['$ref']), value); delete value['$ref']; } let type = normalizeKeyword(value.type); if (!type) { // check for oneOf/anyOf if (value.hasOwnProperty('oneOf')) value = value.oneOf[0];else if (value.hasOwnProperty('anyOf')) value = value.anyOf[0]; type = normalizeKeyword(value.type); } let default_ = value.default; if (isConst) { type = actualType(value.const); default_ = value.const; } if (type === 'array') keys[key] = isRef ? [] : getBlankArray(value, getRef);else if (type === 'object') keys[key] = getBlankObject(value, getRef);else if (type === 'boolean') keys[key] = default_ === false ? false : default_ || null;else if (type === 'integer' || type === 'number') keys[key] = default_ === 0 ? 0 : default_ || null;else keys[key] = default_ || ''; } if (schema.hasOwnProperty('oneOf')) keys = _extends({}, keys, getBlankObject(schema.oneOf[0])); if (schema.hasOwnProperty('anyOf')) keys = _extends({}, keys, getBlankObject(schema.anyOf[0])); if (schema.hasOwnProperty('allOf')) { for (let i = 0; i < schema.allOf.length; i++) { keys = _extends({}, keys, getBlankObject(schema.allOf[i])); } } return keys; } function getBlankArray(schema, getRef) { let minItems = getKeyword(schema, 'minItems', 'min_items') || 0; if (schema.default && schema.default.length >= minItems) return schema.default; let items = []; if (schema.default) items = [...schema.default]; if (minItems === 0) return items; if (schema.items.hasOwnProperty('$ref')) { // :TODO: this mutates the original schema // but i'll fix it later schema.items = _extends({}, getRef(schema.items['$ref']), schema.items); delete schema.items['$ref']; } let type = normalizeKeyword(schema.items.type); if (!type) { if (Array.isArray(schema.items['oneOf'])) type = getSchemaType(schema.items.oneOf[0]);else if (Array.isArray(schema.items['anyOf'])) type = getSchemaType(schema.items.anyOf[0]);else if (Array.isArray(schema.items['allOf'])) type = getSchemaType(schema.items.allOf[0]);else if (schema.items.hasOwnProperty('const')) type = actualType(schema.items.const); } if (type === 'array') { while (items.length < minItems) items.push(getBlankArray(schema.items, getRef)); return items; } else if (type === 'object') { while (items.length < minItems) items.push(getBlankObject(schema.items, getRef)); return items; } else if (type === 'oneOf') { while (items.length < minItems) items.push(getBlankOneOf(schema.items, getRef)); return items; } else if (type === 'anyOf') { while (items.length < minItems) items.push(getBlankOneOf(schema.items, getRef)); return items; } if (schema.items.widget === 'multiselect') return items; let default_ = schema.items.default; if (schema.items.hasOwnProperty('const')) default_ = schema.items.const; if (type === 'boolean') { while (items.length < minItems) items.push(default_ === false ? false : default_ || null); } else if (type === 'integer' || type === 'number') { while (items.length < minItems) items.push(default_ === 0 ? 0 : default_ || null); } else { // string, etc. while (items.length < minItems) items.push(default_ || ''); } return items; } function getBlankAllOf(schema, getRef) { // currently, we support allOf only inside an object return getBlankObject(schema, getRef); } function getBlankOneOf(schema, getRef) { // for blank data, we always return the first option let nextSchema = schema.oneOf[0]; getSchemaType(nextSchema); return getBlankData(nextSchema, getRef); } function getBlankAnyOf(schema, getRef) { // for blank data, we always return the first option let nextSchema = schema.anyOf[0]; getSchemaType(nextSchema); return getBlankData(nextSchema, getRef); } function getBlankData(schema, getRef) { if (schema.hasOwnProperty('$ref')) { schema = _extends({}, getRef(schema['$ref']), schema); delete schema['$ref']; } let type = getSchemaType(schema); let default_ = schema.default; if (schema.hasOwnProperty('const')) { type = actualType(schema.const); default_ = schema.const; } if (type === 'array') return getBlankArray(schema, getRef);else if (type === 'object') return getBlankObject(schema, getRef);else if (type === 'allOf') return getBlankAllOf(schema, getRef);else if (type === 'oneOf') return getBlankOneOf(schema, getRef);else if (type === 'anyOf') return getBlankAnyOf(schema, getRef);else if (type === 'boolean') return default_ === false ? false : default_ || null;else if (type === 'integer' || type === 'number') return default_ === 0 ? 0 : default_ || null;else // string, etc. return default_ || ''; } function getSyncedArray(data, schema, getRef) { if (data === null) data = []; if (actualType(data) !== 'array') throw new Error("Schema expected an 'array' but the data type was '" + actualType(data) + "'"); let newData = JSON.parse(JSON.stringify(data)); if (schema.items.hasOwnProperty('$ref')) { // :TODO: this will most probably mutate the original schema // but i'll fix it later schema.items = _extends({}, getRef(schema.items['$ref']), schema.items); delete schema.items['$ref']; } let type; let default_; if (schema.items.hasOwnProperty('const')) { type = actualType(schema.items.const); default_ = schema.items.const; } else { type = normalizeKeyword(schema.items.type); default_ = schema.items.defualt; } let minItems = schema.minItems || schema.min_items || 0; if (schema.items.widget !== 'multiselect') { while (data.length < minItems) data.push(FILLER); } for (let i = 0; i < data.length; i++) { let item = data[i]; if (type === 'array') { if (item === FILLER) item = []; newData[i] = getSyncedArray(item, schema.items, getRef); } else if (type === 'object') { if (item === FILLER) item = {}; newData[i] = getSyncedObject(item, schema.items, getRef); } else { // if the current value is not in choices, we reset to blank if (!valueInChoices(schema.items, newData[i])) item = FILLER; if (item === FILLER) { if (type === 'integer' || type === 'number') newData[i] = default_ === 0 ? 0 : default_ || null;else if (type === 'boolean') newData[i] = default_ === false ? false : default_ || null;else newData[i] = default_ || ''; } } if (schema.items.hasOwnProperty('const')) newData[i] = schema.items.const; } return newData; } function getSyncedObject(data, schema, getRef) { if (data === null) data = {}; if (actualType(data) !== 'object') throw new Error("Schema expected an 'object' but the data type was '" + actualType(data) + "'"); let newData = JSON.parse(JSON.stringify(data)); let schema_keys = getKeyword(schema, 'keys', 'properties', {}); if (schema.hasOwnProperty('allOf')) { for (let i = 0; i < schema.allOf.length; i++) { // ignore items in allOf which are not object if (getSchemaType(schema.allOf[i]) !== 'object') continue; schema_keys = _extends({}, schema_keys, getKeyword(schema.allOf[i], 'properties', 'keys', {})); } } let keys = [...Object.keys(schema_keys)]; for (let i = 0; i < keys.length; i++) { let key = keys[i]; let schemaValue = schema_keys[key]; let isRef = schemaValue.hasOwnProperty('$ref'); if (isRef) { schemaValue = _extends({}, getRef(schemaValue['$ref']), schemaValue); delete schemaValue['$ref']; } let type; let default_; if (schemaValue.hasOwnProperty('const')) { type = actualType(schemaValue.const); default_ = schemaValue.const; } else { type = getSchemaType(schemaValue); default_ = schemaValue.default; } if (!data.hasOwnProperty(key)) { /* This key is declared in schema but it's not present in the data. So we can use blank data here. */ if (type === 'array') newData[key] = getSyncedArray([], schemaValue, getRef);else if (type === 'object') newData[key] = getSyncedObject({}, schemaValue, getRef);else if (type === 'oneOf') newData[key] = getBlankOneOf(schemaValue, getRef);else if (type === 'anyOf') newData[key] = getBlankAnyOf(schemaValue, getRef);else if (type === 'boolean') newData[key] = default_ === false ? false : default_ || null;else if (type === 'integer' || type === 'number') newData[key] = default_ === 0 ? 0 : default_ || null;else newData[key] = default_ || ''; } else { if (type === 'array') newData[key] = getSyncedArray(data[key], schemaValue, getRef);else if (type === 'object') newData[key] = getSyncedObject(data[key], schemaValue, getRef);else if (type === 'oneOf') newData[key] = getSyncedOneOf(data[key], schemaValue, getRef);else if (type === 'anyOf') newData[key] = getSyncedAnyOf(data[key], schemaValue, getRef);else { // if the current value is not in choices, we reset to blank if (!valueInChoices(schemaValue, data[key])) data[key] = ''; if (data[key] === '') { if (type === 'integer' || type === 'number') newData[key] = default_ === 0 ? 0 : default_ || null;else if (type === 'boolean') newData[key] = default_ === false ? false : default_ || null;else newData[key] = default_ || ''; } else { newData[key] = data[key]; } } } if (schemaValue.hasOwnProperty('const')) newData[key] = schemaValue.const; } if (schema.hasOwnProperty('oneOf')) newData = _extends({}, newData, getSyncedOneOf(data, schema, getRef)); if (schema.hasOwnProperty('anyOf')) newData = _extends({}, newData, getSyncedAnyOf(data, schema, getRef)); return newData; } function getSyncedAllOf(data, schema, getRef) { // currently we only support allOf inside an object // so, we'll treat the curent schema and data to be an object return getSyncedObject(data, schema, getRef); } function getSyncedOneOf(data, schema, getRef) { let index = findMatchingSubschemaIndex(data, schema, getRef, 'oneOf'); let subschema = schema['oneOf'][index]; let syncFunc = getSyncFunc(getSchemaType(subschema)); if (syncFunc) return syncFunc(data, subschema, getRef); return data; } function getSyncedAnyOf(data, schema, getRef) { let index = findMatchingSubschemaIndex(data, schema, getRef, 'anyOf'); let subschema = schema['anyOf'][index]; let syncFunc = getSyncFunc(getSchemaType(subschema)); if (syncFunc) return syncFunc(data, subschema, getRef); return data; } function getSyncedData(data, schema, getRef) { // adds those keys to data which are in schema but not in data if (schema.hasOwnProperty('$ref')) { schema = _extends({}, getRef(schema['$ref']), schema); delete schema['$ref']; } let type = getSchemaType(schema); let syncFunc = getSyncFunc(type); if (syncFunc) return syncFunc(data, schema, getRef); return data; } function getSyncFunc(type) { if (type === 'array') return getSyncedArray;else if (type === 'object') return getSyncedObject;else if (type === 'allOf') return getSyncedAllOf;else if (type === 'oneOf') return getSyncedOneOf;else if (type === 'anyOf') return getSyncedAnyOf; return null; } function findMatchingSubschemaIndex(data, schema, getRef, schemaName) { let dataType = actualType(data); let subschemas = schema[schemaName]; let index = null; for (let i = 0; i < subschemas.length; i++) { let subschema = subschemas[i]; if (subschema.hasOwnProperty('$ref')) { subschema = _extends({}, getRef(subschema['$ref']), subschema); delete subschema['$ref']; } let subType = getSchemaType(subschema); if (dataType === 'object') { // check if all keys match if (dataObjectMatchesSchema(data, subschema)) { index = i; break; } } else if (dataType === 'array') { // check if item types match if (dataArrayMatchesSchema(data, subschema)) { index = i; break; } } else if (dataType === subType) { index = i; break; } } if (index === null) { // no exact match found // so we'll just return the first schema that matches the data type for (let i = 0; i < subschemas.length; i++) { let subschema = subschemas[i]; if (subschema.hasOwnProperty('$ref')) { subschema = _extends({}, getRef(subschema['$ref']), subschema); delete subschema['$ref']; } let subType = getSchemaType(subschema); if (dataType === subType) { index = i; break; } } } if (index === null) { // still no match found if (data === null) // for null data, return the first subschema and hope for the best index = 0;else // for anything else, throw error throw new Error("No matching subschema found in '" + schemaName + "' for data '" + data + "' (type: " + dataType + ")"); } return index; } function dataObjectMatchesSchema(data, subschema) { let dataType = actualType(data); let subType = getSchemaType(subschema); if (subType !== dataType) return false; let subSchemaKeys = getKeyword(subschema, 'properties', 'keys', {}); // check if all keys in the schema are present in the data keyset1 = new Set(Object.keys(data)); keyset2 = new Set(Object.keys(subSchemaKeys)); if (subschema.hasOwnProperty('additionalProperties')) { // subSchemaKeys must be a subset of data if (!isSubset(keyset2, keyset1)) return false; } else { // subSchemaKeys must be equal to data if (!isEqualset(keyset2, keyset1)) return false; } for (let key in subSchemaKeys) { if (!subSchemaKeys.hasOwnProperty(key)) continue; if (!data.hasOwnProperty(key)) return false; if (subSchemaKeys[key].hasOwnProperty('const')) { if (subSchemaKeys[key].const !== data[key]) return false; } let keyType = normalizeKeyword(subSchemaKeys[key].type); let dataValueType = actualType(data[key]); if (keyType === 'number' && ['number', 'integer', 'null'].indexOf(dataValueType) === -1) { return false; } else if (keyType === 'integer' && ['number', 'integer', 'null'].indexOf(dataValueType) === -1) { return false; } else if (keyType === 'boolean' && ['boolean', 'null'].indexOf(dataValueType) === -1) { return false; } else if (keyType === 'string' && dataValueType !== 'string') { return false; } // TODO: also check minimum, maximum, etc. keywords } // if here, all checks have passed return true; } function dataArrayMatchesSchema(data, subschema) { let dataType = actualType(data); let subType = getSchemaType(subschema); if (subType !== dataType) return false; let itemsType = subschema.items.type; // Temporary. Nested subschemas inside array.items won't work. // check each item in data conforms to array items.type for (let i = 0; i < data.length; i++) { dataValueType = actualType(data[i]); if (subschema.items.hasOwnProperty('const')) { if (subschema.items.const !== data[i]) return false; } if (itemsType === 'number' && ['number', 'integer', 'null'].indexOf(dataValueType) === -1) { return false; } else if (itemsType === 'integer' && ['number', 'integer', 'null'].indexOf(dataValueType) === -1) { return false; } else if (itemsType === 'boolean' && ['boolean', 'null'].indexOf(dataValueType) === -1) { return false; } else if (itemsType === 'string' && dataValueType !== 'string') { return false; } } // if here, all checks have passed return true; } const _excluded$2 = ["className", "alterClassName"]; function Button(_ref) { let { className, alterClassName } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$2); if (!className) className = ''; let classes = className.split(' '); if (alterClassName !== false) { className = ''; for (let i = 0; i < classes.length; i++) { className = className + 'rjf-' + classes[i] + '-button '; } } return /*#__PURE__*/React.createElement("button", _extends({ className: className.trim(), type: "button" }, props), props.children); } function Loader(props) { let className = 'rjf-loader'; if (props.className) className = className + ' ' + props.className; return /*#__PURE__*/React.createElement("div", { className: className }); } function Icon(props) { let icon; switch (props.name) { case 'chevron-up': icon = /*#__PURE__*/React$1.createElement(ChevronUp, null); break; case 'chevron-down': icon = /*#__PURE__*/React$1.createElement(ChevronDown, null); break; case 'arrow-down': icon = /*#__PURE__*/React$1.createElement(ArrowDown, null); break; case 'x-lg': icon = /*#__PURE__*/React$1.createElement(XLg, null); break; case 'x-circle': icon = /*#__PURE__*/React$1.createElement(XCircle, null); break; case 'three-dots-vertical': icon = /*#__PURE__*/React$1.createElement(ThreeDotsVertical, null); break; case 'box-arrow-up-right': icon = /*#__PURE__*/React$1.createElement(BoxArrowUpRight, null); break; } return /*#__PURE__*/React$1.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", className: "rjf-icon rjf-icon-" + props.name, viewBox: "0 0 16 16" }, icon); } function ChevronUp(props) { return /*#__PURE__*/React$1.createElement("path", { fillRule: "evenodd", d: "M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z" }); } function ChevronDown(props) { return /*#__PURE__*/React$1.createElement("path", { fillRule: "evenodd", d: "M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" }); } function ArrowDown(props) { return /*#__PURE__*/React$1.createElement("path", { "fill-rule": "evenodd", d: "M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" }); } function XLg(props) { return /*#__PURE__*/React$1.createElement("path", { d: "M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z" }); } function XCircle(props) { return /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement("path", { d: "M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" }), /*#__PURE__*/React$1.createElement("path", { d: "M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" })); } function ThreeDotsVertical(props) { return /*#__PURE__*/React$1.createElement("path", { d: "M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z" }); } function BoxArrowUpRight(props) { return /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement("path", { "fill-rule": "evenodd", d: "M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5" }), /*#__PURE__*/React$1.createElement("path", { "fill-rule": "evenodd", d: "M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z" })); } class TimePicker extends React$1.Component { constructor(...args) { super(...args); this.sendValue = data => { this.props.onChange(data); }; this.validateValue = (name, value) => { if (name === 'hh' && value < 1) return 12;else if (name !== 'hh' && value < 0) return 59;else if (name === 'hh' && value > 12) return 1;else if (name !== 'hh' && value > 59) return 0; return value; }; this.handleChange = e => { let name = e.target.dataset.name; let value = e.target.value; if (isNaN(value)) return; let validValue = this.validateValue(name, parseInt(value) || 0); if (name === 'hh' && (value === '0' || value === '' || value === '00') && validValue === 1) validValue = 0; if (value.startsWith('0') && validValue < 10 && validValue !== 0) { validValue = validValue.toString().padStart(2, '0'); } this.sendValue({ [name]: value !== '' ? validValue.toString() : '' }); }; this.handleKeyDown = e => { if (e.keyCode !== 38 && e.keyCode !== 40) return; let name = e.target.dataset.name; let value = parseInt(e.target.value) || 0; if (e.keyCode === 38) { value++; } else if (e.keyCode === 40) { value--; } this.sendValue({ [name]: this.validateValue(name, value).toString().padStart(2, '0') }); }; this.handleSpin = (name, type) => { let value = this.props[name]; if (name === 'ampm') { value = value === 'am' ? 'pm' : 'am'; } else { value = parseInt(value) || 0; if (type === 'up') { value++; } else { value--; } value = this.validateValue(name, value).toString().padStart(2, '0'); } this.sendValue({ [name]: value }); }; this.handleBlur = e => { let value = this.validateValue(e.target.dataset.name, parseInt(e.target.value) || 0); if (value < 10) { this.sendValue({ [e.target.dataset.name]: value.toString().padStart(2, '0') }); } }; } componentWillUnmount() { let data = { hh: this.validateValue('hh', this.props.hh).toString().padStart(2, '0'), mm: this.validateValue('mm', this.props.mm).toString().padStart(2, '0'), ss: this.validateValue('ss', this.props.ss).toString().padStart(2, '0') }; this.sendValue(data); } render() { return /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker" }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-row rjf-time-picker-labels" }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, "Hrs"), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, "Min"), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, "Sec"), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, "am/pm")), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-row" }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('hh', 'up') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-up" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('mm', 'up') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-up" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('ss', 'up') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-up" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('ampm', 'up') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-up" })))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-row rjf-time-picker-values" }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement("input", { type: "text", "data-name": "hh", value: this.props.hh, onChange: this.handleChange, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown })), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }, ":"), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement("input", { type: "text", "data-name": "mm", value: this.props.mm, onChange: this.handleChange, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown })), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }, ":"), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement("input", { type: "text", "data-name": "ss", value: this.props.ss, onChange: this.handleChange, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown })), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, this.props.ampm)), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-row" }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('hh', 'down') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-down" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('mm', 'down') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-down" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('ss', 'down') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-down" }))), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col rjf-time-picker-col-sm" }), /*#__PURE__*/React$1.createElement("div", { className: "rjf-time-picker-col" }, /*#__PURE__*/React$1.createElement(Button, { onClick: () => this.handleSpin('ampm', 'down') }, /*#__PURE__*/React$1.createElement(Icon, { name: "chevron-down" }))))); } } const _excluded$1 = ["label", "help_text", "error", "inputRef"], _excluded2 = ["label", "help_text", "error", "value"], _excluded3 = ["label", "help_text", "error", "value", "options"], _excluded4 = ["label", "help_text", "error", "value", "options"], _excluded5 = ["label", "value"], _excluded6 = ["label", "help_text", "error", "inputRef"]; function Label(props) { if (!props.label) return null; return /*#__PURE__*/React$1.createElement("label", { className: props.required ? 'rjf-required' : null }, props.children, props.children && ' ', props.label); } function FormInput(_ref) { let { label, help_text, error, inputRef } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded$1); if (props.type === 'string') props.type = 'text'; if (inputRef) props.ref = inputRef; if (props.value === null) props.value = ''; let wrapperProps = {}; if (props.type == 'hidden') wrapperProps['style'] = { display: 'none' }; // readonly inputs are automatically marked disabled // if this is undesired, explicitly pass disabled=false if (props.readOnly && (props.disabled === undefined || props.disabled === null)) props.disabled = true; return /*#__PURE__*/React$1.createElement("div", wrapperProps, /*#__PURE__*/React$1.createElement(Label, { label: label, required: props.required }), /*#__PURE__*/React$1.createElement("div", { className: error ? "rjf-input-group has-error" : "rjf-input-group" }, props.children || /*#__PURE__*/React$1.createElement("input", props), error && error.map((error, i) => /*#__PURE__*/React$1.createElement("span", { className: "rjf-error-text", key: i }, error)), help_text && /*#__PURE__*/React$1.createElement("span", { className: "rjf-help-text" }, help_text))); } function FormCheckInput(_ref2) { let { label, help_text, error, value } = _ref2, props = _objectWithoutPropertiesLoose(_ref2, _excluded2); if (!label) label = props.name.toUpperCase(); if (props.type === 'bool') props.type = 'checkbox'; if (props.checked === undefined) props.checked = value; if (props.checked === '' || props.checked === null || props.checked === undefined) props.checked = false; if (props.readOnly) props.disabled = true; return /*#__PURE__*/React$1.createElement("div", { className: error ? "rjf-check-input has-error" : "rjf-check-input" }, /*#__PURE__*/React$1.createElement(Label, { label: label, required: props.required }, /*#__PURE__*/React$1.createElement("input", props)), error && error.map((error, i) => /*#__PURE__*/React$1.createElement("span", { className: "rjf-error-text", key: i }, error)), help_text && /*#__PURE__*/React$1.createElement("span", { className: "rjf-help-text" }, help_text)); } function FormRadioInput(_ref3) { let { label, help_text, error, value, options } = _ref3, props = _objectWithoutPropertiesLoose(_ref3, _excluded3); if (props.readOnly) props.disabled = true; return /*#__PURE__*/React$1.createElement("div", { className: error ? "rjf-check-input has-error" : "rjf-check-input" }, /*#__PURE__*/React$1.createElement(Label, { label: label, required: props.required }), options.map((option, i) => { let title, inputValue; if (typeof option === 'object') { title = option.title || option.label; inputValue = option.value; } else { title = option; if (typeof title === 'boolean') title = capitalize$1(title.toString()); inputValue = option; } return /*#__PURE__*/React$1.createElement("label", { className: "rjf-radio-option", key: title + '_' + inputValue + '_' + i }, /*#__PURE__*/React$1.createElement("input", _extends({}, props, { value: inputValue, checked: inputValue === value })), " ", title); }), error && error.map((error, i) => /*#__PURE__*/React$1.createElement("span", { className: "rjf-error-text", key: i }, error)), help_text && /*#__PURE__*/React$1.createElement("span", { className: "rjf-help-text" }, help_text)); } function FormSelectInput(_ref4) { let { label, help_text, error, value, options } = _ref4, props = _objectWithoutPropertiesLoose(_ref4, _excluded4); if (props.readOnly) props.disabled = true; if (!value && value !== false && value !== 0) value = ''; return /*#__PURE__*/React$1.createElement("div", null, /*#__PURE__*/React$1.createElement(Label, { label: label, required: props.required }), /*#__PURE__*/React$1.createElement("div", { className: error ? "rjf-input-group has-error" : "rjf-input-group" }, /*#__PURE__*/React$1.createElement("select", _extends({ value: value }, props), /*#__PURE__*/React$1.createElement("option", { disabled: true, value: "", key: '__placeholder' }, "Select..."), options.map((option, i) => { let title, inputValue; if (typeof option === 'object') { title = option.title || option.label; inputValue = option.value; } else { title = option; if (typeof title === 'boolean') title = capitalize$1(title.toString()); inputValue = option; } return /*#__PURE__*/React$1.createElement("option", { value: inputValue, key: title + '_' + inputValue + '_' + i }, title); })), error && error.map((error, i) => /*#__PURE__*/React$1.createElement("span", { className: "rjf-error-text", key: i }, error)), help_text && /*#__PURE__*/React$1.createElement("span", { className: "rjf-help-text" }, help_text))); } class FormMultiSelectInput extends React$1.Component { constructor(props) { super(props); this.handleChange = e => { let value = [...this.props.value]; let val = e.target.value; if (typeof val !== this.props.valueType) val = convertType(val, this.props.valueType); if (e.target.checked) { value.push(val); } else { value = value.filter(item => { return item !== val; }); } let event = { target: { type: this.props.type, value: value, name: this.props.name } }; this.props.onChange(event); }; this.showOptions = e => { if (!this.state.showOptions) this.setState({ showOptions: true }); }; this.hideOptions = e => { this.setState({ showOptions: false }); }; this.toggleOptions = e => { this.setState(state => ({ showOptions: !state.showOptions })); }; this.state = { showOptions: false }; this.optionsContainer = /*#__PURE__*/React$1.createRef(); this.input = /*#__PURE__*/React$1.createRef(); } render() { return /*#__PURE__*/React$1.createElement("div", { className: this.props.readOnly ? "rjf-multiselect-field readonly" : "rjf-multiselect-field" }, /*#__PURE__*/React$1.createElement(FormInput, { label: this.props.label, help_text: this.props.help_text, error: this.props.error }, /*#__PURE__*/React$1.createElement(FormMultiSelectInputField, { inputRef: this.input, onClick: this.toggleOptions, value: this.props.value, options: this.props.options, onChange: this.handleChange, disabled: this.props.readOnly, placeholder: this.props.placeholder })), this.state.showOptions && /*#__PURE__*/React$1.createElement(FormMultiSelectInputOptions, { options: this.props.options, value: this.props.value, hideOptions: this.hideOptions, onChange: this.handleChange, containerRef: this.optionsContainer, inputRef: this.input, disabled: this.props.readOnly, hasHelpText: (this.props.help_text || this.props.error) && 1 })); } } class FormMultiSelectInputField extends React$1.Component { constructor(...args) { super(...args); this.handleRemove = (e, index) => { e.stopPropagation(); // we create a fake event object for the onChange handler let event = { target: { value: this.props.value[index], checked: false } }; this.props.onChange(event); }; } render() { let valueTitleMap = choicesValueTitleMap(this.props.options || this.props.value); return /*#__PURE__*/React$1.createElement("div", { className: "rjf-multiselect-field-input", onClick: this.props.onClick, ref: this.props.inputRef, tabIndex: 0 }, this.props.value.length ? this.props.value.map((item, index) => /*#__PURE__*/React$1.createElement("span", { className: "rjf-multiselect-field-input-item", key: item + '_' + index }, /*#__PURE__*/React$1.createElement("span", null, valueTitleMap[item]), this.props.disabled || /*#__PURE__*/React$1.createElement("button", { title: "Remove", type: "button", onClick: e => this.handleRemove(e, index) }, "\xD7"))) : /*#__PURE__*/React$1.createElement("span", { className: "rjf-multiselect-field-input-placeholder" }, this.props.placeholder || 'Select...')); } } class FormMultiSelectInputOptions extends React$1.Component { constructor(...args) { super(...args); this.handleClickOutside = e => { if (this.props.containerRef.current && !this.props.containerRef.current.contains(e.target) && !this.props.inputRef.current.contains(e.target)) this.props.hideOptions(); }; } componentDidMount() { document.addEventListener('mousedown', this.handleClickOutside); } componentWillUnmount() { document.removeEventListener('mousedown', this.handleClickOutside); } render() { return /*#__PURE__*/React$1.createElement("div", { ref: this.props.containerRef }, /*#__PURE__*/React$1.createElement("div", { className: "rjf-multiselect-field-options-container", style: this.props.hasHelpText ? { marginTop: '-15px' } : {} }, this.props.options.map((option, i) => { let title, inputValue; if (typeof option === 'object') { title = option.title || option.label; inputValue = option.value; } else { title = option; if (typeof title === 'boolean') title = capitalize$1(title.toString()); inputValue = option; } let selected = this.props.value.indexOf(inputValue) > -1; let optionClassName = 'rjf-multiselect-field-option'; if (selected) optionClassName += ' selected'; if (this.props.disabled) optionClassName += ' disabled'; return /*#__PURE__*/React$1.createElement("div", { key: title + '_' + inputValue + '_' + i, className: optionClassName }, /*#__PURE__*/React$1.createElement("label", null, /*#__PURE__*/React$1.createElement("input", { type: "checkbox", onChange: this.props.onChange, value: inputValue, checked: selected, disabled: this.props.disabled }), " ", title)); }))); } } function dataURItoBlob(dataURI) { // Split metadata from data const splitted = dataURI.split(","); // Split params const params = splitted[0].split(";"); // Get mime-type from params const type = params[0].replace("data:", ""); // Filter the name property from params const properties = params.filter(param => { return param.split("=")[0] === "name"; }); // Look for the name and use unknown if no name property. let name; 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. const binary = atob(splitted[1]); const array = []; for (let i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } // Create the blob object const blob = new window.Blob([new Uint8Array(array)], { type }); return { blob, name }; } class FormFileInput extends React$1.Component { constructor(props) { super(props); this.getFileName = () => { if (!this.props.value) return ''; if (this.props.type === 'data-url') { return this.extractFileInfo(this.props.value).name; } else if (this.props.type === 'file-url') { return this.props.value; } else { return 'Unknown file'; } }; this.extractFileInfo = dataURL => { const { blob, name } = dataURItoBlob(dataURL); return { name: name, size: blob.size, type: blob.type }; }; this.addNameToDataURL = (dataURL, name) => { return dataURL.replace(';base64', ';name=' + encodeURIComponent(name) + ';base64'); }; this.handleChange = e => { if (this.props.type === 'data-url') { let file = e.target.files[0]; let fileName = file.name; let reader = new FileReader(); reader.onload = () => { // this.setState({src: reader.result}); // we create a fake event object let event = { target: { type: 'text', value: this.addNameToDataURL(reader.result, fileName), name: this.props.name } }; this.props.onChange(event); }; reader.readAsDataURL(file); } else if (this.props.type === 'file-url') { let endpoint = this.props.handler || this.context.fileHandler; if (!endpoint) { console.error("Error: fileHandler option need to be passed " + "while initializing editor for enabling file uploads."); alert("Files couldn't be uploaded."); return; } this.setState({ loading: true }); let formData = new FormData(); for (let key in this.context.fileHandlerArgs) { if (this.context.fileHandlerArgs.hasOwnProperty(key)) formData.append(key, this.context.fileHandlerArgs[key]); } formData.append('coords', getCoordsFromName(this.props.name)); formData.append('file', e.target.files[0]); fetch(endpoint, { method: 'POST', headers: { 'X-CSRFToken': getCsrfCookie() }, body: formData }).then(response => response.json()).then(result => { // we create a fake event object let event = { target: { type: 'text', value: result.value, name: this.props.name } }; this.props.onChange(event); this.setState({ loading: false }); }).catch(error => { alert('Something went wrong while uploading file'); console.error('Error:', error); this.setState({ loading: false }); }); } }; this.showFileBrowser = () => { this.inputRef.current.click(); }; this.clearFile = () => { if (window.confirm('Do you want to remove this file?')) { let event = { target: { type: 'text', value: '', name: this.props.name } }; this.props.onChange(event); if (this.inputRef.current) this.inputRef.current.value = ''; } }; this.state = { value: props.value, fileName: this.getFileName(), loading: false }; this.inputRef = /*#__PURE__*/React$1.createRef(); } componentDidUpdate(prevProps, prevState) { if (this.props.value !== prevProps.va