class DataSelect extends View { /** * The data type for which this select should display the options. */ get type() { console.error("A DataSelect descendant class did not override the type property."); } /** * Override this to provide filters for the options that should get into the dropdown. */ get filters() { return null; } /** * Override this to specify a custom text for the automatically appearing "unknown option" introduced by setUnknownOption. */ get unknownOptionText() { return DataManager.UNKNOWN_LABEL_GENERIC; } constructor(element) { super(element); this.options = { }; //Add the data listener. TimeCards.dataManager.addDataListener(this.type, this.filters, this.onDataUpdate.bind(this)); } /** * Override this to define what information the options should display. * The value must also be set for the given option. */ setOptionInformation(option, key, entity) { option.value = key; option.innerText = key; } /** * Adds a static option to the dropdown. */ addOption(value, label) { var option = document.createElement("OPTION"); option.value = value; option.innerText = label; this.options[value] = option; this.element.appendChild(option); } /** * Use this function to add a static option to the dropdown. * There can only be one unknown option. So calling this function a second time will replace the previous unknown option. * If at some later point an actual option enters the dataset with the same value, the unknown option is overridden by that. */ setUnknownOption(value, label) { if (!this.unknownOption) { this.addOption(value, label); this.unknownOption = this.options[value]; } else { this.unknownOption.value = value; this.unknownOption.innerText = label; } } /** * Selects the unknown option if there is one. */ selectUnknownOption() { if (this.unknownOption) { this.element.value = this.unknownOption.value; } } /** * Removes any previously set unknown option, if there is one. */ removeUnknownOption() { if (this.unknownOption) { this.element.removeChild(this.unknownOption); delete this.options[this.unknownOption.value]; this.unknownOption = null; } } /** * Selects the given option in the dropdown. * Will cause an unknown option to appear if the given value cannot be found in the dropdown. */ setValue(value) { this.element.value = value; //Add an unknown option if the dropdown could not select the given value. //In case there is already an unknown option, we override it even if the value is not different from the existing unknown option. //If we would not do this, the unknown option would be removed again if the same unknown value is set for the second time. if (value && this.element.value != value || (this.unknownOption && this.unknownOption.value == value)) { this.setUnknownOption(value, this.unknownOptionText); this.selectUnknownOption(); } else { //Hide the unknown option if the dropdown was able to select the given option. this.removeUnknownOption(); } //Select the first option if the value is null and if there are actually any options. if (!value && this.element.children.length > 0) { this.element.value = this.element.firstChild.value; } } onDataUpdate(key, entity, sortedBefore) { if (!entity) { //Handle the deleted entity. if (!(key in this.options)) { console.warn("Trying to remove an entity from a data select that was never added."); return; } var option = this.options[key]; //Do nothing if the entity is not in the dropdown in the first place. if (option.parentNode == this.element) { this.element.removeChild(option); } delete this.options[key]; } else if (!(key in this.options)) { //Handle the added entity. var option = document.createElement("OPTION"); this.setOptionInformation(option, key, entity); this.options[key] = option; this.element.insertBefore(option, sortedBefore ? this.options[sortedBefore] : null); } else { //Reset the unknown option if the changed entity has the same key. if (this.unknownOption && this.unknownOption.value == key) { this.unknownOption = null; } //Handle the changed entity. var option = this.options[key]; this.setOptionInformation(option, key, entity); //Re-add the option to maintain the sort order in case of a name change. this.element.insertBefore(option, sortedBefore ? this.options[sortedBefore] : null); } } }