Tks Joost, I will read then more later tell about your components.
2010/5/1 Joost Schouten (ml) <joost...@jsportal.com> > Hi Pablo, > > This all can be achieved with Mixins. > > For the autocomplete-select I add my own ForceAutocomplete Mixin (which > extends Autocomplete) to a TextField. I have my ForceAutocomplete add a > link (downward arrow img) behind the input which will trigger the > "autocomplete" event. In stead of passing the textfield value, I pass a > static identifier indicating this is a call from the link allowing the > Tapestry providecompletions event to return full list, or whatever other > requirements you might have. > > I've posted some code here below. I apologize where it might be slightly > unclear as I have not yet had the time to refactor so it is clear and clean. > A fair bit of code is also in place to cater for the > FORCE_AUTOCOMPLETE_ON_CHANGE which is triggered when a user selects an item > from the list to allow for another list of options to be shown. We use this > for selections where you need to drill down to a deeper child. > > As for the drag and drop; this one is quite easy. Though we have the code > embedded in our calendar component, it's quite easy to write a mixin that > take a handleDomId and a dropableDomId (or whatever other droppable method > you wish to use) and have it create the appropriate javascript. In the > Draggable.onEnd() method you can have it trigger an ajax call with an T5 > event identifier, so that your Tapestry component can respond appropriately > if needed. > > I hope this is not too unclear and can help you a little bit in getting > started. Don't focus too much on our code I would say but just get started > with your own mixin. > > Cheers, > Joost > > > [1]: > ForceAutocomplete > @IncludeJavaScriptLibrary("ForceAutocomplete.js") > @IncludeStylesheet("ForceAutocomplete.css") > public class ForceAutocomplete extends Autocomplete{ > public static final String NAME_ID_DEVIDER = "_NID_"; > public static final String DOM_ID_ITEM_ID_DEVIDER = "_DIIID_"; > > public static final String FORCE_AUTOCOMPLETE_ON_CLICK = > "FORCE_AUTOCOMPLETE_ON_CLICK"; > public static final String FORCE_AUTOCOMPLETE_ON_CHANGE = > "FORCE_AUTOCOMPLETE_ON_CHANGE"; > public static final String OPTIONS_DEVIDER_END = " ----"; > > public static final String OPTIONS_DEVIDER_START = "---- "; > > @Parameter(value = "prop:componentResources.id", defaultPrefix = > "literal") > private String clientId; > @Parameter(required = false, defaultPrefix = "literal") > private String idFieldClass; > @Inject > private RenderSupport renderSupport; > @Inject > private ComponentResources resources; > @Persist > private String autocompleteFieldId; > /** > * setting this to true will force an autocomplete call to be fired on > every change > */ > @Parameter(required = true, defaultPrefix = "literal") > private boolean addOnchangeCall; > @Inject > @Path("drop_down_arrow.gif") > private Asset dropdownArrow; > @Inject > @Path("context:/static/images/themes/jsportal/loader.gif") > private Asset jsLoaderImage; > private String loaderId; > @AfterRender > private void after(MarkupWriter writer) { > Element container = writer.getElement(); > Element autoCompField = null; > for(Node node : container.getChildren()) { > if(node instanceof Element) { > Element el = (Element) node; > if(el.getName().toLowerCase().equals("input") && > el.getAttribute("type").equals("text")) { > autoCompField = el; > break; > } > } > } > autoCompField.addClassName("forceAutocompleteField"); > autocompleteFieldId = autoCompField.getAttribute("id"); > loaderId = autocompleteFieldId + ":js_loader"; > Element forceAutocompleteFieldContainer = writer.element("div", > "class", > "forceAutocompleteFieldContainer"); > writer.end(); > autoCompField.moveToBottom(forceAutocompleteFieldContainer); > forceAutocompleteFieldContainer.element("img", > "src", jsLoaderImage.toClientURL(), > "class", "t-autoloader-icon " + CSSClassConstants.INVISIBLE, > "id", loaderId); > String handleId = autocompleteFieldId + ":handle_anchor"; > Element handleAnchor = forceAutocompleteFieldContainer.element("a", > "class", "handleAnchor", > "id", handleId); > handleAnchor.element("img", > "class", "forceAutocompleteHandler", > "src", dropdownArrow.toClientURL()); > StringBuffer script = new StringBuffer(); > script.append("$T('" + autocompleteFieldId + "').forceAutocompleter = > new ForceAutocomplete($('" + autocompleteFieldId + "'), '" + > getAutocompleteUrl() + "', " + addOnchangeCall); > if(idFieldClass != null) { > script.append(", '" + idFieldClass + "'"); > } > script.append(");"); > renderSupport.addScript(script.toString()); > } > protected void configure(JSONObject config){ > config.remove("indicator"); > config.put("indicator", loaderId); > } > public String getAutocompleteUrl() { > return > resources.getContainerResources().createEventLink("autocomplete").toAbsoluteURI(); > } > /** > * Overwrites the Autocomplete method to add a title to each li > */ > @Override > protected void generateResponseMarkup(MarkupWriter writer, List matches){ > writer.element("ul"); > > for (Object o : matches){ > String name = o.toString(); > Element li = writer.element("li", > "title", name); > > if(name.indexOf(ForceAutocomplete.OPTIONS_DEVIDER_START) != -1 && > name.indexOf(ForceAutocomplete.OPTIONS_DEVIDER_END) != > -1) { > li.addClassName("devider"); > } > writer.write(name); > writer.end(); > } > > writer.end(); // ul > } > > } > > > ForceAutocomplete.js: > > Ajax.Autocompleter.addMethods({ > onBlur: function() { > fa = $T(this.element).forceAutocompleter; > fa.inputFocus = false; > fa.onBlurSelectorItem(); > } > }); > > var FORCE_AUTOCOMPLETE_CHANGED_EVENT = > 'joostschouten:FORCE_AUTOCOMPLETE_CHANGED_EVENT'; > > function ForceAutocomplete (elem, ajaxUrl, includeChange, idFieldClass) { > this.elem = elem; > $T(this.elem).forceAutocompleter = this; > this.hideTimeoutIndex = null; > this.ajaxUrl = ajaxUrl; > this.includeChange = includeChange; > this.forceAutocompleteFieldContainer = > this.elem.up('.forceAutocompleteFieldContainer'); > this.handle = this.forceAutocompleteFieldContainer.down('.handleAnchor'); > this.lastSelected = null; > this.selectedIdField = null; > if(idFieldClass) { > this.selectedIdField = > this.forceAutocompleteFieldContainer.previous('.' + idFieldClass); > this.lastSelected = this.selectedIdField.value; > } > this.handle.observe('click', > this.handleForcedUpdate.bindAsEventListener(this)); > this.elem.observe('focus', this.onInputFocus.bindAsEventListener(this)); > //this.elem.observe('keydown', > this.handleKeyDown.bindAsEventListener(this)); > > window.setTimeout(this.registerNewOnCompleteMethod.bindAsEventListener(this), > 5); > } > > ForceAutocomplete.prototype = { > registerNewOnCompleteMethod : function () { > this.autocompleter = $T(this.elem).autocompleter; > if(this.autocompleter) { > //stop observing the blur eveny on the AutoComplete > this.autocompleter.onBlur = > this.onBlurSelectorItem.bindAsEventListener(this); > this.autocompleter.options.onComplete = > this.onComplete.bindAsEventListener(this); > this.autocompleter.options.onHide = > this.onHide.bindAsEventListener(this); > this.autocompleter.options.onShow = > this.onShow.bindAsEventListener(this); > } > else { > > window.setTimeout(this.registerNewOnCompleteMethod.bindAsEventListener(this), > 100); > } > }, > onHide : function () { > this.autocompleter.update.hide(); > }, > onShow : function () { > update = this.autocompleter.update; > if(!update.style.position || update.style.position=='absolute') { > update.style.position = 'absolute'; > Position.clone(this.elem, update, { > setHeight: false, > offsetTop: this.elem.offsetHeight > }); > } > update.show(); > }, > onComplete : function (transport) { > var autoCompleter = $T(this.elem).autocompleter; > autoCompleter.changed = false; > autoCompleter.hasFocus = true; > autoCompleter.active = true; > > autoCompleter.update.update(transport.responseText); > lis = autoCompleter.update.down().childElements(); > if(lis.length>0) { > valueMatchesOption = false; > for(i = 0;i<lis.length;i++) { > if(!valueMatchesOption && lis[i].innerHTML == > this.elem.value) { > valueMatchesOption = true; > } > if(lis[i].innerHTML != this.elem.value && > !lis[i].hasClassName('devider')) { > lis[i].observe('click', > this.handleForcedUpdateFromChange.bindAsEventListener(this)); > } > } > if(!valueMatchesOption && this.selectedIdField != null) > { > //set the selected value to null as the option does not exist > this.selectedIdField.value = ''; > } > if(lis.length == 1 && lis[0].innerHTML == > this.elem.value) { > // if there is one exact match, set it and don't show > suggestions > this.loadValueFromItem(lis[0]); > } > else { > //focus on the input field so that the update list will not > be hidden > this.elem.focus(); > autoCompleter.show(); > } > } > else { > autoCompleter.hide(); > } > autoCompleter.stopIndicator(); > }, > onCompleteFromChange : function (transport) { > this.onComplete(transport); > this.elem.fire(FORCE_AUTOCOMPLETE_CHANGED_EVENT); > }, > handleForcedUpdate : function(event) { > var autoCompleter = $T(this.elem).autocompleter; > //focus on the input field so that the update list will not be > hidden > this.elem.focus(); > actionName = 'FORCE_AUTOCOMPLETE_ON_CLICK_' + > this.elem.value; > if(this.lastSelected != null) { > actionName = actionName + '_NID_' + this.lastSelected; > } > //in case when the list is already showing, hide it when > clicked > if(autoCompleter.update.visible()) { > autoCompleter.update.hide(); > } > else { > autoCompleter.startIndicator(); > event.stop(); > var myAjax = new Ajax.Request( this.ajaxUrl , {method: 'post', > parameters: 't:input=' + actionName, onSuccess: > this.onComplete.bindAsEventListener(this)} ); > } > }, > handleForcedUpdateFromChange : function (event) { li = > Event.findElement(event, 'LI'); > this.loadValueFromItem(li); > }, > loadValueFromItem : function(li) { > liCont = li.innerHTML; > if(li.hasClassName('devider')) { > return false; > } > if(this.elem.value != liCont) { > this.elem.value = liCont; > } > //set the value to the content of the li > if(this.includeChange) { > $T(this.elem).autocompleter.startIndicator(); > passValue = liCont; > //strip of the DOM id before passing > if(li.id && li.id != null) { > itemId = li.id; > if(itemId.indexOf('_DIIID_') != -1) { > itemId = itemId.split('_DIIID_')[1]; > } > passValue = passValue + '_NID_' + itemId; > } > tokens = passValue.split('_') > this.lastSelected = tokens[tokens.length-1]; > actionName = 'FORCE_AUTOCOMPLETE_ON_CHANGE_' + passValue; > var myAjax = new Ajax.Request( this.ajaxUrl , {method: 'post', > parameters: 't:input=' + actionName, onSuccess: > this.onCompleteFromChange.bindAsEventListener(this)} ); > } > }, > /** > * called by each item of the selector. Checks to see if any field of the > selector is > * focus'ed if so, don't hide the options, if not, hide > */ > onBlurSelectorItem:function(event) { > autoComp = $T(this.elem).autocompleter; > this.hideTimeoutIndex = setTimeout(autoComp.hide.bind(autoComp), > 250); > this.hasFocus = false; > this.active = false; > }, > onInputFocus:function() { > if(this.hideTimeoutIndex != null) { > clearTimeout(this.hideTimeoutIndex); > > } > } > } > > Pablo dos Reis wrote: > >> Joost, >> >> I liked the video very much. >> If you can pass at least the idea of components I thank. >> How was it implemented? What javascript was use? >> The code would bee good too. >> >> >> And I think interesting you post here for all users can see. >> >> >> >> tks >> >> 2010/4/30 Joost Schouten (ml) <joost...@jsportal.com> >> >> >> >>> Pablo, >>> >>> If interested we have created a full drag and drop calendar and select >>> with >>> auto-complete for our internal project. I could share some of our code to >>> give you a head start. The product is not yet launched but these these >>> features are partly shown in this video [1] >>> >>> Let me know if you would like to have a look >>> >>> Cheers, >>> Joost >>> >>> [1]:http://www.youtube.com/watch?v=_GVW0JvkDkg >>> >>> >>> Pablo dos Reis wrote: >>> >>> >>> >>>> Tks everybody >>>> >>>> Now I am studing about Scriptaculous >>>> I already create a wiki >>>> http://wiki.apache.org/general/PabloGSOC2010 >>>> >>>> I accept sugestions for it >>>> >>>> Soon I' ll post my doubts here. >>>> >>>> >>>> >>>> 2010/4/28 Charith Madusanka <charithc...@gmail.com> >>>> >>>> >>>> >>>> >>>> >>>>> Hi Pablo, >>>>> >>>>> Congratulations and Good luck...............!!! >>>>> >>>>> Charith >>>>> >>>>> On Thu, Apr 29, 2010 at 5:05 AM, Thiago H. de Paula Figueiredo < >>>>> thiag...@gmail.com> wrote: >>>>> >>>>> >>>>> >>>>> >>>>> >>>>>> On Wed, 28 Apr 2010 20:01:23 -0300, Pablo dos Reis < >>>>>> >>>>>> >>>>>> >>>>>> >>>>> pablodosr...@gmail.com> >>>>> >>>>> >>>>> >>>>> >>>>>> wrote: >>>>>> >>>>>> There are ohters students working in tapestry project through GSOC? >>>>>> You're the only one with an approved proposal. >>>>>> >>>>>> -- >>>>>> Thiago H. de Paula Figueiredo >>>>>> Independent Java, Apache Tapestry 5 and Hibernate consultant, >>>>>> developer, >>>>>> and instructor >>>>>> Owner, Ars Machina Tecnologia da Informação Ltda. >>>>>> http://www.arsmachina.com.br >>>>>> >>>>>> --------------------------------------------------------------------- >>>>>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>>>>> For additional commands, e-mail: users-h...@tapestry.apache.org >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>> >>>> >>>> >>>> >>> --------------------------------------------------------------------- >>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>> For additional commands, e-mail: users-h...@tapestry.apache.org >>> >>> >>> >>> >> >> >> >> > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org > For additional commands, e-mail: users-h...@tapestry.apache.org > > -- Pablo Henrique dos Reis