--- a/src/main/scala/bootstrap/liftweb/Boot.scala Thu Apr 12 10:52:42 2012 +0200
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala Thu Apr 12 13:36:45 2012 +0200
@@ -46,6 +46,8 @@
LiftRules.htmlProperties.default.set { r: Req =>
Html5Properties(r.userAgent) }
+
+ ScreenRules.init()
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/base/ui/BootstrapScreen.scala Thu Apr 12 13:36:45 2012 +0200
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Tomas Zeman <tzeman@volny.cz>
+ *
+ * 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.
+ *
+ * BootstrapScreen is based on ScreenWizardRendered which is
+ * Copyright 2010-2011 WorldWide Conferencing, LLC
+ *
+ */
+package fis.base.ui
+
+import net.liftweb.common._
+import net.liftweb.http._
+import net.liftweb.http.js._
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+import scala.xml._
+
+trait BootstrapScreen extends ScreenWizardRendered {
+
+ protected override def renderAll(currentScreenNumber: Box[NodeSeq],
+ screenCount: Box[NodeSeq],
+ wizardTop: Box[Elem],
+ screenTop: Box[Elem],
+ fields: List[ScreenFieldInfo],
+ prev: Box[Elem],
+ cancel: Box[Elem],
+ next: Box[Elem],
+ finish: Box[Elem],
+ screenBottom: Box[Elem],
+ wizardBottom: Box[Elem],
+ nextId: (String, () => JsCmd),
+ prevId: Box[(String, () => JsCmd)],
+ cancelId: (String, () => JsCmd),
+ theScreen: AbstractScreen,
+ ajax_? : Boolean): NodeSeq = {
+
+ val notices: List[(NoticeType.Value, NodeSeq, Box[String])] = S.getAllNotices
+
+ def bindFieldLine(xhtml: NodeSeq): NodeSeq = {
+ fields.flatMap {
+ f =>
+ val theFormEarly = f.input
+ val curId = theFormEarly.flatMap(Helpers.findId) or
+ f.field.uniqueFieldId openOr Helpers.nextFuncName
+
+ val theForm = theFormEarly.map {
+ fe => {
+ val f = Helpers.deepEnsureUniqueId(fe)
+ val id = Helpers.findBox(f)(_.attribute("id").
+ map(_.text).
+ filter(_ == curId))
+ if (id.isEmpty) {
+ Helpers.ensureId(f, curId)
+ } else {
+ f
+ }
+ }
+ }
+
+ val myNotices = notices.filter(fi => fi._3.isDefined && fi._3 == curId)
+ val maxNotice = myNotices.map(_._1).sortWith { _.id > _.id }.headOption
+
+ def doLabel(in: NodeSeq): NodeSeq =
+ bind("wizard", in, AttrBindParam("for", curId, "for"), "bind" -%> f.text)
+
+ val line = bind("wizard", xhtml,
+ "label" -%> doLabel _,
+ "form" -%> theForm,
+ FuncBindParam("help", xml => {
+ f.help match {
+ case Full(hlp) => bind("wizard", xml, "bind" -%> hlp)
+ case _ => NodeSeq.Empty
+ }
+ }),
+ FuncBindParam("field_errors", xml => {
+ myNotices match {
+ case Nil => NodeSeq.Empty
+ case xs => bind("wizard", xml, "error" -%>
+ (innerXml => xs.flatMap {
+ case (noticeType, msg, _) =>
+ val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
+ bind("wizard", innerXml, "bind" -%> msg).map {
+ case e: Elem => e % metaData
+ case x => x
+ }
+ }))
+ }
+ }))
+
+ maxNotice map { t =>
+ (".control-group [class+]" #> t.lowerCaseTitle)(line)
+ } getOrElse line
+ }
+ }
+
+ def url = S.uri
+
+ val snapshot = createSnapshot
+
+ def bindErrors(xhtml: NodeSeq): NodeSeq = notices.filter(_._3.isEmpty) match {
+ case Nil => NodeSeq.Empty
+ case xs =>
+ def doErrors(in: NodeSeq): NodeSeq = xs.flatMap {
+ case (noticeType, msg, _) =>
+ val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
+ bind("wizard", in, "bind" -%>
+ (msg)).map {
+ case e: Elem => e % metaData
+ case x => x
+ }
+ }
+
+ bind("wizard", xhtml,
+ "item" -%> doErrors _)
+ }
+
+ def bindFields(xhtml: NodeSeq): NodeSeq = {
+ val ret =
+ (<form id={nextId._1} action={url}
+ method="post">
+ {S.formGroup(-1)(SHtml.hidden(() =>
+ snapshot.restore()))}{bind("wizard", xhtml,
+ "line" -%> bindFieldLine _)}{S.formGroup(4)(
+ SHtml.hidden(() => {
+ val res = nextId._2();
+ if (!ajax_?) {
+ val localSnapshot = createSnapshot
+ S.seeOther(url, () => {
+ localSnapshot.restore
+ })
+ }
+ res
+ }))}
+ </form> %
+ theScreen.additionalAttributes) ++
+ prevId.toList.map {
+ case (id, func) =>
+ <form id={id} action={url} method="post">
+ {SHtml.hidden(() => {
+ snapshot.restore();
+ val res = func();
+ if (!ajax_?) {
+ val localSnapshot = createSnapshot;
+ S.seeOther(url, () => localSnapshot.restore)
+ }
+ res
+ })}
+ </form>
+ } ++
+ <form id={cancelId._1} action={url} method="post">
+ {SHtml.hidden(() => {
+ snapshot.restore();
+ val res = cancelId._2() // WizardRules.deregisterWizardSession(CurrentSession.is)
+ if (!ajax_?) {
+ S.seeOther(Referer.get)
+ }
+ res
+ })}
+ </form>
+
+ if (ajax_?) {
+ SHtml.makeFormsAjax(ret)
+ } else {
+ ret
+ }
+ }
+
+ def bindScreenInfo(xhtml: NodeSeq): NodeSeq = (currentScreenNumber, screenCount) match {
+ case (Full(num), Full(cnt)) =>
+ bind("wizard", xhtml, "screen_number" -%> num /*Text(CurrentScreen.is.map(s => (s.myScreenNum + 1).toString) openOr "")*/ ,
+ "total_screens" -%> cnt /*Text(screenCount.toString)*/)
+ case _ => NodeSeq.Empty
+ }
+
+ Helpers.bind("wizard", allTemplate,
+ "screen_info" -%> bindScreenInfo _,
+ FuncBindParam("wizard_top", xml => (wizardTop.map(top => bind("wizard", xml, "bind" -%> top)) openOr NodeSeq.Empty)),
+ FuncBindParam("screen_top", xml => (screenTop.map(top => bind("wizard", xml, "bind" -%> top)) openOr NodeSeq.Empty)),
+ FuncBindParam("wizard_bottom", xml => (wizardBottom.map(bottom => bind("wizard", xml, "bind" -%> bottom)) openOr NodeSeq.Empty)),
+ FuncBindParam("screen_bottom", xml => (screenBottom.map(bottom => bind("wizard", xml, "bind" -%> bottom)) openOr NodeSeq.Empty)),
+ "prev" -%> (prev openOr EntityRef("nbsp")),
+ "next" -%> ((next or finish) openOr EntityRef("nbsp")),
+ "cancel" -%> (cancel openOr EntityRef("nbsp")),
+ "errors" -%> bindErrors _,
+ FuncBindParam("fields", bindFields _))
+
+ }
+}
+
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/base/ui/screen.scala Thu Apr 12 10:52:42 2012 +0200
+++ b/src/main/scala/fis/base/ui/screen.scala Thu Apr 12 13:36:45 2012 +0200
@@ -15,9 +15,9 @@
*/
package fis.base.ui
-import net.liftweb.http.LiftScreen
+import net.liftweb.http._
import net.tz.lift.model._
-import scala.xml.{Elem, Text, UnprefixedAttribute}
+import scala.xml.{Elem, MetaData, Null, Text, UnprefixedAttribute}
trait SaveButton { self: LiftScreen =>
override def finishButton: Elem =
@@ -38,9 +38,19 @@
<button class="btn">{l10n("Cancel")}</button>
}
-trait HorizontalScreen extends LiftScreen {
+trait HorizontalScreen extends LiftScreen with BootstrapScreen {
override def additionalAttributes = new UnprefixedAttribute("class",
Text("form-horizontal"), super.additionalAttributes)
}
+object ScreenRules {
+ def init() {
+ LiftScreenRules.messageStyles.default.set(() => {
+ case NoticeType.Notice => new UnprefixedAttribute("class", "notice", Null)
+ case NoticeType.Warning => new UnprefixedAttribute("class", "warning", Null)
+ case NoticeType.Error => new UnprefixedAttribute("class", "error", Null)
+ }: PartialFunction[NoticeType.Value, MetaData])
+ }
+}
+
// vim: set ts=2 sw=2 et:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/net/tz/lift/model/FieldHelp.scala Thu Apr 12 13:36:45 2012 +0200
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 Tomas Zeman <tzeman@volny.cz>
+ *
+ * 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 net.tz.lift.model
+
+import net.liftweb.common._
+import net.liftweb.record._
+import net.liftweb.util.Helpers._
+
+/**
+ * Generic field help.
+ * Field help is taken from resource bundle.
+ */
+trait FieldHelp { self: OwnedField[_ <: Record[_]] =>
+ override def helpAsHtml = Box !! i18n("%s.%s.help".format(
+ camelifyMethod(owner.getClass.getSimpleName),
+ camelifyMethod(name)) filterNot {_ == '$'})
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/webapp/css/base.css Thu Apr 12 10:52:42 2012 +0200
+++ b/src/main/webapp/css/base.css Thu Apr 12 13:36:45 2012 +0200
@@ -56,6 +56,14 @@
font-weight: bold;
}
+.control-group label {
+ float: left;
+}
+
+p.inline {
+ margin: 0px 0px 0px 0px;
+}
+
#secnav li {
float: left;
padding: 0px 10px 10px 0;
--- a/src/main/webapp/templates-hidden/wizard-all.html Thu Apr 12 10:52:42 2012 +0200
+++ b/src/main/webapp/templates-hidden/wizard-all.html Thu Apr 12 13:36:45 2012 +0200
@@ -10,26 +10,26 @@
<fieldset>
<legend></legend>
<wizard:fields>
- <div lift:bind="wizard:line" class="control-group">
- <wizard:label>
- <label wizard:for="" class="control-label"> <wizard:bind></wizard:bind> </label>
- </wizard:label>
- <div class="controls">
- <wizard:form></wizard:form>
- <wizard:help>
- <span class="help-inline"> <wizard:bind></wizard:bind> </span>
- </wizard:help>
- <wizard:field_errors>
+ <wizard:line>
+ <div class="control-group">
+ <wizard:label>
+ <label wizard:for="" class="control-label"> <wizard:bind></wizard:bind> </label>
+ </wizard:label>
+ <div class="controls">
+ <wizard:form></wizard:form>
<span class="help-inline">
- <ul>
- <wizard:error>
- <li><wizard:bind></wizard:bind></li>
- </wizard:error>
- </ul>
+ <wizard:help>
+ <p class="inline"><wizard:bind></wizard:bind></p>
+ </wizard:help>
+ <wizard:field_errors>
+ <wizard:error>
+ <p class="inline"><wizard:bind></wizard:bind></p>
+ </wizard:error>
+ </wizard:field_errors>
</span>
- </wizard:field_errors>
+ </div>
</div>
- </div>
+ </wizard:line>
</wizard:fields>
<div class="form-horizontal">
<div class="form-actions">