Auto-scroll on add ingredient, add Ingredient Group

version2
Tim Holloway 2 years ago
parent a06d8e8f79
commit e859a51772
  1. 6
      src/main/java/com/mousetech/gourmetj/JSFUtils.java
  2. 8
      src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java
  3. 5
      src/main/java/com/mousetech/gourmetj/SpringPrimeFacesApplication.java
  4. 36
      src/main/java/com/mousetech/gourmetj/UserSession.java
  5. 24
      src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml
  6. 8
      src/main/resources/META-INF/resources/detailEdit.xhtml
  7. 13
      src/main/resources/META-INF/resources/error/viewExpired.html
  8. 114
      src/main/resources/META-INF/resources/js/scrolltable.js
  9. 2
      src/main/resources/META-INF/resources/recipePrint.xhtml

@ -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 = "";
/**
@ -112,7 +122,7 @@ public class UserSession implements Serializable {
}
return sb.toString();
}
public String formatTime(Long ltime) {
if (ltime == null) {
return "";
@ -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"

@ -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>

@ -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…
Cancel
Save