Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add CSS bound LiftScreen

  • Loading branch information...
commit 5d1174851c67ebcfd6e24b32d176bc43055f5539 1 parent 4a4dbd3
@pbrant pbrant authored
View
92 web/webkit/src/main/scala/net/liftweb/http/CssBoundLiftScreen.scala
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011-2012 WorldWide Conferencing, LLC
+ *
+ * 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.liftweb
+package http
+
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmd
+import net.liftweb.common._
+import util._
+
+import xml._
+
+trait CssBoundLiftScreen extends LiftScreen with CssBoundScreen {
+ protected object SavedDefaultXml extends ScreenVar[NodeSeq](defaultXml) {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ protected object LocalAction extends TransientRequestVar[String]("") {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ protected object LocalActionRef extends ScreenVar[String](S.fmapFunc(setLocalAction _)(s => s)) {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ protected object PrevId extends TransientRequestVar[Box[String]](Empty) {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ protected object CancelId extends TransientRequestVar[String]("") {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ protected object LocalActions extends ScreenVar[Map[String, () => JsCmd]](Map[String, () => JsCmd]()) {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
+ override def localSetup() {
+ SavedDefaultXml.get
+ LocalActionRef.get
+ }
+
+ override def allTemplate = SavedDefaultXml.get
+
+ protected def defaultAllTemplate = super.allTemplate
+
+ override protected def doFinish(): JsCmd= {
+ val fMap: Map[String, () => JsCmd] = LocalActions.get
+ if (! LocalAction.get.isEmpty)
+ fMap.get(LocalAction.get) map (_()) getOrElse (
+ throw new IllegalArgumentException("No local action available with that binding"))
+ else {
+ validate match {
+ case Nil =>
+ val snapshot = createSnapshot
+ PrevSnapshot.set(Full(snapshot))
+ finish()
+ redirectBack()
+ case xs => {
+ S.error(xs)
+ if (ajaxForms_?) {
+ replayForm
+ } else {
+ Noop
+ }
+ }
+ }
+ }
+ }
+
+ protected def renderWithErrors(errors: List[FieldError]) {
+ S.error(errors)
+ AjaxOnDone.set(replayForm)
+ }
+
+ protected def renderFormCmd: JsCmd = SetHtml(FormGUID, renderHtml())
+
+ protected def replayForm: JsCmd = renderFormCmd
+}
View
377 web/webkit/src/main/scala/net/liftweb/http/CssBoundScreen.scala
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2011-2012 WorldWide Conferencing, LLC
+ *
+ * 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.liftweb
+package http
+
+import js.jquery.JqJE.JqId
+import js.JsCmd
+import js.JsCmds._
+import js.jquery.JqJsCmds._
+import js.JE
+import net.liftweb.util._
+import Helpers._
+import net.liftweb.common._
+import xml._
+
+import FieldBinding._
+
+trait CssBoundScreen extends ScreenWizardRendered with Loggable {
+ self: AbstractScreen =>
+
+ def formName: String
+
+ def labelSuffix: NodeSeq = Text(":")
+
+ protected lazy val cssClassBinding = new CssClassBinding
+
+ protected val LocalActionRef: AnyVar[String, _]
+ protected val LocalAction: AnyVar[String, _]
+ protected val LocalActions: AnyVar[Map[String, () => JsCmd], _]
+
+ val NextId: AnyVar[String, _]
+
+ protected val PrevId: AnyVar[Box[String], _]
+ protected val CancelId: AnyVar[String, _]
+
+ protected def additionalFormBindings: Box[CssSel] = Empty
+
+ private def traceInline[T](msg: => String, v: T): T = {
+ logger.trace(msg)
+ v
+ }
+
+ private object FieldBindingUtils {
+ def sel(f: CssClassBinding => String, sel: String) = sel format (f(cssClassBinding))
+ def replace(f: CssClassBinding => String) = ".%s" format (f(cssClassBinding))
+ def replaceChildren(f: CssClassBinding => String) = ".%s *" format (f(cssClassBinding))
+
+ def remove(f: CssClassBinding => String) =
+ traceInline("Removing %s".format(f(cssClassBinding)), ".%s".format(f(cssClassBinding))) #> NodeSeq.Empty
+
+ def nsSetChildren(f: CssClassBinding => String, value: NodeSeq) =
+ traceInline("Binding %s to %s".format(replaceChildren(f), value), replaceChildren(f) #> value)
+
+ def funcSetChildren(f: CssClassBinding => String, value: NodeSeq => NodeSeq) =
+ traceInline("Binding %s to function".format(replaceChildren(f)), replaceChildren(f) #> value)
+
+ def optSetChildren(f: CssClassBinding => String, value: Box[NodeSeq]) =
+ traceInline("Binding %s to %s".format(replaceChildren(f), value), replaceChildren(f) #> value)
+
+ def nsReplace(f: CssClassBinding=>String, value: NodeSeq) =
+ traceInline("Binding %s to %s".format(replace(f), value), replace(f) #> value)
+
+ def funcReplace(f: CssClassBinding=>String, value: NodeSeq => NodeSeq) =
+ traceInline("Binding %s to function".format(replace(f)), replace(f) #> value)
+
+ def optReplace(f: CssClassBinding=>String, value: Box[NodeSeq]) =
+ traceInline("Binding %s to %s".format(replace(f), value), replace(f) #> value)
+
+ def updateAttrs(metaData: MetaData): NodeSeq => NodeSeq = {
+ case e:Elem => e % metaData
+ }
+
+ def update(f: CssClassBinding => String, metaData: MetaData) =
+ traceInline("Update %s with %s".format(f(cssClassBinding), metaData),
+ ".%s".format(f(cssClassBinding)) #> updateAttrs(metaData))
+ }
+
+ protected def bindLocalAction(selector: String, func: () => JsCmd): CssSel = {
+ mapLocalAction(func)(name =>
+ selector #> (
+ SHtml.makeAjaxCall(LiftRules.jsArtifacts.serialize(NextId.get) + ("&" + LocalActionRef.get + "=" + name)).cmd
+ ).toJsCmd)
+ }
+
+ protected def mapLocalAction[T](func: () => JsCmd)(f: String => T): T = {
+ val name = randomString(20)
+ LocalActions.set(LocalActions.is + (name -> func))
+ f(name)
+ }
+
+ protected def setLocalAction(s: String) {
+ logger.debug("Setting LocalAction (%s) to %s".format(
+ Integer.toString(System.identityHashCode(LocalAction), 16), s))
+ LocalAction.set(s)
+ }
+
+ override protected 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 = {
+
+ import FieldBindingUtils._
+
+ NextId.set(nextId._1)
+ PrevId.set(prevId map (_._1))
+ CancelId.set(cancelId._1)
+
+ val notices: List[(NoticeType.Value, NodeSeq, Box[String])] = S.getAllNotices
+
+ def fieldsWithStyle(style: BindingStyle, includeMissing: Boolean) =
+ logger.trace("Looking for fields with style %s, includeMissing = %s".format(style, includeMissing),
+ fields filter (field => field.binding map (_.bindingStyle == style) openOr (includeMissing)))
+
+ def bindingInfoWithFields(style: BindingStyle) =
+ logger.trace("Looking for fields with style %s".format(style),
+ (for {
+ field <- fields;
+ bindingInfo <- field.binding if bindingInfo.bindingStyle == style
+ } yield (bindingInfo, field)).toList)
+
+ def templateFields: List[CssBindFunc] = List(sel(_.fieldContainer, ".%s") #> (fieldsWithStyle(Template, true) map (field => bindField(field))))
+
+ def selfFields: List[CssBindFunc] =
+ for ((bindingInfo, field) <- bindingInfoWithFields(Self))
+ yield traceInline("Binding self field %s".format(bindingInfo.selector(formName)),
+ bindingInfo.selector(formName) #> bindField(field))
+
+ def defaultFields: List[CssBindFunc] =
+ for ((bindingInfo, field) <- bindingInfoWithFields(Default))
+ yield traceInline("Binding default field %s to %s".format(bindingInfo.selector(formName), defaultFieldNodeSeq),
+ bindingInfo.selector(formName) #> bindField(field)(defaultFieldNodeSeq))
+
+ def customFields: List[CssBindFunc] =
+ for {
+ field <- fields
+ bindingInfo <- field.binding
+ custom <- Some(bindingInfo.bindingStyle) collect { case c:Custom => c }
+ } yield traceInline("Binding custom field %s to %s".format(bindingInfo.selector(formName), custom.template),
+ bindingInfo.selector(formName) #> bindField(field)(custom.template))
+
+ def bindFields: CssBindFunc = {
+ logger.trace("Binding fields", fields)
+ List(templateFields, selfFields, defaultFields, customFields).flatten.reduceLeft(_ & _)
+ }
+
+ def bindField(f: ScreenFieldInfo): NodeSeq => NodeSeq = {
+ 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)
+
+ def bindLabel(): CssBindFunc = {
+ val basicLabel = sel(_.label, ".%s [for]") #> curId & nsSetChildren(_.label, f.text ++ labelSuffix)
+ myNotices match {
+ case Nil => basicLabel
+ case _ =>
+ val maxN = myNotices.map(_._1).sortWith{_.id > _.id}.head // get the maximum type of notice (Error > Warning > Notice)
+ val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(maxN)) openOr Null
+ basicLabel & update(_.label, metaData)
+ }
+ }
+
+ def bindForm(): CssBindFunc =
+ traceInline("Replacing %s with %s".format(replace(_.value), theForm),
+ replace(_.value) #> theForm)
+
+ def bindHelp(): CssBindFunc =
+ f.help match {
+ case Full(hlp) => nsSetChildren(_.help, hlp)
+ case _ => remove(_.help)
+ }
+
+ def bindErrors(): CssBindFunc =
+ myNotices match {
+ case Nil => remove(_.errors)
+ case xs => replaceChildren(_.errors) #> xs.map { case(noticeType, msg, _) =>
+ val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
+ nsSetChildren(_.error, msg) & update(_.error, metaData)
+ }
+ }
+
+ def bindAll() = bindLabel() & bindForm() & bindHelp() & bindErrors()
+
+ f.transform map (func => bindAll() andThen func()) openOr (bindAll())
+ }
+
+ def url = S.uri
+
+ val savAdditionalFormBindings = additionalFormBindings
+
+ def bindErrors: CssBindFunc = notices.filter(_._3.isEmpty) match {
+ case Nil => remove(_.globalErrors)
+ case xs => replaceChildren(_.globalErrors) #> xs.map { case(noticeType, msg, _) =>
+ val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
+ nsSetChildren(_.error, msg) & update(_.error, metaData)
+ }
+ }
+
+ def bindFieldsWithAdditional(xhtml: NodeSeq) =
+ (savAdditionalFormBindings map (bindFields & _) openOr (bindFields))(xhtml)
+
+ def liftScreenAttr(s: String) =
+ new UnprefixedAttribute("data-lift-screen-control", Text(s), Null)
+
+ def bindForm(xhtml: NodeSeq): NodeSeq = {
+ val fields = bindFieldsWithAdditional(xhtml)
+
+ val snapshot = createSnapshot
+
+ val ret =
+ (<form id={nextId._1} action={url}
+ method="post">{S.formGroup(-1)(SHtml.hidden(() =>
+ snapshot.restore()) % liftScreenAttr("restoreAction"))}{fields}{
+ S.formGroup(4)(
+ SHtml.hidden(() =>
+ {val res = nextId._2();
+ if (!ajax_?) {
+ val localSnapshot = createSnapshot
+ S.seeOther(url, () => {
+ localSnapshot.restore
+ })}
+ res
+ })) % liftScreenAttr("nextAction") }</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
+ }) % liftScreenAttr("restoreAction")}</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
+ }) % liftScreenAttr("restoreAction")}</form>
+
+ if (ajax_?) {
+ SHtml.makeFormsAjax(ret)
+ } else {
+ ret
+ }
+ }
+
+ def bindScreenInfo: CssBindFunc = (currentScreenNumber, screenCount) match {
+ case (Full(num), Full(cnt)) =>
+ replaceChildren(_.screenInfo) #> (nsSetChildren(_.screenNumber, num) & nsSetChildren(_.totalScreens, cnt))
+ case _ => remove(_.screenInfo)
+ }
+
+ logger.trace("Preparing to bind", fields)
+
+ val bindingFunc: CssBindFunc =
+ bindScreenInfo &
+ optSetChildren(_.wizardTop, wizardTop) &
+ optSetChildren(_.screenTop, screenTop) &
+ optSetChildren(_.wizardBottom, wizardBottom) &
+ optSetChildren(_.screenBottom, screenBottom) &
+ nsReplace(_.prev, prev openOr EntityRef("nbsp")) &
+ nsReplace(_.next, ((next or finish) openOr EntityRef("nbsp"))) &
+ nsReplace(_.cancel, cancel openOr EntityRef("nbsp")) &
+ bindErrors &
+ funcSetChildren(_.fields, bindForm _)
+
+ val processed = S.session map (_.runTemplate("css-bound-screen", allTemplate)) openOr (allTemplate)
+
+ (savAdditionalFormBindings map (bindingFunc & _) openOr (bindingFunc))(processed)
+ }
+
+ override protected def allTemplateNodeSeq: NodeSeq = {
+ <div>
+ <div class="screenInfo">
+ Page <span class="screenNumber"></span> of <span class="totalScreens"></span>
+ </div>
+ <div class="wizardTop"></div>
+ <div class="screenTop"></div>
+ <div class="globalErrors">
+ <div class="error"></div>
+ </div>
+ <div class="fields">
+ <table>
+ <tr class="fieldContainer">
+ <td>
+ <label class="label field"></label>
+ <span class="help"></span>
+ <div class="errors">
+ <div class="error"></div>
+ </div>
+ </td>
+ <td><span class="value fieldValue"></span></td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <table>
+ <tr>
+ <td><button class="prev"></button></td>
+ <td><button class="cancel"></button></td>
+ <td><button class="next"></button> </td>
+ </tr>
+ </table>
+ </div>
+ <div class="screenBottom"></div>
+ <div class="wizardBottom"></div>
+ </div>
+ }
+
+ def defaultFieldNodeSeq: NodeSeq = NodeSeq.Empty
+
+ class CssClassBinding {
+ def screenInfo = "screenInfo"
+ def wizardTop = "wizardTop"
+ def screenTop = "screenTop"
+ def globalErrors = "globalErrors"
+ def fields = "fields"
+ def fieldContainer = "fieldContainer"
+ def label = "label"
+ def help = "help"
+ def errors = "errors"
+ def error = "error"
+ def value = "value"
+ def prev = "prev"
+ def cancel = "cancel"
+ def next = "next"
+ def screenBottom = "screenBottom"
+ def wizardBottom = "wizardBottom"
+ def screenNumber = "screenNumber"
+ def totalScreens = "totalScreens"
+ }
+}
View
192 web/webkit/src/main/scala/net/liftweb/http/LiftScreen.scala
@@ -169,6 +169,8 @@ trait AbstractScreen extends Factory {
*/
def otherValue: OtherValueType = _otherValue.get
+ def setOtherValue(v: OtherValueType) = _otherValue.set(v)
+
def default: ValueType
def is = _currentValue.is
@@ -224,6 +226,10 @@ trait AbstractScreen extends Factory {
vendAVar(Helpers.nextFuncName)
override def toString = is.toString
+
+ def binding: Box[FieldBinding] = Empty
+
+ def transform: Box[() => CssSel] = Empty
}
protected object currentField extends ThreadGlobal[FieldIdentifier]
@@ -271,6 +277,22 @@ trait AbstractScreen extends Factory {
case OnConfirmScreen => true
}.headOption
+ val newBinding: Box[FieldBinding] = (stuff.collect {
+ case AFieldBinding(i) => i
+ }).headOption
+
+ val newHelp: Box[NodeSeq] = help or (stuff.collect {
+ case Help(ns) => ns
+ }).headOption
+
+ val newTransform: Box[() => CssSel] = (stuff.collect {
+ case FieldTransform(func) => func
+ }).headOption
+
+ val newShow: Box[() => Boolean] = (stuff.collect {
+ case DisplayIf(func) => func
+ }).headOption
+
new Field {
type ValueType = T
@@ -290,7 +312,7 @@ trait AbstractScreen extends Factory {
override implicit def manifest: Manifest[ValueType] = FieldBuilder.this.manifest
- override def helpAsHtml = help
+ override def helpAsHtml = newHelp
override def validations = FieldBuilder.this.validations
@@ -299,8 +321,14 @@ trait AbstractScreen extends Factory {
override def uniqueFieldId: Box[String] =
paramFieldId or Full(_theFieldId.get)
+ override def binding = newBinding
+
private lazy val _theFieldId: NonCleanAnyVar[String] =
vendAVar(Helpers.nextFuncName)
+
+ override def transform = newTransform
+
+ override def show_? = newShow map (_()) openOr (super.show_?)
}
}
}
@@ -345,6 +373,8 @@ trait AbstractScreen extends Factory {
implicit def promoteToFormParam(a: SHtml.ElemAttr): FilterOrValidate[Nothing] = FormParam(a)
implicit def promoteToFormParam(a: (String, String)): FilterOrValidate[Nothing] = FormParam(a)
+
+ implicit def promoteFieldBinding(binding: FieldBinding): FilterOrValidate[Nothing] = AFieldBinding(binding)
}
sealed protected trait FilterOrValidate[+T]
@@ -370,6 +400,14 @@ trait AbstractScreen extends Factory {
protected final case class AVal[T](v: T => List[FieldError]) extends FilterOrValidate[T]
+ protected final case class AFieldBinding(binding: FieldBinding) extends FilterOrValidate[Nothing]
+
+ protected final case class Help(ns: NodeSeq) extends FilterOrValidate[Nothing]
+
+ protected final case class FieldTransform(func: () => CssSel) extends FilterOrValidate[Nothing]
+
+ protected final case class DisplayIf(func: () => Boolean) extends FilterOrValidate[Nothing]
+
protected def field[T](underlying: => BaseField {type ValueType = T},
stuff: FilterOrValidate[T]*)(implicit man: Manifest[T]): Field {type ValueType = T} = {
val paramFieldId: Box[String] = (stuff.collect {
@@ -383,6 +421,22 @@ trait AbstractScreen extends Factory {
case OnConfirmScreen => true
}.headOption
+ val newBinding: Box[FieldBinding] = (stuff.collect {
+ case AFieldBinding(i) => i
+ }).headOption
+
+ val newHelp: Box[NodeSeq] = (stuff.collect {
+ case Help(ns) => ns
+ }).headOption
+
+ val newTransform: Box[() => CssSel] = (stuff.collect {
+ case FieldTransform(func) => func
+ }).headOption
+
+ val newShow: Box[() => Boolean] = (stuff.collect {
+ case DisplayIf(func) => func
+ }).headOption
+
new Field {
type ValueType = T
@@ -396,7 +450,7 @@ trait AbstractScreen extends Factory {
/**
* Give the current state of things, should the this field be shown
*/
- override def show_? = underlying.show_?
+ override def show_? = newShow map (_()) openOr underlying.show_?
/**
* What form elements are we going to add to this field?
@@ -420,7 +474,7 @@ trait AbstractScreen extends Factory {
override implicit def manifest: Manifest[ValueType] = man
- override def helpAsHtml = underlying.helpAsHtml
+ override def helpAsHtml = newHelp or underlying.helpAsHtml
override def validate: List[FieldError] = underlying.validate
@@ -441,6 +495,10 @@ trait AbstractScreen extends Factory {
override def set(v: T) = underlying.set(setFilter.foldLeft(v)((v, f) => f(v)))
override def uniqueFieldId: Box[String] = paramFieldId or underlying.uniqueFieldId or super.uniqueFieldId
+
+ override def binding = newBinding or super.binding
+
+ override def transform = newTransform or super.transform
}
}
@@ -458,6 +516,22 @@ trait AbstractScreen extends Factory {
case FormFieldId(id) => id
}).headOption
+ val newBinding: Box[FieldBinding] = (stuff.collect {
+ case AFieldBinding(i) => i
+ }).headOption
+
+ val newHelp: Box[NodeSeq] = (stuff.collect {
+ case Help(ns) => ns
+ }).headOption
+
+ val newTransform: Box[() => CssSel] = (stuff.collect {
+ case FieldTransform(func) => func
+ }).headOption
+
+ val newShow: Box[() => Boolean] = (stuff.collect {
+ case DisplayIf(func) => func
+ }).headOption
+
val confirmInfo = stuff.collect {
case NotOnConfirmScreen => false
}.headOption orElse
@@ -478,7 +552,7 @@ trait AbstractScreen extends Factory {
/**
* Give the current state of things, should the this field be shown
*/
- override def show_? = underlying.map(_.show_?) openOr false
+ override def show_? = newShow map (_()) openOr (underlying.map(_.show_?) openOr false)
/**
* What form elements are we going to add to this field?
@@ -502,7 +576,7 @@ trait AbstractScreen extends Factory {
override implicit def manifest: Manifest[ValueType] = man
- override def helpAsHtml = underlying.flatMap(_.helpAsHtml)
+ override def helpAsHtml = newHelp or underlying.flatMap(_.helpAsHtml)
override def validate: List[FieldError] = underlying.toList.flatMap(_.validate)
@@ -517,6 +591,10 @@ trait AbstractScreen extends Factory {
override def set(v: T) = underlying.open_!.set(setFilter.foldLeft(v)((v, f) => f(v)))
override def uniqueFieldId: Box[String] = paramFieldId or underlying.flatMap(_.uniqueFieldId) or super.uniqueFieldId
+
+ override def binding = newBinding or super.binding
+
+ override def transform = newTransform or super.transform
}
}
@@ -636,6 +714,22 @@ trait AbstractScreen extends Factory {
otherValue: OtherValueInitializer[OV],
stuff: FilterOrValidate[T]*):
Field {type ValueType = T; type OtherValueType = OV} = {
+ val newBinding: Box[FieldBinding] = (stuff.collect {
+ case AFieldBinding(i) => i
+ }).headOption
+
+ val newHelp: Box[NodeSeq] = (stuff.collect {
+ case Help(ns) => ns
+ }).headOption
+
+ val newTransform: Box[() => CssSel] = (stuff.collect {
+ case FieldTransform(func) => func
+ }).headOption
+
+ val newShow: Box[() => Boolean] = (stuff.collect {
+ case DisplayIf(func) => func
+ }).headOption
+
otherValue match {
case OtherValueInitializerImpl(otherValueInitFunc) => {
new Field {
@@ -664,7 +758,15 @@ trait AbstractScreen extends Factory {
case _ => Nil
}.toList
+ override def binding = newBinding
+
+ override def helpAsHtml = newHelp
+
override def toForm: Box[NodeSeq] = theToForm(this)
+
+ override def transform = newTransform
+
+ override def show_? = newShow map (_()) openOr (super.show_?)
}
}
@@ -693,7 +795,15 @@ trait AbstractScreen extends Factory {
case _ => Nil
}.toList
+ override def binding = newBinding
+
+ override def helpAsHtml = newHelp
+
override def toForm: Box[NodeSeq] = theToForm(this)
+
+ override def transform = newTransform
+
+ override def show_? = newShow map (_()) openOr (super.show_?)
}
}
}
@@ -870,6 +980,7 @@ trait AbstractScreen extends Factory {
OtherValueInitializerImpl[Seq[String]](() => choices),
stuff: _*)
}
+
}
@@ -1205,7 +1316,21 @@ trait ScreenWizardRendered {
}
-case class ScreenFieldInfo(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq])
+case class ScreenFieldInfo(
+ field: FieldIdentifier,
+ text: NodeSeq,
+ help: Box[NodeSeq],
+ input: Box[NodeSeq],
+ binding: Box[FieldBinding],
+ transform: Box[() => CssSel]) {
+ def this(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq]) =
+ this(field, text, help, input, Empty, Empty)
+ }
+
+object ScreenFieldInfo {
+ def apply(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq]) =
+ new ScreenFieldInfo(field, text, help, input)
+}
trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRendered {
def dispatch = {
@@ -1237,7 +1362,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
override lazy val __nameSalt = Helpers.nextFuncName
}
- private object PrevSnapshot extends TransientRequestVar[Box[ScreenSnapshot]](Empty) {
+ protected object PrevSnapshot extends TransientRequestVar[Box[ScreenSnapshot]](Empty) {
override lazy val __nameSalt = Helpers.nextFuncName
}
@@ -1257,6 +1382,10 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
override lazy val __nameSalt = Helpers.nextFuncName
}
+ object NextId extends ScreenVar[String](Helpers.nextFuncName) {
+ override lazy val __nameSalt = Helpers.nextFuncName
+ }
+
/**
* What to do when the Screen is done. By default, will
* do a redirect back to Whence, but you can change this behavior,
@@ -1344,6 +1473,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
Referer.get // touch to capture the referer
Ajax_?.get // capture the ajaxiness of these forms
FormGUID.get
+ NextId.get
if (FirstTime) {
FirstTime.set(false)
@@ -1366,7 +1496,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
}
protected def renderHtml(): NodeSeq = {
- val finishId = Helpers.nextFuncName
+ val finishId = NextId.get
val cancelId = Helpers.nextFuncName
val theScreen = this
@@ -1389,13 +1519,25 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
val url = S.uri
+ def fieldBinding(field: BaseField): Box[FieldBinding] =
+ field match {
+ case f: Field => f.binding
+ case _ => Empty
+ }
+
+ def fieldTransform(field: BaseField): Box[() => CssSel] =
+ field match {
+ case f: Field => f.transform
+ case _ => Empty
+ }
+
renderAll(
Empty, //currentScreenNumber: Box[NodeSeq],
Empty, //screenCount: Box[NodeSeq],
Empty, // wizardTop: Box[Elem],
theScreen.screenTop, //screenTop: Box[Elem],
theScreen.screenFields.filter(_.shouldDisplay_?).flatMap(f =>
- if (f.show_?) List(ScreenFieldInfo(f, f.displayHtml, f.helpAsHtml, f.toForm)) else Nil), //fields: List[ScreenFieldInfo],
+ if (f.show_?) List(ScreenFieldInfo(f, f.displayHtml, f.helpAsHtml, f.toForm, fieldBinding(f), fieldTransform(f))) else Nil), //fields: List[ScreenFieldInfo],
Empty, // prev: Box[Elem],
Full(cancelButton), // cancel: Box[Elem],
Empty, // next: Box[Elem],
@@ -1500,3 +1642,35 @@ object LiftScreenRules extends Factory with FormVendor {
}
+case class FieldBinding(val fieldName: String, val bindingStyle: FieldBinding.BindingStyle) {
+ def fieldId(formName: String) = "%s_%s_field" format (formName, fieldName)
+ def selector(formName: String) = "#%s" format (fieldId(formName))
+ def childSelector(formName: String) = "#%s *" format (fieldId(formName))
+ def idSelector(formName: String) = selector(formName) + " [id]"
+}
+
+object FieldBinding {
+ sealed abstract class BindingStyle
+
+ /**
+ * Bind the field using the default template defined in an external template (as in Bind.helpers()-based binding)
+ */
+ case object Template extends BindingStyle
+
+ /**
+ * Bind the field using the template defined in the body of the field reference
+ */
+ case object Self extends BindingStyle
+
+ /**
+ * Bind the field using the template returned by the <code>defaultFieldNodeSeq</code> method
+ */
+ case object Default extends BindingStyle
+
+ /**
+ * Bind the field using the template provided
+ */
+ case class Custom(template: NodeSeq) extends BindingStyle
+
+ def apply(fieldName: String) = new FieldBinding(fieldName, Default)
+}
Please sign in to comment.
Something went wrong with that request. Please try again.