import deepmerge from 'deepmerge' ; import isArray from 'lodash/isArray' ; import get from 'lodash/get' ; import HtmlParser from 'react-html-parser' ; import { shouldShowFormItem } from './conditionUtils' ; import { Field , RecordObject } from '../types/Fields' ; import { Application , ExternalData , FormValue } from '../types/Application' ; import { Form , FormItemTypes , FormLeaf , FormNode , FormText , FormTextArray , Section , StaticText , StaticTextObject , SubSection , } from '../types/Form' ; const containsArray = ( obj ) => { let contains = false ; for ( const key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) && isArray ( obj [ key ] ) ) { contains = true ; } } return contains ; } ; export function getErrorViaPath ( obj , path ) { return get ( obj , path ) as string ; } export function getValueViaPath < T = unknown > ( obj , path , defaultValue ? ) { if ( path . match ( /.\[\d\]\../g ) && ! containsArray ( obj ) ) { return ( obj ?. [ path ] ?? defaultValue ) as T ; } try { const travel = ( regexp ) => String . prototype . split . call ( path , regexp ) . filter ( Boolean ) . reduce ( ( res , key ) => ( res !== null && res !== undefined ? res [ key ] : res ) , obj ) as RecordObject | string ; const result = travel ( /[,[\]]+?/ ) || travel ( /[,[\].]+?/ ) ; return result === undefined || result === obj ? defaultValue : ( result as T ) ; } catch ( e ) { return undefined ; } } export const isValidScreen = ( node ) : boolean => { switch ( node . type ) { case FormItemTypes . FORM : { return false ; } case FormItemTypes . SECTION : { return false ; } case FormItemTypes . SUB_SECTION : { return false ; } default : { return true ; } } } ; export const getFormNodeLeaves = ( node ) : FormLeaf [ ] => { const { children } = node ; if ( isValidScreen ( node ) ) { return [ node as FormLeaf ] ; } let leaves = [ ] ; let newLeaves = [ ] ; if ( children ) { for ( let i = 0 ; i < children . length ; i ++ ) { newLeaves = getFormNodeLeaves ( children [ i ] ) ; if ( newLeaves . length ) { leaves = [ ... leaves , ... newLeaves ] ; } } } return leaves ; } ; export function getSectionsInForm ( form , answers , externalData ) { const sections = [ ] ; form . children . forEach ( ( child ) => { const shouldShowSection = shouldShowFormItem ( child , answers , externalData ) ; if ( child . type === FormItemTypes . SECTION && shouldShowSection ) { sections . push ( child as Section ) ; } } ) ; return sections ; } export function getSubSectionsInSection ( section , answers , externalData ) { const subSections = [ ] ; section ?. children . forEach ( ( child ) => { const shouldShowSection = shouldShowFormItem ( child , answers , externalData ) ; if ( child . type === FormItemTypes . SUB_SECTION && shouldShowSection ) { subSections . push ( child as SubSection ) ; } } ) ; return subSections ; } export function findSectionIndex ( sections , section ) { if ( ! sections . length ) { return - 1 ; } for ( let i = 0 ; i < sections . length ; i ++ ) { if ( sections [ i ] . id === section . id ) { return i ; } } return - 1 ; } export function findSubSectionIndex ( subSections , subSection ) { for ( let i = 0 ; i < subSections . length ; i ++ ) { if ( subSections [ i ] . id === subSection . id ) { return i ; } } return - 1 ; } type DeepmergeOptions = deepmerge . Options & { cloneUnlessOtherwiseSpecified ( key , options ? ) : any | undefined ; isMergeableObject ( item ) : boolean ; } ; const overwriteArrayMerge = ( destinationArray , sourceArray , options ) => { const destination = destinationArray . slice ( ) ; if ( typeof sourceArray [ sourceArray . length - 1 ] !== 'object' || sourceArray . length < destinationArray . length ) { return sourceArray ; } sourceArray . forEach ( ( item , index ) => { if ( typeof destination [ index ] === 'undefined' ) { destination [ index ] = options . cloneUnlessOtherwiseSpecified ( item , options ) ; } else if ( options . isMergeableObject ( item ) ) { destination [ index ] = deepmerge ( destinationArray [ index ] , item , options ) ; } else if ( destinationArray . indexOf ( item ) === - 1 ) { destination . push ( item ) ; } } ) ; return destination ; } ; export function mergeAnswers ( currentAnswers , newAnswers ) { return deepmerge ( currentAnswers , newAnswers , { arrayMerge : overwriteArrayMerge , } ) ; } export type MessageFormatter = ( descriptor : StaticText , values ? : StaticTextObject [ 'values' ] ) => string ; const handleMessageFormatting = ( message , formatMessage ) => { if ( typeof message === 'string' || ! message ) { return formatMessage ( message ) ; } const { values = { } , ... descriptor } = message ; return formatMessage ( descriptor , values ) ; } ; export function formatText < T extends FormTextArray | FormText > ( text , application , formatMessage ) { if ( typeof text === 'function' ) { const message = ( text as ( _ : Application ) => StaticText | StaticText [ ] ) ( application ) ; if ( Array . isArray ( message ) ) { return message . map ( ( m ) => handleMessageFormatting ( m , formatMessage ) ) as T extends FormTextArray ? string [ ] : string ; } return handleMessageFormatting ( message , formatMessage ) as T extends FormTextArray ? string [ ] : string ; } else if ( Array . isArray ( text ) ) { const texts = text as StaticText [ ] ; return texts . map ( ( m ) => handleMessageFormatting ( m , formatMessage ) ) as T extends FormTextArray ? string [ ] : string ; } else if ( typeof text === 'object' ) { const staticTextObject = text as StaticTextObject ; if ( staticTextObject . values ) { return formatMessage ( staticTextObject , staticTextObject . values ) as T extends FormTextArray ? string [ ] : string ; } } return formatMessage ( text ) as T extends FormTextArray ? string [ ] : string ; } export function formatAndParseAsHTML ( text , application , formatMessage ) { return HtmlParser ( formatText ( text , application , formatMessage ) ) ; } export function extractRepeaterIndexFromField ( field ) { if ( ! field ?. isPartOfRepeater ) { return - 1 ; } let repeaterIndex = '' ; let foundBracketOpen = false ; for ( let i = 0 ; i < field . id . length ; i ++ ) { const char = field . id . charAt ( i ) ; if ( char === ']' ) { break ; } if ( ! foundBracketOpen && char === '[' ) { foundBracketOpen = true ; } else if ( foundBracketOpen ) { const partOfIndex = parseInt ( char , 10 ) ; if ( isNaN ( partOfIndex ) ) { return - 1 ; } repeaterIndex += char ; } } if ( repeaterIndex . length ) { return parseInt ( repeaterIndex , 10 ) ; } return - 1 ; }