Last active
April 22, 2022 05:52
-
-
Save TatuLund/0c4e8ce34a62220e2ba2c654ffdd855f to your computer and use it in GitHub Desktop.
Light version of Vaadin's ComboBox without lazy loading, custom value, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package org.tatu.vaadin20.components.simplecombo; | |
| import java.util.List; | |
| import java.util.Objects; | |
| import java.util.stream.Collectors; | |
| import com.vaadin.flow.component.AttachEvent; | |
| import com.vaadin.flow.component.ComponentEventListener; | |
| import com.vaadin.flow.component.DetachEvent; | |
| import com.vaadin.flow.component.HasHelper; | |
| import com.vaadin.flow.component.HasSize; | |
| import com.vaadin.flow.component.HasValidation; | |
| import com.vaadin.flow.component.ItemLabelGenerator; | |
| import com.vaadin.flow.component.UI; | |
| import com.vaadin.flow.component.combobox.GeneratedVaadinComboBox; | |
| import com.vaadin.flow.data.binder.HasDataProvider; | |
| import com.vaadin.flow.data.provider.DataProvider; | |
| import com.vaadin.flow.data.provider.KeyMapper; | |
| import com.vaadin.flow.data.provider.Query; | |
| import com.vaadin.flow.function.SerializableConsumer; | |
| import com.vaadin.flow.shared.Registration; | |
| import elemental.json.JsonArray; | |
| import elemental.json.JsonFactory; | |
| import elemental.json.JsonObject; | |
| import elemental.json.impl.JreJsonFactory; | |
| public class ComboBoxLight<T> extends GeneratedVaadinComboBox<ComboBoxLight<T>, T> | |
| implements HasSize, HasValidation, HasDataProvider<T>, HasHelper { | |
| private DataProvider<T, ?> dataProvider; | |
| private Registration dataProviderListenerRegistration; | |
| private ItemLabelGenerator<T> itemLabelGenerator = String::valueOf; | |
| private final KeyMapper<T> keyMapper = new KeyMapper<>(); | |
| private static final String PROP_AUTO_OPEN_DISABLED = "autoOpenDisabled"; | |
| private static final String PROP_INPUT_ELEMENT_VALUE = "_inputElementValue"; | |
| private int customValueListenersCount; | |
| private class CustomValueRegistration implements Registration { | |
| private Registration delegate; | |
| private CustomValueRegistration(Registration delegate) { | |
| this.delegate = delegate; | |
| } | |
| @Override | |
| public void remove() { | |
| if (delegate != null) { | |
| delegate.remove(); | |
| customValueListenersCount--; | |
| if (customValueListenersCount == 0) { | |
| setAllowCustomValue(false); | |
| } | |
| delegate = null; | |
| } | |
| } | |
| } | |
| private static <T> T presentationToModel(ComboBoxLight<T> select, | |
| String presentation) { | |
| if (!select.keyMapper.containsKey(presentation)) { | |
| return null; | |
| } | |
| return select.keyMapper.get(presentation); | |
| } | |
| private static <T> String modelToPresentation(ComboBoxLight<T> select, | |
| T model) { | |
| if (!select.keyMapper.has(model)) { | |
| return null; | |
| } | |
| return select.keyMapper.key(model); | |
| } | |
| public ComboBoxLight() { | |
| super(null, null, String.class, ComboBoxLight::presentationToModel, | |
| ComboBoxLight::modelToPresentation, true); | |
| setItemValuePath("key"); | |
| setItemIdPath("key"); | |
| super.addCustomValueSetListener(e -> this.getElement() | |
| .setProperty(PROP_INPUT_ELEMENT_VALUE, e.getDetail())); | |
| super.addValueChangeListener(e -> updateSelectedKey()); | |
| } | |
| private void updateSelectedKey() { | |
| // Send (possibly updated) key for the selected value | |
| getElement().executeJs("this._selectedKey=$0", | |
| getValue() != null ? keyMapper.key(getValue()) : ""); | |
| } | |
| public void setItemLabelGenerator( | |
| ItemLabelGenerator<T> itemLabelGenerator) { | |
| Objects.requireNonNull(itemLabelGenerator, | |
| "The item label generator can not be null"); | |
| this.itemLabelGenerator = itemLabelGenerator; | |
| reset(); | |
| } | |
| public ItemLabelGenerator<T> getItemLabelGenerator() { | |
| return itemLabelGenerator; | |
| } | |
| private void reset() { | |
| keyMapper.removeAll(); | |
| List<String> items = getDataProvider().fetch(new Query<>()) | |
| .map(item -> keyMapper.key(item)) | |
| .collect(Collectors.toList()); | |
| JsonFactory factory = new JreJsonFactory(); | |
| JsonArray jsonItems = factory.createArray(); | |
| int i = 0; | |
| for (String item : items) { | |
| JsonObject object = factory.createObject(); | |
| object.put("key", item); | |
| object.put("label", getItemLabelGenerator().apply(keyMapper.get(item))); | |
| jsonItems.set(i++, object); | |
| } | |
| getElement().setPropertyJson("items", jsonItems); | |
| } | |
| /** | |
| * Enables or disables the dropdown opening automatically. If {@code false} | |
| * the dropdown is only opened when clicking the toggle button or pressing | |
| * Up or Down arrow keys. | |
| * | |
| * @param autoOpen | |
| * {@code false} to prevent the dropdown from opening | |
| * automatically | |
| */ | |
| public void setAutoOpen(boolean autoOpen) { | |
| getElement().setProperty(PROP_AUTO_OPEN_DISABLED, !autoOpen); | |
| } | |
| /** | |
| * Gets whether dropdown will open automatically or not. | |
| * | |
| * @return @{code true} if enabled, {@code false} otherwise | |
| */ | |
| public boolean isAutoOpen() { | |
| return !getElement().getProperty(PROP_AUTO_OPEN_DISABLED, false); | |
| } | |
| @Override | |
| public void setAutofocus(boolean autofocus) { | |
| super.setAutofocus(autofocus); | |
| } | |
| public boolean isAutofocus() { | |
| return isAutofocusBoolean(); | |
| } | |
| @Override | |
| public void setPreventInvalidInput(boolean preventInvalidInput) { | |
| super.setPreventInvalidInput(preventInvalidInput); | |
| } | |
| @Override | |
| public void setLabel(String label) { | |
| super.setLabel(label); | |
| } | |
| public String getLabel() { | |
| return getLabelString(); | |
| } | |
| @Override | |
| public void setErrorMessage(String errorMessage) { | |
| super.setErrorMessage(errorMessage); | |
| } | |
| @Override | |
| public String getErrorMessage() { | |
| return super.getErrorMessageString(); | |
| } | |
| @Override | |
| public void setInvalid(boolean invalid) { | |
| super.setInvalid(invalid); | |
| } | |
| @Override | |
| public boolean isInvalid() { | |
| return super.isInvalidBoolean(); | |
| } | |
| @Override | |
| public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) { | |
| super.setRequiredIndicatorVisible(requiredIndicatorVisible); | |
| runBeforeClientResponse(ui -> getElement().callJsFunction( | |
| "$connector.enableClientValidation", | |
| !requiredIndicatorVisible)); | |
| } | |
| void runBeforeClientResponse(SerializableConsumer<UI> command) { | |
| getElement().getNode().runWhenAttached(ui -> ui | |
| .beforeClientResponse(this, context -> command.accept(ui))); | |
| } | |
| @Override | |
| public void setClearButtonVisible(boolean clearButtonVisible) { | |
| super.setClearButtonVisible(clearButtonVisible); | |
| } | |
| public boolean isClearButtonVisible() { | |
| return super.isClearButtonVisibleBoolean(); | |
| } | |
| @Override | |
| public void setOpened(boolean opened) { | |
| super.setOpened(opened); | |
| } | |
| public boolean isOpened() { | |
| return isOpenedBoolean(); | |
| } | |
| @Override | |
| public void setPlaceholder(String placeholder) { | |
| super.setPlaceholder(placeholder); | |
| } | |
| public String getPlaceholder() { | |
| return getPlaceholderString(); | |
| } | |
| @Override | |
| public void setPattern(String pattern) { | |
| super.setPattern(pattern); | |
| } | |
| public String getPattern() { | |
| return getPatternString(); | |
| } | |
| @Override | |
| public void setDataProvider(DataProvider<T, ?> dataProvider) { | |
| this.dataProvider = dataProvider; | |
| reset(); | |
| setupDataProviderListener(dataProvider); | |
| } | |
| private void setupDataProviderListener(DataProvider<T, ?> dataProvider) { | |
| if (dataProviderListenerRegistration != null) { | |
| dataProviderListenerRegistration.remove(); | |
| } | |
| dataProviderListenerRegistration = dataProvider | |
| .addDataProviderListener(event -> { | |
| reset(); | |
| }); | |
| } | |
| @Override | |
| protected void onAttach(AttachEvent attachEvent) { | |
| super.onAttach(attachEvent); | |
| if (getDataProvider() != null | |
| && dataProviderListenerRegistration == null) { | |
| setupDataProviderListener(getDataProvider()); | |
| } | |
| } | |
| public DataProvider<T, ?> getDataProvider() { | |
| return dataProvider; | |
| } | |
| @Override | |
| protected void onDetach(DetachEvent detachEvent) { | |
| if (dataProviderListenerRegistration != null) { | |
| dataProviderListenerRegistration.remove(); | |
| dataProviderListenerRegistration = null; | |
| } | |
| super.onDetach(detachEvent); | |
| } | |
| @Override | |
| public void setAllowCustomValue(boolean allowCustomValue) { | |
| super.setAllowCustomValue(allowCustomValue); | |
| } | |
| public boolean isAllowCustomValue() { | |
| return isAllowCustomValueBoolean(); | |
| } | |
| @Override | |
| public Registration addCustomValueSetListener( | |
| ComponentEventListener<CustomValueSetEvent<ComboBoxLight<T>>> listener) { | |
| setAllowCustomValue(true); | |
| customValueListenersCount++; | |
| Registration registration = super.addCustomValueSetListener(listener); | |
| return new CustomValueRegistration(registration); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment