Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
components.form.utils.input-utils.ts Maven / Gradle / Ivy
import { getEventValue } from 'components/form/utils/validate-utils';
import { InputProps } from 'contexts/FieldInfoContext';
import { FormInfo } from 'contexts/FormInfoContext';
import { getIn } from 'formik';
import { F } from 'helpers/formatter';
import { T, TranslateFunction } from 'helpers/translator';
import { FormikType } from 'hooks/submit';
import _ from 'lodash';
import { BasicInputError, ErrorHelper, InputError, RuntimeInputError } from 'types/error';
import { FieldRenderType } from 'types/field';
import { ChoicesKind, Field, FieldType, MultipleField } from 'types/graphql';
import { Option } from 'types/option';
import Types, { SubmitValues } from 'types/types';
import { hasNonEmptyValue, isEmptyValue } from 'utils/utils';
/* traverse 'object' recursively until you find 'key', and return its value */
export function findField(object: any, key: string): any | undefined {
if (Array.isArray(object)) {
for (let i = 0; i < object.length; i++) {
const value = findField(object[i], key);
if (value !== undefined)
return value
}
} else if (Types.isObject(object)) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (property === key && !(object['type'] == 'MULTIPLE')) {
return object[property];
} else {
const value = findField(object[property], key)
if (value !== undefined)
return value
}
}
}
}
return undefined
}
export function getPlaceholder(t: TranslateFunction, type: FieldRenderType ) {
if (type == undefined)
return undefined
const key = "placeholder." + type
const res = t(key)
if (res == key)
return ""
else
return res
}
export function toRuntimeErrorMessage(t: TranslateFunction, error: RuntimeInputError) {
if (error === undefined)
return undefined
if (!error?.type)
throw "Input error has no type"
const err = "error.input.1"
const res = t(err, {variables: error})
if (res == err)
return error.message
else
return res
}
export function resolveChoices(choices: any): Option[] {
if (Array.isArray(choices)) {
// for now we only support a direct array of the valid options
return choices;
}
throw new Error(`Unsupported kind of choices '${typeof choices}': ${JSON.stringify(choices)}`)
}
export function formatChoices(translator: T, choices: Option[], kind: ChoicesKind | undefined, processKey: string): Option[] {
const options = choices ? choices : []
const createLabel = (option: Option) => {
// translate
const label = translator.toValue(option.value, option.label, processKey)!
// change casing
return F.toSentenceCase(label)
}
const reformat = kind == "STATIC" || kind == undefined
if (reformat)
return options.map(option => { return {...option, label : createLabel(option) } })
else
return options
}
export function getFocusPathFromValues(formInfo: FormInfo, values: SubmitValues) {
return getFocusPathFromFieldValues(formInfo.form.fields, values)
}
export function getPath(value: any, path: string) {
if (path == "")
return value
else
return _.get(value, path)
}
type FocusOptions = {
required?: boolean
empty?: boolean
}
export function getFocusPathFromFieldValues(fields: Field[] | Field, values: SubmitValues) {
function getFocus(field: Field | Field[], valuePath: string, options: FocusOptions = {}): string | undefined {
// options:
// {empty: true, required: true} : focus on first empty required field
// {empty: true, required: false} : focus on first empty field
// {empty: false} : focus on first field
// a multiple input field
if (Array.isArray(field)) {
return getFocusArray(field, valuePath, options)
} else if (Types.isObject(field) && field.hasOwnProperty('fields')) {
return getFocusMultiple(field as MultipleField, valuePath, options)
} else {
return getFocusSingle(field, valuePath, options)
}
}
function getFocusArray(fields: Field[], valuePath: string, options: FocusOptions){
const createPath = (path: string) => path ? (valuePath ? `${valuePath}.${path}` : path) : valuePath
for (let i = 0; i < fields.length; i++) {
const match = getFocus(fields[i], createPath(fields[i].name), options)
if (match)
return match
}
}
function getFocusMultiple(field: MultipleField, valuePath: string, options: FocusOptions) {
const createPath = (path: string) => path ? (valuePath ? `${valuePath}.${path}` : path) : valuePath
const fields = field.fields
const value = getPath(values, valuePath)
// first traverse values for empty values
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
const match = getFocus(fields, `${valuePath}[${i}]`, options)
if (match)
return match
}
}
// without values, pick the first fields that has no value
else {
for (let i = 0; i < fields.length; i++) {
const match = getFocus(fields[i], createPath(`[0].${fields[i].name}`), options)
if (match)
return match
}
}
}
function getFocusSingle(field: Field, valuePath: string, options: FocusOptions) {
const value = _.get(values, valuePath)
if (options.empty) {
if (isEmptyValue(value) && (!options.required || !field.optional))
return valuePath
} else {
return valuePath
}
}
return getFocus(fields, "", {empty: false})
}
// we add convenience functions to props to make it more easy to work with formik
export function toAugProps({formik, t, rpath, inputProps, ...props}: {formik: FormikType, inputProps: InputProps,t: TranslateFunction, rpath: string, type: FieldType}) {
const runtimeErrorObj = getIn(formik.status, rpath)
const yupError = getIn(formik.errors, rpath)
function setRuntimeError(error: InputError | undefined) {
formik.setStatus(_.set(formik.status, rpath, error))
}
function toMsg(obj: InputError): string | undefined {
if (props.type === "MULTIPLE") {
return hasNonEmptyValue(runtimeErrorObj) || hasNonEmptyValue(yupError) ? t('yup.invalid.multiple') : undefined
} else {
if (ErrorHelper.isRuntimeError(obj))
return toRuntimeErrorMessage(t, obj as RuntimeInputError)
else if (ErrorHelper.isBasicError(obj))
return (obj as BasicInputError).message
else if (obj instanceof Function)
return toMsg(obj())
else if (typeof obj == 'string')
return obj.trim() !== "" ? obj : undefined
else if (obj != null && obj != undefined)
return "error: " + JSON.stringify(obj)
else
return undefined
}
}
return {
inputProps: inputProps,
setValue: function(value: any) { formik.setFieldValue(rpath, value) },
setTouched: function(value: any) { formik.setFieldTouched(rpath, value) },
setError: function(value: any) { formik.setFieldError(rpath, value) },
isError: function() { return this.touched && hasNonEmptyValue(this.rawError) },
setRuntimeError: function(value: any) { setRuntimeError(value) },
// the getters below become static after passing them as a function argument.
get value() { return getIn(formik.values, rpath) },
get yupError() { return toMsg(yupError) },
get runtimeError() { return toMsg(runtimeErrorObj) },
get rawError() { return this && (this.runtimeError || this.yupError) },
get error() { return this.rawError },
get touched() { return getIn(formik.touched, rpath) },
handleBlur: function(e: Event) {
formik.handleBlur(e)
},
handleFocus: function(e: Event) {
formik.setFieldTouched(rpath, true)
},
handleChange: function(e: Event) {
setRuntimeError(undefined)
try {
const value = getEventValue(e, e)
formik.setFieldValue(rpath, value)
} catch (error) {
console.error("formik handle error: %o", error)
}
},
}
}