This page describes the use of the TabbedPanel with close button component.
It is an enhancement of the AjaxTabbedPanel from wicket-extensions allowing tabs to be simply closed by a button.
With this little update, we can now close any tab simply by clicking on its close-button. For example, it's exactly the same graphic method of Firefox, with a little button on the right side of a tab (I say right side, but position can, of course, be changed in css).
First, we have to modify AbstractTab class to introduce two ideas:
/** * @author Jérémy Goussé * @date 10/08/2008 */ public abstract class MyAbstractTab extends AbstractTab { private String title; private boolean canBeClosed; public MyAbstractTab(IModel iModel, String title, boolean canBeClosed) { super(iModel); this.title = title; this.canBeClosed = canBeClosed; } public String getOngletTitle() { return this.title; } public boolean isCanBeClosed() { return canBeClosed; } } |
This class simply extends wicket.extensions.markup.html.tabs.AbstractTab.
We add two variables and so, two methods.
*Variable title and method to get it.
*Variable canBeClosed and method to know if this tab is closeable or not. (true means closeable, false will hide the close button)
So we created tabs, no we have to create a panel who contains our tabs.
I simply rewrited org.apache.wicket.extensions.markup.html.tabs.TabbedPanel and add some modifications.
Let see the code, I'll explain just after my modifications.
/** * * @author Jérémy Goussé * */ public class MyTabbedPanel extends Panel { public static final String TAB_PANEL_ID = "panel"; private String whereAmI; private final List<MyAbstractTab> tabs; public MyTabbedPanel( String id, List<MyAbstractTab> tabs ) { super( id, new Model( new Integer( -1 ) ) ); this.setOutputMarkupId( true ); if ( tabs == null ) { throw new IllegalArgumentException( "argument [tabs] cannot be null" ); } this.tabs = tabs; final IModel tabCount = new AbstractReadOnlyModel() { private static final long serialVersionUID = 1L; public Object getObject() { return new Integer( MyTabbedPanel.this.tabs.size() ); } }; WebMarkupContainer tabsContainer = new WebMarkupContainer( "tabs-container" ) { private static final long serialVersionUID = 1L; protected void onComponentTag( ComponentTag tag ) { super.onComponentTag( tag ); tag.put( "class", getTabContainerCssClass() ); } }; add( tabsContainer ); // add the loop used to generate tab names tabsContainer.add( new Loop( "tabs", tabCount ) { private static final long serialVersionUID = 1L; protected void populateItem( LoopItem item ) { final int index = item.getIteration(); final WebMarkupContainer titleLink = newLink( "link", index ); titleLink.add( newTitle( "title", MyTabbedPanel.this.tabs.get( index ).getOngletTitle(), index ) ); item.add( titleLink ); Form form = new Form( "tabForm" ); AjaxButton closeButton = new AjaxButton( "closeTab", form ) { @Override protected void onSubmit( AjaxRequestTarget target, Form form ) { removeTab( target, index ); } }; if ( !MyTabbedPanel.this.tabs.get( index ).isCanBeClosed() ) closeButton.setVisible( false ); form.add( closeButton ); item.add( form ); } protected LoopItem newItem( int iteration ) { return newTabContainer( iteration ); } } ); this.whereAmI = this.tabs.get( 0 ).getOngletTitle(); } protected LoopItem newTabContainer( int tabIndex ) { return new LoopItem( tabIndex ) { private static final long serialVersionUID = 1L; protected void onComponentTag( ComponentTag tag ) { super.onComponentTag( tag ); String cssClass = (String)tag.getString( "class" ); if ( cssClass == null ) { cssClass = " "; } cssClass += " tab" + getIteration(); if ( getIteration() == getSelectedTab() ) { cssClass += " selected"; } if ( getIteration() == getTabs().size() - 1 ) { cssClass += " last"; } tag.put( "class", cssClass.trim() ); } }; } protected void onBeforeRender() { super.onBeforeRender(); if ( !hasBeenRendered() && getSelectedTab() == -1 ) { // select the first tab by default setSelectedTab( 0 ); } } protected String getTabContainerCssClass() { return "tab-row"; } public final List<*MyAbstractTab*> getTabs() { return tabs; } protected Component newTitle( String titleId, String title, int index ) { return new Label( titleId, title ); } public void removeTab( AjaxRequestTarget target, int index ) { String titleDeletedTab = this.tabs.get( index ).getOngletTitle(); this.tabs.remove( index ); if ( titleDeletedTab.equals( this.whereAmI ) ) { this.setSelectedTab( 0 ); this.whereAmI = this.tabs.get( 0 ).getOngletTitle(); } else { this.setSelectedTab( this.findSelectedTab( whereAmI ) ); } target.addComponent( this ); } protected int findSelectedTab( String title ) { int result = 0; for ( int i = 0 ; i < this.tabs.size() ; i++ ) if ( title.equals( this.tabs.get( i ).getOngletTitle() ) ) result = i; return result; } protected WebMarkupContainer newLink( String linkId, final int index ) { return new Link( linkId ) { public void onClick() { setSelectedTab( index ); } }; } public final void setSelectedTab( int index ) { if ( index < 0 || index >= tabs.size() ) { throw new IndexOutOfBoundsException(); } setModelObject( new Integer( index ) ); ITab tab = (ITab)tabs.get( index ); Panel panel = tab.getPanel( TAB_PANEL_ID ); if ( panel == null ) { throw new WicketRuntimeException( "ITab.getPanel() returned null. TabbedPanel [" + getPath() + "] ITab index [" + index + "]" ); } if ( !panel.getId().equals( TAB_PANEL_ID ) ) { throw new WicketRuntimeException( "ITab.getPanel() returned a panel with invalid id [" + panel.getId() + "]. You must always return a panel with id equal to the provided panelId parameter. TabbedPanel [" + getPath() + "] ITab index [" + index + "]" ); } if ( get( TAB_PANEL_ID ) == null ) { add( panel ); } else { replace( panel ); } this.whereAmI = this.tabs.get( index ).getOngletTitle(); } public final int getSelectedTab() { return ( (Integer)getModelObject() ).intValue(); } |
Let's see and describe my modifications:
To finish, here the corresponding html code of class above.
<wicket:panel> <div wicket:id="tabs-container" class="tab-row"> <ul> <li wicket:id="tabs"> <div class="tab"><a href="#" wicket:id="link"><span wicket:id="title">[[tab title]]</span></a><form wicket:id="tabForm"><input type="button" wicket:id="closeTab"/></form></div> </li> </ul> </div> <div wicket:id="panel" class="tab-panel">[panel]</div> </wicket:panel> |
With a correct CssStylesheet you can easily transform your tab.