139 lines
5.1 KiB
TypeScript
139 lines
5.1 KiB
TypeScript
import type { FormElementDto, FormElementVisibilityCondition, VisibilityConditionOperator } from '~~/.api-client'
|
|
import { VisibilityConditionOperator as VCOperator, VisibilityConditionType as VCType, FormElementType } from '~~/.api-client'
|
|
|
|
export function useFormElementVisibility() {
|
|
/**
|
|
* Evaluates visibility for all form elements based on their visibility conditions.
|
|
* Returns a map of element key (id or reference) to visibility status.
|
|
*/
|
|
function evaluateFormElementVisibility(allFormElements: FormElementDto[]): Map<string, boolean> {
|
|
const formElementsByRef = buildFormElementsMap(allFormElements)
|
|
const visibilityMap = new Map<string, boolean>()
|
|
|
|
allFormElements.forEach((element) => {
|
|
const isVisible = isElementVisible(element, formElementsByRef)
|
|
const key = element.id || element.reference
|
|
if (key) {
|
|
visibilityMap.set(key, isVisible)
|
|
}
|
|
})
|
|
|
|
return visibilityMap
|
|
}
|
|
|
|
function buildFormElementsMap(formElements: FormElementDto[]): Map<string, FormElementDto> {
|
|
const map = new Map<string, FormElementDto>()
|
|
formElements.forEach((element) => {
|
|
if (element.reference) {
|
|
map.set(element.reference, element)
|
|
}
|
|
})
|
|
return map
|
|
}
|
|
|
|
/**
|
|
* Evaluates if an element is visible based on its visibility conditions.
|
|
* Multiple conditions use AND logic - all conditions must be met for the element to be visible.
|
|
*/
|
|
function isElementVisible(element: FormElementDto, formElementsByRef: Map<string, FormElementDto>): boolean {
|
|
const conditions = element.visibilityConditions
|
|
if (!conditions || conditions.length === 0) {
|
|
return true
|
|
}
|
|
|
|
// All conditions must be met (AND logic)
|
|
return conditions.every((condition) => evaluateSingleCondition(condition, formElementsByRef))
|
|
}
|
|
|
|
/**
|
|
* Evaluates a single visibility condition against the form state.
|
|
*/
|
|
function evaluateSingleCondition(
|
|
condition: FormElementVisibilityCondition,
|
|
formElementsByRef: Map<string, FormElementDto>
|
|
): boolean {
|
|
const sourceElement = formElementsByRef.get(condition.sourceFormElementReference)
|
|
if (!sourceElement) {
|
|
return false
|
|
}
|
|
|
|
// Special handling for CHECKBOX with multiple options
|
|
if (sourceElement.type === FormElementType.Checkbox && sourceElement.options.length > 1) {
|
|
const operator = condition.formElementOperator || VCOperator.Equals
|
|
const conditionMet = evaluateCheckboxCondition(sourceElement, condition.formElementExpectedValue, operator)
|
|
return condition.formElementConditionType === VCType.Show ? conditionMet : !conditionMet
|
|
}
|
|
|
|
const sourceValue = getFormElementValue(sourceElement)
|
|
const operator = condition.formElementOperator || VCOperator.Equals
|
|
const conditionMet = evaluateCondition(sourceValue, condition.formElementExpectedValue, operator)
|
|
|
|
return condition.formElementConditionType === VCType.Show ? conditionMet : !conditionMet
|
|
}
|
|
|
|
function getFormElementValue(element: FormElementDto): string {
|
|
// For CHECKBOX with a single option, return the value directly
|
|
if (element.type === FormElementType.Checkbox && element.options.length === 1) {
|
|
return element.options[0]?.value || ''
|
|
}
|
|
|
|
// For other element types (RADIOBUTTON, SELECT, etc.), find the selected option and return its label
|
|
const selectedOption = element.options.find((option) => option.value === 'true')
|
|
return selectedOption?.label || ''
|
|
}
|
|
|
|
/**
|
|
* Evaluates visibility condition for CHECKBOX with multiple options.
|
|
* Checks if ANY of the selected checkboxes matches the expected value.
|
|
*/
|
|
function evaluateCheckboxCondition(
|
|
element: FormElementDto,
|
|
expectedValue: string,
|
|
operator: VisibilityConditionOperator
|
|
): boolean {
|
|
const selectedLabels = element.options
|
|
.filter((option) => option.value === 'true')
|
|
.map((option) => option.label)
|
|
|
|
switch (operator) {
|
|
case VCOperator.Equals:
|
|
// Check if any selected checkbox label matches the expected value
|
|
return selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
|
case VCOperator.NotEquals:
|
|
// Check if no selected checkbox label matches the expected value
|
|
return !selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
|
case VCOperator.IsEmpty:
|
|
// Check if no checkboxes are selected
|
|
return selectedLabels.length === 0
|
|
case VCOperator.IsNotEmpty:
|
|
// Check if at least one checkbox is selected
|
|
return selectedLabels.length > 0
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
function evaluateCondition(
|
|
actualValue: string,
|
|
expectedValue: string,
|
|
operator: VisibilityConditionOperator
|
|
): boolean {
|
|
switch (operator) {
|
|
case VCOperator.Equals:
|
|
return actualValue.toLowerCase() === expectedValue.toLowerCase()
|
|
case VCOperator.NotEquals:
|
|
return actualValue.toLowerCase() !== expectedValue.toLowerCase()
|
|
case VCOperator.IsEmpty:
|
|
return actualValue === ''
|
|
case VCOperator.IsNotEmpty:
|
|
return actualValue !== ''
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
return {
|
|
evaluateFormElementVisibility
|
|
}
|
|
}
|