UNPKG

ddata-ui-input

Version:

DData UI Input module, components, models & services

1 lines 170 kB
{"version":3,"file":"ddata-ui-input.mjs","sources":["../../../projects/ddata-ui-input/src/lib/models/dialog/content/dialog-content-item.ts","../../../projects/ddata-ui-input/src/lib/models/dialog/content/dialog-content-with-options.ts","../../../projects/ddata-ui-input/src/lib/models/search/search-model-functions.ts","../../../projects/ddata-ui-input/src/lib/models/search/search.model.ts","../../../projects/ddata-ui-input/src/lib/models/search/result/search-result.model.ts","../../../projects/ddata-ui-input/src/lib/pipes/description/description.pipe.ts","../../../projects/ddata-ui-input/src/lib/services/input/helper/input-helper.service.ts","../../../projects/ddata-ui-input/src/lib/components/checkbox/checkbox.component.ts","../../../projects/ddata-ui-input/src/lib/components/checkbox/checkbox.component.html","../../../projects/ddata-ui-input/src/lib/components/color/color-input.component.ts","../../../projects/ddata-ui-input/src/lib/components/color/color-input.component.html","../../../projects/ddata-ui-input/src/lib/components/date/date-input.component.ts","../../../projects/ddata-ui-input/src/lib/components/date/date-input.component.html","../../../projects/ddata-ui-input/src/lib/components/input/input.component.ts","../../../projects/ddata-ui-input/src/lib/components/input/input.component.html","../../../projects/ddata-ui-input/src/lib/models/search/search-concrete.model.ts","../../../projects/ddata-ui-input/src/lib/models/search/base-search.model.ts","../../../projects/ddata-ui-input/src/lib/models/search/result/search-result-concrete.model.ts","../../../projects/ddata-ui-input/src/lib/models/search/result/base-search-result.model.ts","../../../projects/ddata-ui-input/src/lib/components/search/search.component.ts","../../../projects/ddata-ui-input/src/lib/components/search/search.component.html","../../../projects/ddata-ui-input/src/lib/components/select/simple-select/simple-select.component.ts","../../../projects/ddata-ui-input/src/lib/components/select/simple-select/simple-select.component.html","../../../projects/ddata-ui-input/src/lib/components/select/autocomplete-select/autocomplete-select.component.ts","../../../projects/ddata-ui-input/src/lib/components/select/autocomplete-select/autocomplete-select.component.html","../../../projects/ddata-ui-input/src/lib/services/select/component-renderer.service.ts","../../../projects/ddata-ui-input/src/lib/components/select/multiple-select/dialog/multiple-select-dialog.component.ts","../../../projects/ddata-ui-input/src/lib/components/select/multiple-select/dialog/multiple-select-dialog.component.html","../../../projects/ddata-ui-input/src/lib/components/select/multiple-select/multiple-select.component.ts","../../../projects/ddata-ui-input/src/lib/components/select/multiple-select/multiple-select.component.html","../../../projects/ddata-ui-input/src/lib/components/select/select.component.ts","../../../projects/ddata-ui-input/src/lib/components/select/select.component.html","../../../projects/ddata-ui-input/src/lib/components/textarea/textarea.component.ts","../../../projects/ddata-ui-input/src/lib/components/textarea/textarea.component.html","../../../projects/ddata-ui-input/src/lib/components/time/time-input.component.ts","../../../projects/ddata-ui-input/src/lib/components/time/time-input.component.html","../../../projects/ddata-ui-input/src/lib/ddata-ui-input.module.ts","../../../projects/ddata-ui-input/src/public-api.ts","../../../projects/ddata-ui-input/src/ddata-ui-input.ts"],"sourcesContent":["import { Type } from '@angular/core';\n\nexport class DialogContentItem {\n constructor(\n public component: Type<unknown>,\n public data: unknown\n ) {}\n}\n","import { Type } from '@angular/core';\n\nexport class DialogContentWithOptions {\n constructor(\n public component: Type<unknown>,\n public options: unknown\n ) {}\n}\n","// tslint:disable: variable-name\nimport { faCog, IconDefinition } from '@fortawesome/free-solid-svg-icons';\nimport { BaseModel, ID } from 'ddata-core';\nimport { IconSetInterface } from '../icon-set/icon-set.interface';\n\nexport class SearchModelFunctions extends BaseModel {\n id: ID;\n name: string;\n description: string;\n type: string;\n found_model_name: string;\n icon: IconDefinition;\n url: string;\n icons: IconSetInterface = {\n cog: faCog\n };\n\n init(data?: unknown): unknown {\n const searchData = !!data ? data : {};\n\n this.initAsNumberWithDefaults(['id'], searchData);\n\n this.initAsStringWithDefaults(['name', 'description', 'type', 'found_model_name'], searchData);\n\n this.icon = this.setIcon(this.type);\n\n this.url = this.setUrl(this.type);\n\n return this;\n }\n\n protected setUrl(type: string): string {\n return type.replace(new RegExp(/_/, 'g'), '/');\n }\n\n protected setIcon(type: string): IconDefinition {\n if (!type) {\n return this.icons.cog;\n }\n\n return this.icons[type] ?? this.icons.cog;\n }\n}\n","import { SearchModelFunctions } from './search-model-functions';\nimport { SearchInterface } from './search.interface';\n\nexport abstract class SearchAbstract extends SearchModelFunctions implements SearchInterface {\n // tslint:disable: variable-name\n readonly api_endpoint = '/search';\n readonly model_name = 'Search';\n searchText: string;\n\n init(data?: unknown): SearchInterface {\n const searchData = !!data ? data : {};\n\n super.init(searchData);\n\n this.initAsStringWithDefaults(['searchText'], searchData);\n\n return this;\n }\n\n prepareToSave(): unknown {\n return {\n term: !!this.searchText ? this.searchText : ''\n };\n }\n}\n","import { SearchModelFunctions } from '../search-model-functions';\nimport { SearchResultInterface } from './search-result.interface';\n\nexport abstract class SearchResultAbstract\n extends SearchModelFunctions\n implements SearchResultInterface\n{\n init(data?: unknown): SearchResultInterface {\n super.init(data);\n\n return this;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'description',\n standalone: false\n})\nexport class DescriptionPipe implements PipeTransform {\n transform(value: string | null | undefined): unknown {\n const transformValue = !!value ? value : '';\n let result = '';\n const parts = transformValue.split('|');\n\n parts.forEach((part: string) => {\n let processedPart = part.replace(\n new RegExp(/^tel:(.*?)$/),\n '<a href=\"tel:$1\" class=\"mr-3\">$1</a>'\n );\n\n processedPart = processedPart.replace(\n new RegExp(/^email:(.*?)$/),\n '<a href=\"mailto:$1\" class=\"mr-3\">$1</a>'\n );\n processedPart = processedPart.replace(\n new RegExp(/^url:(.*?)$/),\n '<a href=\"$1\" class=\"mr-3\" target=\"_blank\">$1</a>'\n );\n processedPart = processedPart.replace(\n new RegExp(/^description:(.*?)$/),\n '<span class=\"description\">$1</span>'\n );\n\n result += `${processedPart} `;\n });\n\n return result;\n }\n}\n","import {\n BaseModelInterface,\n DdataCoreModule,\n FieldsInterface,\n ValidatorService,\n ValidatorServiceInterface\n} from 'ddata-core';\nimport { InputHelperServiceInterface } from './input-helper-service.interface';\n\nexport class InputHelperService implements InputHelperServiceInterface {\n validatorService: ValidatorServiceInterface =\n DdataCoreModule.InjectorInstance.get<ValidatorService>(ValidatorService);\n\n constructor() {}\n\n validateField(\n model: BaseModelInterface<unknown> & FieldsInterface<unknown>,\n field: string\n ): boolean {\n // handle missing validation rule\n if (!model.validationRules[field]) {\n console.error(`Missing validation rule:${field} from model: ${model.constructor.name}`);\n\n return false;\n }\n const isValid: boolean = this.validatorService.validate(\n model[field],\n model.validationRules[field]\n );\n\n // if not valid & validation error is not set\n if (!isValid && !model.validationErrors.includes(field)) {\n model.validationErrors.push(field);\n\n return false;\n }\n\n // it's valid & validation error set - need remove\n if (model.validationErrors.includes(field)) {\n model.validationErrors.splice(model.validationErrors.indexOf(field), 1);\n }\n\n return true;\n }\n\n getTitle(model: BaseModelInterface<unknown> & FieldsInterface<unknown>, field: string): string {\n if (!model || !model.fields[field] || !model.fields[field].title) {\n console.error(\n `The model not contains the '${field}' field's title. You need to set in your model the fields.${field}.title field.`\n );\n\n return '';\n }\n\n return model.fields[field].title;\n }\n\n getLabel(model: BaseModelInterface<unknown> & FieldsInterface<unknown>, field: string): string {\n if (!model || !model.fields[field] || !model.fields[field].label) {\n console.error(\n `The model not contains the '${field}' field's label. You need to set in your model the fields.${field}.label field.`\n );\n\n return '';\n }\n\n return model.fields[field].label;\n }\n\n getPlaceholder(\n model: BaseModelInterface<unknown> & FieldsInterface<unknown>,\n field: string\n ): string {\n if (!model || !model.fields[field] || !model.fields[field].placeholder) {\n console.error(\n `The model not contains the '${field}' field's placeholder. You need to set in your model the fields.${field}.placeholder field.`\n );\n\n return '';\n }\n\n return model.fields[field].title;\n }\n\n getPrepend(model: BaseModelInterface<unknown> & FieldsInterface<unknown>, field: string): string {\n if (!model || !model.fields[field] || !model.fields[field].prepend) {\n return '';\n }\n\n return model.fields[field].prepend;\n }\n\n getAppend(model: BaseModelInterface<unknown> & FieldsInterface<unknown>, field: string): string {\n if (!model || !model.fields[field] || !model.fields[field].append) {\n return '';\n }\n\n return model.fields[field].append;\n }\n\n isRequired(\n model: BaseModelInterface<unknown> & FieldsInterface<unknown>,\n field: string\n ): boolean {\n try {\n // Check if model and validationRules exist\n if (!model || !model.validationRules || !model.validationRules[field]) {\n return false;\n }\n const rules = model.validationRules[field];\n\n // According to ValidationRuleInterface, rules should be Array<Rule>\n if (Array.isArray(rules)) {\n return rules.includes('required');\n }\n\n // Fallback: Handle object format for compatibility (though this shouldn't be the standard)\n if (typeof rules === 'object' && rules !== null) {\n // Object format: { required: true }\n return Boolean((rules as Record<string, unknown>).required);\n }\n\n // String format: 'required'\n if (typeof rules === 'string') {\n return rules === 'required';\n }\n\n // Boolean format: true (means required)\n if (typeof rules === 'boolean') {\n return rules;\n }\n\n return false;\n } catch {\n return false;\n }\n }\n\n randChars(): string {\n let result = '';\n const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n const charactersLength = characters.length;\n\n for (let i = 0; i < 50; i++) {\n result += characters.charAt(Math.floor(Math.random() * charactersLength));\n }\n\n return result;\n }\n}\n","import {\n Component,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { IconDefinition } from '@fortawesome/fontawesome-svg-core';\nimport { faCheckSquare, faSquare } from '@fortawesome/free-solid-svg-icons';\nimport { BaseModel, BaseModelInterface, FieldsInterface } from 'ddata-core';\n\n@Component({\n selector: 'dd-input-checkbox',\n templateUrl: './checkbox.component.html',\n styleUrls: ['./checkbox.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: false\n})\nexport class DdataInputCheckboxComponent implements OnInit {\n // Input properties\n @Input() disabled = false;\n @Input() showLabel = true;\n @Input() showLabelAfter = true;\n @Input() labelClass = 'col pl-2 col-form-label';\n @Input() wrapperClass = 'd-flex';\n @Input() iconOn: IconDefinition = faCheckSquare;\n @Input() iconOff: IconDefinition = faSquare;\n\n // Output properties\n @Output() readonly changed: EventEmitter<boolean> = new EventEmitter();\n\n // tslint:disable: variable-name\n _model: BaseModelInterface<unknown> & FieldsInterface<unknown> = new BaseModel();\n _field = 'isValid';\n _label = '';\n iterable = 0;\n\n @Input() set model(value: (BaseModelInterface<unknown> & FieldsInterface<unknown>) | null) {\n let actualValue = value;\n\n if (!actualValue) {\n actualValue = new BaseModel();\n }\n\n this._model = actualValue;\n\n if (!!this._model.fields) {\n if (!!this._model.fields[this._field]) {\n this._label = this._model.fields[this._field].label ?? '';\n }\n }\n }\n\n get model(): BaseModelInterface<unknown> & FieldsInterface<unknown> {\n return this._model;\n }\n\n @Input() set field(fieldValue: string) {\n let actualValue = fieldValue;\n\n if (actualValue === 'undefined') {\n actualValue = 'isValid';\n }\n\n this._field = actualValue;\n }\n\n get field(): string {\n return this._field;\n }\n\n constructor() {}\n\n ngOnInit(): void {\n this.iterable = Math.floor(Math.random() * 100);\n }\n\n clicked(): void {\n if (!this.disabled) {\n this.model[this._field] = !this.model[this._field];\n this.changed.emit(this.model[this._field]);\n }\n }\n\n getIcon(): IconDefinition {\n return !!this.model[this._field] ? this.iconOn : this.iconOff;\n }\n}\n","<div [class]=\"wrapperClass\">\n <label\n *ngIf=\"showLabel && !showLabelAfter\"\n [class]=\"labelClass\"\n [class.disabled]=\"disabled\"\n [for]=\"_field + iterable\"\n >\n {{ _label }}:\n </label>\n\n <button\n type=\"button\"\n class=\"btn btn-light\"\n (click)=\"clicked()\"\n [disabled]=\"disabled\"\n [id]=\"_field + iterable\"\n [name]=\"_field + iterable\"\n [title]=\"_label\"\n >\n <fa-icon [icon]=\"getIcon()\"></fa-icon>\n </button>\n\n <label\n *ngIf=\"showLabel && showLabelAfter\"\n [class]=\"labelClass\"\n [class.disabled]=\"disabled\"\n [for]=\"_field + iterable\"\n >\n {{ _label }}\n </label>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ViewChild\n} from '@angular/core';\nimport {\n BaseModel,\n BaseModelInterface,\n DdataCoreModule,\n FieldsInterface,\n ValidatorService\n} from 'ddata-core';\nimport { InputHelperService } from '../../services/input/helper/input-helper.service';\n\n@Component({\n selector: 'dd-input-color',\n templateUrl: './color-input.component.html',\n styleUrls: ['./color-input.component.scss'],\n standalone: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DdataInputColorComponent implements OnInit {\n @Input() disabled = false;\n @Input() type = 'text';\n @Input() inputClass = 'form-control';\n @Input() labelClass = 'col-12 col-md-3 px-0 col-form-label';\n @Input() inputBlockClass = 'col-12 d-flex px-0';\n @Input() inputBlockExtraClass = 'col-md-9';\n @Input() showLabel = true;\n @Input() autoFocus = false;\n @Input() wrapperClass = 'd-flex flex-wrap';\n\n @Output() readonly changed: EventEmitter<BaseModelInterface<unknown> & FieldsInterface<unknown>> =\n new EventEmitter();\n\n @ViewChild('inputBox') inputBox: ElementRef;\n\n // tslint:disable: variable-name\n _field = '';\n _title = '';\n _label = '';\n _placeholder = '';\n _prepend = '';\n _append = '';\n _isRequired = false;\n _model: BaseModelInterface<unknown> & FieldsInterface<unknown> = new BaseModel();\n\n @Input() set model(value: (BaseModelInterface<unknown> & FieldsInterface<unknown>) | null) {\n let modelValue = value;\n\n if (!modelValue) {\n modelValue = new BaseModel();\n }\n\n this._model = modelValue;\n\n if (!!this._model && !!this._model.fields[this._field]) {\n this._title = this.helperService.getTitle(this._model, this._field);\n this._placeholder = this.helperService.getPlaceholder(this._model, this._field);\n this._prepend = this.helperService.getPrepend(this._model, this._field);\n this._append = this.helperService.getAppend(this._model, this._field);\n this._label = this.helperService.getLabel(this._model, this._field);\n }\n\n if (!!this._model && !!this._model.validationRules[this._field]) {\n this._isRequired = this.helperService.isRequired(this._model, this._field);\n }\n }\n\n get model(): BaseModelInterface<unknown> & FieldsInterface<unknown> {\n return this._model;\n }\n\n @Input() set field(value: string) {\n let fieldValue = value;\n\n if (fieldValue === 'undefined') {\n fieldValue = 'isValid';\n }\n\n this._field = fieldValue;\n }\n\n @Input() set append(value: string) {\n let appendValue = value;\n\n if (appendValue === 'undefined') {\n appendValue = '';\n }\n\n this._append = appendValue;\n }\n\n @Input() set prepend(value: string) {\n let prependValue = value;\n\n if (prependValue === 'undefined') {\n prependValue = '';\n }\n\n this._prepend = prependValue;\n }\n\n @Input() set labelText(value: string) {\n let labelValue = value;\n\n if (labelValue === 'undefined') {\n labelValue = '';\n }\n\n this._label = labelValue;\n }\n\n random: string = this.helperService.randChars();\n toggle = false;\n validatorService: ValidatorService =\n DdataCoreModule.InjectorInstance.get<ValidatorService>(ValidatorService);\n\n constructor(private readonly helperService: InputHelperService) {}\n\n ngOnInit(): void {\n if (this.autoFocus) {\n this.inputBox.nativeElement.focus();\n }\n }\n\n validateField(): void {\n const isValid = this.helperService.validateField(this._model, this._field);\n\n if (isValid) {\n this.changed.emit(this._model);\n }\n }\n}\n","<div [class]=\"wrapperClass\">\n <label [class]=\"labelClass\" [for]=\"_field + '_' + random\" *ngIf=\"showLabel\">\n {{ _label }}:\n <span *ngIf=\"_isRequired\"> *</span>\n </label>\n\n <div [class]=\"inputBlockClass\" [ngClass]=\"showLabel ? inputBlockExtraClass : ''\">\n <div\n class=\"input-color-container\"\n [style.background]=\"model[_field]\"\n (click)=\"toggle = !toggle\"\n ></div>\n\n <div class=\"input-group-prepend\" *ngIf=\"_prepend !== ''\">\n <div class=\"input-group-text\">{{ _prepend }}</div>\n </div>\n\n <input\n [class.invalid]=\"model.validationErrors.includes(_field)\"\n [class]=\"inputClass\"\n [(ngModel)]=\"model[_field]\"\n [id]=\"_field + '_' + random\"\n [attr.name]=\"_field + '_' + random\"\n [placeholder]=\"_placeholder\"\n [title]=\"_title\"\n [disabled]=\"disabled\"\n [type]=\"type\"\n [(colorPicker)]=\"model[_field]\"\n [(cpToggle)]=\"toggle\"\n cpPosition=\"bottom\"\n cpOutputFormat=\"hex\"\n cpAlphaChannel=\"disabled\"\n cpFallbackColor=\"#c0c0c0\"\n (keyup)=\"validateField()\"\n #inputBox\n />\n\n <div class=\"input-group-append\" *ngIf=\"_append !== ''\">\n <div class=\"input-group-text\">{{ _append }}</div>\n </div>\n </div>\n</div>\n","import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ViewChild\n} from '@angular/core';\nimport { faCalendar } from '@fortawesome/free-solid-svg-icons';\nimport { NgbDate } from '@ng-bootstrap/ng-bootstrap';\nimport { BaseModel, BaseModelInterface, DdataCoreModule, FieldsInterface } from 'ddata-core';\nimport * as moment from 'moment';\nimport { InputHelperServiceInterface } from '../../services/input/helper/input-helper-service.interface';\nimport { InputHelperService } from '../../services/input/helper/input-helper.service';\n\n@Component({\n selector: 'dd-input-date',\n templateUrl: './date-input.component.html',\n styleUrls: ['./date-input.component.scss'],\n standalone: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DdataInputDateComponent implements OnInit {\n @Input() disabled = false;\n @Input() inputClass = 'form-control';\n @Input() labelClass = 'col-12 col-md-3 px-0 col-form-label';\n @Input() inputBlockClass = 'col-12 d-flex px-0';\n @Input() inputBlockExtraClass = 'col-md-9';\n @Input() showLabel = true;\n @Input() autoFocus = false;\n @Input() isViewOnly = false;\n @Input() viewOnlyClass = 'form-control border-0 bg-light';\n @Input() buttonClass = 'input-group-prepend btn btn-light mb-0';\n @Input() wrapperClass = 'd-flex flex-wrap';\n @Input() format = 'YYYY-MM-DD';\n @Input() separator = '-';\n @Input() labelApply = 'OK';\n @Input() labelCancel = 'Cancel';\n @Input() position: 'left' | 'center' | 'right' = 'center';\n @Input() direction: 'up' | 'down' = 'down';\n @Input() showIcon = true;\n @Input() autoApply = true;\n @Input() singleDatePicker = true;\n\n @Output() readonly changed: EventEmitter<BaseModelInterface<unknown> & FieldsInterface<unknown>> =\n new EventEmitter();\n\n @ViewChild('inputBox') inputBox: ElementRef;\n\n helperService: InputHelperServiceInterface =\n DdataCoreModule.InjectorInstance.get<InputHelperServiceInterface>(InputHelperService);\n\n // tslint:disable: variable-name\n _field = '';\n _title = '';\n _label = '';\n _placeholder = '';\n _prepend = '';\n _append = '';\n _isRequired = false;\n _model: BaseModelInterface<unknown> & FieldsInterface<unknown> = new BaseModel();\n _moment = moment;\n\n @Input() set moment(value: typeof moment) {\n let momentValue = value;\n\n if (!momentValue) {\n momentValue = moment;\n }\n\n this._moment = momentValue;\n }\n\n @Input() set model(value: (BaseModelInterface<unknown> & FieldsInterface<unknown>) | null) {\n let modelValue = value;\n\n if (!modelValue) {\n modelValue = new BaseModel();\n }\n\n this._model = modelValue;\n\n if (!!this._model && !!this._model.fields[this._field]) {\n this._title = this.helperService.getTitle(this._model, this._field);\n this._placeholder = this.helperService.getPlaceholder(this._model, this._field);\n this._prepend = this.helperService.getPrepend(this._model, this._field);\n this._append = this.helperService.getAppend(this._model, this._field);\n this._label = this.helperService.getLabel(this._model, this._field);\n }\n\n if (!!this._model && !!this._model.validationRules[this._field]) {\n this._isRequired = this.helperService.isRequired(this._model, this._field);\n }\n }\n\n get model(): BaseModelInterface<unknown> & FieldsInterface<unknown> {\n return this._model;\n }\n\n @Input() set field(value: string) {\n let fieldValue = value;\n\n if (fieldValue === 'undefined') {\n fieldValue = 'isValid';\n }\n\n this._field = fieldValue;\n }\n\n @Input() set append(value: string) {\n let appendValue = value;\n\n if (appendValue === 'undefined') {\n appendValue = '';\n }\n\n this._append = appendValue;\n }\n\n @Input() set prepend(value: string) {\n let prependValue = value;\n\n if (prependValue === 'undefined') {\n prependValue = '';\n }\n\n this._prepend = prependValue;\n }\n\n @Input() set labelText(value: string) {\n let labelValue = value;\n\n if (labelValue === 'undefined') {\n labelValue = '';\n }\n\n this._label = labelValue;\n }\n\n icon = {\n calendar: faCalendar\n };\n\n random: string = this.helperService.randChars();\n selectedValue = !!this.model[this._field] ? this.model[this._field] : '';\n\n constructor(private readonly changeDetector: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n if (!!this.model[this._field]) {\n this.selectedValue = this.model[this._field];\n }\n\n if (this.autoFocus) {\n this.inputBox.nativeElement.focus();\n }\n }\n\n change(value: NgbDate): void {\n this.selectedValue = `${value.year}-${value.month.toString().padStart(2, '0')}-${value.day\n .toString()\n .padStart(2, '0')}`;\n\n this.model[this._field] = this.selectedValue;\n const isValid = this.helperService.validateField(this._model, this._field);\n\n if (isValid) {\n this.changed.emit(this._model);\n }\n }\n\n typeChange(event: Event): void {\n this._model[this._field] = (event.target as HTMLInputElement).value;\n }\n}\n","<div [class]=\"wrapperClass\">\n <label [class]=\"labelClass\" [for]=\"_field + '_' + random\" *ngIf=\"showLabel\">\n {{ _label }}:\n <span *ngIf=\"_is_required\"> *</span>\n </label>\n\n <div [class]=\"inputBlockClass\" [ngClass]=\"showLabel ? inputBlockExtraClass : ''\">\n <div class=\"input-group-prepend\" *ngIf=\"_prepend !== ''\">\n <div class=\"input-group-text\">{{ _prepend }}</div>\n </div>\n\n <ng-container *ngIf=\"!isViewOnly\">\n <input\n [class.invalid]=\"model.validationErrors.includes(_field)\"\n [class]=\"inputClass\"\n [disabled]=\"disabled\"\n [id]=\"_field + '_' + random\"\n [name]=\"_field + '_' + random\"\n [placeholder]=\"_placeholder\"\n [title]=\"_title\"\n [value]=\"model[_field]\"\n (dateSelect)=\"change($event)\"\n type=\"text\"\n ngbDatepicker\n #d=\"ngbDatepicker\"\n (click)=\"d.toggle()\"\n (change)=\"typeChange($event)\"\n />\n </ng-container>\n\n <ng-container *ngIf=\"isViewOnly\">\n <div [class]=\"viewOnlyClass\">{{ model[_field] }}</div>\n </ng-container>\n\n <label [class]=\"buttonClass\" *ngIf=\"!disabled && showIcon\" [for]=\"_field + '_' + random\">\n <fa-icon [icon]=\"icon.calendar\"></fa-icon>\n </label>\n\n <div class=\"input-group-append\" *ngIf=\"_append !== ''\">\n <div class=\"input-group-text\">{{ _append }}</div>\n </div>\n </div>\n</div>\n","import {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n Output,\n ViewChild,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { BaseModelInterface, FieldsInterface, BaseModel, DdataCoreModule } from 'ddata-core';\nimport { InputHelperServiceInterface } from '../../services/input/helper/input-helper-service.interface';\nimport { InputHelperService } from '../../services/input/helper/input-helper.service';\n\n@Component({\n selector: 'dd-input',\n templateUrl: './input.component.html',\n styleUrls: ['./input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: false\n})\nexport class DdataInputComponent implements AfterViewInit {\n // Input properties\n @Input() disabled = false;\n @Input() isViewOnly = false;\n @Input() type = 'text';\n @Input() inputClass = 'form-control';\n @Input() labelClass = 'col-12 col-md-3 px-0 col-form-label';\n @Input() inputBlockClass = 'col-12 d-flex px-0';\n @Input() inputBlockExtraClass = 'col-md-9';\n @Input() viewOnlyClass = 'form-control border-0 bg-light';\n @Input() wrapperClass = 'd-flex flex-wrap';\n @Input() showLabel = true;\n @Input() autoFocus = false;\n @Input() enableCharacterCounter = false;\n @Input() enableWordCounter = false;\n @Input() maxLength = 255;\n @Input() maxWords = 7;\n @Input() wordCounterWarningMessage = '';\n\n // Output properties\n @Output() readonly changed: EventEmitter<unknown> = new EventEmitter();\n @Output() readonly maxLengthReached: EventEmitter<boolean> = new EventEmitter();\n\n // ViewChild properties\n @ViewChild('inputBox') inputBox: ElementRef;\n\n // Private properties\n helperService: InputHelperServiceInterface =\n DdataCoreModule.InjectorInstance.get<InputHelperServiceInterface>(InputHelperService);\n\n // tslint:disable: variable-name\n _field = '';\n _title = '';\n _label = '';\n _placeholder = '';\n _prepend = '';\n _append = '';\n _max = '';\n _isRequired = false;\n _model: BaseModelInterface<unknown> & FieldsInterface<unknown> = new BaseModel();\n\n random: string = this.helperService.randChars();\n displayWordCounterWarning = false;\n\n @Input() set model(value: (BaseModelInterface<unknown> & FieldsInterface<unknown>) | null) {\n // prevent undefined\n if (!value) {\n console.error('The input-box component get undefined model');\n\n return;\n }\n\n this._model = value;\n\n if (!this._model.fields) {\n console.error(`Your ${this._model.model_name}'s 'fields' field is`, this._model.fields);\n\n return;\n }\n\n if (!this._model.fields[this._field]) {\n console.error(\n `The ${this._model.model_name}'s ${this._field} field is `,\n this._model.fields[this._field]\n );\n\n return;\n }\n\n if (!!this._model && !!this._model.fields[this._field]) {\n this._title = this.helperService.getTitle(this._model, this._field);\n this._placeholder = this.helperService.getPlaceholder(this._model, this._field);\n this._prepend = this.helperService.getPrepend(this._model, this._field);\n this._append = this.helperService.getAppend(this._model, this._field);\n this._label = this.helperService.getLabel(this._model, this._field);\n }\n\n if (!!this._model && !!this._model.validationRules[this._field]) {\n this._isRequired = this.helperService.isRequired(this._model, this._field);\n }\n }\n\n get model(): BaseModelInterface<unknown> & FieldsInterface<unknown> {\n return this._model;\n }\n\n @Input() set field(fieldValue: string) {\n let actualValue = fieldValue;\n\n if (actualValue === 'undefined') {\n actualValue = 'isValid';\n }\n\n this._field = actualValue;\n }\n\n @Input() set append(appendValue: string) {\n let actualValue = appendValue;\n\n if (actualValue === 'undefined') {\n actualValue = '';\n }\n\n this._append = actualValue;\n }\n\n @Input() set prepend(prependValue: string) {\n let actualValue = prependValue;\n\n if (actualValue === 'undefined') {\n actualValue = '';\n }\n\n this._prepend = actualValue;\n }\n\n @Input() set labelText(labelValue: string) {\n let actualValue = labelValue;\n\n if (actualValue === 'undefined') {\n actualValue = '';\n }\n\n this._label = actualValue;\n }\n\n constructor() {}\n\n ngAfterViewInit(): void {\n if (this.autoFocus) {\n this.inputBox.nativeElement.focus();\n }\n }\n\n validateField(): void {\n const isValid = this.helperService.validateField(this._model, this._field);\n\n if (isValid) {\n this.changed.emit(this._model);\n }\n }\n\n setWordCounterWarning(value: boolean): void {\n this.displayWordCounterWarning = value;\n }\n}\n","<div [class]=\"wrapperClass\">\n <label [class]=\"labelClass\" [for]=\"_field + '_' + random\" *ngIf=\"showLabel\">\n {{ _label }}:\n <span *ngIf=\"_isRequired\"> *</span>\n </label>\n <div [class]=\"inputBlockClass\" [ngClass]=\"showLabel ? inputBlockExtraClass : ''\">\n <div class=\"input-group-prepend\" *ngIf=\"_prepend !== ''\">\n <div class=\"input-group-text\">{{ _prepend }}</div>\n </div>\n\n <ng-container *ngIf=\"!isViewOnly\">\n <input\n [class.invalid]=\"model.validationErrors.includes(_field)\"\n [class]=\"inputClass\"\n [(ngModel)]=\"model[_field]\"\n [id]=\"_field + '_' + random\"\n [attr.name]=\"_field + '_' + random\"\n [placeholder]=\"_placeholder\"\n [title]=\"_title\"\n [disabled]=\"disabled\"\n [type]=\"type\"\n [autocomplete]=\"random\"\n (keyup)=\"validateField()\"\n #inputBox\n />\n <ng-container *ngIf=\"enableCharacterCounter\">\n <dd-character-counter\n [currentLength]=\"model[_field]?.length || 0\"\n [maxLength]=\"maxLength\"\n ></dd-character-counter>\n </ng-container>\n <ng-container *ngIf=\"enableWordCounter\">\n <dd-word-counter\n [currentLength]=\"model[_field]\"\n [maxLength]=\"maxWords\"\n (maxLentghReached)=\"showWordCounterWarning = $event\"\n ></dd-word-counter>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"isViewOnly\">\n <div\n [id]=\"_field + '_' + random\"\n [class]=\"viewOnlyClass\"\n [attr.name]=\"_field + '_' + random\"\n [title]=\"_title\"\n >\n {{ model[_field] }}\n </div>\n </ng-container>\n <div class=\"input-group-append\" *ngIf=\"_append !== ''\">\n <div class=\"input-group-text\">{{ _append }}</div>\n </div>\n </div>\n <div *ngIf=\"displayWordCounterWarning && wordCounterWarningMessage !== ''\" class=\"d-flex pb-2\">\n <div class=\"col-12 col-md-7 offset-md-3 bg-warning p-2 rounded\">\n {{ wordCounterWarningMessage }}\n </div>\n </div>\n</div>\n","import { SearchAbstract } from './search.model';\n\nexport class Search extends SearchAbstract {\n // Concrete implementation of SearchAbstract\n}\n","import { Search } from './search-concrete.model';\nimport { SearchInterface } from './search.interface';\n\nexport class BaseSearch extends Search implements SearchInterface {\n // only for prevent undefined values\n}\n","import { SearchResultAbstract } from './search-result.model';\n\nexport class SearchResult extends SearchResultAbstract {\n // Concrete implementation of SearchResultAbstract\n}\n","import { SearchResult } from './search-result-concrete.model';\nimport { SearchResultInterface } from './search-result.interface';\n\nexport class BaseSearchResult extends SearchResult implements SearchResultInterface {\n // only for prevent undefined values\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n HostListener,\n Input,\n OnDestroy,\n ViewChild\n} from '@angular/core';\nimport { Router } from '@angular/router';\nimport { faSearch } from '@fortawesome/free-solid-svg-icons';\n// tslint:disable-next-line: max-line-length\nimport {\n DdataCoreModule,\n Paginate,\n PaginateInterface,\n ProxyFactoryService,\n ProxyServiceInterface,\n SpinnerService,\n SpinnerServiceInterface\n} from 'ddata-core';\nimport { BehaviorSubject, fromEvent, Observable } from 'rxjs';\nimport {\n debounceTime,\n distinctUntilChanged,\n finalize,\n map,\n switchMap,\n take,\n takeUntil,\n tap\n} from 'rxjs/operators';\nimport { IconSetInterface } from '../../models/icon-set/icon-set.interface';\nimport { BaseSearch } from '../../models/search/base-search.model';\nimport { BaseSearchResult } from '../../models/search/result/base-search-result.model';\nimport { SearchResultInterface } from '../../models/search/result/search-result.interface';\nimport { SearchInterface } from '../../models/search/search.interface';\n\n@Component({\n selector: 'dd-search',\n templateUrl: './search.component.html',\n styleUrls: ['./search.component.scss'],\n standalone: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DdataInputSearchComponent implements OnDestroy {\n @Input() model: SearchInterface = new BaseSearch().init();\n @Input() pageNumber = 0;\n @Input() service: ProxyServiceInterface<SearchInterface> =\n new ProxyFactoryService<SearchInterface>().get(BaseSearch);\n\n @ViewChild('searchInput') searchInput: ElementRef;\n\n icon: IconSetInterface = {\n search: faSearch\n };\n\n isActive: BehaviorSubject<boolean> = new BehaviorSubject(false);\n models: Array<SearchResultInterface> = [];\n paginate: PaginateInterface = new Paginate(BaseSearchResult);\n spinner: SpinnerServiceInterface =\n DdataCoreModule.InjectorInstance.get<SpinnerServiceInterface>(SpinnerService);\n\n constructor(\n private readonly elementRef: ElementRef,\n private readonly router: Router\n ) {}\n\n @HostListener('document:click', ['$event']) clickout(event: Event): void {\n if (!this.elementRef.nativeElement.contains(event.target)) {\n // click out of component\n this.close();\n }\n }\n\n ngOnDestroy(): void {\n this.isActive.next(false);\n }\n\n close(): void {\n this.models = [];\n this.isActive.next(false);\n }\n\n search(): Observable<Array<SearchResultInterface>> {\n // don't run if search string is empty, but reset models & close previous connection\n if (this.model.searchText === '') {\n this.isActive.next(false);\n this.models = [];\n\n return;\n }\n\n // close previous connection\n this.isActive.next(false);\n\n return fromEvent(this.searchInput.nativeElement, 'keyup').pipe(\n // run after 500 ms of last keyup\n debounceTime(500),\n\n // run only if value is changed\n distinctUntilChanged(),\n\n // run only if search input is still active and value is not empty string\n takeUntil(this.isActive),\n\n // switch on spinner\n tap(() => this.spinner.on('search')),\n\n // run search method\n switchMap(() =>\n this.service.search(this.model.prepareToSave(), this.pageNumber).pipe(\n map((result: PaginateInterface) => {\n this.setResult(result);\n\n return result.data as Array<SearchResultInterface>;\n })\n )\n ),\n\n // switch off spinner\n finalize(() => this.spinner.off('search'))\n );\n }\n\n changePage(turnToPage: number): void {\n this.service\n .getPage(turnToPage)\n .pipe(\n // run only if search input is still active and value is not empty string\n takeUntil(this.isActive),\n\n // take only last result\n take(1),\n\n // switch on spinner\n tap(() => this.spinner.on('global-search-change-page')),\n\n // set result\n map((result: PaginateInterface) => {\n this.setResult(result);\n\n return result;\n }),\n\n // switch off spinner\n finalize(() => this.spinner.off('global-search-change-page'))\n )\n .subscribe();\n }\n\n go(model: SearchInterface): void {\n const url = `${model.url}/edit/${model.id}`;\n\n this.close();\n this.router.navigateByUrl(url);\n }\n\n private setResult(result: PaginateInterface): void {\n this.paginate = result;\n this.models = [];\n\n result.data.forEach((item: SearchResultInterface) => {\n const model = new BaseSearchResult().init(item);\n\n this.models.push(model);\n });\n }\n}\n","<div class=\"col-12 px-0\">\n <!-- kereső input -->\n <div class=\"input-group\" id=\"dropdownMenuButton\">\n <input\n #searchInput\n type=\"text\"\n autocomplete=\"off\"\n class=\"form-control border-0\"\n placeholder=\"Keresés...\"\n name=\"search-string\"\n [(ngModel)]=\"model.searchText\"\n />\n\n <div class=\"input-group-append\">\n <button type=\"submit\" class=\"btn btn-secondary border-0\" (click)=\"search()\">\n <fa-icon [icon]=\"icon.search\" [fixedWidth]=\"true\"></fa-icon>\n </button>\n </div>\n </div>\n\n <!-- dropdown menü -->\n <div\n class=\"search-dropdown col-12\"\n *ngIf=\"models.length > 0\"\n [ngStyle]=\"{ display: models.length > 0 ? 'block' : 'none' }\"\n >\n <div\n class=\"border-bottom text-left dropdown-item\"\n *ngFor=\"let model of models\"\n (click)=\"go(model)\"\n >\n <div class=\"row\">\n <!-- icon -->\n <div class=\"col-1 px-0 text-center text-muted\">\n <fa-icon [icon]=\"model.icon\" [fixedWidth]=\"true\"></fa-icon>\n </div>\n <div class=\"col-10\">\n <!-- name -->\n <div class=\"row\">\n <div class=\"col-12 font-bold\">\n {{ model.name }}\n </div>\n </div>\n <!-- description -->\n <div class=\"row\">\n <div\n class=\"col-12 small text-muted\"\n [innerHTML]=\"model.description | description\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n\n <app-paginate [paginate]=\"paginate\" (changePage)=\"changePage($event)\"></app-paginate>\n </div>\n</div>\n","import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';\nimport { BaseModelInterface, DdataCoreModule, FieldsInterface } from 'ddata-core';\nimport { InputHelperServiceInterface } from '../../../services/input/helper/input-helper-service.interface';\nimport { InputHelperService } from '../../../services/input/helper/input-helper.service';\n\n@Component({\n selector: 'dd-simple-select',\n templateUrl: './simple-select.component.html',\n styleUrls: ['./simple-select.component.scss'],\n standalone: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DdataSimpleSelectComponent {\n // All @Input and @Output properties first\n @Input() wrapperClass = 'd-flex flex-wrap';\n @Input() inputBlockClass = 'col-12 d-flex px-0';\n @Input() inputBlockExtraClass = 'col-md-9';\n @Input() unselectedText = 'Válassz';\n @Input() isRequired = false;\n @Input() disabledAppearance = false;\n @Input() disabled = false;\n @Input() addEmptyOption = true;\n @Input() labelClass = 'col-12 col-md-3 px-0 col-form-label';\n @Input() showLabel = true;\n @Input() labelText = '';\n @Input() prepend = '';\n @Input() append = '';\n @Input() model: (BaseModelInterface<unknown> & FieldsInterface<unknown>) | null = null;\n @Input() field = 'id';\n @Input() items: Array<unknown> = [];\n @Input() text = 'name';\n @Input() valueField = 'id';\n\n @Output() readonly selected: EventEmitter<unknown> = new EventEmitter();\n @Output() readonly selectModel: EventEmitter<unknown> = new EventEmitter();\n\n // Private fields after @Input/@Output\n private readonly helperService: InputHelperServiceInterface =\n DdataCoreModule.InjectorInstance.get<InputHelperServiceInterface>(InputHelperService);\n\n private readonly random: string = this.helperService.randChars();\n private selectedModel: unknown;\n\n get id(): string {\n return `${this.field}_${this.random}`;\n }\n\n selectItem(): void {\n if (!this.model) {\n return;\n }\n\n this.selectedModel = this.items.find(\n (item) => item[this.valueField] === this.model[this.field]\n );\n\n this.selected.emit(this.model[this.field]);\n this.selectModel.emit(this.selectedModel);\n }\n}\n","@if (model) {\n <div [class]=\"wrapperClass\">\n <!-- label -->\n @if (showLabel) {\n <label [class]=\"labelClass\" [for]=\"id\">\n {{ labelText }}:\n @if (isRequired) {\n <span aria-hidden=\"true\"> *</span>\n }\n </label>\n }\n\n <!-- select -->\n <div [class]=\"inputBlockClass\" [ngClass]=\"showLabel ? inputBlockExtraClass : ''\">\n <!-- prepend -->\n @if (!!prepend) {\n <div class=\"input-group-prepend\">\n <div class=\"input-group-text\">{{ prepend }}</div>\n </div>\n }\n\n <select\n class=\"form-select\"\n [id]=\"id\"\n [(ngModel)]=\"model[field]\"\n [class.invalid]=\"model.validationErrors?.includes(field)\"\n [class.disable-appearance]=\"disabledAppearance\"\n [disabled]=\"disabled\"\n [attr.aria-required]=\"isRequired\"\n (change)=\"selectItem()\"\n >\n @if (addEmptyOption) {\n <option data-default value=\"0\">-- {{ unselectedText }} --</option>\n }\n\n @for (item of items; track item[valueField]) {\n <option [value]=\"item[valueField]\" [selected]=\"item[valueField] === model[field]\">\n {{ item[text] }}\n </option>\n }\n </select>\n\n <!-- append -->\n @if (!!append) {\n <div class=\"input-group-append\">\n <div class=\"input-group-text\">{{ append }}</div>\n </div>\n }\n </div>\n </div>\n}\n","import {\n Component,\n EventEmitter,\n Input,\n Output,\n ViewChild,\n ElementRef,\n HostListener,\n OnInit,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { BaseModelInterface, DdataCoreModule, FieldsInterface } from 'ddata-core';\nimport { InputHelperServiceInterface } from '../../../services/input/helper/input-helper-service.interface';\nimport { InputHelperService } from '../../../services/input/helper/input-helper.service';\n\n@Component({\n selector: 'dd-autocomplete-select',\n templateUrl: './autocomplete-select.component.html',\n styleUrls: ['./autocomplete-select.component.scss'],\n standalone: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DdataAutocompleteSelectComponent implements OnInit {\n // All @Input and @Output properties first\n @Input() wrapperClass = 'd-flex flex-wrap';\n @Input() inputBlockClass = 'col-12 d-flex px-0';\n @Input() inputBlockExtraClass = 'col-md-9';\n @Input() unselectedText = 'Válassz vagy írj...';\n @Input() isRequired = false;\n @Input() disabledAppearance = false;\n @Input() disabled = false;\n @Input() addEmptyOption = true;\n @Input() labelClass = 'col-12 col-md-3 px-0 col-form-label';\n @Input() showLabel = true;\n @Input() labelText = '';\n @Input() prepend = '';\n @Input() append = '';\n @Input() model: BaseModelInterface<unknown> & FieldsInterface<unknown>;\n @Input() field = 'id';\n @Input() items: Array<unknown> = [];\n @Input() text = 'name';\n @Input() valueField = 'id';\n\n @Output() readonly selected: EventEmitter<unknown> = new EventEmitter();\n @Output() readonly selectModel: EventEmitter<unknown> = new EventEmitter();\n\n @ViewChild('inputBox') inputBox: ElementRef;\n\n // Private and internal fields after @Input/@Output\n private helperService: InputHelperServiceInterface;\n private random: string;\n private selectedModel: unknown;\n\n // Internal state\n isOpen = false;\n filteredItems: Array<unknown> = [];\n selectedIndex = -1;\n inputValue = '';\n\n constructor(private readonly elementRef: ElementRef) {}\n\n @HostListener('document:click', ['$event'])\n onClickOutside(event: Event): void {\n if (!this.elementRef.nativeElement.contains(event.target)) {\n this.closeDropdown();\n }\n }\n\n ngOnInit(): void {\n this.inputValue = this.selectedItemText;\n this.filteredItems = [...this.items];\n }\n\n onInputFocus(): void {\n this.openDropdown();\n }\n\n onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n\n this.inputValue = target.value;\n this.filterItems();\n this.openDropdown();\n this.selectedIndex = -1;\n }\n\n onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.navigateDown();\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.navigateUp();\n break;\n case 'Enter':\n event.preventDefault();\n this.selectCurrentItem();\n break;\n case 'Escape':\n event.preventDefault();\n this.closeDropdown();\n this.inputBox.nativeElement.blur();\n break;\n case 'Tab':\n this.closeDropdown();\n break;\n default:\n // Handle other key presses if needed\n break;\n }\n }\n\n selectItem(item: unknown, index?: number): void {\n this.selectedModel = item;\n this.model[this.field] = item[this.valueField];\n this.inputValue = item[this.text];\n\n if (typeof index === 'number') {\n this.selectedIndex = index;\n }\n\n this.closeDropdown();\n\n this.selected.emit(this.model[this.field]);\n this.selectModel.emit(this.selectedModel);\n }\n\n getOptionId(index: number): string {\n return `${this.listboxId}_option_${index}`;\n }\n\n getActiveDescendant(): string | null {\n return this.isOpen && this.selectedIndex >= 0 ? this.getOptionId(this.selectedIndex) : null;\n }\n\n private getHelperService(): InputHelperServiceInterface {\n if (!this.helperService) {\n this.helperService =\n DdataCoreModule.InjectorInstance?.get<InputHelperServiceInterface>(InputHelperService);\n }\n\n return this.helperService;\n }\n\n get id(): string {\n if (!this.random) {\n const helperRandom = this.getHelperService()?.randChars();\n const randomBase = Math.random().toString(36);\n const fallbackRandom = randomBase.substr(2, 9);\n\n this.random = helperRandom || `autocomplete-${fallbackRandom}`;\n }\n\n return `${this.field}_${this.random}`;\n }\n\n get listboxId(): string {\n return `${this.id}_listbox`;\n }\n\n get selectedItemText(): string {\n if (!this.model[this.field]) {\n return '';\n }\n const selectedItem = this.items.find(\n (item) => item[this.valueField] === this.model[this.field]\n );\n\n return selectedItem ? selectedItem[this.text] : '';\n }\n\n private filterItems(): void {\n if (!this.inputValue.trim()) {\n this.filteredItems = [...this.items];\n\n return;\n }\n const searchText = this.inputValue.toLowerCase();\n\n this.filteredItems = this.items.filter((item) =>\n item[this.text].toLowerCase().includes(searchText)\n );\n }\n\n private openDropdown(): void {\n if (!this.disabled) {\n this.isOpen = true;\n }\n }\n\n private closeDropdown(): void {\n this.isOpen = false;\n this.selectedIndex = -1;\n }\n\n private navigateDown(): void {\n if (!this.isOpen) {\n this.openDropdown();\n\n return;\n }\n\n if (this.selectedIndex < this.filteredItems.length - 1) {\n this.selectedIndex++;\