blob: 22ac209a2ca0b7ae66cc96a2408f2bbd4e7c39dd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.wicket.extensions.ajax.markup.html.modal;
import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxCallListener;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes.EventPropagation;
import org.apache.wicket.extensions.ajax.markup.html.modal.theme.DefaultTheme;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.Panel;
/**
* Presents a modal dialog to the user. See {@link #open(Component, AjaxRequestTarget)} and
* {@link #close(AjaxRequestTarget)} methods.
* <p>
* Note: This component does not provide any styling by itself, so you have can add a
* {@link DefaultTheme} to this component if aren't styling these CSS classes by yourself:
* <dl>
* <dt>modal-dialog-overlay</dt>
* <dd>the wrapper around the actual dialog, usually used to overlay the rest of the document</dd>
* <dt>modal-dialog</dt>
* <dd>the actual dialog</dd>
* <dt>modal-dialog-content</dt>
* <dd>any additional styling for the content of this dialog</dd>
* </dl>
*
* @author Igor Vaynberg (ivaynberg)
* @author svenmeier
*/
public class ModalDialog extends Panel
{
private static final long serialVersionUID = 1L;
private static final String OVERLAY_ID = "overlay";
private static final String DIALOG_ID = "dialog";
/**
* The id for the content of this dialoh.
*
* @see #setContent(Component)
* @see #open(Component, AjaxRequestTarget)
*/
public static final String CONTENT_ID = "content";
private final WebMarkupContainer overlay;
private final WebMarkupContainer dialog;
private boolean removeContentOnClose;
public ModalDialog(String id)
{
super(id);
setOutputMarkupId(true);
overlay = newOverlay(OVERLAY_ID);
overlay.setVisible(false);
add(overlay);
dialog = newDialog(DIALOG_ID);
overlay.add(dialog);
}
/**
* Factory method for the overlay markup around the dialog.
*
* @param overlayId
* id
* @return overlay
*/
protected WebMarkupContainer newOverlay(String overlayId)
{
return new WebMarkupContainer(overlayId);
}
/**
* Factory method for the dialog markup around the content.
*
* @param dialogId
* id
* @return overlay
*/
protected WebMarkupContainer newDialog(String dialogId)
{
return new WebMarkupContainer(dialogId);
}
/**
* Set a content.
*
* @param content
* @see #open(AjaxRequestTarget)
*/
public void setContent(Component content)
{
if (!CONTENT_ID.equals(content.getId()))
{
throw new IllegalArgumentException(
"Content must have wicket id set to ModalDialog.CONTENT_ID");
}
dialog.addOrReplace(content);
removeContentOnClose = false;
}
/**
* Open the dialog with a content.
* <p>
* The content will be removed on close of the dialog.
*
* @param content
* the content
* @param target
* an optional Ajax target
* @return this
* @see #setContent(Component)
* @see #close(AjaxRequestTarget)
*/
public ModalDialog open(Component content, AjaxRequestTarget target)
{
setContent(content);
removeContentOnClose = true;
overlay.setVisible(true);
if (target != null)
{
target.add(this);
}
return this;
}
/**
* Open the dialog.
*
* @param target
* an optional Ajax target
* @return this
* @see #setContent(Component)
*/
public ModalDialog open(AjaxRequestTarget target)
{
if (overlay.size() == 0)
{
throw new WicketRuntimeException(
String.format("ModalDialog with id '%s' has no content set!", getId()));
}
overlay.setVisible(true);
if (target != null)
{
target.add(this);
}
return this;
}
/**
* Is this dialog open.
*
* @return <code>true</code> if open
*/
public boolean isOpen()
{
return overlay.isVisible();
}
/**
* Close this dialog.
* <p>
* If opened via {@link #open(Component, AjaxRequestTarget)}, the content is removed from the
* component tree
*
* @param target
* an optional Ajax target
* @return this
* @see #open(Component, AjaxRequestTarget)
*/
public ModalDialog close(AjaxRequestTarget target)
{
overlay.setVisible(false);
if (removeContentOnClose)
{
dialog.removeAll();
}
if (target != null)
{
target.add(this);
}
return this;
}
/**
* Close this dialog on press of escape key.
*
* @return this
*/
public ModalDialog closeOnEscape()
{
overlay.add(new CloseBehavior("keydown")
{
protected CharSequence getPrecondition()
{
return "return Wicket.Event.keyCode(attrs.event) == 27";
}
});
return this;
}
/**
* Close this dialog on click outside.
*
* @return this
*/
public ModalDialog closeOnClick()
{
overlay.add(new CloseBehavior("click")
{
protected CharSequence getPrecondition()
{
return String.format("return attrs.event.target.id === '%s';",
overlay.getMarkupId());
}
});
return this;
}
/**
* Convenience method to trap focus inside the overlay.
*
* @return this
* @see TrapFocusBehavior
*/
public ModalDialog trapFocus()
{
overlay.add(new TrapFocusBehavior());
return this;
}
/**
* can be overridden to change the {@link AjaxRequestAttributes} of the default
* {@link CloseBehavior} executed on close. For example to change the Ajax Channel.
*
* @param attributes
* the {@link AjaxRequestAttributes} of the default {@link CloseBehavior}
*/
protected void postProcessCloseBehaviorAjaxAttributes(AjaxRequestAttributes attributes)
{
// no op
}
private abstract class CloseBehavior extends AjaxEventBehavior
{
private CloseBehavior(String event)
{
super(event);
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
super.updateAjaxAttributes(attributes);
// has to stop immediately to prevent an enclosing dialog to close too
attributes.setEventPropagation(EventPropagation.STOP_IMMEDIATE);
attributes.getAjaxCallListeners().add(new AjaxCallListener()
{
@Override
public CharSequence getPrecondition(Component component)
{
return CloseBehavior.this.getPrecondition();
}
});
postProcessCloseBehaviorAjaxAttributes(attributes);
}
protected CharSequence getPrecondition()
{
return "";
}
@Override
protected void onEvent(AjaxRequestTarget target)
{
close(target);
}
}
}