org.got5.tapestry5.jquery.components.Palette Maven / Gradle / Ivy
The newest version!
// Copyright 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
//
// 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 org.got5.tapestry5.jquery.components;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.Binding;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.FieldValidationSupport;
import org.apache.tapestry5.FieldValidator;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.OptionGroupModel;
import org.apache.tapestry5.OptionModel;
import org.apache.tapestry5.Renderable;
import org.apache.tapestry5.SelectModel;
import org.apache.tapestry5.SelectModelVisitor;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ValidationException;
import org.apache.tapestry5.ValidationTracker;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.base.AbstractField;
import org.apache.tapestry5.corelib.components.Checklist;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.Select;
import org.apache.tapestry5.internal.util.SelectModelRenderer;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.ComponentDefaultProvider;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.got5.tapestry5.jquery.ImportJQueryUI;
/**
* Multiple selection component. Generates a UI consisting of two <select> elements configured for multiple
* selection; the one on the left is the list of "available" elements, the one on the right is "selected". Elements can
* be moved between the lists by clicking a button, or double clicking an option (and eventually, via drag and drop).
*
* The items in the available list are kept ordered as per {@link SelectModel} order. When items are moved from the
* selected list to the available list, they items are inserted back into their proper positions.
*
* The Palette may operate in normal or re-orderable mode, controlled by the reorder parameter.
*
* In normal mode, the items in the selected list are kept in the same "natural" order as the items in the available
* list.
*
* In re-order mode, items moved to the selected list are simply added to the bottom of the list. In addition, two extra
* buttons appear to move items up and down within the selected list.
*
* Much of the look and feel is driven by CSS, the default Tapestry CSS is used to set up the columns, etc. By default,
* the <select> element's widths are 200px, and it is common to override this to a specific value:
*
*
*
* <style>
* DIV.t-palette SELECT { width: 300px; }
* </style>
*
*
* You'll want to ensure that both <select> in each column is the same width, otherwise the display will update
* poorly as options are moved from one column to the other.
*
* Option groups within the {@link SelectModel} will be rendered, but are not supported by many browsers, and are not
* fully handled on the client side.
*
* For an alternative component that can be used for similar purposes, see
* {@link Checklist}.
*
* @tapestrydoc
* @see Form
* @see Select
*/
@ImportJQueryUI(value = "jquery.ui.widget")
@Import(library= "${assets.path}/components/palette/palette.js")
public class Palette extends AbstractField
{
// These all started as anonymous inner classes, and were refactored out to here.
// I was chasing down one of those perplexing bytecode errors.
private final class AvailableRenderer implements Renderable
{
public void render(MarkupWriter writer)
{
writer.element("select", "id", getClientId() + "-avail", "multiple", "multiple", "size", getSize(), "name",
getControlName() + "-avail");
writeDisabled(writer, isDisabled());
for (Runnable r : availableOptions)
r.run();
writer.end();
}
}
private final class OptionGroupEnd implements Runnable
{
private final OptionGroupModel model;
private OptionGroupEnd(OptionGroupModel model)
{
this.model = model;
}
public void run()
{
renderer.endOptionGroup(model);
}
}
private final class OptionGroupStart implements Runnable
{
private final OptionGroupModel model;
private OptionGroupStart(OptionGroupModel model)
{
this.model = model;
}
public void run()
{
renderer.beginOptionGroup(model);
}
}
private final class RenderOption implements Runnable
{
private final OptionModel model;
private RenderOption(OptionModel model)
{
this.model = model;
}
public void run()
{
renderer.option(model);
}
}
private final class SelectedRenderer implements Renderable
{
public void render(MarkupWriter writer)
{
writer.element("select", "id", getClientId(), "multiple", "multiple", "size", getSize(), "name",
getControlName());
writeDisabled(writer, isDisabled());
putPropertyNameIntoBeanValidationContext("selected");
Palette.this.validate.render(writer);
removePropertyNameFromBeanValidationContext();
for (Object value : getSelected())
{
OptionModel model = valueToOptionModel.get(value);
renderer.option(model);
}
writer.end();
}
}
/**
* List of Runnable commands to render the available options.
*/
private List availableOptions;
/**
* The image to use for the deselect button (the default is a left pointing arrow).
*/
@Parameter(value = "asset:deselect.png")
@Property(write = false)
private Asset deselect;
/**
* A ValueEncoder used to convert server-side objects (provided from the
* "source" parameter) into unique client-side strings (typically IDs) and
* back. Note: this component does NOT support ValueEncoders configured to
* be provided automatically by Tapestry.
*/
@Parameter(required = true, allowNull = false)
private ValueEncoder