|
|
@@ -29,6 +29,7 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
Seq(),
|
|
|
Seq(),
|
|
|
None,
|
|
|
+ None,
|
|
|
None
|
|
|
))
|
|
|
|
|
|
@@ -74,6 +75,7 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
case (None, "") => "100"
|
|
|
case (Some(""), "") => "100"
|
|
|
case (Some(_), str) => str
|
|
|
+ case (None, str) => str
|
|
|
}).map(_.toFloatOption)
|
|
|
|
|
|
private val _volumeUnit = Var[MeasureUnit](Milliliter)
|
|
|
@@ -115,6 +117,12 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
case (None, _) => None
|
|
|
}
|
|
|
|
|
|
+ private val _sourceIn = input(idAttr := "source",
|
|
|
+ cls := "form-control",
|
|
|
+ defaultValue := _defaultRecipe.source.getOrElse("")
|
|
|
+ )
|
|
|
+ val source: Signal[Option[String]] = _sourceIn.value.map((s) => Option.when(s.size > 0)(s))
|
|
|
+
|
|
|
|
|
|
private val internalFoodDS = Dataset(
|
|
|
{ _ => Nil},
|
|
|
@@ -141,59 +149,71 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
|
|
|
private val _ingredients = Var[Seq[Ingredient]](_defaultRecipe.ingredients)
|
|
|
val ingredients = _ingredients.signal
|
|
|
- private val _ingredientSearch = input(typ := "text",
|
|
|
+ private val _ingredientSearch = _ingredientInput() { (e) =>
|
|
|
+ e.selectable.map(_.data).foreach({ (node) =>
|
|
|
+ // TODO: default unit
|
|
|
+ _editIngredient(Ingredient.fromFoodNode(node, 0, Gram)) { (in) =>
|
|
|
+ _ingredients.update(_ :+ in)
|
|
|
+ e.target.asInstanceOf[HTMLInputElement].value = ""
|
|
|
+ Future.successful(())
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ private def _ingredientInput(defaul: Signal[Option[FoodNode]] = Val(None))(onSelect: (CursorEvent[FoodNode]) => Unit) = input(typ := "text",
|
|
|
cls := "form-control input-sm",
|
|
|
+ value <-- defaul.optionMap(_.name).withDefault(""),
|
|
|
onMountCallback({(ctx) =>
|
|
|
val elm = ctx.thisNode
|
|
|
TypeaheadElement[FoodNode](
|
|
|
elm.ref.asInstanceOf[HTMLInputElement],
|
|
|
minLength = 3
|
|
|
)(Seq(internalFoodDS, usdaFoodDS))
|
|
|
- elm.amend(
|
|
|
- Typeahead.onSelected[FoodNode] --> { (e: CursorEvent[FoodNode]) =>
|
|
|
- e.selectable.map(_.data).foreach({ node =>
|
|
|
- // TODO: default unit
|
|
|
- _editIngredient(Ingredient.fromFoodNode(node, 0, Gram)) { in =>
|
|
|
- _ingredients.update(_ :+ in)
|
|
|
- elm.ref.value = ""
|
|
|
- Future.successful(())
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- )
|
|
|
+ elm.amend(Typeahead.onSelected[FoodNode] --> onSelect)
|
|
|
})
|
|
|
)
|
|
|
|
|
|
private def _editIngredient(ing: Ingredient)(
|
|
|
callback: (Ingredient => Future[_])
|
|
|
) = {
|
|
|
+ val id = Var[Ingredient.IngredientId](ing.id)
|
|
|
val amount = Var[Float](ing.amount)
|
|
|
- val unit = Var[MeasureUnit](Gram)
|
|
|
+ val unit = Var[MeasureUnit](ing.unit)
|
|
|
|
|
|
Overlay.confirmFuture(ing.food.map({ (food) =>
|
|
|
val amountIn = input(typ := "number",
|
|
|
- cls := "form-control input-sm col-9",
|
|
|
+ cls := "form-control input-sm",
|
|
|
minAttr := "0",
|
|
|
defaultValue := ing.amount.toString,
|
|
|
onInput.mapToValue.map(_.toFloat) --> amount
|
|
|
)
|
|
|
- val unitIn = select(cls := "col-3 custom-select",
|
|
|
+ val unitIn = select(cls := "custom-select input-group-append",
|
|
|
MeasureUnit.units.zipWithIndex.map({ case (unit, idx) =>
|
|
|
// TODO: default selected dynamic
|
|
|
option(value := idx.toString,
|
|
|
- selected := unit.abr == Gram.abr,
|
|
|
+ selected := unit == ing.unit,
|
|
|
unit.name
|
|
|
)
|
|
|
}),
|
|
|
onChange.mapToValue.map(_.toInt).map(MeasureUnit.units(_)) --> unit
|
|
|
)
|
|
|
|
|
|
- div(cls := "row", amountIn, unitIn)
|
|
|
+ div(cls := "row",
|
|
|
+ div(cls := "col-12 input-group",
|
|
|
+ _ingredientInput(Signal.fromFuture(ing.food)) { (e) =>
|
|
|
+ e.selectable.map(_.data).foreach({ (node) => id.set(Ingredient.IngredientId.fromFoodNode(node)) })
|
|
|
+ },
|
|
|
+ amountIn,
|
|
|
+ unitIn
|
|
|
+ )
|
|
|
+ )
|
|
|
}),
|
|
|
- amount.signal.recoverToTry.map(_.isSuccess) &&
|
|
|
+ id.signal.recoverToTry.map(_.isSuccess) &&
|
|
|
+ amount.signal.recoverToTry.map(_.isSuccess) &&
|
|
|
unit.signal.recoverToTry.map(_.isSuccess)
|
|
|
) { () =>
|
|
|
callback(ing.copy(
|
|
|
+ id = id.now(),
|
|
|
amount = amount.now(),
|
|
|
unit = unit.now()
|
|
|
))
|
|
|
@@ -300,13 +320,13 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
|
|
|
val newInstance: Signal[Try[RecipeNodeNoId]] =
|
|
|
name.combineWithFn(stdQties, stdQtiesPServing, defaultUnitType,
|
|
|
- ingredients, steps, density, massPerUnit.signal) {
|
|
|
+ ingredients, steps, density, massPerUnit.signal, source) {
|
|
|
case (
|
|
|
name, stdQties, stdQtiesPServing, defaultUnitType, ingredients, steps,
|
|
|
- density, massPerUnit
|
|
|
+ density, massPerUnit, source
|
|
|
) => Success(RecipeNodeNoId(
|
|
|
name, stdQties, stdQtiesPServing, defaultUnitType, ingredients, steps,
|
|
|
- density, massPerUnit
|
|
|
+ density, massPerUnit, source
|
|
|
))
|
|
|
}
|
|
|
|
|
|
@@ -314,8 +334,10 @@ case class RecipeVar(recipe: Option[RecipeNode])
|
|
|
form(cls := "form-group",
|
|
|
div(cls := "container",
|
|
|
div(cls := "row",
|
|
|
- div(cls := "col-md-12",
|
|
|
- label("Name: "),
|
|
|
+ div(cls := "col-md-12 input-group",
|
|
|
+ div(cls := "input-group-prepend",
|
|
|
+ label(cls := "input-group-text", "Name")
|
|
|
+ ),
|
|
|
_nameIn
|
|
|
)
|
|
|
),
|