Skip to content

Commit f8b95e1

Browse files
authored
configurable subtype selection (#57)
1 parent ea0a4f4 commit f8b95e1

File tree

3 files changed

+45
-35
lines changed

3 files changed

+45
-35
lines changed

formidable/src/main/scala-2/Form-Scala2.scala

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package formidable
22

33
import outwatch._
4-
import outwatch.dsl._
54
import colibri.reactive._
65

76
import magnolia1._
@@ -15,7 +14,7 @@ trait FormDerivation {
1514
def join[T](ctx: CaseClass[Typeclass, T]): Form[T] = new Form[T] {
1615
override def default: T = ctx.construct(param => param.default.getOrElse(param.typeclass.default))
1716

18-
override def render(state: Var[T], config: FormConfig): VModifier = Owned {
17+
override def render(state: Var[T], config: FormConfig): VModifier = Owned.function { implicit owner =>
1918
val subStates: Var[Seq[Any]] =
2019
state.imap[Seq[Any]](seq => ctx.rawConstruct(seq))(_.asInstanceOf[Product].productIterator.toList)
2120

@@ -38,26 +37,23 @@ trait FormDerivation {
3837
val defaultSubtype = ctx.subtypes.find(_.annotations.exists(_.isInstanceOf[Default])).getOrElse(ctx.subtypes.head)
3938
defaultSubtype.typeclass.default
4039
}
41-
override def render(state: Var[T], config: FormConfig): VModifier = Owned {
42-
val labelToSubtype =
43-
ctx.subtypes.view.map { sub => sub.typeName.short -> sub }.toMap
40+
override def render(selectedValue: Var[T], config: FormConfig): VModifier = Owned.function { implicit owner =>
4441

45-
div(
46-
select(
47-
ctx.subtypes.map { subtype =>
48-
option(
49-
subtype.typeName.short,
50-
selected <-- state.map(value => ctx.split(value)(_ == subtype)),
51-
)
52-
}.toSeq,
53-
onChange.value.map(label => labelToSubtype(label).typeclass.default) --> state,
42+
val selectedSubtype: Var[Subtype[Form, T]] =
43+
selectedValue.imap[Subtype[Form, T]](subType => subType.typeclass.default)(value => ctx.split(value)(identity))
44+
45+
config.unionSubform(
46+
config.selectInput[Subtype[Form, T]](
47+
options = ctx.subtypes,
48+
selectedValue = selectedSubtype,
49+
show = subtype => subtype.typeName.short,
5450
),
55-
state.map { value =>
51+
subForm = selectedValue.map { value =>
5652
ctx.split(value) { sub =>
57-
VModifier.when(value.isInstanceOf[T])(sub.typeclass.asInstanceOf[Form[T]].render(state, config))
53+
VModifier.when(value.isInstanceOf[T])(sub.typeclass.asInstanceOf[Form[T]].render(selectedValue, config))
5854
}
5955
},
60-
): VModifier
56+
)
6157
}
6258

6359
}

formidable/src/main/scala-3/Form-Scala3.scala

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package formidable
22

33
import outwatch._
4-
import outwatch.dsl._
54
import colibri.reactive._
65

76
import magnolia1._
@@ -13,7 +12,7 @@ trait FormDerivation extends AutoDerivation[Form] {
1312
override def join[T](ctx: CaseClass[Typeclass, T]): Form[T] = new Form[T] {
1413
override def default: T = ctx.construct(param => param.default.getOrElse(param.typeclass.default))
1514

16-
override def render(state: Var[T], config: FormConfig): VModifier = Owned {
15+
override def render(state: Var[T], config: FormConfig): VModifier = Owned.function { implicit owner =>
1716
val subStates: Var[Seq[Any]] =
1817
state.imap[Seq[Any]](seq => ctx.rawConstruct(seq))(_.asInstanceOf[Product].productIterator.toList)
1918

@@ -35,26 +34,25 @@ trait FormDerivation extends AutoDerivation[Form] {
3534
val defaultSubtype = ctx.subtypes.find(_.annotations.exists(_.isInstanceOf[Default])).getOrElse(ctx.subtypes.head)
3635
defaultSubtype.typeclass.default
3736
}
38-
override def render(state: Var[T], config: FormConfig): VModifier = Owned {
39-
val labelToSubtype =
40-
ctx.subtypes.view.map { sub => sub.typeInfo.short -> sub }.toMap
41-
42-
div(
43-
select(
44-
ctx.subtypes.map { subtype =>
45-
option(
46-
subtype.typeInfo.short,
47-
selected <-- state.map(value => ctx.choose(value)(_.subtype == subtype)),
48-
)
49-
}.toSeq,
50-
onChange.value.map(label => labelToSubtype(label).typeclass.default) --> state,
37+
override def render(selectedValue: Var[T], config: FormConfig): VModifier = Owned.function { implicit owner =>
38+
39+
val selectedSubtype: Var[SealedTrait.Subtype[Form, T, _]] =
40+
selectedValue.imap[SealedTrait.Subtype[Form, T, _]](subType => subType.typeclass.default)(value =>
41+
ctx.choose(value)(_.subtype),
42+
)
43+
44+
config.unionSubform(
45+
config.selectInput[SealedTrait.Subtype[Form, T, _]](
46+
options = ctx.subtypes,
47+
selectedValue = selectedSubtype,
48+
show = subtype => subtype.typeInfo.short,
5149
),
52-
state.map { value =>
50+
selectedValue.map { value =>
5351
ctx.choose(value) { sub =>
54-
VModifier.when(value.isInstanceOf[T])(sub.typeclass.asInstanceOf[Form[T]].render(state, config))
52+
VModifier.when(value.isInstanceOf[T])(sub.typeclass.asInstanceOf[Form[T]].render(selectedValue, config))
5553
}
5654
},
57-
): VModifier
55+
)
5856
}
5957

6058
}

formidable/src/main/scala/FormConfig.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,22 @@ trait FormConfig {
6565
validationMessage.map(_.map(msg => div(msg, color.red))),
6666
): VModifier
6767
}
68+
69+
def selectInput[T](options: Seq[T], selectedValue: Var[T], show: T => String): VModifier = {
70+
select(
71+
Owned.function { implicit owner =>
72+
options.zipWithIndex.map { case (opt, ind) =>
73+
option(dsl.value := ind.toString)(
74+
show(opt),
75+
selectedValue.map(sel => selected := opt == sel),
76+
)
77+
}
78+
},
79+
onChange.value.map(index => options(index.toInt)) --> selectedValue,
80+
)
81+
}
82+
83+
def unionSubform(selectForm: VModifier, subForm: VModifier) = div(selectForm, subForm)
6884
}
6985
object FormConfig {
7086
val default: FormConfig = new FormConfig {}

0 commit comments

Comments
 (0)