001package com.github.gwtbootstrap.client.ui; 002 003import java.util.Collection; 004 005import com.github.gwtbootstrap.client.ui.base.MarkupWidget; 006import com.github.gwtbootstrap.client.ui.base.TextBoxBase; 007import com.google.gwt.core.client.JavaScriptObject; 008import com.google.gwt.core.client.JsArrayString; 009import com.google.gwt.core.client.Scheduler; 010import com.google.gwt.core.client.Scheduler.ScheduledCommand; 011import com.google.gwt.user.client.Element; 012import com.google.gwt.user.client.ui.MultiWordSuggestOracle; 013import com.google.gwt.user.client.ui.SuggestOracle; 014import com.google.gwt.user.client.ui.SuggestOracle.Callback; 015import com.google.gwt.user.client.ui.SuggestOracle.Request; 016import com.google.gwt.user.client.ui.SuggestOracle.Response; 017import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; 018import com.google.gwt.user.client.ui.Widget; 019 020public class Typeahead extends MarkupWidget { 021 022 public interface UpdaterCallback { 023 String onSelection(Suggestion selectedSuggestion); 024 } 025 026 private int displayItems = 8; 027 028 private int minLength = 1; 029 030 private final SuggestOracle oracle; 031 private Collection<? extends Suggestion> suggestions; 032 033 private UpdaterCallback updaterCallback; 034 035 /** 036 * Constructor for {@link Typeahead}. Creates a {@link MultiWordSuggestOracle} to use with this 037 */ 038 public Typeahead() { 039 this(new MultiWordSuggestOracle()); 040 } 041 042 /** 043 * Constructor for {@link Typeahead}. 044 * 045 * @param oracle the oracle for this <code>Typeahead</code> 046 */ 047 public Typeahead(SuggestOracle oracle) { 048 this.oracle = oracle; 049 this.updaterCallback = createDefaultUpdaterCallback(); 050 } 051 052 private UpdaterCallback createDefaultUpdaterCallback() { 053 return new UpdaterCallback() { 054 @Override 055 public String onSelection(Suggestion selectedSuggestion) { 056 return selectedSuggestion.getReplacementString(); 057 } 058 }; 059 } 060 061 /** 062 * {@inheritDoc} 063 */ 064 @Override 065 public void setWidget(Widget w) { 066 067 if (!(w instanceof TextBoxBase || w instanceof com.google.gwt.user.client.ui.TextBoxBase)) { 068 throw new IllegalArgumentException("Typeahead should be set TextBoxBase childs"); 069 } 070 071 super.setWidget(w); 072 } 073 074 @Override 075 public Widget asWidget() { 076 077 if (widget != null) { 078 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 079 @Override 080 public void execute() { 081 reconfigure(); 082 } 083 }); 084 085 } 086 087 return super.asWidget(); 088 } 089 090 /** 091 * reconfigure setting. 092 */ 093 public void reconfigure() { 094 095 if (widget == null) { 096 return; 097 } 098 099 removeDataIfExists(widget.getElement()); 100 101 configure(widget.getElement(), displayItems, minLength); 102 } 103 104 /** 105 * Get the max number of items to display in the dropdown. 106 * 107 * @return the max number of items to display in the dropdown 108 */ 109 public int getDisplayItemCount() { 110 return displayItems; 111 } 112 113 /** 114 * Set max number of items to display in the dropdown. 115 * 116 * @param displayItems the max number of items to display in the dropdown 117 */ 118 public void setDisplayItemCount(int displayItems) { 119 this.displayItems = displayItems; 120 } 121 122 /** 123 * Get the minimum character length needed before triggering autocomplete suggestions. 124 * 125 * @return the minimum character length needed before triggering autocomplete suggestions 126 */ 127 public int getMinLength() { 128 return minLength; 129 } 130 131 /** 132 * Set the minimum character length needed before triggering autocomplete suggestions. 133 * 134 * @param minLength The minimum character length needed before triggering autocomplete suggestions. 135 */ 136 public void setMinLength(int minLength) { 137 this.minLength = minLength; 138 } 139 140 public void setUpdaterCallback(UpdaterCallback updaterCallback) { 141 this.updaterCallback = updaterCallback; 142 } 143 144 /** 145 * Get suggest oracle 146 * 147 * @return oracle 148 */ 149 public SuggestOracle getSuggestOracle() { 150 return oracle; 151 } 152 153 private void query(String query, final JavaScriptObject process) { 154 Callback callback = new Callback() { 155 @Override 156 public void onSuggestionsReady(Request request, Response response) { 157 callback(process, response); 158 } 159 }; 160 161 if (query != null && !query.isEmpty()) { 162 oracle.requestSuggestions(new Request(query, displayItems), callback); 163 } else { 164 oracle.requestDefaultSuggestions(new Request(), callback); 165 } 166 } 167 168 private void callback(final JavaScriptObject process, Response response) { 169 suggestions = response.getSuggestions(); 170 171 JsArrayString jsArrayString = JavaScriptObject.createArray().cast(); 172 173 for (Suggestion suggestion : suggestions) { 174 jsArrayString.push(suggestion.getDisplayString()); 175 } 176 callProcess(jsArrayString, process); 177 } 178 179 private String updater(String item) { 180 for (Suggestion suggestion : suggestions) { 181 if (suggestion.getDisplayString().equals(item)) { 182 return this.updaterCallback.onSelection(suggestion); 183 } 184 } 185 186 return item; 187 } 188 189 //@formatter:off 190 private native void callProcess(JsArrayString items ,JavaScriptObject process) /*-{ 191 process(items); 192 }-*/; 193 194 private native void removeDataIfExists(Element element) /*-{ 195 var $this = $wnd.jQuery(element); 196 $this.data("typeahead") && $this.removeData("typeahead"); 197 }-*/; 198 199 public native void configure(Element element,int items, int minLength) /*-{ 200 var that = this; 201 $wnd.jQuery(element).typeahead({ 202 "source" : function(query , process) { 203 that.@com.github.gwtbootstrap.client.ui.Typeahead::query(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(query , process); 204 }, 205 "items" : items, 206 "minLength" : minLength, 207 "updater" : function(item) { 208 return that.@com.github.gwtbootstrap.client.ui.Typeahead::updater(Ljava/lang/String;)(item); 209 }, 210 "highlighter" : function(item) { 211 return item; 212 } 213 }); 214 }-*/; 215 //@formatter:on 216}