// Copyright 2012 Google Inc. All Rights Reserved. // // 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.google.collide.client.ui.dropdown; import com.google.collide.json.shared.JsonArray; import com.google.gwt.user.client.Timer; import elemental.events.Event; import elemental.events.EventListener; import elemental.events.KeyboardEvent; import elemental.events.KeyboardEvent.KeyCode; import elemental.html.InputElement; /** * Provides autocomplete functionality for an input using simple list. * */ public class AutocompleteController<M> { /* * hide long enough for the click to be caught by handlers but not long enough * to be noticable by the user. */ private static final int HIDE_DELAY = 100; public static <M> AutocompleteController<M> create( InputElement inputBox, DropdownController<M> controller, AutocompleteHandler<M> callback) { return new AutocompleteController<M>(inputBox, controller, callback); } public interface AutocompleteHandler<M> { public JsonArray<M> doCompleteQuery(String query); public void onItemSelected(M item); } /** * The amount of time to wait after the user has finished typing before * updating autocompletion results. */ private static final int AUTOCOMPLETE_DELAY = 30; private final InputElement inputBox; private final DropdownController<M> dropdown; private final AutocompleteHandler<M> callback; private int minimumCharactersBeforeCompletion = 1; public AutocompleteController( InputElement inputBox, DropdownController<M> dropdown, AutocompleteHandler<M> callback) { this.inputBox = inputBox; this.dropdown = dropdown; this.callback = callback; dropdown.preventDefaultOnMouseDown(); attachHandlers(); } public DropdownController<M> getDropdown() { return dropdown; } /** * Specifies the minimum number of characters to be typed in before completion * will take place. */ public void setMinimumCharactersBeforeCompletion(int minimum) { minimumCharactersBeforeCompletion = minimum; } private void attachHandlers() { inputBox.addEventListener(Event.INPUT, new EventListener() { final Timer deferredShow = new Timer() { @Override public void run() { JsonArray<M> items = callback.doCompleteQuery(inputBox.getValue()); if (items.size() > 0) { dropdown.setItems(items); dropdown.show(); } else { dropdown.hide(); } } }; @Override public void handleEvent(Event evt) { KeyboardEvent event = (KeyboardEvent) evt; if (inputBox.getValue().length() < minimumCharactersBeforeCompletion) { dropdown.hide(); } else { deferredShow.cancel(); deferredShow.schedule(AUTOCOMPLETE_DELAY); } } }, false); inputBox.addEventListener(Event.BLUR, new EventListener() { @Override public void handleEvent(Event evt) { dropdown.hide(); } }, false); inputBox.addEventListener(Event.KEYUP, new EventListener() { @Override public void handleEvent(Event evt) { KeyboardEvent event = (KeyboardEvent) evt; if (event.getKeyCode() == KeyCode.ESC) { dropdown.hide(); } } }, false); } }