@kronscht/former
Version:
Former is an Angular library that provides a declarative way to generate complex forms based on primeng components
1 lines • 101 kB
Source Map (JSON)
{"version":3,"file":"kronscht-former.mjs","sources":["../../../projects/former/src/lib/former.service.ts","../../../projects/former/src/lib/elements/base-element.component.ts","../../../projects/former/src/lib/elements/validation-error/validation-error.component.ts","../../../projects/former/src/lib/elements/validation-error/validation-error.component.html","../../../projects/former/src/lib/elements/calendar-element/calendar-element.component.ts","../../../projects/former/src/lib/elements/calendar-element/calendar-element.component.html","../../../projects/former/src/lib/elements/dropdown-element/dropdown-element.component.ts","../../../projects/former/src/lib/elements/dropdown-element/dropdown-element.component.html","../../../projects/former/src/lib/elements/input-group-element/input-group-element.component.ts","../../../projects/former/src/lib/elements/input-group-element/input-group-element.component.html","../../../projects/former/src/lib/elements/text-element/text-element.component.ts","../../../projects/former/src/lib/elements/text-element/text-element.component.html","../../../projects/former/src/lib/model/elements/autocomplete.model.ts","../../../projects/former/src/lib/elements/autocomplete-element/autocomplete-element.component.ts","../../../projects/former/src/lib/elements/autocomplete-element/autocomplete-element.component.html","../../../projects/former/src/lib/elements/checkbox-element/checkbox-element.component.ts","../../../projects/former/src/lib/elements/checkbox-element/checkbox-element.component.html","../../../projects/former/src/lib/elements/number-element/number-element.component.ts","../../../projects/former/src/lib/elements/number-element/number-element.component.html","../../../projects/former/src/lib/elements/text-area-element/text-area-element.component.ts","../../../projects/former/src/lib/elements/text-area-element/text-area-element.component.html","../../../projects/former/src/lib/elements/display-text-element/display-text-element.component.ts","../../../projects/former/src/lib/elements/display-text-element/display-text-element.component.html","../../../projects/former/src/lib/elements/template-element/template-element.component.ts","../../../projects/former/src/lib/elements/template-element/template-element.component.html","../../../projects/former/src/lib/generated-form-element/generated-form-element.component.ts","../../../projects/former/src/lib/generated-form-element/generated-form-element.component.html","../../../projects/former/src/lib/elements/grid-layout-element/grid-layout-element.component.html","../../../projects/former/src/lib/elements/grid-column-element/grid-column-element.component.html","../../../projects/former/src/lib/elements/group-element/group-element.component.html","../../../projects/former/src/lib/elements/card-element/card-element.component.html","../../../projects/former/src/lib/directive/template-name.directive.ts","../../../projects/former/src/lib/generated-form/generated-form.component.ts","../../../projects/former/src/lib/generated-form/generated-form.component.html","../../../projects/former/src/lib/former.utils.ts","../../../projects/former/src/lib/generated-form-actions/generated-form-actions.component.ts","../../../projects/former/src/lib/generated-form-actions/generated-form-actions.component.html","../../../projects/former/src/lib/former.module.ts","../../../projects/former/src/lib/model/former.model.ts","../../../projects/former/src/public-api.ts","../../../projects/former/src/kronscht-former.ts"],"sourcesContent":["/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { inject, Injectable } from '@angular/core';\nimport { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';\nimport { ElementType, ValidationType } from './model/former.enum';\nimport { BaseElement, Element, Elements, FormDefinition, MaxFormValidator, MaxLengthFormValidator, MinFormValidator, MinLengthFormValidator, PatternFormValidator } from './model/former.model';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class FormerService {\n readonly fb = inject(FormBuilder);\n\n generateForm(formDefinition: FormDefinition): FormGroup {\n const formGroup = this.fb.group({});\n\n this.addControlRecursive(formGroup, formDefinition.elements);\n\n return formGroup;\n }\n\n addControlRecursive(formGroup: FormGroup, elements: Elements) {\n for (const key of Object.keys(elements)) {\n const element: Element = elements[key];\n switch (element.type) {\n case ElementType.GroupElement: {\n const nestedGroup = this.fb.group({});\n this.addControlRecursive(nestedGroup, element.elements);\n formGroup.addControl(key, nestedGroup);\n break;\n }\n case ElementType.GridColumnElement:\n case ElementType.GridLayoutElement:\n case ElementType.CardElement:\n this.addControlRecursive(formGroup, element.elements);\n break;\n case ElementType.TemplateElement:\n break;\n default:\n formGroup.addControl(key, new FormControl(undefined, this.addValidators(element)));\n }\n }\n }\n\n addValidators(element: BaseElement): ValidatorFn[] {\n const validators: ValidatorFn[] = [];\n\n if (element.validators) {\n for (const validator of element.validators) {\n switch (validator.validationType) {\n case ValidationType.Min:\n validators.push(Validators.max((validator as MinFormValidator).min));\n break;\n case ValidationType.Max:\n validators.push(Validators.max((validator as MaxFormValidator).max));\n break;\n case ValidationType.Required:\n validators.push(Validators.required);\n break;\n case ValidationType.Email:\n validators.push(Validators.email);\n break;\n case ValidationType.MinLength:\n validators.push(Validators.minLength((validator as MinLengthFormValidator).minLength));\n break;\n case ValidationType.MaxLength:\n validators.push(Validators.maxLength((validator as MaxLengthFormValidator).maxLength));\n break;\n case ValidationType.Pattern:\n validators.push(Validators.pattern((validator as PatternFormValidator).pattern));\n break;\n default:\n break;\n }\n }\n }\n\n return validators;\n }\n}\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { KeyValue } from '@angular/common';\nimport { Component, Input, OnInit } from '@angular/core';\nimport { AbstractControl, FormGroup } from '@angular/forms';\nimport { DisableElement } from '../model/former.model';\n\n@Component({\n selector: 'lib-base-element-component',\n template: '<div>do not use directly</div>',\n standalone: false,\n})\nexport abstract class BaseElementComponent<T extends object> implements OnInit {\n @Input() parentKey = '';\n @Input() element!: KeyValue<string, T>;\n @Input() formGroup!: FormGroup;\n\n get key(): string {\n return this.element.key;\n }\n\n get field(): T {\n return this.element.value;\n }\n\n get control(): AbstractControl {\n return this.formGroup.get(this.key) as AbstractControl;\n }\n\n ngOnInit(): void {\n if (this.control && (this.field as DisableElement)) {\n if ((this.field as DisableElement).disabled) {\n this.control.disable();\n } else {\n this.control.enable();\n }\n }\n }\n}\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component, Input } from '@angular/core';\nimport { AbstractControl } from '@angular/forms';\nimport { BaseElement } from '../../model/former.model';\n\n@Component({\n selector: 'lib-validation-error',\n templateUrl: './validation-error.component.html',\n styleUrls: ['./validation-error.component.css'],\n standalone: false,\n})\nexport class ValidationErrorComponent {\n @Input() control?: AbstractControl;\n @Input() field?: BaseElement;\n @Input() key?: string;\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div *ngIf=\"control?.invalid\">\n <div *ngFor=\"let validator of field?.validators\">\n <small *ngIf=\"control?.hasError(validator.validationType)\" [id]=\"key + '-message'\" class=\"p-error\">{{ validator.messageKey | translate: validator.messageArgs }}</small>\n </div>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { CalendarElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-calendar-element',\n templateUrl: './calendar-element.component.html',\n styleUrls: ['./calendar-element.component.css'],\n standalone: false,\n})\nexport class CalendarElementComponent extends BaseElementComponent<CalendarElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <p-datePicker [id]=\"key\" [name]=\"key\" [formControlName]=\"key\" [dateFormat]=\"element.value.dateFormat\" [styleClass]=\"element.value.styleClass\" [panelStyle]=\"{ minWidth: 'initial' }\"></p-datePicker>\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { DropdownElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-dropdown-element',\n templateUrl: './dropdown-element.component.html',\n styleUrls: ['./dropdown-element.component.css'],\n standalone: false,\n})\nexport class DropdownElementComponent extends BaseElementComponent<DropdownElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <p-select [formControlName]=\"key\" [options]=\"element.value.options | async\" [showClear]=\"true\" optionLabel=\"{{ element.value.optionLabel || 'name' }}\" optionValue=\"{{ element.value.optionValue || 'code' }}\" [styleClass]=\"'w-full'\"> </p-select>\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { InputGroupElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-input-group-element',\n templateUrl: './input-group-element.component.html',\n styleUrls: ['./input-group-element.component.css'],\n standalone: false,\n})\nexport class InputGroupElementComponent extends BaseElementComponent<InputGroupElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <p-inputgroup [class]=\"field.wrapperClass\">\n <p-inputgroup-addon *ngIf=\"field.icon\">{{ field.icon }}</p-inputgroup-addon>\n <p-floatlabel [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <input pInputText [id]=\"key\" [name]=\"key\" [formControlName]=\"key\" [pKeyFilter]=\"field.keyFilter\" />\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <p-inputgroup-addon *ngIf=\"field.unit\">{{ field.unit }}</p-inputgroup-addon>\n </p-inputgroup>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { TextElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-text-element',\n templateUrl: './text-element.component.html',\n styleUrls: ['./text-element.component.css'],\n standalone: false,\n})\nexport class TextElementComponent extends BaseElementComponent<TextElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <div>\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <input pInputText [id]=\"key\" [name]=\"key\" [class]=\"field.cssClass\" [formControlName]=\"key\" />\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n </div>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\n\nimport { ElementType } from '../former.enum';\nimport { Observable } from 'rxjs';\nimport { BaseElement, DisableElement } from '../former.model';\nimport { AutoCompleteCompleteEvent } from 'primeng/autocomplete';\nimport { ScrollerOptions } from 'primeng/api';\n\nexport type FilterProvider = LocalFilterProvider | RemoteFilterProvider;\n\nexport interface LocalFilterProvider {\n local: (event: AutoCompleteCompleteEvent, data: any[], field?: string) => any[];\n}\n\nexport interface RemoteFilterProvider {\n remote: string;\n}\n\nexport function isRemoteFilterProvider(value: FilterProvider): value is RemoteFilterProvider {\n return 'remote' in value;\n}\n\nexport function isLocalFilterProvider(value: FilterProvider): value is LocalFilterProvider {\n return 'local' in value;\n}\n\nexport type Suggestions = StaticSuggestions | ObservableSuggestions | RemoteSuggestions;\n\nexport interface RemoteSuggestions {\n remote: string;\n}\n\nexport interface StaticSuggestions {\n static: any[];\n}\n\nexport interface ObservableSuggestions {\n observable: Observable<any[]>;\n}\n\n/**\n * Checks, if the suggestion are remote suggestion, i.e. loaded by a specific REST endpoint\n */\nexport function isRemoteSuggestions(value: Suggestions): value is RemoteSuggestions {\n return 'remote' in value;\n}\n\n/**\n * Checks, if the suggestions are static suggestions, i.e. an array of plain strings or complex objects\n * @param value\n */\nexport function isStaticSuggestions(value: Suggestions): value is StaticSuggestions {\n return 'static' in value;\n}\n\n/**\n * Checks, if the suggestions are observable suggestions, i.e. suggestions provided by an observable as an array of plain strings or complex objects\n * @param value\n */\nexport function isObservableSuggestions(value: Suggestions): value is ObservableSuggestions {\n return 'observable' in value;\n}\n\nexport interface AutoCompleteElement extends BaseElement, DisableElement {\n type: ElementType.AutocompleteElement;\n suggestions: Suggestions;\n field?: string;\n filter?: FilterProvider;\n width?: string;\n options?: AutoCompleteOptions;\n}\n\n// Reflects all properties from https://primeng.org/autocomplete\nexport interface AutoCompleteOptions {\n minLength?: number; // default 1\n delay?: number; // default 300\n style?: Record<string, any> | null | undefined;\n panelStyle?: Record<string, any> | null | undefined;\n styleClass?: string | undefined;\n panelStyleClass?: string | undefined;\n inputStyle?: Record<string, any> | null | undefined;\n inputId?: string | undefined;\n inputStyleClass?: string | undefined;\n placeholder?: string | undefined;\n readonly?: boolean | undefined;\n disabled?: boolean | undefined;\n scrollHeight?: string;\n lazy?: boolean;\n virtualScroll?: ScrollerOptions | undefined;\n virtualScrollItemSize?: number;\n virtualScrollOptions?: ScrollerOptions | undefined;\n maxLength?: number | undefined;\n name?: string;\n size?: number;\n appendTo?: any; // Target element to attach the overlay\n autoHighlight?: boolean;\n forceSelection?: boolean;\n type?: string;\n autoZIndex?: boolean;\n baseZIndex?: boolean;\n}\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { AfterViewInit, Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core';\nimport { AutoCompleteCompleteEvent } from 'primeng/autocomplete';\nimport { BaseElementComponent } from '../base-element.component';\n\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject } from 'rxjs';\nimport {\n AutoCompleteElement,\n AutoCompleteOptions,\n FilterProvider,\n isLocalFilterProvider,\n isObservableSuggestions,\n isRemoteSuggestions,\n isStaticSuggestions,\n ObservableSuggestions,\n RemoteSuggestions,\n StaticSuggestions,\n Suggestions,\n} from '../../model/elements/autocomplete.model';\n\n@Component({\n selector: 'lib-autocomplete-element',\n templateUrl: './autocomplete-element.component.html',\n styleUrls: ['./autocomplete-element.component.css'],\n standalone: false,\n})\nexport class AutocompleteElementComponent extends BaseElementComponent<AutoCompleteElement> implements OnInit, AfterViewInit {\n @ViewChild('autoCompleteElement') elementRef?: ElementRef;\n\n private readonly http: HttpClient = inject(HttpClient);\n\n // This is the internal list of all available suggestions.\n internalSuggestions: any[] = [];\n\n // Filtered suggestions\n filteredSuggestions$: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);\n\n defaultFilter = (query: string, data: any[]) => {\n const result = data.filter(d => d.toLowerCase().includes(query.toLowerCase()));\n return result;\n };\n\n defaultComplexFilter = (query: string, field: string, data: any[]) => {\n return data.filter(d => d[field].toLowerCase().includes(query.toLowerCase()));\n };\n\n /**\n * Static suggestion can be a list of strings or a list of complex objects.\n * @param suggestions\n * @param query\n * @param field\n */\n handleStaticSuggestions(suggestions: StaticSuggestions, query: string, field?: string, filter?: FilterProvider): any[] {\n const data = suggestions.static;\n let filtered: any[] = [];\n if (this.isArrayOfStrings(data)) {\n filtered = this.defaultFilter(query, this.internalSuggestions);\n // filtered = this.filterWithFallback(filter, this.defaultFilter);\n } else {\n if (field) {\n filtered = this.defaultComplexFilter(query, field, this.internalSuggestions);\n } else {\n throw new Error('Need to provide a field to filter a complex data structure');\n }\n }\n return filtered;\n }\n\n // filterWithFallback(filter?: FilterProvider, defaultFilter: FilterProvider) {\n // let result = defaultFilter;\n // if (filter) {\n // result = filter;\n // }\n // return result;\n // }\n\n // filterReworked(event: AutoCompleteCompleteEvent) {\n // const query = event.query;\n // const suggestions: Suggestions = this.field.suggestions;\n // const filter = this.field.filter;\n // const filterField = this.field.field;\n //\n // if (isStaticSuggestions(suggestions)) {\n // this.filteredSuggestions$.next(this.handleStaticSuggestions(suggestions, query, filterField, filter));\n // } else if (isObservableSuggestions(suggestions)) {\n // } else if (isRemoteSuggestions(suggestions)) {\n // } else {\n // console.warn('Provided suggestion type is not supported', suggestions);\n // }\n // }\n\n filter(event: AutoCompleteCompleteEvent) {\n let filtered: any[] = [];\n const query = event.query;\n\n const suggestions: Suggestions = this.field.suggestions;\n const filter = this.field.filter;\n\n if (filter) {\n if (isLocalFilterProvider(filter)) {\n filtered = filter.local(event, this.internalSuggestions, this.field.field);\n } else {\n const evaluated = eval(filter.remote);\n filtered = evaluated(event, this.internalSuggestions, this.field.field);\n }\n this.filteredSuggestions$.next(filtered);\n } else {\n if (isStaticSuggestions(suggestions)) {\n filtered = this.handleStaticSuggestions(suggestions, query, this.field.field);\n this.filteredSuggestions$.next(filtered);\n } else if (isObservableSuggestions(this.field.suggestions)) {\n if (!this.isArrayOfStrings(this.internalSuggestions)) {\n if (!this.field.field) {\n throw new Error('You have to provide a custom filter function or a field to filter on complex suggestions.');\n }\n\n for (const suggestion of this.internalSuggestions as any[]) {\n if (suggestion[this.field.field!].toLowerCase().includes(query.toLowerCase())) {\n filtered.push(suggestion);\n }\n }\n } else {\n for (const suggestion of this.internalSuggestions as any[]) {\n if (suggestion.toLowerCase().includes(query.toLowerCase())) {\n filtered.push(suggestion);\n }\n }\n }\n this.filteredSuggestions$.next(filtered);\n } else if (isRemoteSuggestions(this.field.suggestions)) {\n this.http.get<any[]>(this.field.suggestions.remote + '?name=' + query).subscribe(data => this.filteredSuggestions$.next(data as any[]));\n } else {\n console.error('Suggestion type unknown');\n }\n }\n }\n\n getAvailableFields(data: any[]) {\n if (data && data.length > 1) {\n return Object.getOwnPropertyNames(data[0]);\n } else {\n return '';\n }\n }\n\n checkSuggestionsIncludesField(field: string): boolean {\n return this.internalSuggestions && this.internalSuggestions.every(i => Object.prototype.hasOwnProperty.call(i, field));\n }\n\n override ngOnInit(): void {\n super.ngOnInit();\n\n this.validateParameters();\n\n if (isRemoteSuggestions(this.element.value.suggestions)) {\n this.http.get<any[]>((this.element.value.suggestions as RemoteSuggestions).remote).subscribe(data => (this.internalSuggestions = data));\n }\n\n if (isStaticSuggestions(this.element.value.suggestions)) {\n this.internalSuggestions = (this.element.value.suggestions as StaticSuggestions).static;\n console.log('static');\n }\n\n if (isObservableSuggestions(this.element.value.suggestions)) {\n // TODO: destroy\n (this.element.value.suggestions as ObservableSuggestions).observable.subscribe(data => (this.internalSuggestions = data));\n console.log('observable');\n }\n\n // if (isObservable(this.element.value.suggestions)) {\n // (this.field.suggestions as Observable<any>)?.subscribe((data: any) => (this.internalSuggestions = data));\n // } else {\n // this.internalSuggestions = this.element.value.suggestions as any[];\n // }\n // this.field.suggestions.subscribe((data: any) => (this.suggestions = data));\n }\n\n ngAfterViewInit(): void {\n if (this.field.options) {\n Object.keys(this.field.options).forEach(option => {\n // if (this.elementRef?.hasOwnProperty(option)) {\n if (Object.prototype.hasOwnProperty.call(this.elementRef, option)) {\n (this.elementRef as any)[option] = (this.field.options as any)[option as keyof AutoCompleteOptions];\n }\n });\n }\n }\n\n isArrayOfStrings(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string');\n }\n\n validateParameters(): void {\n if (this.element.value.suggestions) {\n if (isStaticSuggestions(this.element.value.suggestions)) {\n if (!this.element.value.field && !this.isArrayOfStrings(this.element.value.suggestions.static)) {\n throw new Error('You have to provide a field to filter on complex static suggestions.');\n }\n }\n\n // if (isObservableSuggestions(this.element.value.suggestions)) {\n // if (!this.element.value.field && !this.isArrayOfStrings(this.element.value.suggestions.observable)) {\n // throw new Error('You have to provide a field to filter on complex static suggestions.');\n // }\n // }\n }\n\n // if (!isObservable(this.element.value.suggestions)) {\n // if (this.element.value.suggestions && !this.element.value.field && !this.isArrayOfStrings(this.element.value.suggestions)) {\n // throw new Error('You have to provide a field to filter on complex static suggestions.');\n // }\n // }\n }\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <p-autoComplete\n #autoCompleteElement\n [formControlName]=\"key\"\n [suggestions]=\"(filteredSuggestions$ | async) || []\"\n [optionLabel]=\"field.field\"\n (completeMethod)=\"filter($event)\"\n [style]=\"{ width: field.width ?? '100%' }\"\n [inputStyle]=\"{ width: '100%' }\"\n [inputId]=\"key\"></p-autoComplete>\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { CheckboxElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-checkbox-element',\n templateUrl: './checkbox-element.component.html',\n styleUrls: ['./checkbox-element.component.css'],\n standalone: false,\n})\nexport class CheckboxElementComponent extends BaseElementComponent<CheckboxElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\" [class]=\"field.wrapperClass\">\n <p-checkbox [formControlName]=\"key\" [value]=\"field.value\" [id]=\"key\"></p-checkbox>\n <label for=\"{{ key }}\">{{ field.title }}</label>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component, OnInit } from '@angular/core';\nimport { NumberElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\nimport { NumberMode } from '../../model/former.enum';\n\n@Component({\n selector: 'lib-number-element',\n templateUrl: './number-element.component.html',\n styleUrls: ['./number-element.component.css'],\n standalone: false,\n})\nexport class NumberElementComponent extends BaseElementComponent<NumberElement> implements OnInit {\n get mode() {\n if (this.field && this.field.mode) {\n return this.field.mode;\n } else {\n return NumberMode.Decimal;\n }\n }\n\n get currency() {\n if (this.field && this.field.currency) {\n return this.field.currency;\n } else {\n return 'EUR';\n }\n }\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <div>\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <p-inputNumber [id]=\"key\" [name]=\"key\" [mode]=\"mode\" [currency]=\"currency\" [locale]=\"field.locale\" [minFractionDigits]=\"field.minFractionDigits\" [maxFractionDigits]=\"field.maxFractionDigits\" [class]=\"field.cssClass\" [formControlName]=\"key\" />\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n </div>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component } from '@angular/core';\nimport { TextAreaElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-text-area-element',\n templateUrl: './text-area-element.component.html',\n styleUrls: ['./text-area-element.component.css'],\n standalone: false,\n})\nexport class TextAreaElementComponent extends BaseElementComponent<TextAreaElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <div>\n <p-floatlabel [class]=\"field.wrapperClass\" [variant]=\"field.floatLabel ? field.floatLabel : 'over'\">\n <textarea pTextarea [id]=\"key\" [name]=\"key\" [rows]=\"field.rows\" [cols]=\"field.cols\" [class]=\"field.cssClass\" [formControlName]=\"key\" [autoResize]=\"false\"></textarea>\n <label [for]=\"key\">{{ element.value.title }}</label>\n </p-floatlabel>\n <lib-validation-error [control]=\"control\" [key]=\"key\" [field]=\"field\"></lib-validation-error>\n </div>\n</div>\n","import { Component } from '@angular/core';\nimport { DisplayTextElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-display-text-element',\n templateUrl: './display-text-element.component.html',\n styleUrls: ['./display-text-element.component.css'],\n standalone: false,\n})\nexport class DisplayTextElementComponent extends BaseElementComponent<DisplayTextElement> {}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <div>\n <span [class]=\"field.wrapperClass\">\n <label [class]=\"field.labelClass\" [for]=\"key\">{{ element.value.title }}</label>\n <span [id]=\"key\">{{ control.value }}</span>\n </span>\n </div>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Component, Input, OnChanges, QueryList, SimpleChanges, TemplateRef } from '@angular/core';\nimport { TemplateNameDirective } from '../../directive/template-name.directive';\nimport { TemplateElement } from '../../model/former.model';\nimport { BaseElementComponent } from '../base-element.component';\n\n@Component({\n selector: 'lib-template-element',\n templateUrl: './template-element.component.html',\n styleUrls: ['./template-element.component.css'],\n standalone: false,\n})\nexport class TemplateElementComponent extends BaseElementComponent<TemplateElement> implements OnChanges {\n @Input() templates?: QueryList<TemplateNameDirective>;\n\n template?: TemplateRef<any>;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ngOnChanges(changes: SimpleChanges): void {\n if (this.templates) {\n this.template = this.templates.find(template => template.name === this.field.templateId)?.template;\n }\n }\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<ng-container [ngTemplateOutlet]=\"template!\"></ng-container>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { KeyValue } from '@angular/common';\nimport { Component, inject, Input, QueryList, Sanitizer } from '@angular/core';\nimport { FormGroup } from '@angular/forms';\nimport { ElementType } from '../model/former.enum';\nimport { CardElement, Element, GridColumnElement, GridLayoutElement, GroupElement, LayoutElement } from '../model/former.model';\nimport { BaseElementComponent } from '../elements/base-element.component';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[lib-generated-form-element]',\n templateUrl: './generated-form-element.component.html',\n styleUrls: ['./generated-form-element.component.css'],\n standalone: false,\n})\nexport class GeneratedFormElementComponent {\n protected readonly TextElement = ElementType.TextElement;\n protected readonly DisplayTextElement = ElementType.DisplayTextElement;\n protected readonly TextAreaElement = ElementType.TextAreaElement;\n protected readonly NumberElement = ElementType.NumberElement;\n protected readonly GroupElement = ElementType.GroupElement;\n protected readonly CalendarElement = ElementType.CalendarElement;\n protected readonly DropdownElement = ElementType.DropdownElement;\n protected readonly InputGroupElement = ElementType.InputGroupElement;\n protected readonly GridLayoutElement = ElementType.GridLayoutElement;\n protected readonly GridColumnElement = ElementType.GridColumnElement;\n protected readonly AutocompleteElement = ElementType.AutocompleteElement;\n protected readonly CheckboxElement = ElementType.CheckboxElement;\n protected readonly CardElement = ElementType.CardElement;\n protected readonly TemplateElement = ElementType.TemplateElement;\n\n @Input() parentKey = '';\n @Input() element!: KeyValue<string, Element>;\n @Input() formGroup!: FormGroup;\n @Input() class!: string;\n\n @Input() templates?: QueryList<any>;\n\n get anyElement(): any {\n return this.element as any;\n }\n}\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[lib-grid-layout-element]',\n templateUrl: '../elements/grid-layout-element/grid-layout-element.component.html',\n styleUrls: ['../elements/grid-layout-element/grid-layout-element.component.css'],\n standalone: false,\n})\nexport class GridLayoutElementComponent extends BaseElementComponent<GridLayoutElement> {\n sanitizer = inject(DomSanitizer);\n\n getElementClass(element: any) {\n if ((element.value as any).type === ElementType.GridColumnElement) {\n return (element.value as any as LayoutElement).class + ' col-no-padding';\n } else {\n return '';\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n originalOrder = (a: KeyValue<any, any>, b: KeyValue<any, any>): number => {\n return 0;\n };\n}\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[lib-grid-column-element]',\n templateUrl: '../elements/grid-column-element/grid-column-element.component.html',\n styleUrls: ['../elements/grid-column-element/grid-column-element.component.css'],\n standalone: false,\n})\nexport class GridColumnElementComponent extends BaseElementComponent<GridColumnElement> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n originalOrder = (a: KeyValue<any, any>, b: KeyValue<any, any>): number => {\n return 0;\n };\n}\n\n@Component({\n selector: 'lib-group-element',\n templateUrl: '../elements/group-element/group-element.component.html',\n styleUrls: ['../elements/group-element/group-element.component.css'],\n standalone: false,\n})\nexport class GroupElementComponent extends BaseElementComponent<GroupElement> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n originalOrder = (a: KeyValue<any, any>, b: KeyValue<any, any>): number => {\n return 0;\n };\n\n get childGroup() {\n return this.formGroup.get(this.key) as FormGroup;\n }\n}\n\n@Component({\n selector: 'lib-card-element',\n templateUrl: '../elements/card-element/card-element.component.html',\n styleUrls: ['../elements/card-element/card-element.component.css'],\n standalone: false,\n})\nexport class CardElementComponent extends BaseElementComponent<CardElement> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n originalOrder = (a: KeyValue<any, any>, b: KeyValue<any, any>): number => {\n return 0;\n };\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [ngSwitch]=\"element.value.type\">\n <lib-text-element *ngSwitchCase=\"TextElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-text-element>\n <lib-display-text-element *ngSwitchCase=\"DisplayTextElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-display-text-element>\n <lib-text-area-element *ngSwitchCase=\"TextAreaElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-text-area-element>\n <lib-number-element *ngSwitchCase=\"NumberElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-number-element>\n <lib-group-element *ngSwitchCase=\"GroupElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-group-element>\n <lib-calendar-element *ngSwitchCase=\"CalendarElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-calendar-element>\n <lib-dropdown-element *ngSwitchCase=\"DropdownElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-dropdown-element>\n <lib-checkbox-element *ngSwitchCase=\"CheckboxElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-checkbox-element>\n <lib-input-group-element *ngSwitchCase=\"InputGroupElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-input-group-element>\n <lib-autocomplete-element *ngSwitchCase=\"AutocompleteElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-autocomplete-element>\n <lib-card-element *ngSwitchCase=\"CardElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></lib-card-element>\n <div lib-grid-layout-element *ngSwitchCase=\"GridLayoutElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></div>\n <lib-template-element *ngSwitchCase=\"TemplateElement\" [element]=\"anyElement\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\" [templates]=\"templates\"></lib-template-element>\n</div>\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [class]=\"field.class + ' no-top-margin'\">\n <ng-container *ngFor=\"let element of field.elements | keyvalue: originalOrder\">\n <div [class]=\"getElementClass(element)\">\n <div lib-grid-column-element [element]=\"element\" [formGroup]=\"formGroup\" [parentKey]=\"parentKey\"></div>\n </div>\n </ng-container>\n</div>\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<ng-container *ngFor=\"let element of field.elements | keyvalue: originalOrder\">\n <div lib-generated-form-element [formGroup]=\"formGroup\" [element]=\"element\" [parentKey]=\"key\"></div>\n</ng-container>\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div [formGroup]=\"formGroup\">\n <div *ngFor=\"let element of field.elements | keyvalue: originalOrder\" [formGroupName]=\"key\">\n <div lib-generated-form-element [formGroup]=\"childGroup\" [element]=\"element\" [parentKey]=\"key\"></div>\n </div>\n</div>\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<p-card [header]=\"field.title\" [styleClass]=\"field.cssClass\">\n <div *ngFor=\"let element of field.elements | keyvalue: originalOrder\">\n <div lib-generated-form-element [formGroup]=\"formGroup\" [element]=\"element\" [parentKey]=\"key\"></div>\n </div>\n</p-card>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { Directive, Input, TemplateRef } from '@angular/core';\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'ng-template[name]',\n standalone: false,\n})\nexport class TemplateNameDirective {\n @Input() name?: string;\n\n constructor(public template: TemplateRef<any>) {}\n}\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { KeyValue } from '@angular/common';\nimport { AfterContentChecked, ChangeDetectorRef, Component, ContentChildren, EventEmitter, inject, Input, OnChanges, OnInit, Output, QueryList, SimpleChanges } from '@angular/core';\nimport { FormGroup } from '@angular/forms';\nimport { TemplateNameDirective } from '../directive/template-name.directive';\nimport { FormerService } from '../former.service';\nimport { ActionType } from '../model/former.enum';\nimport { ActionButton, ActionResult, FormDefinition } from '../model/former.model';\n\n@Component({\n selector: 'lib-generated-form',\n templateUrl: './generated-form.component.html',\n styleUrls: ['./generated-form.component.css'],\n standalone: false,\n})\nexport class GeneratedFormComponent implements OnInit, OnChanges, AfterContentChecked {\n readonly formerService = inject(FormerService);\n readonly cdr = inject(ChangeDetectorRef);\n\n @ContentChildren(TemplateNameDirective)\n templates!: QueryList<TemplateNameDirective>;\n\n @Input() formDefinition!: FormDefinition;\n\n @Input() formValues!: any;\n\n form!: FormGroup;\n\n @Output() changesEvent = new EventEmitter<any>();\n @Output() submitEvent = new EventEmitter<ActionResult>();\n\n actionHandler?: ActionButton;\n\n ngOnInit(): void {\n this.form = this.formerService.generateForm(this.formDefinition);\n this.form.valueChanges.subscribe(data => this.changesEvent.emit(data));\n this.form.patchValue(this.formValues);\n\n this.form.valueChanges.subscribe(() => this.updateButtonState());\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (this.form && changes['formValues']) {\n this.form.patchValue(this.formValues, { emitEvent: false });\n }\n }\n\n ngAfterContentChecked(): void {\n this.cdr.detectChanges();\n }\n\n // Preserve original property order\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n originalOrder = (a: KeyValue<any, any>, b: KeyValue<any, any>): number => {\n return 0;\n };\n\n setActionHandler(action: ActionButton) {\n this.actionHandler = action;\n }\n\n callActionHandler() {\n if (this.actionHandler) {\n this.submitEvent.emit({\n action: this.actionHandler,\n payload: this.form.value,\n });\n }\n }\n\n public isButtonDisabled(action: KeyValue<string, ActionButton>) {\n return action.value.disabled;\n }\n\n findActionForKey(action: KeyValue<string, ActionButton>): ActionButton | undefined {\n return this.formDefinition.actions[action.key ?? ''];\n }\n\n findSubmitAction(): ActionButton | undefined {\n return Object.values(this.formDefinition.actions).find(action => action.type === ActionType.Submit);\n }\n\n private updateButtonState() {\n if (this.findSubmitAction()) {\n if (this.form.invalid) {\n this.findSubmitAction()!.disabled = true;\n } else {\n this.findSubmitAction()!.disabled = false;\n }\n }\n }\n\n public reset() {\n this.form.reset();\n }\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div>\n <form [formGroup]=\"form\">\n <div *ngFor=\"let element of formDefinition.elements | keyvalue: originalOrder\">\n <div lib-generated-form-element [element]=\"element\" [formGroup]=\"form\" [templates]=\"templates\"></div>\n </div>\n </form>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { KeyValue } from '@angular/common';\n\nexport default class FormerUtils {\n static asKeyValueArray<V>(obj: Record<string, V>): KeyValue<string, V>[] {\n return Array.from(FormerUtils.keyValueGenerator(obj));\n }\n\n static *keyValueGenerator<V>(obj: Record<string, V>): Iterable<KeyValue<string, V>> {\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n yield { key, value };\n }\n }\n}\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { KeyValue } from '@angular/common';\nimport { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';\nimport FormerUtils from '../former.utils';\nimport { GeneratedFormComponent } from '../generated-form/generated-form.component';\nimport { Align } from '../model/former.enum';\nimport { ActionButton } from '../model/former.model';\n\n@Component({\n selector: 'lib-generated-form-actions',\n templateUrl: './generated-form-actions.component.html',\n styleUrls: ['./generated-form-actions.component.css'],\n standalone: false,\n})\nexport class GeneratedFormActionsComponent implements OnInit, OnChanges {\n @Input() form?: GeneratedFormComponent;\n\n leftActions: KeyValue<string, ActionButton>[] = [];\n rightActions: KeyValue<string, ActionButton>[] = [];\n\n ngOnChanges(changes: SimpleChanges): void {\n if (this.form && this.form.formDefinition && this.form.formDefinition.actions && changes['form']) {\n this.initActions();\n }\n }\n\n onButtonClick(action: ActionButton) {\n const form = this.form;\n if (form) {\n form.setActionHandler(action);\n form.callActionHandler();\n }\n }\n\n isButtonDisabled(action: KeyValue<string, ActionButton>) {\n return this.form?.isButtonDisabled(action);\n }\n\n ngOnInit(): void {\n this.initActions();\n }\n\n initActions() {\n if (this.form && this.form.formDefinition && this.form.formDefinition.actions) {\n this.leftActions = FormerUtils.asKeyValueArray(this.form?.formDefinition.actions).filter(action => action.value.align === Align.left);\n this.rightActions = FormerUtils.asKeyValueArray(this.form?.formDefinition.actions).filter(action => action.value.align !== Align.left);\n }\n }\n}\n","<!--\n ~ Apache-2.0 Licence\n ~ Copyright (c) 2023 Tobias Kronschnabl\n ~\n ~ Please see LICENCE for complete licence text.\n -->\n<div class=\"flex flex-row justify-between flex-wrap\">\n <div class=\"mt-2 flex items-center justify-center gap-1\">\n <button *ngFor=\"let action of leftActions\" pButton [label]=\"action.value.label\" [disabled]=\"isButtonDisabled(action)\" (click)=\"onButtonClick(action.value)\"></button>\n </div>\n\n <div class=\"mt-2 flex items-center justify-center gap-1\">\n <button *ngFor=\"let action of rightActions\" pButton [label]=\"action.value.label\" [disabled]=\"isButtonDisabled(action)\" (click)=\"onButtonClick(action.value)\"></button>\n </div>\n</div>\n","/*\n * Apache-2.0 Licence\n * Copyright (c) 2023 Tobias Kronschnabl\n *\n * Please see LICENCE for complete licence text.\n */\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { AutoCompleteModule } from 'primeng/autocomplete';\nimport { ButtonModule } from 'primeng/button';\nimport { CardModule } from 'primeng/card';\nimport { CheckboxModule } from 'primeng/checkbox';\nimport { InputNumberModule } from 'primeng/inputnumber';\nimport { InputTextModule } from 'primeng/inputtext';\nimport { KeyFilterModule } from 'primeng/keyfilter';\nimport { MessageModule } from 'primeng/message';\nimport { CalendarElementComponent } from './elements/calendar-element/calendar-element.component';\nimport { DropdownElementComponent } from './elements/dropdown-element/dropdown-element.component';\nimport { InputGroupElementComponent } from './elements/input-group-element/input-group-element.component';\nimport { TextElementComponent } from './elements/text-element/text-element.component';\nimport { CardElementComponent, GeneratedFormElementComponent, GridColumnElementComponent, GridLayoutElementComponent, GroupElementComponent } from './generated-form-element/generated-form-element.component';\nimport { GeneratedFormComponent } from './generated-form/generated-form.component';\nimport { AutocompleteElementComponent } from './elements/autocomplete-element/autocomplete-element.component';\nimport { CheckboxElementComponent } from './elements/checkbox-element/checkbox-element.component';\nimport { GeneratedFormActionsComponent } from './generated-form-actions/generated-form-actions.component';\ni