diff --git a/dist/index.js b/dist/index.js
index ebbeefb..a335712 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -776,14 +776,14 @@ function Autocomplete(_ref) {
} else {
newValue = [v];
}
+ const e = {
+ target: {
+ name
+ }
+ };
if (setValue) {
setValue(newValue, e);
} else {
- const e = {
- target: {
- name
- }
- };
setValues(e, name, newValue, afterChange);
}
setIsOpen(false);
@@ -806,14 +806,14 @@ function Autocomplete(_ref) {
} else {
newValue = '';
}
+ const e = {
+ target: {
+ name
+ }
+ };
if (setValue) {
setValue(newValue, e);
} else {
- const e = {
- target: {
- name
- }
- };
setValues(e, name, newValue, afterChange);
}
focus();
@@ -873,14 +873,14 @@ function Autocomplete(_ref) {
};
const clear = () => {
const newValue = [];
+ const e = {
+ target: {
+ name
+ }
+ };
if (setValue) {
setValue(newValue, e);
} else {
- const e = {
- target: {
- name
- }
- };
setValues(e, name, newValue, afterChange);
}
setFilter('');
diff --git a/dist/index.js.map b/dist/index.js.map
index b7a176d..a387848 100644
--- a/dist/index.js.map
+++ b/dist/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sources":["../src/js/Alert.js","../src/js/Helpers/JsonApi.js","../src/js/Helpers/Api.js","../src/js/FormContext.js","../src/js/Error.js","../src/js/Helpers/Options.js","../src/js/Input/Autocomplete.js","../src/js/Input/Checkbox.js","../src/js/Input/CheckboxList.js","../src/js/Input/File.js","../src/js/ConditionalWrapper.js","../src/js/Input.js","../src/js/Input/Password.js","../src/js/Input/Radio.js","../src/js/Input/Search.js","../src/js/Input/Select.js","../src/js/Input/Textarea.js","../src/js/FieldInput.js","../src/js/ExportableInput.js","../src/js/Label.js","../src/js/Field.js","../src/js/FormosaContext.js","../src/js/FormInner.js","../src/js/Form.js","../src/js/FormAlert.js","../src/js/Spinner.js","../src/js/Toast.js","../src/js/ToastContainer.js","../src/js/FormContainer.js","../src/js/Submit.js"],"sourcesContent":["import PropTypes from 'prop-types';\nimport React from 'react'; // eslint-disable-line import/no-unresolved\n\nexport default function Alert({ className, children, type, ...otherProps }) {\n\tif (!children) {\n\t\treturn null;\n\t}\n\n\tlet alertClass = 'formosa-alert';\n\tif (type) {\n\t\talertClass += ` formosa-alert--${type}`;\n\t}\n\tif (className) {\n\t\talertClass += ` ${className}`;\n\t}\n\n\treturn (\n\t\t
\n\t\t\t{children}\n\t\t
\n\t);\n}\n\nAlert.propTypes = {\n\tclassName: PropTypes.string,\n\tchildren: PropTypes.node.isRequired,\n\ttype: PropTypes.string,\n};\n\nAlert.defaultProps = {\n\tclassName: '',\n\ttype: null,\n};\n","import get from 'get-value';\nimport set from 'set-value';\n\nconst findIncluded = (included, id, type, mainRecord) => {\n\tif (mainRecord && id === mainRecord.id && type === mainRecord.type) {\n\t\tconst output = {\n\t\t\tid: mainRecord.id,\n\t\t\ttype: mainRecord.type,\n\t\t};\n\t\tif (Object.prototype.hasOwnProperty.call(mainRecord, 'attributes')) {\n\t\t\toutput.attributes = mainRecord.attributes;\n\t\t}\n\t\tif (Object.prototype.hasOwnProperty.call(mainRecord, 'meta')) {\n\t\t\toutput.meta = mainRecord.meta;\n\t\t}\n\t\treturn output;\n\t}\n\treturn included.find((data) => (data.id === id && data.type === type));\n};\n\nconst deserializeSingle = (data, otherRows = [], included = [], mainRecord = null) => {\n\tif (!data) {\n\t\treturn data;\n\t}\n\tconst output = {\n\t\tid: data.id,\n\t\ttype: data.type,\n\t\t...data.attributes,\n\t};\n\n\tif (Object.prototype.hasOwnProperty.call(data, 'relationships')) {\n\t\tlet includedRecord;\n\t\tObject.keys(data.relationships).forEach((relationshipName) => {\n\t\t\toutput[relationshipName] = data.relationships[relationshipName].data;\n\t\t\tif (Array.isArray(output[relationshipName])) {\n\t\t\t\toutput[relationshipName].forEach((rel, i) => {\n\t\t\t\t\tincludedRecord = findIncluded(included, rel.id, rel.type, mainRecord);\n\t\t\t\t\tif (includedRecord) {\n\t\t\t\t\t\toutput[relationshipName][i] = deserializeSingle(includedRecord, otherRows, included, mainRecord);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tincludedRecord = findIncluded(otherRows, rel.id, rel.type, mainRecord);\n\t\t\t\t\t\tif (includedRecord) {\n\t\t\t\t\t\t\toutput[relationshipName][i] = deserializeSingle(includedRecord, otherRows, included, mainRecord);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else if (output[relationshipName] !== null) {\n\t\t\t\tincludedRecord = findIncluded(included, output[relationshipName].id, output[relationshipName].type, mainRecord);\n\t\t\t\tif (includedRecord) {\n\t\t\t\t\toutput[relationshipName] = deserializeSingle(includedRecord, otherRows, included, mainRecord);\n\t\t\t\t} else {\n\t\t\t\t\tincludedRecord = findIncluded(otherRows, output[relationshipName].id, output[relationshipName].type, mainRecord);\n\t\t\t\t\tif (includedRecord) {\n\t\t\t\t\t\toutput[relationshipName] = deserializeSingle(includedRecord, otherRows, included, mainRecord);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tif (Object.prototype.hasOwnProperty.call(data, 'meta')) {\n\t\toutput.meta = data.meta;\n\t}\n\n\treturn output;\n};\n\nexport const deserialize = (body) => {\n\tif (Array.isArray(body.data)) {\n\t\tconst output = [];\n\t\tbody.data.forEach((data) => {\n\t\t\toutput.push(deserializeSingle(data, body.data, body.included, null));\n\t\t});\n\n\t\tif (Object.prototype.hasOwnProperty.call(body, 'meta')) {\n\t\t\treturn { data: output, meta: body.meta };\n\t\t}\n\n\t\treturn output;\n\t}\n\treturn deserializeSingle(body.data, [], body.included, body.data);\n};\n\nconst cleanSingleRelationship = (values) => ({\n\tid: values.id,\n\ttype: values.type,\n});\n\nconst cleanRelationship = (values) => {\n\tif (Array.isArray(values)) {\n\t\treturn values.map((value) => cleanSingleRelationship(value));\n\t}\n\treturn cleanSingleRelationship(values);\n};\n\nconst getIncludedItemData = (rel, relName, childRelationshipNames, dirtyRelationships, relIndex = null) => {\n\tconst relData = {\n\t\tid: rel.id,\n\t\ttype: rel.type,\n\t\tattributes: {},\n\t\trelationships: {},\n\t};\n\n\tif (rel.id.startsWith('temp-')) {\n\t\t// This is a new record; include all attributes.\n\t\tObject.keys(rel).forEach((key) => {\n\t\t\tif (key !== 'id' && key !== 'type') {\n\t\t\t\tif (childRelationshipNames[relName].includes(key)) {\n\t\t\t\t\tconst childRel = {\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tid: rel[key].id,\n\t\t\t\t\t\t\ttype: rel[key].type,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tset(relData.relationships, key, childRel);\n\t\t\t\t} else {\n\t\t\t\t\tset(relData.attributes, key, rel[key]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t} else {\n\t\t// This is an existing record; include only the dirty attributes.\n\t\tObject.keys(rel).forEach((key) => {\n\t\t\tif (key !== 'id' && key !== 'type') {\n\t\t\t\tif (relIndex === null) {\n\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(dirtyRelationships[relName], key)) {\n\t\t\t\t\t\tset(relData.attributes, key, rel[key]);\n\t\t\t\t\t}\n\t\t\t\t} else if (Object.prototype.hasOwnProperty.call(dirtyRelationships[relName], relIndex)) {\n\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(dirtyRelationships[relName][relIndex], key)) {\n\t\t\t\t\t\tset(relData.attributes, key, rel[key]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tconst hasAttributes = Object.keys(relData.attributes).length > 0;\n\tif (!hasAttributes) {\n\t\tdelete relData.attributes;\n\t}\n\tconst hasRelationships = Object.keys(relData.relationships).length > 0;\n\tif (!hasRelationships) {\n\t\tdelete relData.relationships;\n\t}\n\n\tif (!hasAttributes && !hasRelationships) {\n\t\treturn null;\n\t}\n\n\treturn relData;\n};\n\nconst getIncluded = (data, dirtyKeys, relationshipNames) => {\n\tconst included = [];\n\n\tconst dirtyRelationships = {};\n\tdirtyKeys.forEach((key) => {\n\t\tconst currentKeys = [];\n\t\tkey.split('.').forEach((k) => {\n\t\t\tcurrentKeys.push(k);\n\t\t\tif (typeof get(dirtyRelationships, currentKeys.join('.')) === 'undefined') {\n\t\t\t\tset(dirtyRelationships, currentKeys.join('.'), {});\n\t\t\t}\n\t\t});\n\t});\n\n\tconst childRelationshipNames = {};\n\trelationshipNames.forEach((relName) => {\n\t\tchildRelationshipNames[relName] = [];\n\t\tif (relName.includes('.')) {\n\t\t\tconst keys = relName.split('.');\n\t\t\tchildRelationshipNames[keys.shift()].push(keys.join('.'));\n\t\t}\n\t});\n\n\tObject.keys(dirtyRelationships).forEach((relName) => {\n\t\tif (Object.prototype.hasOwnProperty.call(data.relationships, relName)) {\n\t\t\tlet relData;\n\t\t\tif (Array.isArray(data.relationships[relName].data)) {\n\t\t\t\tObject.keys(data.relationships[relName].data).forEach((relIndex) => {\n\t\t\t\t\tconst rel = data.relationships[relName].data[relIndex];\n\t\t\t\t\tif (rel) {\n\t\t\t\t\t\trelData = getIncludedItemData(rel, relName, childRelationshipNames, dirtyRelationships, relIndex);\n\t\t\t\t\t\tif (relData) {\n\t\t\t\t\t\t\tincluded.push(relData);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst rel = data.relationships[relName].data;\n\t\t\t\tif (rel) {\n\t\t\t\t\trelData = getIncludedItemData(rel, relName, childRelationshipNames, dirtyRelationships);\n\t\t\t\t\tif (relData) {\n\t\t\t\t\t\tincluded.push(relData);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\treturn included;\n};\n\nconst unset = (obj, key) => {\n\tif (Object.prototype.hasOwnProperty.call(obj, key)) {\n\t\treturn delete obj[key];\n\t}\n\n\tconst keys = key.split('.');\n\tconst lastKey = keys.pop();\n\tlet o = obj;\n\tkeys.forEach((k) => {\n\t\to = o[k];\n\t});\n\treturn delete o[lastKey];\n};\n\nconst appendToFormData = (obj, formData, prefix = '') => {\n\tObject.entries(obj).forEach((entry) => {\n\t\tconst [key, value] = entry;\n\t\tconst newKey = prefix ? `${prefix}[${key}]` : key;\n\t\tif (typeof value === 'object') {\n\t\t\tformData = appendToFormData(value, formData, newKey);\n\t\t} else {\n\t\t\tformData.append(newKey, JSON.stringify(value));\n\t\t}\n\t});\n\treturn formData;\n};\n\nexport const getBody = (\n\tmethod,\n\ttype,\n\tid,\n\tformState,\n\tdirtyKeys,\n\trelationshipNames,\n\tfilterBody,\n\tfilterValues\n) => {\n\tlet body = null;\n\n\tif (method === 'PUT' || method === 'POST') {\n\t\t// Initialize the basic body structure.\n\t\tconst data = {\n\t\t\ttype,\n\t\t\tattributes: {},\n\t\t\trelationships: {},\n\t\t\tmeta: {},\n\t\t};\n\t\tif (method === 'PUT' && id) {\n\t\t\tdata.id = id;\n\t\t}\n\n\t\t// Allow the app to modify values before they are sorted into sections.\n\t\tlet values = { ...formState.row };\n\t\tif (filterValues) {\n\t\t\tvalues = filterValues(values);\n\t\t}\n\n\t\t// Get keys for file fields.\n\t\tconst fileKeys = Object.keys(formState.files);\n\n\t\t// If this is an update request, only include keys for changed values.\n\t\t// Otherwise (this must be an add request), include keys for all values.\n\t\tconst keys = method === 'PUT' ? dirtyKeys : Object.keys(formState.row);\n\n\t\t// Sort each value into the proper section.\n\t\tkeys.forEach((key) => {\n\t\t\tconst firstPartOfKey = key.replace(/\\..+$/, '');\n\t\t\tif (relationshipNames.includes(firstPartOfKey)) {\n\t\t\t\tdata.relationships[firstPartOfKey] = {\n\t\t\t\t\tdata: get(values, firstPartOfKey),\n\t\t\t\t};\n\t\t\t} else if (relationshipNames.includes(key)) {\n\t\t\t\tdata.relationships[key] = {\n\t\t\t\t\tdata: get(values, firstPartOfKey),\n\t\t\t\t};\n\t\t\t} else if (key.startsWith('meta.')) {\n\t\t\t\tset(data, key, get(values, key));\n\t\t\t} else if (key === 'meta') {\n\t\t\t\tdata.meta = values.meta;\n\t\t\t} else if (fileKeys.includes(key)) {\n\t\t\t\tunset(data.attributes, key);\n\t\t\t} else {\n\t\t\t\tset(data.attributes, key, get(values, key));\n\t\t\t}\n\t\t});\n\n\t\tbody = { data };\n\n\t\tconst included = getIncluded(data, dirtyKeys, relationshipNames);\n\t\tif (included.length > 0) {\n\t\t\tbody.included = included;\n\t\t}\n\n\t\tObject.keys(data.relationships).forEach((relationshipName) => {\n\t\t\tif (typeof data.relationships[relationshipName].data === 'string') {\n\t\t\t\tif (data.relationships[relationshipName].data === '') {\n\t\t\t\t\tdata.relationships[relationshipName].data = null;\n\t\t\t\t} else {\n\t\t\t\t\tdata.relationships[relationshipName].data = JSON.parse(data.relationships[relationshipName].data);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (data.relationships[relationshipName].data) {\n\t\t\t\tdata.relationships[relationshipName].data = cleanRelationship(data.relationships[relationshipName].data);\n\t\t\t}\n\t\t});\n\n\t\t// Allow the app to modify values after they have been sorted into sections.\n\t\tif (filterBody) {\n\t\t\tbody = filterBody(body, formState.row);\n\t\t}\n\n\t\t// Remove any sections with no values.\n\t\tif (Object.keys(data.attributes).length <= 0) {\n\t\t\tdelete data.attributes;\n\t\t}\n\t\tif (Object.keys(data.meta).length <= 0) {\n\t\t\tdelete data.meta;\n\t\t}\n\t\tif (Object.keys(data.relationships).length <= 0) {\n\t\t\tdelete data.relationships;\n\t\t}\n\n\t\t// Handle file fields.\n\t\tconst filenames = fileKeys.filter((filename) => (formState.files[filename] !== false));\n\t\tif (filenames.length > 0) {\n\t\t\tconst formData = appendToFormData(body, new FormData());\n\t\t\tformData.append('meta[files]', JSON.stringify(filenames));\n\n\t\t\tfilenames.forEach((filename) => {\n\t\t\t\tformData.append(filename, formState.files[filename]);\n\t\t\t});\n\n\t\t\tbody = formData;\n\t\t}\n\t}\n\n\treturn body;\n};\n","import { deserialize } from './JsonApi';\nimport { trackPromise } from 'react-promise-tracker';\n\nexport default class Api {\n\tstatic instance() {\n\t\tconst responses = {};\n\t\treturn (url, showSpinner) => {\n\t\t\tif (!Object.prototype.hasOwnProperty.call(responses, url)) {\n\t\t\t\tresponses[url] = Api.get(url, showSpinner);\n\t\t\t}\n\t\t\treturn responses[url];\n\t\t};\n\t}\n\n\tstatic get(url, showSpinner = true) {\n\t\treturn Api.request('GET', url, null, showSpinner);\n\t}\n\n\tstatic delete(url, showSpinner = true) {\n\t\treturn Api.request('DELETE', url, null, showSpinner);\n\t}\n\n\tstatic post(url, body, showSpinner = true) {\n\t\treturn Api.request('POST', url, body, showSpinner);\n\t}\n\n\tstatic put(url, body, showSpinner = true) {\n\t\treturn Api.request('PUT', url, body, showSpinner);\n\t}\n\n\tstatic request(method, url, body = null, showSpinner = true) {\n\t\tconst options = {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\tAccept: 'application/json',\n\t\t\t\t'X-Requested-With': 'XMLHttpRequest',\n\t\t\t},\n\t\t};\n\t\tif (typeof body === 'string') {\n\t\t\toptions.headers['Content-Type'] = 'application/json';\n\t\t}\n\t\tif (Api.getToken()) {\n\t\t\toptions.headers.Authorization = `Bearer ${Api.getToken()}`;\n\t\t}\n\t\tif (body) {\n\t\t\toptions.body = body;\n\t\t}\n\n\t\tlet fullUrl = url;\n\t\tif (process.env.REACT_APP_API_URL && !url.startsWith('http')) {\n\t\t\tfullUrl = `${process.env.REACT_APP_API_URL.replace(/\\/$/, '')}/${url.replace(/^\\//, '')}`;\n\t\t}\n\n\t\tconst event = new CustomEvent('formosaApiRequest', { cancelable: true, detail: { url: fullUrl, options } });\n\t\tif (!document.dispatchEvent(event)) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tconst promise = fetch(fullUrl, options)\n\t\t\t.then((response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn response.json()\n\t\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\t\t\t\t\tthrow { // eslint-disable-line no-throw-literal\n\t\t\t\t\t\t\t\t\terrors: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttitle: 'Unable to connect to the server. Please try again later.',\n\t\t\t\t\t\t\t\t\t\t\tstatus: '500',\n\t\t\t\t\t\t\t\t\t\t\tdetail: 'The server returned invalid JSON.',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((json) => {\n\t\t\t\t\t\t\tjson.status = response.status;\n\t\t\t\t\t\t\tthrow json;\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (response.status === 204) {\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\t\t\t\treturn response.json();\n\t\t\t})\n\t\t\t.then((json) => {\n\t\t\t\tif (Object.prototype.hasOwnProperty.call(json, 'data')) {\n\t\t\t\t\treturn deserialize(json);\n\t\t\t\t}\n\t\t\t\treturn json;\n\t\t\t});\n\n\t\treturn showSpinner ? trackPromise(promise) : promise;\n\t}\n\n\tstatic getToken() {\n\t\treturn window.FORMOSA_TOKEN;\n\t}\n\n\tstatic setToken(token) {\n\t\twindow.FORMOSA_TOKEN = token;\n\t}\n\n\tstatic deserialize(json) {\n\t\tif (Object.prototype.hasOwnProperty.call(json, 'data')) {\n\t\t\treturn deserialize(json);\n\t\t}\n\t\treturn json;\n\t}\n}\n","import React from 'react'; // eslint-disable-line import/no-unresolved\n\nexport default React.createContext(\n\t{\n\t\talertClass: '',\n\t\talertText: '',\n\t\terrors: {},\n\t\tfiles: {},\n\t\toriginalRow: {},\n\t\trow: {},\n\t\tresponse: null,\n\t\tsetRow: null,\n\t\ttoastClass: '',\n\t\ttoastText: '',\n\t\tuuid: null,\n\t}\n);\n","import React, { useContext } from 'react'; // eslint-disable-line import/no-unresolved\nimport FormContext from './FormContext';\nimport PropTypes from 'prop-types';\n\nexport default function Error({\n\tid,\n\tname,\n}) {\n\tconst { formState } = useContext(FormContext);\n\tconst hasError = formState && Object.prototype.hasOwnProperty.call(formState.errors, name);\n\n\tconst props = {};\n\tif (name) {\n\t\t// Used for matching the attribute name from the API to this error element.\n\t\tprops['data-name'] = name;\n\t}\n\tif (id || name) {\n\t\tprops.id = `${id || name}-error`;\n\t}\n\n\treturn (\n\t\t\n\t\t\t{hasError && formState.errors[name].map((e) => (
{e}
))}\n\t\t
\n\t);\n}\n\nError.propTypes = {\n\tid: PropTypes.string,\n\tname: PropTypes.string,\n};\n\nError.defaultProps = {\n\tid: null,\n\tname: '',\n};\n","import get from 'get-value';\n\nexport const normalizeOptions = (options, labelKey, valueKey = null) => {\n\tif (!options) {\n\t\treturn [];\n\t}\n\n\tconst output = [];\n\tif (Array.isArray(options)) {\n\t\toptions.forEach((option) => {\n\t\t\tif (typeof option === 'string') {\n\t\t\t\toutput.push({\n\t\t\t\t\tlabel: option,\n\t\t\t\t\tvalue: option,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\toutput.push({\n\t\t\t\t\t...option,\n\t\t\t\t\tlabel: typeof labelKey === 'function' ? labelKey(option) : get(option, labelKey),\n\t\t\t\t\tvalue: typeof valueKey === 'function' ? valueKey(option) : get(option, valueKey),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t} else {\n\t\tObject.keys(options).forEach((value) => {\n\t\t\tconst option = options[value];\n\t\t\tif (typeof value === 'string') {\n\t\t\t\toutput.push({\n\t\t\t\t\tlabel: option,\n\t\t\t\t\tvalue,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\toutput.push({\n\t\t\t\t\t...option,\n\t\t\t\t\tlabel: typeof labelKey === 'function' ? labelKey(option) : get(option, labelKey),\n\t\t\t\t\tvalue: typeof valueKey === 'function' ? valueKey(option) : get(option, valueKey),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\treturn output;\n};\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping\nexport const escapeRegExp = (string) => (string.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, '\\\\$&'));\n\nconst toSlug = (s) => (\n\ts.toLowerCase()\n\t\t.replace(/ & /g, '-and-')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/['’.]/g, '')\n\t\t.replace(/[^a-z0-9-]+/g, '-')\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/--+/g, '-')\n);\n\nexport const filterByKey = (records, key, value) => {\n\tvalue = value.toLowerCase();\n\tconst escapedValue = escapeRegExp(value);\n\trecords = records.filter((record) => {\n\t\tconst recordValue = get(record, key).toString().toLowerCase() || '';\n\t\treturn recordValue.match(new RegExp(`(^|[^a-z])${escapedValue}`));\n\t});\n\tvalue = toSlug(value);\n\trecords = records.sort((a, b) => {\n\t\tconst aValue = toSlug(get(a, key).toString());\n\t\tconst bValue = toSlug(get(b, key).toString());\n\t\tconst aPos = aValue.indexOf(value) === 0;\n\t\tconst bPos = bValue.indexOf(value) === 0;\n\t\tif ((aPos && bPos) || (!aPos && !bPos)) {\n\t\t\treturn aValue.localeCompare(bValue);\n\t\t}\n\t\tif (aPos && !bPos) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn 1;\n\t});\n\treturn records;\n};\n","import { filterByKey, normalizeOptions } from '../Helpers/Options';\nimport React, { useContext, useEffect, useRef, useState } from 'react'; // eslint-disable-line import/no-unresolved\nimport Api from '../Helpers/Api';\nimport { ReactComponent as CloseIcon } from '../../svg/x.svg';\nimport FormContext from '../FormContext';\nimport get from 'get-value';\nimport PropTypes from 'prop-types';\n\nexport default function Autocomplete({\n\tafterAdd,\n\tafterChange,\n\tclearable,\n\tclearButtonAttributes,\n\tclearButtonClassName,\n\tclearIconAttributes,\n\tclearIconHeight,\n\tclearIconWidth,\n\tclearText,\n\tdisabled,\n\tid,\n\tinputClassName,\n\tlabelFn,\n\tlabelKey,\n\tloadingText,\n\tmax,\n\tname,\n\toptionButtonAttributes,\n\toptionButtonClassName,\n\toptionLabelFn,\n\toptionListAttributes,\n\toptionListClassName,\n\toptionListItemAttributes,\n\toptionListItemClassName,\n\toptions,\n\tplaceholder,\n\treadOnly,\n\tremoveButtonAttributes,\n\tremoveButtonClassName,\n\tremoveIconAttributes,\n\tremoveIconHeight,\n\tremoveIconWidth,\n\tremoveText,\n\tsetValue,\n\tshowLoading,\n\turl,\n\tvalue,\n\tvalueKey,\n\tvalueListItemAttributes,\n\twrapperAttributes,\n\twrapperClassName,\n\t...otherProps\n}) {\n\tconst { formState, setValues } = useContext(FormContext);\n\tconst clearButtonRef = useRef(null);\n\tconst inputRef = useRef(null);\n\tconst removeButtonRef = useRef(null);\n\tconst [filter, setFilter] = useState('');\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [highlightedIndex, setHighlightedIndex] = useState(0);\n\tconst [optionValues, setOptionValues] = useState(options ? normalizeOptions(options, labelKey, valueKey) : []);\n\tconst [isLoading, setIsLoading] = useState(showLoading || !!url);\n\tconst [loadError, setLoadError] = useState('');\n\tconst api = Api.instance();\n\n\tuseEffect(() => {\n\t\tif (url) {\n\t\t\tapi(url, false)\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(error, 'errors')) {\n\t\t\t\t\t\tsetLoadError(error.errors.map((e) => (e.title)).join(' '));\n\t\t\t\t\t\tsetIsLoading(false);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (!response) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tsetOptionValues(normalizeOptions(response, labelKey, valueKey));\n\t\t\t\t\tsetIsLoading(false);\n\t\t\t\t});\n\t\t}\n\t}, [url]);\n\n\tuseEffect(() => {\n\t\tsetOptionValues(options ? normalizeOptions(options, labelKey, valueKey) : []);\n\t}, [options]);\n\n\tuseEffect(() => {\n\t\tsetIsLoading(showLoading);\n\t}, [showLoading]);\n\n\tif (isLoading) {\n\t\treturn ({loadingText}
);\n\t}\n\n\tif (loadError) {\n\t\treturn ({loadError}
);\n\t}\n\n\tlet currentValue = null;\n\tif (setValue !== null) {\n\t\tcurrentValue = value;\n\t} else {\n\t\tif (formState === undefined) {\n\t\t\tthrow new Error(' component must be inside a