Auto-scroll on add ingredient, add Ingredient Group
This commit is contained in:
parent
a06d8e8f79
commit
e859a51772
|
@ -92,10 +92,12 @@ public class JSFUtils {
|
|||
*/
|
||||
public static Object getFlash(String key) {
|
||||
Flash flash = flashScope();
|
||||
if ( flash == null ) {
|
||||
try {
|
||||
return flash.get(key);
|
||||
} catch ( NullPointerException nex ) {
|
||||
// If session is expired, flash.get() will fail!
|
||||
return null;
|
||||
}
|
||||
return flashScope().get(key);
|
||||
}
|
||||
|
||||
public static void putFlash(String key, Object value) {
|
||||
|
|
|
@ -21,7 +21,6 @@ import javax.inject.Named;
|
|||
import javax.servlet.http.Part;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.primefaces.PrimeFaces;
|
||||
import org.primefaces.event.FileUploadEvent;
|
||||
import org.primefaces.model.UploadedFile;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -40,8 +39,6 @@ import com.mousetech.gourmetj.springweb.PictureController;
|
|||
* Backing bean for display/edit recipe detail
|
||||
*
|
||||
* @author timh
|
||||
* @since Jun 28, 2012 TODO: Cross-reference ingredients to
|
||||
* keylookup TODO: Cross-reference shopcats
|
||||
*/
|
||||
|
||||
@Named
|
||||
|
@ -616,7 +613,7 @@ public class RecipeDetailBean implements Serializable {
|
|||
|
||||
if (recipeService.save(this.getRecipe())) {
|
||||
userSession.setRecipe(null);
|
||||
return "main";
|
||||
return "recipeDetails";
|
||||
} else {
|
||||
JSFUtils.addErrorMessage("Save recipe failed");
|
||||
return null;
|
||||
|
@ -1031,12 +1028,11 @@ public class RecipeDetailBean implements Serializable {
|
|||
* Add new group to bottom of model as AJAX operation.
|
||||
* @return null
|
||||
*/
|
||||
public String doAddGroup() {
|
||||
public void doAddGroup() {
|
||||
IngredientUI iui = new IngredientUI(null);
|
||||
iui.setIngGroup(true);
|
||||
iui.setItem(this.getNewGroupName());
|
||||
List<IngredientUI> ingUIList = this.getWrappedIngredients();
|
||||
ingUIList.add(iui);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import javax.faces.application.ViewExpiredException;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
@ -22,6 +23,7 @@ public class SpringPrimeFacesApplication {
|
|||
|
||||
final String errorPage = "/error/error.html";
|
||||
final String error404Page = "/error/error404.html";
|
||||
final String expiredPage = "/error/viewExpired.html";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringPrimeFacesApplication.class,
|
||||
|
@ -59,6 +61,9 @@ public class SpringPrimeFacesApplication {
|
|||
ErrorPageRegistry registry) {
|
||||
registry.addErrorPages(new ErrorPage(
|
||||
HttpStatus.NOT_FOUND, error404Page));
|
||||
registry.addErrorPages(new ErrorPage(
|
||||
ViewExpiredException.class,
|
||||
expiredPage));
|
||||
registry.addErrorPages(new ErrorPage(
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
errorPage));
|
||||
|
|
|
@ -5,6 +5,10 @@ import java.io.Serializable;
|
|||
import javax.enterprise.context.SessionScoped;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.primefaces.PrimeFaces;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Category;
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
|
||||
|
@ -18,6 +22,12 @@ public class UserSession implements Serializable {
|
|||
private static final long serialVersionUID =
|
||||
7449440266704831598L;
|
||||
|
||||
/* Logger */
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(UserSession.class);
|
||||
|
||||
|
||||
private String lastSearch = "";
|
||||
|
||||
/**
|
||||
|
@ -145,4 +155,28 @@ public class UserSession implements Serializable {
|
|||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
// Primefaces handle session timeout
|
||||
|
||||
/**
|
||||
* Session timeout, msec.
|
||||
*/
|
||||
private long sessionTimeoutInterval = 300000;
|
||||
|
||||
/**
|
||||
* @return the sessionTimeoutInterval
|
||||
*/
|
||||
public long getSessionTimeoutInterval() {
|
||||
return sessionTimeoutInterval;
|
||||
}
|
||||
|
||||
public void sessionIdleListener() {
|
||||
log.warn("Session Idle Listener fired.");
|
||||
PrimeFaces.current()
|
||||
.executeScript("sessionExpiredConfirmation.show()");
|
||||
}
|
||||
|
||||
public void logoutAction() {
|
||||
log.warn("Session Idle listener logout");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,30 @@
|
|||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
>Apache License, Version 2.0</a>.
|
||||
<p>Based on Gourmet Recipe Manager by T. Hinkle</p>
|
||||
<h:form id="ftmTimeout">
|
||||
<p:idleMonitor
|
||||
timeout="#{userSession.sessionTimeoutInterval}"
|
||||
onidle="sessionExpiredConfirmation.show()"
|
||||
>
|
||||
<p:ajax event="idle"
|
||||
listener="#{userSession.sessionIdleListener}"
|
||||
/>
|
||||
</p:idleMonitor>
|
||||
<p:confirmDialog closable="false"
|
||||
id="sessionExpiredDlg"
|
||||
message="Your session expired."
|
||||
header="#{msgs['confirmDialog.initiatingDestroyProcess.label']}"
|
||||
severity="alert"
|
||||
widgetVar="sessionExpiredConfirmation"
|
||||
style="z-index: 25000"
|
||||
>
|
||||
<p:commandButton id="confirmRouteDel"
|
||||
value="Ok"
|
||||
oncomplete="sessionExpiredConfirmation.hide()"
|
||||
actionListener="#{userSession.logoutAction}"
|
||||
/>
|
||||
</p:confirmDialog>
|
||||
</h:form>
|
||||
</h:body>
|
||||
</f:view>
|
||||
</ui:composition>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<ui:composition template="/WEB-INF/layout/layout.xhtml">
|
||||
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
||||
<ui:define name="content">
|
||||
<h:outputScript name="js/scrolltable.js"/>
|
||||
<style>
|
||||
.ingSel {
|
||||
width: 3em;
|
||||
|
@ -307,6 +308,7 @@
|
|||
action="#{recipeDetailBean.doAddIngredient}"
|
||||
process="@this ctlAddIngTxt"
|
||||
update="pnlIngredients"
|
||||
oncomplete="scrollAndFocus('form1:tabGroupClient' , 'ingredientTable', 'ctlAddIngTxt');"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Add Ingredient: "
|
||||
|
@ -352,12 +354,12 @@
|
|||
</p:tab>
|
||||
</p:tabView>
|
||||
<p:commandButton id="doSave" value="Save"
|
||||
icon="pi pi-check"
|
||||
icon="ui-icon-pencil"
|
||||
disabled="{not recipeDetailBean.dirty}"
|
||||
action="#{recipeDetailBean.doSave}"
|
||||
/>
|
||||
<p:commandButton id="doCancel" value="Cancel"
|
||||
immediate="true" action="/main.jsf"
|
||||
immediate="true" action="recipeDetails.jsf"
|
||||
/>
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
@ -377,7 +379,7 @@
|
|||
value="OK" style="width: 6em"
|
||||
action="#{recipeDetailBean.doAddGroup}"
|
||||
update="form1:tabGroupClient:ingredientTable"
|
||||
oncomplete="PF('addGroupDlg').hide()"
|
||||
oncomplete="PF('addGroupDlg').hide(); scrollAndFocus('form1:tabGroupClient' , 'ingredientTable', 'ctlAddIngTxt');"
|
||||
/>
|
||||
<p:commandButton id="agDlgCan"
|
||||
value="Cancel" style="width: 6em"
|
||||
|
|
13
src/main/resources/META-INF/resources/error/viewExpired.html
Normal file
13
src/main/resources/META-INF/resources/error/viewExpired.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<title>ERROR - Page Expired</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Page Expired</h1>
|
||||
<p>The page state could not be restored because it was left
|
||||
idle too long.</p>
|
||||
<p><a href="/main.jsf">Return to Main Page</a></p>
|
||||
</body>
|
||||
</html>
|
114
src/main/resources/META-INF/resources/js/scrolltable.js
Normal file
114
src/main/resources/META-INF/resources/js/scrolltable.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
// scrolltable.js - scroll functions for dataTable
|
||||
|
||||
function scrollAndFocus(rootId, tableId, txtId) {
|
||||
scrollToBottom(rootId + ":" + tableId);
|
||||
$(rootId +":" + txtId).focus();
|
||||
}
|
||||
|
||||
function scrollToBottom(idDataTable) {
|
||||
pTable = digestTable(idDataTable);
|
||||
|
||||
scrollToRow(pTable, 9999);
|
||||
}
|
||||
|
||||
function digestTable(idDataTable) {
|
||||
pTable = {
|
||||
id: idDataTable
|
||||
}
|
||||
pTable.jqId = '#' + idDataTable.replaceAll(':', '\\:');
|
||||
pTable.jqData = pTable.jqId + "_data";
|
||||
pTable.heignt = $(pTable.jqId + " .ui-datatable-scrollable-body").height();
|
||||
//var lichildren = $(pTable.jqId).children("tr");
|
||||
var lichildren = $(pTable.jqId).find("tr");
|
||||
pTable.itemHeight = lichildren.height();
|
||||
pTable.itemCount = lichildren.length;
|
||||
pTable.visibleItemCount = pTable.height / pTable.itemHeight;
|
||||
return pTable;
|
||||
}
|
||||
|
||||
|
||||
function scrollToRow(pTable, selItem) {
|
||||
var maxScrollItem = pTable.itemCount - pTable.visibleItemCount;
|
||||
var scrollTopInPx;
|
||||
if(selItem >= maxScrollItem)
|
||||
{
|
||||
// scroll table to the bottom
|
||||
scrollTopInPx = pTable.itemCount * pTable.itemHeight;
|
||||
}
|
||||
else if(selItem < 2)
|
||||
{
|
||||
// scroll table to the top
|
||||
scrollTopInPx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// scroll selected item to the 1.2 th position
|
||||
scrollTopInPx = (selItem - 1.2) * pTable.itemHeight;
|
||||
}
|
||||
|
||||
$(pTable.jqId + " .ui-datatable-scrollable-body").animate({scrollTop:scrollTopInPx, duration: 20}, scrollTopInPx);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function scrolls the selected Item of a
|
||||
* <p:dataTable id="idDataTable" selectionMode="single"
|
||||
* into the visible area
|
||||
* <p:dataTable renderes to a scroll-container with the css-class: ui-datatable-scrollable-body (inside the element with the id : "idDataTable")
|
||||
* and to a container holding all items with the id: "idDataTable"_data
|
||||
*
|
||||
* @param idDataTable z.B.: idForm:idDataTable
|
||||
*/
|
||||
function autoScrollPDatatable(idDataTable)
|
||||
{
|
||||
// for selection in JQuery the ids with : must be encoded with \\:
|
||||
var primfcid = idDataTable.replace(':', '\\:');
|
||||
var idDataTbl = '#' + primfcid;
|
||||
var idDataContainer = idDataTbl + "_data";
|
||||
|
||||
var totalHeight = $(idDataTbl + " .ui-datatable-scrollable-body").height();
|
||||
var lichildren = $(idDataContainer).children("tr");
|
||||
var itemHeight = $(idDataContainer).children("tr").height();
|
||||
var anzItems = lichildren.length;
|
||||
var anzVisItems = totalHeight / itemHeight;
|
||||
var selItem = detectDataTableScrollPos(lichildren);
|
||||
if(selItem == -1)
|
||||
{
|
||||
// no selection found...
|
||||
return;
|
||||
}
|
||||
|
||||
var maxScrollItem = anzItems - anzVisItems;
|
||||
var scrollTopInPx;
|
||||
if(selItem >= maxScrollItem)
|
||||
{
|
||||
// scroll table to the bottom
|
||||
scrollTopInPx = maxScrollItem * itemHeight;
|
||||
}
|
||||
else if(selItem < 2)
|
||||
{
|
||||
// scroll table to the top
|
||||
scrollTopInPx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// scroll selected item to the 1.2 th position
|
||||
scrollTopInPx = (selItem - 1.2) * itemHeight;
|
||||
}
|
||||
|
||||
$(idDataTbl + " .ui-datatable-scrollable-body").animate({scrollTop:scrollTopInPx}, scrollTopInPx);
|
||||
}
|
||||
|
||||
function detectDataTableScrollPos(liChildren)
|
||||
{
|
||||
for (i=0;i< liChildren.length;++i)
|
||||
{
|
||||
var chd = liChildren[i];
|
||||
var x = chd.getAttribute("aria-selected");
|
||||
if(x === "true")
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
<p:commandButton value="Back"
|
||||
icon="ui-icon-arrowthick-1-w"
|
||||
style="margin-left: 2em" immediate="true"
|
||||
action="/main.jsf"
|
||||
action="/recipeDetails.jsf"
|
||||
/>
|
||||
</p:column>
|
||||
</p:panelGrid>
|
||||
|
|
Loading…
Reference in New Issue
Block a user