UNPKG

@rjsf/core

Version:

A simple React component capable of building HTML forms out of a JSON schema.

1,382 lines (1,374 loc) 144 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { default: () => src_default, getDefaultRegistry: () => getDefaultRegistry, withTheme: () => withTheme }); module.exports = __toCommonJS(src_exports); // src/components/Form.tsx var import_react17 = require("react"); var import_utils39 = require("@rjsf/utils"); var import_forEach = __toESM(require("lodash/forEach")); var import_get4 = __toESM(require("lodash/get")); var import_isEmpty2 = __toESM(require("lodash/isEmpty")); var import_isNil = __toESM(require("lodash/isNil")); var import_pick = __toESM(require("lodash/pick")); var import_toPath = __toESM(require("lodash/toPath")); // src/getDefaultRegistry.ts var import_utils38 = require("@rjsf/utils"); // src/components/fields/ArrayField.tsx var import_react = require("react"); var import_utils = require("@rjsf/utils"); var import_cloneDeep = __toESM(require("lodash/cloneDeep")); var import_get = __toESM(require("lodash/get")); var import_isObject = __toESM(require("lodash/isObject")); var import_set = __toESM(require("lodash/set")); var import_nanoid = require("nanoid"); var import_jsx_runtime = require("react/jsx-runtime"); function generateRowId() { return (0, import_nanoid.nanoid)(); } function generateKeyedFormData(formData) { return !Array.isArray(formData) ? [] : formData.map((item) => { return { key: generateRowId(), item }; }); } function keyedToPlainFormData(keyedFormData) { if (Array.isArray(keyedFormData)) { return keyedFormData.map((keyedItem) => keyedItem.item); } return []; } var ArrayField = class extends import_react.Component { /** Constructs an `ArrayField` from the `props`, generating the initial keyed data from the `formData` * * @param props - The `FieldProps` for this template */ constructor(props) { super(props); const { formData = [] } = props; const keyedFormData = generateKeyedFormData(formData); this.state = { keyedFormData, updatedKeyedFormData: false }; } /** React lifecycle method that is called when the props are about to change allowing the state to be updated. It * regenerates the keyed form data and returns it * * @param nextProps - The next set of props data * @param prevState - The previous set of state data */ static getDerivedStateFromProps(nextProps, prevState) { if (prevState.updatedKeyedFormData) { return { updatedKeyedFormData: false }; } const nextFormData = Array.isArray(nextProps.formData) ? nextProps.formData : []; const previousKeyedFormData = prevState.keyedFormData || []; const newKeyedFormData = nextFormData.length === previousKeyedFormData.length ? previousKeyedFormData.map((previousKeyedFormDatum, index) => { return { key: previousKeyedFormDatum.key, item: nextFormData[index] }; }) : generateKeyedFormData(nextFormData); return { keyedFormData: newKeyedFormData }; } /** Returns the appropriate title for an item by getting first the title from the schema.items, then falling back to * the description from the schema.items, and finally the string "Item" */ get itemTitle() { const { schema, registry } = this.props; const { translateString } = registry; return (0, import_get.default)( schema, [import_utils.ITEMS_KEY, "title"], (0, import_get.default)(schema, [import_utils.ITEMS_KEY, "description"], translateString(import_utils.TranslatableString.ArrayItemTitle)) ); } /** Determines whether the item described in the schema is always required, which is determined by whether any item * may be null. * * @param itemSchema - The schema for the item * @return - True if the item schema type does not contain the "null" type */ isItemRequired(itemSchema) { if (Array.isArray(itemSchema.type)) { return !itemSchema.type.includes("null"); } return itemSchema.type !== "null"; } /** Determines whether more items can be added to the array. If the uiSchema indicates the array doesn't allow adding * then false is returned. Otherwise, if the schema indicates that there are a maximum number of items and the * `formData` matches that value, then false is returned, otherwise true is returned. * * @param formItems - The list of items in the form * @returns - True if the item is addable otherwise false */ canAddItem(formItems) { const { schema, uiSchema, registry } = this.props; let { addable } = (0, import_utils.getUiOptions)(uiSchema, registry.globalUiOptions); if (addable !== false) { if (schema.maxItems !== void 0) { addable = formItems.length < schema.maxItems; } else { addable = true; } } return addable; } /** Returns the default form information for an item based on the schema for that item. Deals with the possibility * that the schema is fixed and allows additional items. */ _getNewFormDataRow = () => { const { schema, registry } = this.props; const { schemaUtils } = registry; let itemSchema = schema.items; if ((0, import_utils.isFixedItems)(schema) && (0, import_utils.allowAdditionalItems)(schema)) { itemSchema = schema.additionalItems; } return schemaUtils.getDefaultFormState(itemSchema); }; /** Callback handler for when the user clicks on the add or add at index buttons. Creates a new row of keyed form data * either at the end of the list (when index is not specified) or inserted at the `index` when it is, adding it into * the state, and then returning `onChange()` with the plain form data converted from the keyed data * * @param event - The event for the click * @param [index] - The optional index at which to add the new data */ _handleAddClick(event, index) { if (event) { event.preventDefault(); } const { onChange, errorSchema } = this.props; const { keyedFormData } = this.state; let newErrorSchema; if (errorSchema) { newErrorSchema = {}; for (const idx in errorSchema) { const i = parseInt(idx); if (index === void 0 || i < index) { (0, import_set.default)(newErrorSchema, [i], errorSchema[idx]); } else if (i >= index) { (0, import_set.default)(newErrorSchema, [i + 1], errorSchema[idx]); } } } const newKeyedFormDataRow = { key: generateRowId(), item: this._getNewFormDataRow() }; const newKeyedFormData = [...keyedFormData]; if (index !== void 0) { newKeyedFormData.splice(index, 0, newKeyedFormDataRow); } else { newKeyedFormData.push(newKeyedFormDataRow); } this.setState( { keyedFormData: newKeyedFormData, updatedKeyedFormData: true }, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema) ); } /** Callback handler for when the user clicks on the add button. Creates a new row of keyed form data at the end of * the list, adding it into the state, and then returning `onChange()` with the plain form data converted from the * keyed data * * @param event - The event for the click */ onAddClick = (event) => { this._handleAddClick(event); }; /** Callback handler for when the user clicks on the add button on an existing array element. Creates a new row of * keyed form data inserted at the `index`, adding it into the state, and then returning `onChange()` with the plain * form data converted from the keyed data * * @param index - The index at which the add button is clicked */ onAddIndexClick = (index) => { return (event) => { this._handleAddClick(event, index); }; }; /** Callback handler for when the user clicks on the copy button on an existing array element. Clones the row of * keyed form data at the `index` into the next position in the state, and then returning `onChange()` with the plain * form data converted from the keyed data * * @param index - The index at which the copy button is clicked */ onCopyIndexClick = (index) => { return (event) => { if (event) { event.preventDefault(); } const { onChange, errorSchema } = this.props; const { keyedFormData } = this.state; let newErrorSchema; if (errorSchema) { newErrorSchema = {}; for (const idx in errorSchema) { const i = parseInt(idx); if (i <= index) { (0, import_set.default)(newErrorSchema, [i], errorSchema[idx]); } else if (i > index) { (0, import_set.default)(newErrorSchema, [i + 1], errorSchema[idx]); } } } const newKeyedFormDataRow = { key: generateRowId(), item: (0, import_cloneDeep.default)(keyedFormData[index].item) }; const newKeyedFormData = [...keyedFormData]; if (index !== void 0) { newKeyedFormData.splice(index + 1, 0, newKeyedFormDataRow); } else { newKeyedFormData.push(newKeyedFormDataRow); } this.setState( { keyedFormData: newKeyedFormData, updatedKeyedFormData: true }, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema) ); }; }; /** Callback handler for when the user clicks on the remove button on an existing array element. Removes the row of * keyed form data at the `index` in the state, and then returning `onChange()` with the plain form data converted * from the keyed data * * @param index - The index at which the remove button is clicked */ onDropIndexClick = (index) => { return (event) => { if (event) { event.preventDefault(); } const { onChange, errorSchema } = this.props; const { keyedFormData } = this.state; let newErrorSchema; if (errorSchema) { newErrorSchema = {}; for (const idx in errorSchema) { const i = parseInt(idx); if (i < index) { (0, import_set.default)(newErrorSchema, [i], errorSchema[idx]); } else if (i > index) { (0, import_set.default)(newErrorSchema, [i - 1], errorSchema[idx]); } } } const newKeyedFormData = keyedFormData.filter((_, i) => i !== index); this.setState( { keyedFormData: newKeyedFormData, updatedKeyedFormData: true }, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema) ); }; }; /** Callback handler for when the user clicks on one of the move item buttons on an existing array element. Moves the * row of keyed form data at the `index` to the `newIndex` in the state, and then returning `onChange()` with the * plain form data converted from the keyed data * * @param index - The index of the item to move * @param newIndex - The index to where the item is to be moved */ onReorderClick = (index, newIndex) => { return (event) => { if (event) { event.preventDefault(); event.currentTarget.blur(); } const { onChange, errorSchema } = this.props; let newErrorSchema; if (errorSchema) { newErrorSchema = {}; for (const idx in errorSchema) { const i = parseInt(idx); if (i == index) { (0, import_set.default)(newErrorSchema, [newIndex], errorSchema[index]); } else if (i == newIndex) { (0, import_set.default)(newErrorSchema, [index], errorSchema[newIndex]); } else { (0, import_set.default)(newErrorSchema, [idx], errorSchema[i]); } } } const { keyedFormData } = this.state; function reOrderArray() { const _newKeyedFormData = keyedFormData.slice(); _newKeyedFormData.splice(index, 1); _newKeyedFormData.splice(newIndex, 0, keyedFormData[index]); return _newKeyedFormData; } const newKeyedFormData = reOrderArray(); this.setState( { keyedFormData: newKeyedFormData }, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema) ); }; }; /** Callback handler used to deal with changing the value of the data in the array at the `index`. Calls the * `onChange` callback with the updated form data * * @param index - The index of the item being changed */ onChangeForIndex = (index) => { return (value, newErrorSchema, id) => { const { formData, onChange, errorSchema } = this.props; const arrayData = Array.isArray(formData) ? formData : []; const newFormData = arrayData.map((item, i) => { const jsonValue = typeof value === "undefined" ? null : value; return index === i ? jsonValue : item; }); onChange( newFormData, errorSchema && errorSchema && { ...errorSchema, [index]: newErrorSchema }, id ); }; }; /** Callback handler used to change the value for a checkbox */ onSelectChange = (value) => { const { onChange, idSchema } = this.props; onChange(value, void 0, idSchema && idSchema.$id); }; /** Renders the `ArrayField` depending on the specific needs of the schema and uischema elements */ render() { const { schema, uiSchema, idSchema, registry } = this.props; const { schemaUtils, translateString } = registry; if (!(import_utils.ITEMS_KEY in schema)) { const uiOptions = (0, import_utils.getUiOptions)(uiSchema); const UnsupportedFieldTemplate = (0, import_utils.getTemplate)( "UnsupportedFieldTemplate", registry, uiOptions ); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( UnsupportedFieldTemplate, { schema, idSchema, reason: translateString(import_utils.TranslatableString.MissingItems), registry } ); } if (schemaUtils.isMultiSelect(schema)) { return this.renderMultiSelect(); } if ((0, import_utils.isCustomWidget)(uiSchema)) { return this.renderCustomWidget(); } if ((0, import_utils.isFixedItems)(schema)) { return this.renderFixedArray(); } if (schemaUtils.isFilesArray(schema, uiSchema)) { return this.renderFiles(); } return this.renderNormalArray(); } /** Renders a normal array without any limitations of length */ renderNormalArray() { const { schema, uiSchema = {}, errorSchema, idSchema, name, title, disabled = false, readonly = false, autofocus = false, required = false, registry, onBlur, onFocus, idPrefix, idSeparator = "_", rawErrors } = this.props; const { keyedFormData } = this.state; const fieldTitle = schema.title || title || name; const { schemaUtils, formContext } = registry; const uiOptions = (0, import_utils.getUiOptions)(uiSchema); const _schemaItems = (0, import_isObject.default)(schema.items) ? schema.items : {}; const itemsSchema = schemaUtils.retrieveSchema(_schemaItems); const formData = keyedToPlainFormData(this.state.keyedFormData); const canAdd = this.canAddItem(formData); const arrayProps = { canAdd, items: keyedFormData.map((keyedItem, index) => { const { key, item } = keyedItem; const itemCast = item; const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast); const itemErrorSchema = errorSchema ? errorSchema[index] : void 0; const itemIdPrefix = idSchema.$id + idSeparator + index; const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator); return this.renderArrayFieldItem({ key, index, name: name && `${name}-${index}`, title: fieldTitle ? `${fieldTitle}-${index + 1}` : void 0, canAdd, canMoveUp: index > 0, canMoveDown: index < formData.length - 1, itemSchema, itemIdSchema, itemErrorSchema, itemData: itemCast, itemUiSchema: uiSchema.items, autofocus: autofocus && index === 0, onBlur, onFocus, rawErrors, totalItems: keyedFormData.length }); }), className: `field field-array field-array-of-${itemsSchema.type}`, disabled, idSchema, uiSchema, onAddClick: this.onAddClick, readonly, required, schema, title: fieldTitle, formContext, formData, rawErrors, registry }; const Template = (0, import_utils.getTemplate)("ArrayFieldTemplate", registry, uiOptions); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Template, { ...arrayProps }); } /** Renders an array using the custom widget provided by the user in the `uiSchema` */ renderCustomWidget() { const { schema, idSchema, uiSchema, disabled = false, readonly = false, autofocus = false, required = false, hideError, placeholder, onBlur, onFocus, formData: items = [], registry, rawErrors, name } = this.props; const { widgets: widgets2, formContext, globalUiOptions, schemaUtils } = registry; const { widget, title: uiTitle, ...options } = (0, import_utils.getUiOptions)(uiSchema, globalUiOptions); const Widget = (0, import_utils.getWidget)(schema, widget, widgets2); const label = uiTitle ?? schema.title ?? name; const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( Widget, { id: idSchema.$id, name, multiple: true, onChange: this.onSelectChange, onBlur, onFocus, options, schema, uiSchema, registry, value: items, disabled, readonly, hideError, required, label, hideLabel: !displayLabel, placeholder, formContext, autofocus, rawErrors } ); } /** Renders an array as a set of checkboxes */ renderMultiSelect() { const { schema, idSchema, uiSchema, formData: items = [], disabled = false, readonly = false, autofocus = false, required = false, placeholder, onBlur, onFocus, registry, rawErrors, name } = this.props; const { widgets: widgets2, schemaUtils, formContext, globalUiOptions } = registry; const itemsSchema = schemaUtils.retrieveSchema(schema.items, items); const enumOptions = (0, import_utils.optionsList)(itemsSchema, uiSchema); const { widget = "select", title: uiTitle, ...options } = (0, import_utils.getUiOptions)(uiSchema, globalUiOptions); const Widget = (0, import_utils.getWidget)(schema, widget, widgets2); const label = uiTitle ?? schema.title ?? name; const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( Widget, { id: idSchema.$id, name, multiple: true, onChange: this.onSelectChange, onBlur, onFocus, options: { ...options, enumOptions }, schema, uiSchema, registry, value: items, disabled, readonly, required, label, hideLabel: !displayLabel, placeholder, formContext, autofocus, rawErrors } ); } /** Renders an array of files using the `FileWidget` */ renderFiles() { const { schema, uiSchema, idSchema, name, disabled = false, readonly = false, autofocus = false, required = false, onBlur, onFocus, registry, formData: items = [], rawErrors } = this.props; const { widgets: widgets2, formContext, globalUiOptions, schemaUtils } = registry; const { widget = "files", title: uiTitle, ...options } = (0, import_utils.getUiOptions)(uiSchema, globalUiOptions); const Widget = (0, import_utils.getWidget)(schema, widget, widgets2); const label = uiTitle ?? schema.title ?? name; const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( Widget, { options, id: idSchema.$id, name, multiple: true, onChange: this.onSelectChange, onBlur, onFocus, schema, uiSchema, value: items, disabled, readonly, required, registry, formContext, autofocus, rawErrors, label, hideLabel: !displayLabel } ); } /** Renders an array that has a maximum limit of items */ renderFixedArray() { const { schema, uiSchema = {}, formData = [], errorSchema, idPrefix, idSeparator = "_", idSchema, name, title, disabled = false, readonly = false, autofocus = false, required = false, registry, onBlur, onFocus, rawErrors } = this.props; const { keyedFormData } = this.state; let { formData: items = [] } = this.props; const fieldTitle = schema.title || title || name; const uiOptions = (0, import_utils.getUiOptions)(uiSchema); const { schemaUtils, formContext } = registry; const _schemaItems = (0, import_isObject.default)(schema.items) ? schema.items : []; const itemSchemas = _schemaItems.map( (item, index) => schemaUtils.retrieveSchema(item, formData[index]) ); const additionalSchema = (0, import_isObject.default)(schema.additionalItems) ? schemaUtils.retrieveSchema(schema.additionalItems, formData) : null; if (!items || items.length < itemSchemas.length) { items = items || []; items = items.concat(new Array(itemSchemas.length - items.length)); } const canAdd = this.canAddItem(items) && !!additionalSchema; const arrayProps = { canAdd, className: "field field-array field-array-fixed-items", disabled, idSchema, formData, items: keyedFormData.map((keyedItem, index) => { const { key, item } = keyedItem; const itemCast = item; const additional = index >= itemSchemas.length; const itemSchema = (additional && (0, import_isObject.default)(schema.additionalItems) ? schemaUtils.retrieveSchema(schema.additionalItems, itemCast) : itemSchemas[index]) || {}; const itemIdPrefix = idSchema.$id + idSeparator + index; const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator); const itemUiSchema = additional ? uiSchema.additionalItems || {} : Array.isArray(uiSchema.items) ? uiSchema.items[index] : uiSchema.items || {}; const itemErrorSchema = errorSchema ? errorSchema[index] : void 0; return this.renderArrayFieldItem({ key, index, name: name && `${name}-${index}`, title: fieldTitle ? `${fieldTitle}-${index + 1}` : void 0, canAdd, canRemove: additional, canMoveUp: index >= itemSchemas.length + 1, canMoveDown: additional && index < items.length - 1, itemSchema, itemData: itemCast, itemUiSchema, itemIdSchema, itemErrorSchema, autofocus: autofocus && index === 0, onBlur, onFocus, rawErrors, totalItems: keyedFormData.length }); }), onAddClick: this.onAddClick, readonly, required, registry, schema, uiSchema, title: fieldTitle, formContext, errorSchema, rawErrors }; const Template = (0, import_utils.getTemplate)("ArrayFieldTemplate", registry, uiOptions); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Template, { ...arrayProps }); } /** Renders the individual array item using a `SchemaField` along with the additional properties required to be send * back to the `ArrayFieldItemTemplate`. * * @param props - The props for the individual array item to be rendered */ renderArrayFieldItem(props) { const { key, index, name, canAdd, canRemove = true, canMoveUp, canMoveDown, itemSchema, itemData, itemUiSchema, itemIdSchema, itemErrorSchema, autofocus, onBlur, onFocus, rawErrors, totalItems, title } = props; const { disabled, hideError, idPrefix, idSeparator, readonly, uiSchema, registry, formContext } = this.props; const { fields: { ArraySchemaField, SchemaField: SchemaField2 }, globalUiOptions } = registry; const ItemSchemaField = ArraySchemaField || SchemaField2; const { orderable = true, removable = true, copyable = false } = (0, import_utils.getUiOptions)(uiSchema, globalUiOptions); const has2 = { moveUp: orderable && canMoveUp, moveDown: orderable && canMoveDown, copy: copyable && canAdd, remove: removable && canRemove, toolbar: false }; has2.toolbar = Object.keys(has2).some((key2) => has2[key2]); return { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( ItemSchemaField, { name, title, index, schema: itemSchema, uiSchema: itemUiSchema, formData: itemData, formContext, errorSchema: itemErrorSchema, idPrefix, idSeparator, idSchema: itemIdSchema, required: this.isItemRequired(itemSchema), onChange: this.onChangeForIndex(index), onBlur, onFocus, registry, disabled, readonly, hideError, autofocus, rawErrors } ), className: "array-item", disabled, canAdd, hasCopy: has2.copy, hasToolbar: has2.toolbar, hasMoveUp: has2.moveUp, hasMoveDown: has2.moveDown, hasRemove: has2.remove, index, totalItems, key, onAddIndexClick: this.onAddIndexClick, onCopyIndexClick: this.onCopyIndexClick, onDropIndexClick: this.onDropIndexClick, onReorderClick: this.onReorderClick, readonly, registry, schema: itemSchema, uiSchema: itemUiSchema }; } }; var ArrayField_default = ArrayField; // src/components/fields/BooleanField.tsx var import_utils2 = require("@rjsf/utils"); var import_isObject2 = __toESM(require("lodash/isObject")); var import_jsx_runtime2 = require("react/jsx-runtime"); function BooleanField(props) { const { schema, name, uiSchema, idSchema, formData, registry, required, disabled, readonly, hideError, autofocus, title, onChange, onFocus, onBlur, rawErrors } = props; const { title: schemaTitle } = schema; const { widgets: widgets2, formContext, translateString, globalUiOptions } = registry; const { widget = "checkbox", title: uiTitle, // Unlike the other fields, don't use `getDisplayLabel()` since it always returns false for the boolean type label: displayLabel = true, ...options } = (0, import_utils2.getUiOptions)(uiSchema, globalUiOptions); const Widget = (0, import_utils2.getWidget)(schema, widget, widgets2); const yes = translateString(import_utils2.TranslatableString.YesLabel); const no = translateString(import_utils2.TranslatableString.NoLabel); let enumOptions; const label = uiTitle ?? schemaTitle ?? title ?? name; if (Array.isArray(schema.oneOf)) { enumOptions = (0, import_utils2.optionsList)( { oneOf: schema.oneOf.map((option) => { if ((0, import_isObject2.default)(option)) { return { ...option, title: option.title || (option.const === true ? yes : no) }; } return void 0; }).filter((o) => o) // cast away the error that typescript can't grok is fixed }, uiSchema ); } else { const schemaWithEnumNames = schema; const enums = schema.enum ?? [true, false]; if (!schemaWithEnumNames.enumNames && enums.length === 2 && enums.every((v) => typeof v === "boolean")) { enumOptions = [ { value: enums[0], label: enums[0] ? yes : no }, { value: enums[1], label: enums[1] ? yes : no } ]; } else { enumOptions = (0, import_utils2.optionsList)( { enum: enums, // NOTE: enumNames is deprecated, but still supported for now. enumNames: schemaWithEnumNames.enumNames }, uiSchema ); } } return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Widget, { options: { ...options, enumOptions }, schema, uiSchema, id: idSchema.$id, name, onChange, onFocus, onBlur, label, hideLabel: !displayLabel, value: formData, required, disabled, readonly, hideError, registry, formContext, autofocus, rawErrors } ); } var BooleanField_default = BooleanField; // src/components/fields/MultiSchemaField.tsx var import_react2 = require("react"); var import_get2 = __toESM(require("lodash/get")); var import_isEmpty = __toESM(require("lodash/isEmpty")); var import_omit = __toESM(require("lodash/omit")); var import_utils3 = require("@rjsf/utils"); var import_jsx_runtime3 = require("react/jsx-runtime"); var AnyOfField = class extends import_react2.Component { /** Constructs an `AnyOfField` with the given `props` to initialize the initially selected option in state * * @param props - The `FieldProps` for this template */ constructor(props) { super(props); const { formData, options, registry: { schemaUtils } } = this.props; const retrievedOptions = options.map((opt) => schemaUtils.retrieveSchema(opt, formData)); this.state = { retrievedOptions, selectedOption: this.getMatchingOption(0, formData, retrievedOptions) }; } /** React lifecycle method that is called when the props and/or state for this component is updated. It recomputes the * currently selected option based on the overall `formData` * * @param prevProps - The previous `FieldProps` for this template * @param prevState - The previous `AnyOfFieldState` for this template */ componentDidUpdate(prevProps, prevState) { const { formData, options, idSchema } = this.props; const { selectedOption } = this.state; let newState = this.state; if (!(0, import_utils3.deepEquals)(prevProps.options, options)) { const { registry: { schemaUtils } } = this.props; const retrievedOptions = options.map((opt) => schemaUtils.retrieveSchema(opt, formData)); newState = { selectedOption, retrievedOptions }; } if (!(0, import_utils3.deepEquals)(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) { const { retrievedOptions } = newState; const matchingOption = this.getMatchingOption(selectedOption, formData, retrievedOptions); if (prevState && matchingOption !== selectedOption) { newState = { selectedOption: matchingOption, retrievedOptions }; } } if (newState !== this.state) { this.setState(newState); } } /** Determines the best matching option for the given `formData` and `options`. * * @param formData - The new formData * @param options - The list of options to choose from * @return - The index of the `option` that best matches the `formData` */ getMatchingOption(selectedOption, formData, options) { const { schema, registry: { schemaUtils } } = this.props; const discriminator = (0, import_utils3.getDiscriminatorFieldFromSchema)(schema); const option = schemaUtils.getClosestMatchingOption(formData, options, selectedOption, discriminator); return option; } /** Callback handler to remember what the currently selected option is. In addition to that the `formData` is updated * to remove properties that are not part of the newly selected option schema, and then the updated data is passed to * the `onChange` handler. * * @param option - The new option value being selected */ onOptionChange = (option) => { const { selectedOption, retrievedOptions } = this.state; const { formData, onChange, registry } = this.props; const { schemaUtils } = registry; const intOption = option !== void 0 ? parseInt(option, 10) : -1; if (intOption === selectedOption) { return; } const newOption = intOption >= 0 ? retrievedOptions[intOption] : void 0; const oldOption = selectedOption >= 0 ? retrievedOptions[selectedOption] : void 0; let newFormData = schemaUtils.sanitizeDataForNewSchema(newOption, oldOption, formData); if (newOption) { newFormData = schemaUtils.getDefaultFormState(newOption, newFormData, "excludeObjectChildren"); } this.setState({ selectedOption: intOption }, () => { onChange(newFormData, void 0, this.getFieldId()); }); }; getFieldId() { const { idSchema, schema } = this.props; return `${idSchema.$id}${schema.oneOf ? "__oneof_select" : "__anyof_select"}`; } /** Renders the `AnyOfField` selector along with a `SchemaField` for the value of the `formData` */ render() { const { name, disabled = false, errorSchema = {}, formContext, onBlur, onFocus, readonly, registry, schema, uiSchema } = this.props; const { widgets: widgets2, fields: fields2, translateString, globalUiOptions, schemaUtils } = registry; const { SchemaField: _SchemaField } = fields2; const { selectedOption, retrievedOptions } = this.state; const { widget = "select", placeholder, autofocus, autocomplete, title = schema.title, ...uiOptions } = (0, import_utils3.getUiOptions)(uiSchema, globalUiOptions); const Widget = (0, import_utils3.getWidget)({ type: "number" }, widget, widgets2); const rawErrors = (0, import_get2.default)(errorSchema, import_utils3.ERRORS_KEY, []); const fieldErrorSchema = (0, import_omit.default)(errorSchema, [import_utils3.ERRORS_KEY]); const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions); const option = selectedOption >= 0 ? retrievedOptions[selectedOption] || null : null; let optionSchema; if (option) { const { required } = schema; optionSchema = required ? (0, import_utils3.mergeSchemas)({ required }, option) : option; } let optionsUiSchema = []; if (import_utils3.ONE_OF_KEY in schema && uiSchema && import_utils3.ONE_OF_KEY in uiSchema) { if (Array.isArray(uiSchema[import_utils3.ONE_OF_KEY])) { optionsUiSchema = uiSchema[import_utils3.ONE_OF_KEY]; } else { console.warn(`uiSchema.oneOf is not an array for "${title || name}"`); } } else if (import_utils3.ANY_OF_KEY in schema && uiSchema && import_utils3.ANY_OF_KEY in uiSchema) { if (Array.isArray(uiSchema[import_utils3.ANY_OF_KEY])) { optionsUiSchema = uiSchema[import_utils3.ANY_OF_KEY]; } else { console.warn(`uiSchema.anyOf is not an array for "${title || name}"`); } } let optionUiSchema = uiSchema; if (selectedOption >= 0 && optionsUiSchema.length > selectedOption) { optionUiSchema = optionsUiSchema[selectedOption]; } const translateEnum = title ? import_utils3.TranslatableString.TitleOptionPrefix : import_utils3.TranslatableString.OptionPrefix; const translateParams = title ? [title] : []; const enumOptions = retrievedOptions.map((opt, index) => { const { title: uiTitle = opt.title } = (0, import_utils3.getUiOptions)(optionsUiSchema[index]); return { label: uiTitle || translateString(translateEnum, translateParams.concat(String(index + 1))), value: index }; }); return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "panel panel-default panel-body", children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "form-group", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( Widget, { id: this.getFieldId(), name: `${name}${schema.oneOf ? "__oneof_select" : "__anyof_select"}`, schema: { type: "number", default: 0 }, onChange: this.onOptionChange, onBlur, onFocus, disabled: disabled || (0, import_isEmpty.default)(enumOptions), multiple: false, rawErrors, errorSchema: fieldErrorSchema, value: selectedOption >= 0 ? selectedOption : void 0, options: { enumOptions, ...uiOptions }, registry, formContext, placeholder, autocomplete, autofocus, label: title ?? name, hideLabel: !displayLabel, readonly } ) }), optionSchema && optionSchema.type !== "null" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(_SchemaField, { ...this.props, schema: optionSchema, uiSchema: optionUiSchema }) ] }); } }; var MultiSchemaField_default = AnyOfField; // src/components/fields/NumberField.tsx var import_react3 = require("react"); var import_utils4 = require("@rjsf/utils"); var import_jsx_runtime4 = require("react/jsx-runtime"); var trailingCharMatcherWithPrefix = /\.([0-9]*0)*$/; var trailingCharMatcher = /[0.]0*$/; function NumberField(props) { const { registry, onChange, formData, value: initialValue } = props; const [lastValue, setLastValue] = (0, import_react3.useState)(initialValue); const { StringField: StringField2 } = registry.fields; let value = formData; const handleChange = (0, import_react3.useCallback)( (value2, errorSchema, id) => { setLastValue(value2); if (`${value2}`.charAt(0) === ".") { value2 = `0${value2}`; } const processed = typeof value2 === "string" && value2.match(trailingCharMatcherWithPrefix) ? (0, import_utils4.asNumber)(value2.replace(trailingCharMatcher, "")) : (0, import_utils4.asNumber)(value2); onChange(processed, errorSchema, id); }, [onChange] ); if (typeof lastValue === "string" && typeof value === "number") { const re = new RegExp(`^(${String(value).replace(".", "\\.")})?\\.?0*$`); if (lastValue.match(re)) { value = lastValue; } } return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StringField2, { ...props, formData: value, onChange: handleChange }); } var NumberField_default = NumberField; // src/components/fields/ObjectField.tsx var import_react4 = require("react"); var import_utils5 = require("@rjsf/utils"); var import_markdown_to_jsx = __toESM(require("markdown-to-jsx")); var import_get3 = __toESM(require("lodash/get")); var import_has = __toESM(require("lodash/has")); var import_isObject3 = __toESM(require("lodash/isObject")); var import_set2 = __toESM(require("lodash/set")); var import_unset = __toESM(require("lodash/unset")); var import_jsx_runtime5 = require("react/jsx-runtime"); var ObjectField = class extends import_react4.Component { /** Set up the initial state */ state = { wasPropertyKeyModified: false, additionalProperties: {} }; /** Returns a flag indicating whether the `name` field is required in the object schema * * @param name - The name of the field to check for required-ness * @returns - True if the field `name` is required, false otherwise */ isRequired(name) { const { schema } = this.props; return Array.isArray(schema.required) && schema.required.indexOf(name) !== -1; } /** Returns the `onPropertyChange` handler for the `name` field. Handles the special case where a user is attempting * to clear the data for a field added as an additional property. Calls the `onChange()` handler with the updated * formData. * * @param name - The name of the property * @param addedByAdditionalProperties - Flag indicating whether this property is an additional property * @returns - The onPropertyChange callback for the `name` property */ onPropertyChange = (name, addedByAdditionalProperties = false) => { return (value, newErrorSchema, id) => { const { formData, onChange, errorSchema } = this.props; if (value === void 0 && addedByAdditionalProperties) { value = ""; } const newFormData = { ...formData, [name]: value }; onChange( newFormData, errorSchema && errorSchema && { ...errorSchema, [name]: newErrorSchema }, id ); }; }; /** Returns a callback to handle the onDropPropertyClick event for the given `key` which removes the old `key` data * and calls the `onChange` callback with it * * @param key - The key for which the drop callback is desired * @returns - The drop property click callback */ onDropPropertyClick = (key) => { return (event) => { event.preventDefault(); const { onChange, formData } = this.props; const copiedFormData = { ...formData }; (0, import_unset.default)(copiedFormData, key); onChange(copiedFormData); }; }; /** Computes the next available key name from the `preferredKey`, indexing through the already existing keys until one * that is already not assigned is found. * * @param preferredKey - The preferred name of a new key * @param [formData] - The form data in which to check if the desired key already exists * @returns - The name of the next available key from `preferredKey` */ getAvailableKey = (preferredKey, formData) => { const { uiSchema, registry } = this.props; const { duplicateKeySuffixSeparator = "-" } = (0, import_utils5.getUiOptions)(uiSchema, registry.globalUiOptions); let index = 0; let newKey = preferredKey; while ((0, import_has.default)(formData, newKey)) { newKey = `${preferredKey}${duplicateKeySuffixSeparator}${++index}`; } return newKey; }; /** Returns a callback function that deals with the rename of a key for an additional property for a schema. That * callback will attempt to rename the key and move the existing data to that key, calling `onChange` when it does. * * @param oldValue - The old value of a field * @returns - The key change callback function */ onKeyChange = (oldValue) => { return (value, newErrorSchema) => { if (oldValue === value) { return; } const { formData, onChange, errorSchema } = this.props; value = this.getAvailableKey(value, formData); const newFormData = { ...formData }; const newKeys = { [oldValue]: value }; const keyValues = Object.keys(newFormData).map((key) => { const newKey = newKeys[key] || key; return { [newKey]: newFormData[key] }; }); const renamedObj = Object.assign({}, ...keyValues); this.setState({ wasPropertyKeyModified: true }); onChange( renamedObj, errorSchema && errorSchema && { ...errorSchema, [value]: newErrorSchema } ); }; }; /** Returns a default value to be used for a new additional schema property of the given `type` * * @param type - The type of the new additional schema property */ getDefaultValue(type) { const { registry: { translateString } } = this.props; switch (type) { case "array": return []; case "boolean": return false; case "null": return null; case "number": return 0; case "object": return {}; case "string": default: return translateString(import_utils5.TranslatableString.NewStringDefault); } } /** Handles the adding of a new additional property on the given `schema`. Calls the `onChange` callback once the new * default data for that field has been added to the formData. * * @param schema - The schema element to which the new property is being added */ handleAddClick = (schema) => () => { if (!schema.additionalProperties) { return; } const { formData, onChange, registry } = this.props; const newFormData = { ...formData }; let type = void 0; let constValue = void 0; let defaultValue = void 0; if ((0, import_isObject3.default)(schema.additionalProperties)) { type = schema.additionalProperties.type; constValue = schema.additionalProperties.const; defaultValue = schema.additionalProperties.default; let apSchema = schema.additionalProperties; if (import_utils5.REF_KEY in apSchema) { const { schemaUtils } = registry; apSchema = schemaUtils.retrieveSchema({ $ref: apSchema[import_utils5.REF_KEY] }, formData); type = apSchema.type; constValue = apSchema.const; defaultValue = apSchema.default; } if (!type && (import_utils5.ANY_OF_KEY in apSchema || import_utils5.ONE_OF_KEY in apSchema)) { type = "object"; } } const newKey = this.getAvailableKey("newKey", newFormData); const newValue = constValue ?? defaultValue ?? this.getDefaultValue(type); (0, import_set2.default)(newFormData, newKey, newValue); onChange(newFormData); }; /** Renders the `ObjectField` from the given props */ render() { const { schema: rawSchema, uiSchema = {}, formData, errorSchema, idSchema, name, required = false, disabled, readonly, hideError, idPrefix, idSeparator, onBlur, onFocus, registry, title } = this.props; const { fields: fields2, formContext, schemaUtils, translateString, globalUiOptions } = registry; const { SchemaField: SchemaField2 } = fields2; const schema = schemaUtils.retrieveSchema(rawSchema, formData); const uiOptions = (0, import_utils5.getUiOptions)(uiSchema, globalUiOptions); const { properties: schemaProperties = {} } = schema; const templateTitle = uiOptions.title ?? schema.title ?? title ?? name; const description = uiOptions.description ?? schema.description; let orderedProperties; try { const properties = Object.keys(schemaProperties); orderedProperties = (0, import_utils5.orderProperties)(properties, uiOptions.order); } catch (err) { return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "config-error", style: { color: "red" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_markdown_to_jsx.default, { options: { disableParsingRawHTML: true }, children: translateString(import_utils5.TranslatableString.InvalidObjectField, [name || "root", err.message]) }) }), /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { children: JSON.stringify(schema) }) ] }); } const Template = (0, import_utils5.getTemplate)("ObjectFieldTemplate", registry, uiOptions); const templateProps = { // getDisplayLabel() always returns false for object types, so just check the `uiOptions.label` title: uiOptions.label === false ? "" : templateTitle, description: uiOptions.label === false ? void 0 : description, properties: orderedProperties.map((name2) => { const addedByAdditionalProperties = (0, import_has.default)(schema, [import_utils5.PROPERTIES_KEY, name2, import_utils5.ADDITIONAL_PROPERTY_FLAG]); const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[name2]; const hidden = (0, import_utils5.getUiOptions)(fieldUiSchema).widget === "hidden"; const fieldIdSchema = (0, import_get3.default)(idSchema, [name2], {}); return { content: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( SchemaField2, { name: name2, required: this.isRequired(name2), schema: (0, import_get3.default)(schema, [i