com.github.fluorumlabs.disconnect.vaadin.Select Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of disconnect-vaadin Show documentation
Show all versions of disconnect-vaadin Show documentation
Vaadin components bindings for Disconnect Zero
The newest version!
package com.github.fluorumlabs.disconnect.vaadin;
import com.github.fluorumlabs.disconnect.core.annotations.WebComponent;
import com.github.fluorumlabs.disconnect.polymer.types.BooleanPropertyChangeEvent;
import com.github.fluorumlabs.disconnect.polymer.types.StringPropertyChangeEvent;
import com.github.fluorumlabs.disconnect.vaadin.elements.SelectElement;
import com.github.fluorumlabs.disconnect.vaadin.mixins.HasControlStateMixin;
import com.github.fluorumlabs.disconnect.vaadin.mixins.HasElementMixin;
import com.github.fluorumlabs.disconnect.vaadin.mixins.HasThemableMixin;
import com.github.fluorumlabs.disconnect.vaadin.renderers.SelectRenderer;
import com.github.fluorumlabs.disconnect.zero.component.*;
import com.github.fluorumlabs.disconnect.zero.observable.ObservableEvent;
import com.github.fluorumlabs.disconnect.zero.observable.ObservableValue;
import js.web.dom.ParentNode;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* <vaadin-select>
is a Web Component for selecting idToValues from a list of itemToComponents.
* The content of the
* the select can be populated in two ways: imperatively by using renderer callback function and
* declaratively by using Polymer's Templates.
*
* Rendering
* By default, the select uses the content provided by using the renderer callback function.
*
* The renderer function provides root
, select
arguments.
* Generate DOM content, append it to the root
element and control the state
* of the host element by accessing select
.
*
*
<vaadin-select id="select"></vaadin-select>
*
* const select = document.querySelector('#select');
* select.renderer = function(root, select) {
* const listBox = document.createElement('vaadin-list-box');
* // append 3 <vaadin-item> elements
* ['Jose', 'Manolo', 'Pedro'].forEach(function(name) {
* const item = document.createElement('vaadin-item');
* item.textContent = name;
* listBox.appendChild(item);
* });
*
* // update the content
* root.appendChild(listBox);
* };
*
* Renderer is called on initialization of new select and on its opening.
* DOM generated during the renderer call can be reused
* in the next renderer call and will be provided with the root
argument.
* On first call it will be empty.
*
* Polymer Templates
* Alternatively, the content can be provided with Polymer's Template.
* Select finds the first child template and uses that in case renderer callback function
* is not provided. You can also set a custom template using the template
property.
*
* <vaadin-select>
* <template>
* <vaadin-list-box>
* <vaadin-item label="foo">Foo</vaadin-item>
* <vaadin-item>Bar</vaadin-item>
* <vaadin-item>Baz</vaadin-item>
* </vaadin-list-box>
* </template>
* </vaadin-select>
*
* Hint: By setting the label
property of inner vaadin-itemToComponents you will
* be able to change the visual representation of the selected value in the input part.
*
* Styling
* The following shadow DOM parts are available for styling:
*
*
*
* Part name Description
*
*
* toggle-button
The toggle button
*
*
* The following state attributes are available for styling:
*
*
*
* Attribute Description Part name
*
*
* opened
Set when the select is open :host
* invalid
Set when the element is invalid :host
* focused
Set when the element is focused :host
* focus-ring
Set when the element is keyboard focused :host
* readonly
Set when the select is read only :host
*
*
* <vaadin-select>
element sets these custom CSS properties:
*
*
*
* Property name Description Theme for element
*
*
* --vaadin-select-text-field-width
Width of the select text
* field vaadin-select-overlay
*
*
* See
* ThemableMixin – how to apply styles for shadow parts
*
* In addition to <vaadin-select>
itself, the following internal
* components are themable:
*
*
* <vaadin-select-text-field>
* <vaadin-select-overlay>
*
* Note: the theme
attribute value set on <vaadin-select>
is
* propagated to the internal themable components listed above.
*/
@WebComponent
public class Select extends AbstractComponent
implements HasElementMixin>,
HasControlStateMixin>,
HasThemableMixin>,
HasSlots,
HasStyle>, HasComponents, Component>> {
/**
* Backing ListBox
*/
private ListBox backingListBox = new ListBox();
private Map idToValues = new HashMap<>();
private Map> itemToComponents = new HashMap<>();
private Function idExtractor = Object::toString;
private Function> itemRenderer =
item -> {
String id = idExtractor.apply(item);
return new Item().value(id).text(id);
};
public Select() {
super(SelectElement.TAGNAME());
renderer(() -> backingListBox);
}
public Select(String label) {
this();
label(label);
}
public Select setItems(Iterable items, Function idExtractor) {
removeAll();
this.idExtractor = idExtractor;
items.forEach(this::add);
return this;
}
public Select setItems(Iterable items) {
removeAll();
items.forEach(this::add);
return this;
}
public Select setItems(T[] items, Function idExtractor) {
removeAll();
this.idExtractor = idExtractor;
add(items);
return this;
}
public Select setItems(T... items) {
removeAll();
add(items);
return this;
}
public Select setItemRenderer(Function> itemRenderer) {
this.itemRenderer = itemRenderer;
for (Map.Entry> renderedItem : itemToComponents.entrySet()) {
Component> newItem = this.itemRenderer.apply(renderedItem.getKey());
ParentNode parentNode = renderedItem.getValue().getRenderedNode().getParentNode();
if (parentNode != null) {
parentNode.replaceChild(newItem.getRenderedNode(), renderedItem.getValue().getRenderedNode());
}
}
return this;
}
/**
* It stores the the value
property of the selected item, providing the
* value for iron-form.
* When there’s an item selected, it's the value of that item, otherwise
* it's an empty string.
* On change or initialization, the component finds the item which matches the
* value and displays it.
* If no value is provided to the component, it selects the first item without
* value or empty value.
* Hint: If you do not want to select any item by default, you can either set all
* the idToValues of inner vaadin-itemToComponents, or set the vaadin-select value to
* an inexistent value in the itemToComponents list.
*/
public ObservableValue value() {
return createObservableValue(
() -> idToValues.get(getNode().getValue()),
(item) -> getNode().setValue(item == null ? null : idExtractor.apply(item)),
valueChangedEvent());
}
/**
* Set to true if the value is invalid.
*/
public ObservableValue invalid() {
return createObservableValue(getNode()::isInvalid, getNode()::setInvalid, invalidChangedEvent());
}
/**
* Set when the select is open
*/
public ObservableValue opened() {
return createObservableValue(getNode()::isOpened, getNode()::setOpened, openedChangedEvent());
}
/**
* Custom function for rendering the content of the <vaadin-select>
.
* Receives two arguments:
*
*
* root
The <vaadin-select-overlay>
internal container
* DOM element. Append your content to it.
* select
The reference to the <vaadin-select>
element.
*
*/
@Nullable
public SelectRenderer renderer() {
return getNode().getRenderer();
}
/**
* Custom function for rendering the content of the <vaadin-select>
.
* Receives two arguments:
*
*
* root
The <vaadin-select-overlay>
internal container
* DOM element. Append your content to it.
* select
The reference to the <vaadin-select>
element.
*
*/
public Select renderer(Supplier> renderer) {
getNode().setRenderer(((root, select) -> root.appendChild(renderer.get().getRenderedNode())));
return this;
}
/**
* The error message to display when the select value is invalid
*/
@Nullable
public String errorMessage() {
return getNode().getErrorMessage();
}
/**
* The error message to display when the select value is invalid
*/
public Select errorMessage(String errorMessage) {
getNode().setErrorMessage(errorMessage);
return this;
}
/**
* String used for the label element.
*/
@Nullable
public String label() {
return getNode().getLabel();
}
/**
* String used for the label element.
*/
public Select label(String label) {
getNode().setLabel(label);
return this;
}
/**
* The current required state of the select. True if required.
*/
public boolean required() {
return getNode().isRequired();
}
/**
* The current required state of the select. True if required.
*/
public Select required(boolean required) {
getNode().setRequired(required);
return this;
}
/**
* The name of this element.
*/
@Nullable
public String name() {
return getNode().getName();
}
/**
* The name of this element.
*/
public Select name(String name) {
getNode().setName(name);
return this;
}
/**
* A hint to the user of what can be entered in the control.
* The placeholder will be displayed in the case that there
* is no item selected, or the selected item has an empty
* string label, or the selected item has no label and it's
* DOM content is empty.
*/
@Nullable
public String placeholder() {
return getNode().getPlaceholder();
}
/**
* A hint to the user of what can be entered in the control.
* The placeholder will be displayed in the case that there
* is no item selected, or the selected item has an empty
* string label, or the selected item has no label and it's
* DOM content is empty.
*/
public Select placeholder(String placeholder) {
getNode().setPlaceholder(placeholder);
return this;
}
/**
* When present, it specifies that the element is read-only.
*/
public boolean readonly() {
return getNode().isReadonly();
}
/**
* When present, it specifies that the element is read-only.
*/
public Select readonly(boolean readonly) {
getNode().setReadonly(readonly);
return this;
}
/**
* Manually invoke existing renderer.
*/
public void render() {
getNode().render();
}
/**
* Returns true if value
is valid, and sets the invalid
flag appropriately.
*
* @return True if the value is valid and sets the invalid
flag appropriately
*/
public boolean validate() {
return getNode().validate();
}
/**
* Fired when the opened
property changes.
*/
public ObservableEvent openedChangedEvent() {
return createEvent("opened-changed");
}
/**
* Fired when the value
property changes.
*/
public ObservableEvent valueChangedEvent() {
return createEvent("value-changed");
}
/**
* Fired when the invalid
property changes.
*/
public ObservableEvent invalidChangedEvent() {
return createEvent("invalid-changed");
}
public HasSlots.Container prefixSlot() {
return slotted("prefix");
}
@Override
public Select add(Component> component) {
backingListBox.add(component);
return this;
}
public Select add(T item) {
idToValues.put(idExtractor.apply(item), item);
return add(itemToComponents.computeIfAbsent(item, itemRenderer));
}
@Override
public Select add(Component>... components) {
backingListBox.add(components);
return this;
}
public Select add(T... items) {
for (T item : items) {
add(item);
}
return this;
}
@Override
public Select insert(Component>... components) {
backingListBox.insert(components);
return this;
}
public Select insert(T... items) {
for (int i = items.length - 1; i >= 0; i--) {
idToValues.put(idExtractor.apply(items[i]), items[i]);
insert(itemToComponents.computeIfAbsent(items[i], itemRenderer));
}
return this;
}
@Override
public Select insert(Component> component) {
backingListBox.insert(component);
return this;
}
public Select insert(T item) {
idToValues.put(idExtractor.apply(item), item);
insert(itemToComponents.computeIfAbsent(item, itemRenderer));
return this;
}
@Override
public Select replaceContent(Component> component) {
backingListBox.replaceContent(component);
return this;
}
public Select replaceContent(T item) {
return removeAll().add(item);
}
@Override
public Select replaceContent(Component>... components) {
backingListBox.replaceContent(components);
return this;
}
public Select replaceContent(T... items) {
return removeAll().add(items);
}
@Override
public Select remove(Component> component) {
backingListBox.remove(component);
return this;
}
public Select remove(T item) {
idToValues.remove(idExtractor.apply(item));
Component> component = itemToComponents.get(item);
if (component != null) {
itemToComponents.remove(item);
remove(component);
}
return this;
}
@Override
public Select remove(Component>... components) {
backingListBox.remove(components);
return this;
}
public Select remove(T... items) {
for (T item : items) {
remove(item);
}
return this;
}
@Override
public Select removeAll() {
backingListBox.removeAll();
idToValues.clear();
itemToComponents.clear();
return this;
}
@Override
public Select stamp(Template template) {
backingListBox.stamp(template);
return this;
}
@Override
public Select stamp(Template template, boolean deepClone) {
backingListBox.stamp(template, deepClone);
return this;
}
@Override
public Select stampReplace(Template template) {
backingListBox.stampReplace(template);
return this;
}
@Override
public Select stampReplace(Template template, boolean deepClone) {
backingListBox.stampReplace(template, deepClone);
return this;
}
@Override
public Select stampInsert(Template template) {
backingListBox.stampInsert(template);
return this;
}
@Override
public Select stampInsert(Template template, boolean deepClone) {
backingListBox.stampInsert(template, deepClone);
return this;
}
@Override
public Select text(String text) {
backingListBox.text(text);
return this;
}
@Override
public String text() {
return backingListBox.text();
}
}