import { ChangeDetectionStrategy, ChangeDetectorRef, Renderer2, ViewEncapsulation, } from '@angular/core'; import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, Self, SimpleChanges, ViewChild, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { debounceTime, skip, Subject, takeUntil } from 'rxjs'; // bootstrap import { NgbPopover, NgbPopoverModule, NgbModule, } from '@ng-bootstrap/ng-bootstrap'; // animation import { inputDropdownAnimation } from './animations'; // config import { ICaInput } from '../ca-input/config'; // components import { CaInputComponent } from '../ca-input/ca-input.component'; import { CaInputDropdownLoadBrokerComponent } from './components/ca-input-dropdown-load-broker/ca-input-dropdown-load-broker.component'; import { CaInputDropdownLoadDispatchesTtdComponent } from './components/ca-input-dropdown-load-dispatches-ttd/ca-input-dropdown-load-dispatches-ttd.component'; import { CaInputDropdownLoadDispatcherComponent } from './components/ca-input-dropdown-load-dispatcher/ca-input-dropdown-load-dispatcher.component'; import { CaInputDropdownSvgTemplateComponent } from './components/ca-input-dropdown-svg-template/ca-input-dropdown-svg-template.component'; import { CaInputDropdownSvgtextTemplateComponent } from './components/ca-input-dropdown-svgtext-template/ca-input-dropdown-svgtext-template.component'; import { CaInputDropdownSvgtextDispatchTemplateComponent } from './components/ca-input-dropdown-svgtext-dispatch-template/ca-input-dropdown-svgtext-dispatch-template.component'; import { CaInputDropdownGroupsComponent } from './components/ca-input-dropdown-groups/ca-input-dropdown-groups.component'; import { CaInputDropdownMultiselectComponent } from './components/ca-input-dropdown-multiselect/ca-input-dropdown-multiselect.component'; import { CaInputDropdownLabelsComponent } from './components/ca-input-dropdown-labels/ca-input-dropdown-labels.component'; import { CaInputDropdownLoadBrokerContactComponent } from './components/ca-input-dropdown-load-broker-contact/ca-input-dropdown-load-broker-contact.component'; import { CaInputDropdownLoadBrokerShipperComponent } from './components/ca-input-dropdown-load-broker-shipper/ca-input-dropdown-load-broker-shipper.component'; import { CaInputDropdownTextCounterComponent } from './components/ca-input-dropdown-text-counter/ca-input-dropdown-text-counter.component'; import { CaInputDropdownDoubleTextTemplateComponent } from './components/ca-input-dropdown-double-text-template/ca-input-dropdown-double-text-template.component'; import { CaInputDropdownTripleTextTemplateComponent } from './components/ca-input-dropdown-triple-text-template/ca-input-dropdown-triple-text-template.component'; import { CaInputDropdownDefaultTemplateComponent } from './components/ca-input-dropdown-default-template/ca-input-dropdown-default-template.component'; import { CaInputDropdownFuelFranchiseComponent } from './components/ca-input-dropdown-fuel-franchise/ca-input-dropdown-fuel-franchise.component'; import { CaInputDropdownDispatchComponent } from './components/ca-input-dropdown-dispatch/ca-input-dropdown-dispatch.component'; import { CaInputDropdownDetailsTemplateComponent } from './components/ca-input-dropdown-details-template/ca-input-dropdown-details-template.component'; import { CaInputDropdownPayrollTrucksComponent } from './components/ca-input-dropdown-payroll-trucks/ca-input-dropdown-payroll-trucks.component'; import { CaInputDropdownParkingComponent } from './components/ca-input-dropdown-parking/ca-input-dropdown-parking.component'; // pipes import { FormControlPipe } from '../ca-input/pipes'; import { DropdownCountPipe, InputDropdownMultiselectClassPipe } from './pipes'; // modules import { AngularSvgIconModule } from 'angular-svg-icon'; // directives import { ControlValueAccessor, FormsModule, NgControl, ReactiveFormsModule, } from '@angular/forms'; // models import { CommandsEvent } from '../ca-input/models'; import { OptionModel } from './models/input-dropdown-option.model'; // services import { ImageBase64Service } from '../../services/image-base64.service'; // svg routes import { InputDropdownSvgRoutes } from './utils/svg-routes/input-dropdown-svg-routes'; // enums import { DropdownStringEnum, DropdownTemplateTypeEnum } from './enums'; // helpers import { uuidv4 } from '../../utils/helpers'; @Component({ selector: 'app-ca-input-dropdown', templateUrl: './ca-input-dropdown.component.html', styleUrls: ['./ca-input-dropdown.component.scss'], encapsulation: ViewEncapsulation.None, providers: [FormControlPipe], changeDetection: ChangeDetectionStrategy.OnPush, animations: [inputDropdownAnimation('showHideDropdownOptions')], imports: [ // Module CommonModule, FormsModule, NgbPopoverModule, ReactiveFormsModule, NgbModule, AngularSvgIconModule, // Component CaInputComponent, CaInputDropdownSvgTemplateComponent, CaInputDropdownSvgtextTemplateComponent, CaInputDropdownSvgtextDispatchTemplateComponent, CaInputDropdownGroupsComponent, CaInputDropdownMultiselectComponent, CaInputDropdownLabelsComponent, CaInputDropdownLoadBrokerComponent, CaInputDropdownLoadDispatchesTtdComponent, CaInputDropdownLoadDispatcherComponent, CaInputDropdownLoadBrokerContactComponent, CaInputDropdownLoadBrokerShipperComponent, CaInputDropdownTextCounterComponent, CaInputDropdownDoubleTextTemplateComponent, CaInputDropdownTripleTextTemplateComponent, CaInputDropdownDefaultTemplateComponent, CaInputDropdownFuelFranchiseComponent, CaInputDropdownDispatchComponent, CaInputDropdownDetailsTemplateComponent, CaInputDropdownPayrollTrucksComponent, CaInputDropdownParkingComponent, // Pipes FormControlPipe, DropdownCountPipe, InputDropdownMultiselectClassPipe, ] }) export class CaInputDropdownComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor { @ViewChild('input') inputRef!: CaInputComponent; @ViewChild('t2') public popoverRef!: NgbPopover; // different templates for body rendering public _template!: string; _canAddNew: boolean = false; @Input() set template(value: string) { this._template = value; if (value === 'details-template' && this.isDetailsPages) { this.clearTimeoutDropdown = setTimeout(() => { this.inputRef.setInputCursorAtTheEnd( this.inputRef.input.nativeElement ); const option = this._options.find((item) => item.active); this._activeItem = option || null; this.getSuperControl?.setValue(option?.name); const timeout2 = setTimeout(() => { this.popoverRef.open(); clearTimeout(timeout2); }, 150); }); } } @Input() multiselectTemplate!: string; @Input() inputConfig!: ICaInput; @Input() set canAddNew(value: boolean) { this._canAddNew = value; } // ADD NEW item in options @Input() canOpenModal!: boolean; // open modal with ADD NEW button @Input() isAddressDropdown!: boolean; // only for address dropdown // sort-template for different options public _sort!: string; @Input() set sort(value: string) { this._sort = value; } // currently active item public _activeItem!: OptionModel | null; @Input() set activeItem(value: OptionModel | null) { this.inputConfig = { ...this.inputConfig, blackInput: true, }; this._activeItem = value; // With address if ( this.inputConfig.name && this.inputConfig.name.toLowerCase().includes('address') && this._activeItem ) { if (Object.keys(this._activeItem).length > 0) { this.getSuperControl?.patchValue( value?.address ? value?.address : null ); } if (!this.inputConfig.hideColorValidations) { this.clearTimeoutDropdown = setTimeout(() => { this.inputConfig = { ...this.inputConfig, blackInput: false, }; this.cdRef.detectChanges(); }, 150); } } // Without address else { if (this._activeItem) { this.clearTimeoutDropdown = setTimeout(() => { this.getSuperControl!.patchValue( value?.number ? value?.number : value?.name ? value?.name : (value?.companyName ?? null) ); if (!this.inputConfig.hideColorValidations) { this.inputConfig = { ...this.inputConfig, blackInput: false, }; } this.cdRef.detectChanges(); }, 150); } } } @Input() activeItemColor!: OptionModel | null; // currently active color in dropdown @Input() labelMode!: 'Label' | 'Color'; // when send SVG, please premmaped object: add 'folder' | 'subfolder' public _options: OptionModel[] = []; @Input() set options(values: OptionModel[]) { if (values) this._options = [...values]; switch (this._sort) { case 'active-drivers': { this._options = values.sort( (x, y) => Number(y.status) - Number(x.status) ); this.originalOptions = [...this._options]; break; } default: { setTimeout(() => { if ( this._canAddNew && !this._options.find((item) => item.id === 7655) ) { this._options.unshift({ id: 7655, name: DropdownStringEnum.ADD_NEW, }); } this.originalOptions = this._options; }, 100); break; } } } // MultiSelect Selected Items From Backend @Input() set preloadMultiselectItems(values: OptionModel[]) { if (this.inputConfig.multiselectDropdown) { if (!values) { this.deleteAllMultiSelectItems(this.inputConfig.label); return; } if (values?.length) { values.forEach((item) => { this.onMultiselectSelect(item); }); } } } @Input() isDetailsPages!: boolean; // only for details pages @Input() isIncorrectValue!: boolean; // applicant review option @Output() selectedItem: EventEmitter = new EventEmitter(); @Output() selectedItems: EventEmitter = new EventEmitter(); @Output() selectedItemColor: EventEmitter = new EventEmitter(); @Output() selectedLabelMode: EventEmitter = new EventEmitter(); @Output() closeDropdown: EventEmitter = new EventEmitter(); @Output() saveItem: EventEmitter<{ data: OptionModel | null; action: string; }> = new EventEmitter<{ data: OptionModel | null; action: string }>(); @Output() incorrectEvent: EventEmitter = new EventEmitter(); @Output() placeholderIconEvent: EventEmitter = new EventEmitter(); @Output('pagination') paginationEvent: EventEmitter = new EventEmitter(); @Output('activeGroup') activeGroupEvent: EventEmitter = new EventEmitter(); @Output('clearInputEvent') clearInputEvent: EventEmitter = new EventEmitter(); // Copy of Options public originalOptions: OptionModel[] = []; // Pagination public paginationNumber: number = 0; // Multiselect dropdown options public multiselectItems: OptionModel[] = []; public isMultiSelectInputFocus: boolean = false; public multiSelectLabel!: string | undefined; public lastActiveMultiselectItem!: OptionModel | null; // Add mode public isInAddMode: boolean = false; // Dropdown navigation with keyboard private dropdownPosition: number = -1; // Dropdown Cleartimeout public clearTimeoutDropdown: string | number | ReturnType | undefined; public hoveringIndex!: number; public inputDropdownSvgRoutes = InputDropdownSvgRoutes; public dropdownTemplateTypeEnum = DropdownTemplateTypeEnum; public originalInputConfig!: ICaInput; // Destroy private destroy$ = new Subject(); constructor( @Self() public superControl: NgControl, public imageBase64Service: ImageBase64Service, private cdRef: ChangeDetectorRef, private renderer: Renderer2 ) { this.superControl.valueAccessor = this; } get getSuperControl() { return this.superControl.control; } writeValue(_: any): void {} registerOnChange(_: any): void {} registerOnTouched(_: any): void {} ngOnInit(): void { // Multiselect if (this.inputConfig.multiselectDropdown) { this.multiSelectLabel = this.inputConfig.label; } // Search this.getSuperControl!.valueChanges.pipe( debounceTime(50), takeUntil(this.destroy$), skip(1) ).subscribe((searchText) => { if (this.labelMode === 'Color') { return; } this.search(searchText); }); this.originalInputConfig = structuredClone(this.inputConfig); } ngOnChanges(changes: SimpleChanges) { if (changes['options'] && changes['options'].currentValue) { this.originalOptions = changes['options'].currentValue; } } ngAfterViewInit() { if (this.inputConfig.autoFocus) { this.clearTimeoutDropdown = setTimeout(() => { this.popoverRef.open(); }, 450); } } public onScrollDropdown(event: EventTarget | null) { const target = event as HTMLElement; if (target.scrollTop + target.offsetHeight === target.scrollHeight) { this.paginationNumber += 1; this.paginationEvent.emit(this.paginationNumber); } } public onActiveItem(option: OptionModel, group?: any): void { // Prevent user to pick franchise, without group if ( this._template === 'fuel-franchise' && option?.isFranchise && !group ) { return; } // Disable to picking banned or dnu user if (option?.dnu || option?.ban) { return; } // No Result if (option.id === 7654 || option.id === 7656) { return; } // ADD NEW else if (option.id === 7655) { // Open New Modal if (this.canOpenModal) { this.selectedItem.emit({ ...option, canOpenModal: true }); } // Work with current modal else { // DropDown label if (this.inputConfig.dropdownLabel) { this.inputConfig.dropdownLabelNew = true; this.inputRef.isEditInput = true; this.selectedLabelMode.emit('Color'); this.inputConfig.commands!.active = true; this.inputRef.setInputCursorAtTheEnd( this.inputRef.input.nativeElement ); this.selectedItem.emit(option); } // Normal Dropdown else { this.addNewConfig(); } } } // Pick the item else { this.inputConfig.selectedDropdown = true; // Dropdown labels option selected if (this.inputConfig.dropdownLabel) { if (this.labelMode === 'Label') { this._activeItem = option; this.getSuperControl?.setValue(option.name); this._options = this.originalOptions; this.selectedItem.emit(option); } if (this.labelMode === 'Color') { this.activeItemColor = option; this.selectedItemColor.emit(this.activeItemColor); } } // Normal Dropdown option selected else { this._activeItem = option; this._options = this.originalOptions; if (this.inputConfig.name !== 'RoutingAddress') { this.getSuperControl?.patchValue( option?.number ? option.number : option.name ); if (this._template === 'fuel-franchise') { this.getSuperControl!.patchValue( group ? option.name : option.businessName ); const { id } = option; group ? this.selectedItem.emit({ ...option, ...group, storeId: id, }) : this.selectedItem.emit(option); } else { group ? this.selectedItem.emit({ ...option, ...group, }) : this.selectedItem.emit(option); } } } this.getSuperControl?.markAsDirty(); this.popoverRef.close(); } if (this._template === 'fuel-franchise') { this.clearTimeoutDropdown = setTimeout(() => { this.popoverRef.close(); }, 100); } } public onActiveItemGroup(event: { option: OptionModel; group: any }) { this.onActiveItem(event.option, event.group); } public onClearSearch(): void { this._options = this.originalOptions; this._activeItem = null; this.inputConfig.selectedDropdown = false; this.getSuperControl?.patchValue(null); this.inputConfig = { ...this.inputConfig, placeholder: this.inputConfig.placeholderWithLabel ? this.originalInputConfig.placeholder : '', dropdownImageInput: null, }; this.selectedItem.emit(null); } public clearDropdownLabel() { this._activeItem = null; this.activeItemColor = null; this.selectedItem.emit(null); this.selectedItemColor.emit(null); this.selectedLabelMode.emit('Label'); } public commandEvent(event: CommandsEvent) { if (event.action === 'Edit Input') { this.selectedLabelMode.emit('Color'); this.inputConfig.dropdownLabelNew = false; } if (event.action === 'Toggle Dropdown') { this.popoverRef.toggle(); } if (event.action === 'confirm' && event.mode === 'new') { this.addNewItem(); } if (event.action === 'confirm' && event.mode === 'edit') { this.updateItem(); } if (event.action === 'Placeholder Icon Event') { this.placeholderIconEvent.emit(true); } if (event.action === 'cancel') { this.saveItem.emit({ data: this._activeItem, action: 'cancel', }); this.selectedLabelMode.emit('Label'); } } public addNewItem(): void { this._activeItem = { id: parseInt(uuidv4()), name: this.getSuperControl!.value, }; this.inputConfig.commands!.active = false; this.inputRef.isVisibleCommands = false; this.inputRef.isFocusInput = false; this.saveItem.emit({ data: this._activeItem, action: 'new' }); if (this.inputConfig.dropdownLabel) { this.selectedLabelMode.emit('Label'); this.inputRef.isTouchedInput = true; } } public updateItem(): void { if (this.inputConfig.dropdownLabel) { this._activeItem = { ...this._activeItem, name: this.getSuperControl?.value, colorId: this.activeItemColor ? this.activeItemColor.id : this._activeItem?.colorId, color: this.activeItemColor ? this.activeItemColor.name : this._activeItem?.color, code: this.activeItemColor ? this.activeItemColor.code : this._activeItem?.code, }; this.selectedLabelMode.emit('Label'); } else { this._activeItem = { ...this._activeItem, name: this.getSuperControl?.value, }; } this.saveItem.emit({ data: this._activeItem, action: 'edit', }); } public addNewConfig() { this.inputConfig = { ...this.inputConfig, commands: { active: true, type: 'confirm-cancel', firstCommand: { popup: { name: 'Confirm', backgroundColor: '#3074d3', }, name: 'confirm', svg: InputDropdownSvgRoutes.specConfirmSvg, }, secondCommand: { popup: { name: 'Cancel', backgroundColor: '#2f2f2f', }, name: 'cancel', svg: InputDropdownSvgRoutes.xClearSvg, }, }, placeholder: '', }; this.popoverRef.close(); this.isInAddMode = true; this.clearTimeoutDropdown = setTimeout(() => { this.isInAddMode = false; }, 200); } public onIncorrectInput(event: boolean) { this.incorrectEvent.emit(event); } public identity(index: number, item: OptionModel): number | undefined { return item.id; } public toggleNestedList(option: OptionModel): void { if (option.open) { option.open = false; return; } this._options.filter((item) => (item.open = false)); option.open = !option.open; if (option.open) { this.activeGroupEvent.emit(option); } } public onBlurInput(event: boolean) { this.closeDropdown.emit(event); } public onClearInputEvent(event: boolean) { this.clearInputEvent.emit(event); if (event) { this.popoverRef.close(); // label dropdown if (this.inputConfig.dropdownLabel) { this.clearDropdownLabel(); } // normal dropdown else { this.onClearSearch(); } } } public showHideDropdown(action: boolean) { if (this.inputConfig.multiselectDropdown) { this.isMultiSelectInputFocus = action; } if (this.labelMode !== 'Color') { // Focus Out if (!action) { if (this._template !== 'fuel-franchise') { this.popoverRef.open(); } // Prevent user to typing dummmy data if _activeItem doesn't exist if (this._activeItem) { this.getSuperControl!.setValue(this._activeItem.name); } else { const index = this.originalOptions.findIndex( (item) => item.name === this.getSuperControl!.value ); if (index === -1) { this.onClearSearch(); } } if (this._template !== 'fuel-franchise') { this.popoverRef.close(); } } // Focus In else { this.inputConfig = { ...this.inputConfig, placeholder: this.getSuperControl!.value ? this.getSuperControl!.value : this.inputConfig.placeholderWithLabel ? this.originalInputConfig.placeholder : this._activeItem?.name, }; this.getSuperControl!.setValue(null); if (this.popoverRef) { this.popoverRef.close(); } if (this.isInAddMode) { this.inputConfig = { ...this.inputConfig, placeholder: '', }; } } } // Details pages if ( this.inputConfig.customClass?.includes('details-pages') && !action ) { this.selectedItem.emit(this._activeItem); } } public dropDownKeyNavigation({ keyCode, data, }: { keyCode: number; data: any; }) { // Navigate down if (keyCode === 40) { this.dropdownNavigation(1); } // Navigate up if (keyCode === 38) { this.dropdownNavigation(-1); } // Press Escape if (keyCode === 27) { if (this.inputConfig?.commands?.active) { this.inputConfig.commands = undefined; this.inputRef.isVisibleCommands = false; this.inputRef.isFocusInput = false; } if (this.inputConfig.dropdownLabel) { this.getSuperControl?.setErrors(null); this.inputConfig.dropdownLabelNew = false; this.inputConfig.commands!.active = false; if (!this.inputConfig.hideColorValidations) { this.inputConfig.blackInput = false; } this.inputRef.isFocusInput = false; this.inputRef.isEditInput = false; this.inputRef.input.nativeElement.blur(); if (this.labelMode === 'Color') { this.getSuperControl!.patchValue(null); this.selectedLabelMode.emit('Label'); } } } // Press 'enter' if (keyCode === 13) { let selectedItem = this.renderer.selectRootElement( '.dropdown-option-hovered', true ); // // Open New Modal if (this.canOpenModal && selectedItem.toLowerCase() === 'add new') { this.selectedItem.emit({ id: 7655, name: DropdownStringEnum.ADD_NEW, canOpenModal: true, }); } else { if (this._options.length === 1 && !selectedItem) { if (this._template === 'fuel-franchise') { selectedItem = this._options[0]?.businessName ? this._options[0]?.businessName : this._options[0]?.name || ''; } else { selectedItem = this._options[0]?.number ? this._options[0]?.number.toString().trim() : this._options[0].name?.toString().trim(); } } this.pickupElementWithKeyboard(selectedItem, data); } } if (keyCode === 9) { if ( this._options.length === 1 && this._options[0].id !== 7655 && this._options[0].id !== 7654 ) { let selectedItem = null; if (this._template === 'fuel-franchise') { selectedItem = this._options[0]?.businessName ? this._options[0]?.businessName : this._options[0]?.name; } else { selectedItem = this._options[0]?.number ? this._options[0]?.number.toString().trim() : this._options[0].name!.toString().trim(); } this.pickupElementWithKeyboard(selectedItem, data); } else { this.popoverRef.open(); } } } // ---------------------------------- Multiselect Dropdown ---------------------------------- public onMultiselectSelect(option: OptionModel): void { this.isMultiSelectInputFocus = false; this.inputConfig.label = undefined; if (this.multiselectItems.some((item) => item.id === option.id)) { return; } this._options = this._options.map((item) => { if (item.id === option.id) { return { ...item, active: true, }; } else { if (!item.active) { return { ...item, active: false, }; } else { return { ...item, active: true, }; } } }); this.multiselectItems = this._options.filter((item) => item.active); this.selectedItems.emit(this.multiselectItems); this._options = this._options.sort( (x, y) => Number(y.active) - Number(x.active) ); this.originalOptions = [...this._options]; this.lastActiveMultiselectItem = this._options .filter((item) => item.active) .slice(-1)[0]; if (this.inputRef) { this.inputRef.isFocusInput = false; this.inputRef.input.nativeElement.blur(); } this.inputConfig = { ...this.inputConfig, multiSelectDropdownActive: true, }; } public removeMultiSelectItem(index: number) { this._options = this.originalOptions.map((item) => { if (item.id === this.multiselectItems[index].id) { return { ...this.multiselectItems[index], active: false, }; } return item; }); this._options = this._options.sort( (x, y) => Number(y.active) - Number(x.active) ); this.originalOptions = this._options; this.multiselectItems.splice(index, 1); if (!this.multiselectItems.length) { this.inputConfig = { ...this.inputConfig, multiSelectDropdownActive: undefined, }; this.lastActiveMultiselectItem = null; this.inputConfig.label = this.multiSelectLabel; } else { this.lastActiveMultiselectItem = this._options .filter((item) => item.active) .slice(-1)[0]; } this.selectedItems.emit( this.multiselectItems.map((item) => { return { ...item }; }) ); } public deleteAllMultiSelectItems(currentLabel?: string) { this.multiselectItems = []; this.inputConfig = { ...this.inputConfig, multiSelectDropdownActive: undefined, }; this.inputConfig.label = currentLabel ? currentLabel : this.multiSelectLabel; this._options = this._options.map((item) => { return { ...item, active: false, }; }); this.originalOptions = this._options; this.selectedItems.emit(null); this.lastActiveMultiselectItem = null; } public toggleMultiselectDropdown() { if (this.inputConfig.isDisabled) { return; } this.isMultiSelectInputFocus = !this.isMultiSelectInputFocus; if (this.isMultiSelectInputFocus) { this.inputRef.setInputCursorAtTheEnd( this.inputRef.input.nativeElement ); this.clearTimeoutDropdown = setTimeout(() => { this.popoverRef.open(); }, 150); } else { this.inputRef.isFocusInput = false; this.popoverRef.close(); } } // ---------------------------------- End ---------------------------------- private pickupElementWithKeyboard( selectedItem: string | undefined, data: any ) { // Address Select if ( (this.inputConfig.name == 'Address' || this.inputConfig.name == 'RoutingAddress') && (!selectedItem || selectedItem == '') ) { selectedItem = this._options[0].name; } // Input Dropdown Bank Name if ( !selectedItem && this.inputConfig.name === 'Input Dropdown Bank Name' ) { this.addNewItem(); } // Input Dropdown Label if (!selectedItem && this.inputConfig.dropdownLabel) { this.commandEvent({ data: this.getSuperControl!.value, action: 'confirm', mode: data.dropdownLabelNew ? 'new' : 'edit', }); this.clearTimeoutDropdown = setTimeout(() => { this.getSuperControl!.setErrors(null); this.inputConfig.dropdownLabelNew = false; this.inputConfig.commands!.active = false; if (!this.inputConfig.hideColorValidations) { this.inputConfig.blackInput = false; } this.inputRef.isFocusInput = false; this.inputRef.isEditInput = false; this.inputRef.input.nativeElement.blur(); }, 150); } // ADD NEW Option if (selectedItem === DropdownStringEnum.ADD_NEW) { this.addNewConfig(); if (this.inputConfig.dropdownLabel) { // DropDown label if (this.inputConfig.dropdownLabel) { this.inputConfig.dropdownLabelNew = true; this.inputRef.isEditInput = true; this.selectedLabelMode.emit('Color'); this.inputConfig.commands!.active = true; this.inputRef.setInputCursorAtTheEnd( this.inputRef.input.nativeElement ); this.inputConfig.blackInput = true; this.selectedItem.emit({ id: 7655, name: DropdownStringEnum.ADD_NEW, }); } } selectedItem = undefined; } // Normal Pick Dropdown else { const existItem = this._options .map((item) => { // Address if ( item.name && (this.inputConfig.name == 'Address' || this.inputConfig.name == 'RoutingAddress') ) { return { id: item.id, name: item.name, address: item.address, longLat: item.longLat, }; } // Load Dispatches TTD else if ( [ 'load-dispatches-ttd', 'load-broker', 'load-shipper', ].includes(this._template) ) { return { ...item }; } // Image (must be before type code, because color has same prop like other dropdown pop) else if (item.logoName) { return { ...item }; } // Code else if (item.code && item.description) { return { id: item.id, name: item.code.concat(' - ', item.description), }; } // Dropdown Labels else if ( item?.dropLabel || this.inputConfig.dropdownLabel ) { return { ...item }; } // Default else { if (item.name) { return { id: item.id, name: item.name, }; } } return { ...item }; }) .find((item) => { // Dropdown Label if ( (item?.dropLabel || this.inputConfig.dropdownLabel) && selectedItem!.substring( 0, selectedItem!.lastIndexOf(' ') ) === item?.name!.toLowerCase() ) { return item; } // Dropdown Load Dispatcher, Broker if (item?.name) if ( [ 'load-broker', 'load-dispatcher', 'load-shipper', ].includes(this._template) && selectedItem ?.toLowerCase() .includes(item?.name?.toLowerCase()) ) { return item; } // Dropdown Load Dispatches if ( item?.trailer?.trailerNumber && item?.truck?.truckNumber && item?.driver?.firstName && item?.driver?.lastName ) if ( this._template === 'load-dispatches-ttd' && selectedItem ?.toLowerCase() .includes( item?.trailer?.trailerNumber.toLowerCase() ) && selectedItem ?.toLowerCase() .includes( item?.truck?.truckNumber.toLowerCase() ) && selectedItem ?.toLowerCase() .includes( item?.driver?.firstName .concat(' ', item?.driver?.lastName) .toLowerCase() ) ) { return item; } // Default if ( !this.inputConfig.dropdownLabel && this._template !== 'load-dispatches-ttd' && selectedItem?.toLowerCase() === item?.name!.toLowerCase() ) { return item; } return item; }) as OptionModel; // MultiSelect Dropdown if (this.inputConfig.multiselectDropdown) { this.onMultiselectSelect(existItem); } // Dropdown labels option selected if (this.inputConfig.dropdownLabel) { if (this.labelMode === 'Label') { this._activeItem = existItem || null; this.getSuperControl!.setValue(existItem!.name); this._options = this.originalOptions; this.selectedItem.emit(existItem); this.inputRef.isDropdownToggler = false; this.inputRef.isFocusInput = false; this.inputRef.input.nativeElement.blur(); } if (this.labelMode === 'Color') { this.activeItemColor = existItem || null; this.selectedItemColor.emit(this.activeItemColor); } } // Normal else { this.getSuperControl!.setValue(existItem?.name); this.selectedItem.emit(existItem); this._activeItem = existItem || null; this.inputRef.isFocusInput = false; this.inputRef.input.nativeElement.blur(); } this.popoverRef.close(); } } private search(searchText: string): void { // Single Dropdown if ( ![ 'groups', 'load-broker-contact', 'fuel-franchise', 'load-dispatches-ttd', ].includes(this._template) ) { if ( searchText?.length && this.getSuperControl?.value && this._activeItem?.name !== this.getSuperControl?.value ) { this._options = this.originalOptions.filter((item) => { if (item.name === DropdownStringEnum.ADD_NEW) return true; return item.name ? item.name .toLowerCase() .includes(searchText.toLowerCase()) : item.code ? item.code .concat(' - ', item.description!) .toLowerCase() .includes(searchText.toLowerCase()) : searchText.toLowerCase(); }); if ( this._template !== this.dropdownTemplateTypeEnum .SVGTEXT_DISPATCH_TEMPLATE && !this.isAddressDropdown ) { if (!this._options.length) { this.getSuperControl!.setErrors({ invalid: true }); this.inputConfig.isInvalidSearchInDropdown = true; } else { this.getSuperControl!.setErrors(null); this.inputConfig.isInvalidSearchInDropdown = false; } } if ( ['truck', 'trailer'].includes( this.inputConfig?.dropdownImageInput?.template! ) ) { this.inputConfig = { ...this.inputConfig, dropdownImageInput: { ...this.inputConfig.dropdownImageInput!, remove: true, }, }; } const emptyOptionsWihAddNew = this._options.length === 1 && this._options[0].name === DropdownStringEnum.ADD_NEW; if (!this._options.length || emptyOptionsWihAddNew) { if ( this._template === 'svgtext-dispatch-template' && !this.inputConfig.isDispatchLocationDropdown && !emptyOptionsWihAddNew ) { this._options.push({ id: 7655, name: DropdownStringEnum.ADD_NEW, }); } this._options.push({ id: 7654, name: DropdownStringEnum.NO_RESULTS, }); this.inputConfig = { ...this.inputConfig, hideAllItemsInInputDropdown: true, }; if ( (this.inputConfig.name === 'Address' || this.inputConfig.name === 'RoutingAddress') && this.inputRef.isFocusInput ) { this.popoverRef?.open(); } } } else { this._options = this.originalOptions; if ( ['truck', 'trailer'].includes( this.inputConfig?.dropdownImageInput?.template! ) ) { this.inputConfig = { ...this.inputConfig, dropdownImageInput: { ...this.inputConfig?.dropdownImageInput!, remove: false, }, }; } this.inputConfig = { ...this.inputConfig, hideAllItemsInInputDropdown: false, }; } } // Group Dropdown Items else { if ( searchText?.length && this.getSuperControl?.value?.toLowerCase() ) { if (this._template === 'groups') { this._options = this.originalOptions .map((element) => { const filteredGroups = element.groups!.filter( (subElement) => subElement?.name ?.toLowerCase() .includes(searchText.toLowerCase()) ); return { ...element, groups: filteredGroups, items: filteredGroups, }; }) .filter((item) => item.groups.length); if (!this._options.length) { this._options.push({ items: [ { id: 7654, name: DropdownStringEnum.NO_RESULTS, }, ], groups: [ { id: 7654, name: DropdownStringEnum.NO_RESULTS, }, ], }); } } if (this._template === 'load-broker-contact') { this._options = this.originalOptions.map((element) => { return { ...element, contacts: element?.contacts?.filter( (subElement) => { return subElement ?.fullName!.toLowerCase() .includes(searchText?.toLowerCase()); } ), }; }); } if (this._template === 'fuel-franchise') { this._options = this.originalOptions.map((element) => { return { ...element, stores: element?.stores?.filter((subElement) => subElement .name!.toString() .toLowerCase() .includes(searchText.toLowerCase()) ), }; }); } if (this._template === 'load-dispatches-ttd') { this._options = this.originalOptions.filter((item) => { if ( item.fullName ?.toLowerCase() .includes(searchText.toLowerCase()) ) { return item; } return false; }); if (!this._options.length) { this._options.push({ id: 7654, name: DropdownStringEnum.NO_RESULTS, }); } } } else { this._options = this.originalOptions; } } } /** * Navigate through dropdown with keyboard arrows */ private dropdownNavigation(step: number) { this.dropdownPosition += step; if (this.dropdownPosition > this._options.length - 1) { this.dropdownPosition = 0; } if (this.dropdownPosition < 0) { this.dropdownPosition = this._options.length - 1; } let cssClass = 'dropdown-option-hovered'; let dropdownContainer = this.renderer.selectRootElement( '.dropdown-options', true ); let dropdownOption = this.renderer.selectRootElement( '.dropdown-option', true ); let elOffset = dropdownOption.height()! * this.dropdownPosition + (this.dropdownPosition !== 0 ? this.dropdownPosition * 6 : 0); let viewport = dropdownContainer.scrollTop()! + dropdownContainer.height()!; if ( elOffset < dropdownContainer.scrollTop()! || elOffset + dropdownOption.height()! > viewport ) dropdownContainer.scrollTop(elOffset); dropdownOption .removeClass(cssClass) .eq(this.dropdownPosition) .addClass(cssClass); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); clearTimeout(this.clearTimeoutDropdown as ReturnType); } }