import { IComponentController } from 'angular' import _ from 'lodash' import { Component, Inject, Input, Output } from '../decorators' import type { TranslateFactory } from '../translate/translate-factory' export type MFDropdownInputEnumOption = { id: MFDropdownInputOptionValue, label: string } | string export type MFDropdownInputOption = { id: MFDropdownInputOptionValue, label: string } export type MFDropdownInputOptionValue = string | undefined | null @Component({ selector: 'mflyDropdown', template: require('./dropdown.html'), }) export class MflyDropdown implements IComponentController { @Input('@') dropdownTheme?: string @Input() enumOptions: MFDropdownInputEnumOption[] | undefined @Input('@') inputId: string @Input() isDisabled: boolean @Input('@') noEmptyOption: boolean @Input('@') required: boolean @Input('=') value: MFDropdownInputOptionValue @Output() onChange?: (v: { value: MFDropdownInputOptionValue }) => void @Output() onClick?: () => void protected options: Array<{ id: MFDropdownInputOptionValue, label: string }> = [] protected requiredText: string constructor( @Inject('translateFactory') private translateFactory: TranslateFactory, ) {} $onChanges(changes: { enumOptions?: ng.IChangesObject }) { // $onChanges triggers before the first $onInit, so this will handle initializing the options too if (changes.enumOptions) { this.options = this.transformOptions(changes.enumOptions.currentValue) } } $onInit() { this.requiredText = this.translateFactory.instant('JSUI.REQUIRED') this.manageEmptyOptions() } valueWasChanged(value: MFDropdownInputOptionValue) { this.manageEmptyOptions() if (this.onChange) { this.onChange({ value }) } } inputWasClicked() { if (this.onClick) { this.onClick() } } private transformOptions(incomingOptions: MFDropdownInputEnumOption[] | undefined): MFDropdownInputOption[] { return (incomingOptions || []).map(option => { if (typeof option === 'string') { return { id: option, label: option } } return option }) } private manageEmptyOptions() { // Start by removing all empty options, we will put them back if needed. _.remove(this.options, option => !option.id) // This is a weird case; if value is set to something that can't be matched, // Angular inserts an option with a value of the string `?`. In this case // we don't want to add a null option because the ? option already has a blank label. const valueDoesNotMatchAnOption = !this.options.find(option => option.id === this.value) if (valueDoesNotMatchAnOption) { return } // There is a selected value and we want to allow empty options. // In this case, prepend an empty option to allow the user to clear the value. // This id is set to `null` because we use that to indicate "unset this" in bulk editing. if (!!this.value && !this.noEmptyOption) { this.options.unshift({ id: null, label: '' }) } // If value is null or undefined, we don't need to do anything. Angular will add an // option with the current value (that will be blank), which will vanish when the // user selects another option. This code will then add back a "blank" value if needed. } }