/** * Copyright 2015 ArcBees Inc. * * 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. */ package com.arcbees.gaestudio.client.application.widget.dropdown; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.query.client.Function; import com.google.gwt.query.client.GQuery; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.text.shared.Renderer; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import static com.google.gwt.query.client.GQuery.$; public class Dropdown<T> implements IsWidget, AttachEvent.Handler, HasValueChangeHandlers<T> { public interface Resources extends ClientBundle { ImageResource dropDownArrowUp(); ImageResource dropDownArrowRl(); Style styles(); } public interface Style extends CssResource { String selectedLi(); String dropDownArrow(); String hiddenLi(); String openedList(); String ul(); } private final Widget widget; private final Element list; private final Map<Element, T> valuesIndex; private final Renderer<T> renderer; private final EventBus eventBus; private final Resources resources; private final Function clickhandler = new Function() { @Override public boolean f(Event e) { GQuery li = $(getElement()); if (li.hasClass(resources.styles().selectedLi())) { if (opened) { close(); makeSelected(li, true); } else { open(); } } else { close(); makeSelected(li, true); } return true; } }; private boolean opened; Dropdown( Renderer<T> renderer, Resources resources) { this.renderer = renderer; this.eventBus = new SimpleEventBus(); this.resources = resources; resources.styles().ensureInjected(); widget = new HTMLPanel("ul", ""); widget.setStyleName(resources.styles().ul()); widget.addAttachHandler(this); list = widget.getElement(); valuesIndex = new HashMap<>(); } public void clear() { clearChildrenBindings(); valuesIndex.clear(); list().children().remove(); } public void addValue(T value) { Element elem = Document.get().createLIElement(); if (valuesIndex.isEmpty()) { elem.addClassName(resources.styles().selectedLi()); elem.addClassName(resources.styles().dropDownArrow()); } else { elem.addClassName(resources.styles().hiddenLi()); } elem.setInnerSafeHtml(SafeHtmlUtils.fromTrustedString(renderer.render(value))); valuesIndex.put(elem, value); $(list).append(elem); $(elem).unbind(Event.ONCLICK).click(clickhandler); } public void addValues(List<T> values) { for (T value : values) { addValue(value); } } public void setValue(T value) { setValue(value, true); } public void setValue(T value, boolean fireEvent) { for (Map.Entry<Element, T> valueEntry : valuesIndex.entrySet()) { T entryValue = valueEntry.getValue(); if (entryValue == null && value == null || entryValue != null && entryValue.equals(value)) { makeSelected($(valueEntry.getKey()), fireEvent); return; } } } @Override public void fireEvent(GwtEvent<?> event) { eventBus.fireEvent(event); } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) { return eventBus.addHandler(ValueChangeEvent.getType(), handler); } public T getValue() { Element element = $("li." + resources.styles().selectedLi(), list).get(0); return valuesIndex.get(element); } @Override public Widget asWidget() { return widget; } @Override public void onAttachOrDetach(AttachEvent event) { if (event.isAttached()) { init(); } else { clearBindings(); } } private void init() { list().blur(new Function() { @Override public boolean f(Event e) { GQuery selected = $("li." + resources.styles().selectedLi()); close(); makeSelected(selected, false); return true; } }); clearChildrenBindings(); } private void clearChildrenBindings() { for (Element element : valuesIndex.keySet()) { $(element).unbind(Event.ONCLICK).click(clickhandler); } } private void close() { list().removeClass(resources.styles().openedList()); everyLi().addClass(resources.styles().hiddenLi()); everyLi().removeClass(resources.styles().selectedLi(), resources.styles().dropDownArrow()); opened = false; } private void makeSelected(GQuery selectedLi, boolean fireEvent) { $("." + resources.styles().selectedLi(), widget) .removeClass(resources.styles().selectedLi(), resources.styles().dropDownArrow()) .addClass(resources.styles().hiddenLi()); $(selectedLi).removeClass(resources.styles().hiddenLi()); $(selectedLi).addClass(resources.styles().selectedLi(), resources.styles().dropDownArrow()); Element element = $(selectedLi).get(0); T value = valuesIndex.get(element); if (fireEvent) { ValueChangeEvent.fire(this, value); } } private void open() { list().addClass(resources.styles().openedList()); everyLi().removeClass(resources.styles().hiddenLi(), resources.styles().dropDownArrow()); $("li:first-child", list).addClass(resources.styles().dropDownArrow()); opened = true; } private void clearBindings() { list().unbind(Event.ONBLUR); everyLi().unbind(Event.ONCLICK); } private GQuery list() { return $(list); } private GQuery everyLi() { return $("li", list); } }