All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gwtbootstrap3.extras.select.client.ui.SelectBase Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
package org.gwtbootstrap3.extras.select.client.ui;

/*
 * #%L
 * GwtBootstrap3
 * %%
 * Copyright (C) 2016 GwtBootstrap3
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.CONTAINER;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.DROPDOWN_ALIGN_RIGHT;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.DROPUP_AUTO;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.HEADER;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.HIDE_DISABLED;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.LIVE_SEARCH;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.LIVE_SEARCH_NORMALIZE;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.LIVE_SEARCH_PLACEHOLDER;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.LIVE_SEARCH_STYLE;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.MOBILE;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.SELECT_ON_TAB;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.SHOW_CONTENT;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.SHOW_ICON;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.SHOW_SUBTEXT;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.SIZE;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.STYLE;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.WIDTH;
import static org.gwtbootstrap3.extras.select.client.ui.SelectOptions.WINDOW_PADDING;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.gwtbootstrap3.client.ui.base.ComplexWidget;
import org.gwtbootstrap3.client.ui.base.HasSize;
import org.gwtbootstrap3.client.ui.base.HasType;
import org.gwtbootstrap3.client.ui.base.mixin.AttributeMixin;
import org.gwtbootstrap3.client.ui.base.mixin.EnabledMixin;
import org.gwtbootstrap3.client.ui.constants.ButtonSize;
import org.gwtbootstrap3.client.ui.constants.ButtonType;
import org.gwtbootstrap3.client.ui.constants.Styles;
import org.gwtbootstrap3.extras.select.client.ui.constants.DropdownAlignRight;
import org.gwtbootstrap3.extras.select.client.ui.constants.LiveSearchStyle;
import org.gwtbootstrap3.extras.select.client.ui.constants.MenuSize;
import org.gwtbootstrap3.extras.select.client.ui.constants.SelectStyles;
import org.gwtbootstrap3.extras.select.client.ui.constants.SelectWidth;
import org.gwtbootstrap3.extras.select.client.ui.event.HasAllSelectHandlers;
import org.gwtbootstrap3.extras.select.client.ui.event.HiddenEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.HiddenHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.HideEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.HideHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.LoadedEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.LoadedHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.RefreshedEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.RefreshedHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.RenderedEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.RenderedHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.ShowEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.ShowHandler;
import org.gwtbootstrap3.extras.select.client.ui.event.ShownEvent;
import org.gwtbootstrap3.extras.select.client.ui.event.ShownHandler;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayNumber;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.OptionElement;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.editor.client.adapters.TakesValueEditor;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;

/**
 * Bootstrap select widget base
 *
 * @param  select value type
 *
 * @see http://silviomoreto.github.io/bootstrap-select/
 * @author Xiaodong Sun
 */
public abstract class SelectBase extends ComplexWidget implements HasValue, HasEnabled, Focusable,
        HasType, HasSize, IsEditor>, HasAllSelectHandlers {

    private LeafValueEditor editor;
    private ButtonType type;
    private ButtonSize size;

    /**
     * Default language: {@link SelectLanguage#EN}
     */
    private static final SelectLanguage DEFAULT_LANGUAGE = SelectLanguage.EN;

    /**
     * Language; defaults to {@value #DEFAULT_LANGUAGE}
     */
    private SelectLanguage language = DEFAULT_LANGUAGE;

    protected final SelectElement selectElement;
    protected final Map itemMap = new HashMap<>(0);
    protected final AttributeMixin> attrMixin = new AttributeMixin<>(this);
    private final EnabledMixin> enabledMixin = new EnabledMixin<>(this);
    private final FocusImpl focusImpl = FocusImpl.getFocusImplForWidget();

    /**
     * Initialize options
     */
    protected SelectOptions options = SelectOptions.newOptions();

    protected SelectBase() {
        this.selectElement = Document.get().createSelectElement();
        setElement(selectElement);
        setStyleName(SelectStyles.SELECT_PICKER);
        addStyleName(Styles.FORM_CONTROL);
    }

    /**
     * Returns true if multiple selection is allowed.
     *
     * @return true if multiple selection is allowed
     */
    public abstract boolean isMultiple();

    @Override
    protected void onLoad() {
        super.onLoad();
        // Inject the language JS is necessary
        if (language.getJs() != null) {
            ScriptInjector.fromString(language.getJs().getText())
                .setWindow(ScriptInjector.TOP_WINDOW).inject();
        }
        initialize(getElement(), options);
        bindSelectEvents(getElement());
    }

    @Override
    protected void onUnload() {
        super.onUnload();
        unbindSelectEvents(getElement());
        command(getElement(), SelectCommand.DESTROY);
    }

    @Override
    public void add(Widget child) {
        super.add(child);
        updateItemMap(child, true);
    }

    @Override
    public void insert(Widget child, int beforeIndex) {
        super.insert(child, beforeIndex);
        updateItemMap(child, true);
    }

    @Override
    public boolean remove(Widget w) {
        boolean removed = super.remove(w);
        if (removed) {
            updateItemMap(w, false);
        }
        return removed;
    }

    void updateItemMap(Widget widget, boolean toAdd) {
        // Option ==> update with this option
        if (widget instanceof Option) {
            Option option = (Option) widget;
            if (toAdd)
                itemMap.put(option.getSelectElement(), option);
            else
                itemMap.remove(option.getSelectElement());
        } else if (widget instanceof OptGroup) {
            // OptGroup ==> update with all optGroup options
            OptGroup optGroup = (OptGroup) widget;
            if (toAdd)
                itemMap.putAll(optGroup.getItemMap());
            else
                for (Entry entry : optGroup.getItemMap().entrySet()) {
                    OptionElement optElem = entry.getKey();
                    itemMap.remove(optElem);
                }
        }
    }

    /**
     * Set the select language.
     *
     * @param language
     */
    public void setLanguage(final SelectLanguage language) {
        this.language = (language == null) ? DEFAULT_LANGUAGE : language;
    }

    /**
     * Returns the select language.
     *
     * @return
     */
    public SelectLanguage getLanguage() {
        return language;
    }

    /**
     * Sets a container to which the select will be appended.
     *
     * @param container specific element or selector, e.g., "body", ".my-container"
     */
    public void setContainer(final String container) {
        if (container != null)
            attrMixin.setAttribute(CONTAINER, container);
        else
            attrMixin.removeAttribute(CONTAINER);
    }

    /**
     * Sets the handler to get the text displayed when selectedTextFormat
     * is count or count > #, or null
     * to use the default text: X of Y selected.
     *
     * @param handler
     */
    public void setCountSelectedTextHandler(final CountSelectedTextHandler handler) {
        options.setCountSelectedTextHandler(handler);
    }

    /**
     * Sets the drop-down menu right alignment.
*
* Defaults to {@link DropdownAlignRight#FALSE}. * * @param dropdownAlignRight * @see DropdownAlignRight */ public void setDropdownAlignRight(final DropdownAlignRight dropdownAlignRight) { if (dropdownAlignRight != null) attrMixin.setAttribute(DROPDOWN_ALIGN_RIGHT, dropdownAlignRight.getValue()); else attrMixin.removeAttribute(DROPDOWN_ALIGN_RIGHT); } /** * Checks to see which has more room, above or below. * If the drop-up has enough room to fully open normally, * but there is more room above, the drop-up still opens * normally. Otherwise, it becomes a drop-up. * If dropupAuto is set to false, drop-ups * must be called manually.
*
* Defaults to true. * * @param dropupAuto */ public void setDropupAuto(final boolean dropupAuto) { if (!dropupAuto) attrMixin.setAttribute(DROPUP_AUTO, Boolean.toString(false)); else attrMixin.removeAttribute(DROPUP_AUTO); } /** * If drop-up auto is set to false, force to make * the select a drop-up menu if set to true. * * @param forceDropup * @see #setDropupAuto(boolean) */ public void setForceDropup(final boolean forceDropup) { if (forceDropup) { addStyleName(SelectStyles.DROPUP); } else { removeStyleName(SelectStyles.DROPUP); } } /** * Adds a header to the top of the menu; includes * a close button by default.
*
* No header by default. * * @param header */ public void setHeader(final String header) { if (header != null) attrMixin.setAttribute(HEADER, header); else attrMixin.removeAttribute(HEADER); } /** * Removes disabled options and optgroups from the menu
*
* Defaults to false. * * @param hideDisabled */ public void setHideDisabled(final boolean hideDisabled) { if (hideDisabled) attrMixin.setAttribute(HIDE_DISABLED, Boolean.toString(true)); else attrMixin.removeAttribute(HIDE_DISABLED); } /** * When set to true, adds a search box to the * top of the select picker drop-down.
*
* Defaults to false. * * @param liveSearch */ public void setLiveSearch(final boolean liveSearch) { if (liveSearch) attrMixin.setAttribute(LIVE_SEARCH, Boolean.toString(true)); else attrMixin.removeAttribute(LIVE_SEARCH); } /** * Setting liveSearchNormalize to true allows for * accent-insensitive searching.
*
* Defaults to false. * * @param liveSearchNormalize */ public void setLiveSearchNormalize(final boolean liveSearchNormalize) { if (liveSearchNormalize) attrMixin.setAttribute(LIVE_SEARCH_NORMALIZE, Boolean.toString(true)); else attrMixin.removeAttribute(LIVE_SEARCH_NORMALIZE); } /** * Set live search style.
*
* Defaults to {@link LiveSearchStyle#CONTAINS}. * * @param liveSearchStyle * @see LiveSearchStyle */ public void setLiveSearchStyle(final LiveSearchStyle liveSearchStyle) { if (liveSearchStyle != null) attrMixin.setAttribute(LIVE_SEARCH_STYLE, liveSearchStyle.getValue()); else attrMixin.removeAttribute(LIVE_SEARCH_STYLE); } /** * Set a placeholder to the live search input.
*
* Defaults to null. * * @param liveSearchPlaceholder */ public void setLiveSearchPlaceholder(final String liveSearchPlaceholder) { if (liveSearchPlaceholder != null) attrMixin.setAttribute(LIVE_SEARCH_PLACEHOLDER, liveSearchPlaceholder); else attrMixin.removeAttribute(LIVE_SEARCH_PLACEHOLDER); } /** * When set to true, enables the device's native * menu for select menus.
*
* Defaults to false. * * @param mobile */ public void setMobile(final boolean mobile) { if (mobile) attrMixin.setAttribute(MOBILE, Boolean.toString(true)); else attrMixin.removeAttribute(MOBILE); } /** * When set to true, treats the tab character like the * enter or Space characters within the * select picker drop-down.
*
* Defaults to false. * * @param selectOnTab */ public void setSelectOnTab(final boolean selectOnTab) { if (selectOnTab) attrMixin.setAttribute(SELECT_ON_TAB, Boolean.toString(true)); else attrMixin.removeAttribute(SELECT_ON_TAB); } /** * When set to true, display custom HTML associated with * selected option(s) in the button. When set to false, * the option value will be displayed instead.
*
* Defaults to true. * * @param showContent */ public void setShowContent(final boolean showContent) { if (!showContent) attrMixin.setAttribute(SHOW_CONTENT, Boolean.toString(false)); else attrMixin.removeAttribute(SHOW_CONTENT); } /** * When set to true, display icon(s) associated with * selected option(s) in the button.
*
* Defaults to true. * * @param showIcon */ public void setShowIcon(final boolean showIcon) { if (!showIcon) attrMixin.setAttribute(SHOW_ICON, Boolean.toString(false)); else attrMixin.removeAttribute(SHOW_ICON); } /** * When set to true, display sub-text associated with a * selected option in the button.
*
* Defaults to false. * * @param showSubtext */ public void setShowSubtext(final boolean showSubtext) { if (showSubtext) attrMixin.setAttribute(SHOW_SUBTEXT, Boolean.toString(true)); else attrMixin.removeAttribute(SHOW_SUBTEXT); } /** * Specifies the number of visible menu items.
*
* Defaults to {@link MenuSize#AUTO}. * * @param size * @see MenuSize */ public void setMenuSize(final MenuSize size) { if (size != null) attrMixin.setAttribute(SIZE, size.getValue()); else attrMixin.removeAttribute(SIZE); } /** * The menu will show the given number of items, even if * the drop-down is cut off.
*
* Defaults to {@link MenuSize#AUTO}. * * @param size */ public void setFixedMenuSize(final int size) { attrMixin.setAttribute(SIZE, Integer.toString(size)); } /** * Sets the {@link ButtonType} of the select.
*
* IMPORTANT: This method will override the style set by * {@link #setStyle(String)}. */ @Override public void setType(final ButtonType type) { this.type = type; updateStyle(); } /** * Returns the {@link ButtonType} of the select; may be null. * * @return the {@link ButtonType} of the select */ @Override public ButtonType getType() { return type; } /** * Sets the {@link ButtonSize} of the select.
*
* IMPORTANT: This method will override the style set by * {@link #setStyle(String)}. */ @Override public void setSize(final ButtonSize size) { this.size = size; updateStyle(); } /** * Returns the {@link ButtonSize} of the select; may be null. * * @return the {@link ButtonSize} of the select */ @Override public ButtonSize getSize() { return size; } private void updateStyle() { StringBuilder sb = new StringBuilder(); if (type != null) { sb.append(type.getCssName()); } if (size != null) { if (!sb.toString().isEmpty()) { sb.append(" "); } sb.append(size.getCssName()); } setStyle(sb.toString()); } /** * Set the customized style name to the select.
*
* Defaults to null.
*
* IMPORTANT: This method will override the style set by * {@link #setType(ButtonType)} and/or {@link #setSize(ButtonSize)}. * * @param styleName */ public void setStyle(final String styleName) { if (styleName != null) attrMixin.setAttribute(STYLE, styleName); else attrMixin.removeAttribute(STYLE); } /** * Returns the style name applied to the select. * * @return */ public String getStyle() { return attrMixin.getAttribute(STYLE); } /** * Set the default placeholder text when nothing is selected. * This works for both multiple and standard select boxes.
*
* Defaults to null. * * @param title * @see #setTitle(String) */ public void setPlaceholder(final String placeholder) { setTitle(placeholder); } /** * Set the specified width to the select.
*
* Defaults to {@link SelectWidth#NONE}. * * @param width * @see #setWidth(String) * @see SelectWidth */ public void setSelectWidth(final SelectWidth width) { setWidth((width != null) ? width.getValue() : null); } /** * Set the select width witch is forced inline to the given value. * * @param cssWidth a CSS width with units, e.g. 100px */ @Override public void setWidth(final String cssWidth) { if (cssWidth != null) attrMixin.setAttribute(WIDTH, cssWidth); else attrMixin.removeAttribute(WIDTH); } /** * Sets the window padding to all sides. This is useful in cases where * the window has areas that the drop-down menu should not cover - for * instance a fixed header. * * @param padding */ public void setWindowPadding(final int padding) { attrMixin.setAttribute(WINDOW_PADDING, Integer.toString(padding)); } /** * Sets the window padding to top, right, bottom, and right sides. This * is useful in cases where the window has areas that the drop-down menu * should not cover - for instance a fixed header. * * @param top * @param right * @param bottom * @param left */ public void setWindowPaddingTopRightBottomLeft(final int top, final int right, final int bottom, final int left) { JsArrayNumber array = JavaScriptObject.createArray(4).cast(); array.push(top); array.push(right); array.push(bottom); array.push(left); attrMixin.setAttribute(WINDOW_PADDING, JsonUtils.stringify(array)); } /** * Set to true to add the Bootstrap menu arrow. * * @param showMenuArrow */ public void setShowMenuArrow(final boolean showMenuArrow) { if (showMenuArrow) { addStyleName(SelectStyles.SHOW_MENU_ARROW); } else { removeStyleName(SelectStyles.SHOW_MENU_ARROW); } } @Override public LeafValueEditor asEditor() { if (editor == null) { editor = TakesValueEditor.of(this); } return editor; } @Override public void setValue(final T value) { setValue(value, false); } @Override public void setValue(final T value, final boolean fireEvents) { T oldValue = fireEvents ? getValue() : null; setSelectedValue(value); if (fireEvents) { T newValue = getValue(); ValueChangeEvent.fireIfNotEqual(this, oldValue, newValue); } } /** * Fires {@link ValueChangeEvent} with the current value. */ private void onValueChange() { T newValue = getValue(); ValueChangeEvent.fire(this, newValue); } /** * Selects the given value. If the value is null * or does not match any option, no option will be selected. * * @param value */ protected abstract void setSelectedValue(final T value); @Override public void setEnabled(boolean enabled) { enabledMixin.setEnabled(enabled); refresh(); } @Override public boolean isEnabled() { return enabledMixin.isEnabled(); } @Override public int getTabIndex() { return focusImpl.getTabIndex(getFocusElement()); } @Override public void setAccessKey(char key) { focusImpl.setAccessKey(getFocusElement(), key); } @Override public void setFocus(boolean focused) { if (focused) { focusImpl.focus(getFocusElement()); } else { focusImpl.blur(getFocusElement()); } } @Override public void setTabIndex(int index) { focusImpl.setTabIndex(getFocusElement(), index); } private Element getFocusElement() { if (!isAttached()) { return selectElement; } return getElement().getParentElement().getFirstChildElement(); } /** * Returns the number of items present in the select. * * @return the number of items */ public int getItemCount() { return selectElement.getOptions().getLength(); } /** * Returns the item list. * * @return the item list */ public List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy