blob: 6f06a0943879a7ecedf2168fd0b621c8320f7076 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {ConnectedPosition} from "@angular/cdk/overlay";
import {Component, ElementRef, EventEmitter, forwardRef, Input, Output, ViewChild} from "@angular/core";
import {NG_VALUE_ACCESSOR} from "@angular/forms";
import {EKeyboardKeys} from "../../../../../util/events";
import {DropDownDirective} from "../../../../layout/drop-down";
import {AbstractControlValueAccessorComponent} from "../../../common";
import {ISelectOption} from "../../model";
@Component({
selector: "app-select",
templateUrl: "./select.component.html",
styleUrls: ["./select.component.scss"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent),
multi: true
}
]
})
export class SelectComponent<T = any> extends AbstractControlValueAccessorComponent<T> {
private static id = 0;
/**
* This ID is placed on the toggle button of the drop down menu
*/
@Input()
public appId = `CalendarControlComponent${SelectComponent.id++}`;
/**
* Text which is placed in the drop down button as a placeholder text if nothing is selected
*/
@Input()
public appPlaceholder = "";
/**
* List of selectable options displayed in the drop down menu
*/
@Input()
public appOptions: ISelectOption<T>[] = [];
/**
* Shows select with less padding if set.
*/
@Input()
public appSmall = false;
@Input()
public appMaxWidth: string;
/**
* Outputs when the drop down is closed for parent components to react to.
*/
@Output()
public appClose = new EventEmitter<void>();
@ViewChild(DropDownDirective)
public dropDown: DropDownDirective;
@ViewChild("toggleButtonRef", {static: true})
public toggleButton: ElementRef<HTMLElement>;
public readonly connectedPositions: ConnectedPosition[] = [
{
originX: "start",
originY: "bottom",
overlayX: "start",
overlayY: "top",
offsetX: 4,
panelClass: "bottom"
},
{
originX: "start",
originY: "top",
overlayX: "start",
overlayY: "bottom",
offsetX: 4,
panelClass: "top"
},
{
originX: "start",
originY: "center",
overlayX: "start",
overlayY: "center",
panelClass: "center",
offsetX: 4
}
];
/**
* Toggles the drop down menu to open or to close
* @param openOrClose If true, the menu is opened, if false it is closed, if unset it is toggled.
*/
public toggle(openOrClose?: boolean) {
this.dropDown.toggle(openOrClose);
if (this.dropDown.isOpen) {
this.toggleButton.nativeElement.focus();
}
}
public onClickOnOption(value: any) {
this.toggle(false);
if (this.appDisabled) {
return;
}
this.writeValue(value, true);
}
public onKeyDown(event: KeyboardEvent) {
switch (event?.key) {
case EKeyboardKeys.ESCAPE:
case EKeyboardKeys.TAB:
return this.toggle(false);
case EKeyboardKeys.ARROW_DOWN:
case EKeyboardKeys.ARROW_RIGHT: {
const value = this.getNextValue();
if (this.appDisabled || value == null) {
return;
}
event.preventDefault();
return this.writeValue(value, true);
}
case EKeyboardKeys.ARROW_UP:
case EKeyboardKeys.ARROW_LEFT: {
const value = this.getPreviousValue();
if (this.appDisabled || value == null) {
return;
}
event.preventDefault();
return this.writeValue(value, true);
}
}
}
public getNextValue(): T {
if (!Array.isArray(this.appOptions)) {
return;
}
if (this.appValue == null) {
return this.appOptions[0]?.value;
} else {
const index = this.appOptions.findIndex((o) => o?.value === this.appValue) + 1;
return this.appOptions[index < this.appOptions?.length ? index : 0]?.value;
}
}
public getPreviousValue(): T {
if (!Array.isArray(this.appOptions)) {
return;
}
if (this.appValue == null) {
return this.appOptions[this.appOptions.length - 1]?.value;
} else {
const index = this.appOptions.findIndex((o) => o?.value === this.appValue) - 1;
return this.appOptions[index > -1 ? index : this.appOptions.length - 1]?.value;
}
}
}