nove components to separate package
This commit is contained in:
parent
81bd1fa16b
commit
323d16b465
6 changed files with 211 additions and 175 deletions
71
src/main/scala/fahrtenbuch/Components/AppComponent.scala
Normal file
71
src/main/scala/fahrtenbuch/Components/AppComponent.scala
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package fahrtenbuch.components
|
||||||
|
|
||||||
|
import com.raquo.laminar.api.L.{*, given}
|
||||||
|
import com.raquo.laminar.api.features.unitArrows
|
||||||
|
import fahrtenbuch.Entry
|
||||||
|
import fahrtenbuch.Main.allEntries
|
||||||
|
import fahrtenbuch.Main.entryEditBus
|
||||||
|
import rdts.base.Uid
|
||||||
|
|
||||||
|
class AppComponent(allEntries: Signal[List[Entry]]):
|
||||||
|
// tracks whenever a user clicks on an edit button
|
||||||
|
val editClickBus = new EventBus[(Uid, Boolean)]
|
||||||
|
|
||||||
|
// tracks which entries are currently being edited
|
||||||
|
val editStateSignal: Signal[Map[Uid, Boolean]] =
|
||||||
|
editClickBus.stream.foldLeft(Map.empty[Uid, Boolean]) {
|
||||||
|
case (acc, (id, value)) =>
|
||||||
|
acc + (id -> value)
|
||||||
|
}
|
||||||
|
|
||||||
|
val entryComponents: Signal[List[EntryComponent]] =
|
||||||
|
allEntries
|
||||||
|
.combineWith(editStateSignal)
|
||||||
|
.map { case (entries, editState) =>
|
||||||
|
entries.toList
|
||||||
|
.sortBy(_.id)
|
||||||
|
.map(entry =>
|
||||||
|
EntryComponent(
|
||||||
|
entry,
|
||||||
|
editState.getOrElse(entry.id, false),
|
||||||
|
editClickBus,
|
||||||
|
entryEditBus
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val showNewEntryField = Var(false)
|
||||||
|
|
||||||
|
def render(): HtmlElement =
|
||||||
|
div(
|
||||||
|
cls := "app content",
|
||||||
|
h1("Fahrtenbuch"),
|
||||||
|
table(
|
||||||
|
cls := "table",
|
||||||
|
thead(
|
||||||
|
tr(
|
||||||
|
// th("Date"),
|
||||||
|
th("Fahrer*in"),
|
||||||
|
th("Start Km"),
|
||||||
|
th("Ende Km"),
|
||||||
|
th("Tier"),
|
||||||
|
th("Abnutzung"),
|
||||||
|
th("Gesamtkosten"),
|
||||||
|
th("Bezahlt"),
|
||||||
|
th()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tbody(
|
||||||
|
children <-- entryComponents.map(_.map(_.render)),
|
||||||
|
child(NewEntryInput(showNewEntryField).render) <-- showNewEntryField
|
||||||
|
)
|
||||||
|
),
|
||||||
|
button(
|
||||||
|
cls := "button is-primary",
|
||||||
|
onClick --> { _ =>
|
||||||
|
showNewEntryField.set(true)
|
||||||
|
},
|
||||||
|
"Eintrag hinzufügen"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end AppComponent
|
||||||
75
src/main/scala/fahrtenbuch/Components/EntryComponent.scala
Normal file
75
src/main/scala/fahrtenbuch/Components/EntryComponent.scala
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package fahrtenbuch.components
|
||||||
|
import fahrtenbuch.Entry
|
||||||
|
import com.raquo.laminar.nodes.ReactiveHtmlElement
|
||||||
|
import org.scalajs.dom.HTMLTableRowElement
|
||||||
|
import com.raquo.laminar.api.L.{*, given}
|
||||||
|
import com.raquo.laminar.api.features.unitArrows
|
||||||
|
import rdts.base.Uid
|
||||||
|
|
||||||
|
class EntryComponent(
|
||||||
|
entry: Entry,
|
||||||
|
editMode: Boolean,
|
||||||
|
editClickBus: EventBus[(Uid, Boolean)],
|
||||||
|
entryEditBus: EventBus[Entry]
|
||||||
|
):
|
||||||
|
def render: ReactiveHtmlElement[HTMLTableRowElement] = {
|
||||||
|
if editMode then
|
||||||
|
val driverInput = input(cls := "input", value := entry.driver)
|
||||||
|
val startKmInput =
|
||||||
|
input(cls := "input", value := entry.startKm.toString())
|
||||||
|
val endKmInput = input(cls := "input", value := entry.endKm.toString())
|
||||||
|
val animalInput = input(cls := "input", value := entry.animal)
|
||||||
|
val costWearInput =
|
||||||
|
input(cls := "input", value := entry.costWear.toString())
|
||||||
|
val costTotalInput =
|
||||||
|
input(cls := "input", value := entry.costTotal.toString())
|
||||||
|
val paidCheckbox = input(`type` := "checkbox", checked := entry.paid)
|
||||||
|
tr(
|
||||||
|
// td(input(cls := "input", value := entry.date.toDateString())),
|
||||||
|
td(driverInput),
|
||||||
|
td(startKmInput),
|
||||||
|
td(endKmInput),
|
||||||
|
td(animalInput),
|
||||||
|
td(),
|
||||||
|
td(),
|
||||||
|
td(paidCheckbox),
|
||||||
|
td(
|
||||||
|
button(
|
||||||
|
cls := "button is-success",
|
||||||
|
onClick --> {
|
||||||
|
editClickBus.emit(entry.id, false)
|
||||||
|
entryEditBus.emit(
|
||||||
|
entry.copy(
|
||||||
|
startKm = startKmInput.ref.value.toDouble,
|
||||||
|
endKm = endKmInput.ref.value.toDouble,
|
||||||
|
animal = animalInput.ref.value,
|
||||||
|
paid = paidCheckbox.ref.checked
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
span(
|
||||||
|
cls := "icon edit",
|
||||||
|
i(cls := "mdi mdi-18px mdi-check-bold")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
tr(
|
||||||
|
// td(entry.date.toDateString()),
|
||||||
|
td(entry.driver),
|
||||||
|
td(entry.startKm),
|
||||||
|
td(entry.endKm),
|
||||||
|
td(entry.animal),
|
||||||
|
td(s"${entry.costWear}€"),
|
||||||
|
td(s"${entry.costTotal}€"),
|
||||||
|
td(if entry.paid then "Ja" else "Nein"),
|
||||||
|
td(
|
||||||
|
button(
|
||||||
|
cls := "button is-link",
|
||||||
|
onClick --> editClickBus.emit(entry.id, true),
|
||||||
|
span(cls := "icon edit", i(cls := "mdi mdi-18px mdi-pencil"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
51
src/main/scala/fahrtenbuch/Components/NewEntryInput.scala
Normal file
51
src/main/scala/fahrtenbuch/Components/NewEntryInput.scala
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package fahrtenbuch.components
|
||||||
|
|
||||||
|
import com.raquo.laminar.api.L.{*, given}
|
||||||
|
import com.raquo.laminar.api.features.unitArrows
|
||||||
|
import fahrtenbuch.Entry
|
||||||
|
import fahrtenbuch.Main.entryEditBus
|
||||||
|
import rdts.base.Uid
|
||||||
|
|
||||||
|
class NewEntryInput(showNewEntryField: Var[Boolean]):
|
||||||
|
val newEntryDriver = input(cls := "input")
|
||||||
|
val newEntryStartKm = input(cls := "input")
|
||||||
|
val newEntryEndKm = input(cls := "input")
|
||||||
|
val newEntryAnimal = input(cls := "input")
|
||||||
|
val newEntryPaid = input(`type` := "checkbox")
|
||||||
|
|
||||||
|
def render =
|
||||||
|
tr(
|
||||||
|
td(newEntryDriver),
|
||||||
|
td(newEntryStartKm),
|
||||||
|
td(newEntryEndKm),
|
||||||
|
td(newEntryAnimal),
|
||||||
|
td(),
|
||||||
|
td(),
|
||||||
|
td(newEntryPaid),
|
||||||
|
td(
|
||||||
|
button(
|
||||||
|
cls := "button is-success",
|
||||||
|
onClick --> {
|
||||||
|
val id = Uid.gen()
|
||||||
|
val driver = newEntryDriver.ref.value
|
||||||
|
val startKm = newEntryStartKm.ref.value.toDouble
|
||||||
|
val endKm = newEntryEndKm.ref.value.toDouble
|
||||||
|
val animal = newEntryAnimal.ref.value
|
||||||
|
val paid = newEntryPaid.ref.checked
|
||||||
|
entryEditBus.emit(
|
||||||
|
Entry(id, startKm, endKm, animal, paid, driver)
|
||||||
|
)
|
||||||
|
showNewEntryField.set(false)
|
||||||
|
newEntryDriver.ref.value = ""
|
||||||
|
newEntryStartKm.ref.value = ""
|
||||||
|
newEntryEndKm.ref.value = ""
|
||||||
|
newEntryAnimal.ref.value = ""
|
||||||
|
newEntryPaid.ref.checked = false
|
||||||
|
},
|
||||||
|
span(
|
||||||
|
cls := "icon edit",
|
||||||
|
i(cls := "mdi mdi-18px mdi-check-bold")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
@ -8,6 +8,8 @@ import scala.scalajs.js
|
||||||
import org.getshaka.nativeconverter.NativeConverter
|
import org.getshaka.nativeconverter.NativeConverter
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import typings.dexie.mod.Observable
|
import typings.dexie.mod.Observable
|
||||||
|
import com.raquo.airstream.core.Signal
|
||||||
|
import com.raquo.airstream.core.EventStream
|
||||||
|
|
||||||
object DexieDB {
|
object DexieDB {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import com.raquo.laminar.api.L.{*, given}
|
||||||
import com.raquo.laminar.api.features.unitArrows
|
import com.raquo.laminar.api.features.unitArrows
|
||||||
import org.scalajs.dom.HTMLTableRowElement
|
import org.scalajs.dom.HTMLTableRowElement
|
||||||
import com.raquo.laminar.nodes.ReactiveHtmlElement
|
import com.raquo.laminar.nodes.ReactiveHtmlElement
|
||||||
import fahrtenbuch.Main.editClickBus
|
|
||||||
import scala.annotation.threadUnsafe
|
import scala.annotation.threadUnsafe
|
||||||
import fahrtenbuch.Main.entryEditBus
|
import fahrtenbuch.Main.entryEditBus
|
||||||
import org.getshaka.nativeconverter.NativeConverter
|
import org.getshaka.nativeconverter.NativeConverter
|
||||||
|
|
@ -14,73 +13,6 @@ import fahrtenbuch.Main.entryPrinter
|
||||||
import org.getshaka.nativeconverter.ParseState
|
import org.getshaka.nativeconverter.ParseState
|
||||||
import scala.scalajs.js
|
import scala.scalajs.js
|
||||||
|
|
||||||
case class EntryComponent(
|
|
||||||
entry: Entry,
|
|
||||||
editMode: Boolean
|
|
||||||
):
|
|
||||||
def render: ReactiveHtmlElement[HTMLTableRowElement] = {
|
|
||||||
if editMode then
|
|
||||||
val driverInput = input(cls := "input", value := entry.driver)
|
|
||||||
val startKmInput =
|
|
||||||
input(cls := "input", value := entry.startKm.toString())
|
|
||||||
val endKmInput = input(cls := "input", value := entry.endKm.toString())
|
|
||||||
val animalInput = input(cls := "input", value := entry.animal)
|
|
||||||
val costWearInput =
|
|
||||||
input(cls := "input", value := entry.costWear.toString())
|
|
||||||
val costTotalInput =
|
|
||||||
input(cls := "input", value := entry.costTotal.toString())
|
|
||||||
val paidCheckbox = input(`type` := "checkbox", checked := entry.paid)
|
|
||||||
tr(
|
|
||||||
// td(input(cls := "input", value := entry.date.toDateString())),
|
|
||||||
td(driverInput),
|
|
||||||
td(startKmInput),
|
|
||||||
td(endKmInput),
|
|
||||||
td(animalInput),
|
|
||||||
td(),
|
|
||||||
td(),
|
|
||||||
td(paidCheckbox),
|
|
||||||
td(
|
|
||||||
button(
|
|
||||||
cls := "button is-success",
|
|
||||||
onClick --> {
|
|
||||||
editClickBus.emit(entry.id, false)
|
|
||||||
entryEditBus.emit(
|
|
||||||
entry.copy(
|
|
||||||
startKm = startKmInput.ref.value.toDouble,
|
|
||||||
endKm = endKmInput.ref.value.toDouble,
|
|
||||||
animal = animalInput.ref.value,
|
|
||||||
paid = paidCheckbox.ref.checked
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
entryEditBus.stream --> entryPrinter,
|
|
||||||
span(
|
|
||||||
cls := "icon edit",
|
|
||||||
i(cls := "mdi mdi-18px mdi-check-bold")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
tr(
|
|
||||||
// td(entry.date.toDateString()),
|
|
||||||
td(entry.driver),
|
|
||||||
td(entry.startKm),
|
|
||||||
td(entry.endKm),
|
|
||||||
td(entry.animal),
|
|
||||||
td(s"${entry.costWear}€"),
|
|
||||||
td(s"${entry.costTotal}€"),
|
|
||||||
td(if entry.paid then "Ja" else "Nein"),
|
|
||||||
td(
|
|
||||||
button(
|
|
||||||
cls := "button is-link",
|
|
||||||
onClick --> editClickBus.emit(entry.id, true),
|
|
||||||
span(cls := "icon edit", i(cls := "mdi mdi-18px mdi-pencil"))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Entry(
|
case class Entry(
|
||||||
id: Uid,
|
id: Uid,
|
||||||
startKm: Double,
|
startKm: Double,
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,31 @@
|
||||||
package fahrtenbuch
|
package fahrtenbuch
|
||||||
|
|
||||||
import scala.scalajs.js
|
|
||||||
import scala.scalajs.js.annotation.*
|
|
||||||
import org.scalajs.dom
|
|
||||||
import com.raquo.laminar.api.L.{*, given}
|
import com.raquo.laminar.api.L.{*, given}
|
||||||
import com.raquo.laminar.api.features.unitArrows
|
import com.raquo.laminar.api.features.unitArrows
|
||||||
import scala.scalajs.js.Date
|
|
||||||
import rdts.base.Uid
|
|
||||||
import fahrtenbuch.DexieDB.entriesObservable
|
import fahrtenbuch.DexieDB.entriesObservable
|
||||||
|
import org.scalajs.dom
|
||||||
|
import rdts.base.Uid
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.scalajs.js
|
||||||
|
import scala.scalajs.js.Date
|
||||||
|
import scala.scalajs.js.annotation.*
|
||||||
import scala.util.Failure
|
import scala.util.Failure
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
|
|
||||||
// import javascriptLogo from "/javascript.svg"
|
import components.AppComponent
|
||||||
//@js.native @JSImport("/javascript.svg", JSImport.Default)
|
|
||||||
//val javascriptLogo: String = js.native
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
def Fahrtenbuch(): Unit =
|
def Fahrtenbuch(): Unit =
|
||||||
|
val appComponent = AppComponent(Main.allEntries)
|
||||||
|
|
||||||
renderOnDomContentLoaded(
|
renderOnDomContentLoaded(
|
||||||
dom.document.getElementById("app"),
|
dom.document.getElementById("app"),
|
||||||
Main.appElement()
|
AppComponent(Main.allEntries).render()
|
||||||
)
|
)
|
||||||
println("Hello, Worlds!")
|
|
||||||
|
|
||||||
object Main {
|
object Main {
|
||||||
|
|
||||||
// tracks whenever a user clicks on an edit button
|
|
||||||
val editClickBus = new EventBus[(Uid, Boolean)]
|
|
||||||
val editStateSignal: Signal[Map[Uid, Boolean]] =
|
|
||||||
editClickBus.stream.foldLeft(Map.empty[Uid, Boolean]) {
|
|
||||||
case (acc, (id, value)) =>
|
|
||||||
acc + (id -> value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// track changes to entries
|
// track changes to entries
|
||||||
val entryEditBus = new EventBus[Entry]
|
val entryEditBus = new EventBus[Entry]
|
||||||
val entryObserver =
|
val entryObserver =
|
||||||
|
|
@ -52,98 +44,11 @@ object Main {
|
||||||
case Success(value) => allEntriesVar.set(value.toSet)
|
case Success(value) => allEntriesVar.set(value.toSet)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val allEntries = allEntriesVar.signal
|
val allEntries: Signal[List[Entry]] = allEntriesVar.signal.map(_.toList)
|
||||||
// val allEntries = entryEditBus.stream.foldLeft(Map.empty[Uid, Entry]) {
|
// val allEntries = entryEditBus.stream.foldLeft(Map.empty[Uid, Entry]) {
|
||||||
// case (acc, entry) =>
|
// case (acc, entry) =>
|
||||||
// acc + (entry.id -> entry)
|
// acc + (entry.id -> entry)
|
||||||
// }
|
// }
|
||||||
entryEditBus.stream.addObserver(entryObserver)(using unsafeWindowOwner)
|
entryEditBus.stream.addObserver(entryObserver)(using unsafeWindowOwner)
|
||||||
|
|
||||||
val entryComponents: Signal[List[EntryComponent]] =
|
|
||||||
allEntries
|
|
||||||
.combineWith(editStateSignal)
|
|
||||||
.map { case (entries, editState) =>
|
|
||||||
entries.toList
|
|
||||||
.sortBy(_.id)
|
|
||||||
.map(entry =>
|
|
||||||
EntryComponent(entry, editState.getOrElse(entry.id, false))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val showNewEntryField = Var(false)
|
|
||||||
|
|
||||||
val newEntryInput =
|
|
||||||
val newEntryDriver = input(cls := "input")
|
|
||||||
val newEntryStartKm = input(cls := "input")
|
|
||||||
val newEntryEndKm = input(cls := "input")
|
|
||||||
val newEntryAnimal = input(cls := "input")
|
|
||||||
val newEntryPaid = input(`type` := "checkbox")
|
|
||||||
tr(
|
|
||||||
td(newEntryDriver),
|
|
||||||
td(newEntryStartKm),
|
|
||||||
td(newEntryEndKm),
|
|
||||||
td(newEntryAnimal),
|
|
||||||
td(),
|
|
||||||
td(),
|
|
||||||
td(newEntryPaid),
|
|
||||||
td(
|
|
||||||
button(
|
|
||||||
cls := "button is-success",
|
|
||||||
onClick --> {
|
|
||||||
val id = Uid.gen()
|
|
||||||
val driver = newEntryDriver.ref.value
|
|
||||||
val startKm = newEntryStartKm.ref.value.toDouble
|
|
||||||
val endKm = newEntryEndKm.ref.value.toDouble
|
|
||||||
val animal = newEntryAnimal.ref.value
|
|
||||||
val paid = newEntryPaid.ref.checked
|
|
||||||
entryEditBus.emit(
|
|
||||||
Entry(id, startKm, endKm, animal, paid, driver)
|
|
||||||
)
|
|
||||||
showNewEntryField.set(false)
|
|
||||||
newEntryDriver.ref.value = ""
|
|
||||||
newEntryStartKm.ref.value = ""
|
|
||||||
newEntryEndKm.ref.value = ""
|
|
||||||
newEntryAnimal.ref.value = ""
|
|
||||||
newEntryPaid.ref.checked = false
|
|
||||||
},
|
|
||||||
span(
|
|
||||||
cls := "icon edit",
|
|
||||||
i(cls := "mdi mdi-18px mdi-check-bold")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def appElement(): HtmlElement =
|
|
||||||
div(
|
|
||||||
cls := "app content",
|
|
||||||
h1("Fahrtenbuch"),
|
|
||||||
table(
|
|
||||||
cls := "table",
|
|
||||||
thead(
|
|
||||||
tr(
|
|
||||||
// th("Date"),
|
|
||||||
th("Fahrer*in"),
|
|
||||||
th("Start Km"),
|
|
||||||
th("Ende Km"),
|
|
||||||
th("Tier"),
|
|
||||||
th("Abnutzung"),
|
|
||||||
th("Gesamtkosten"),
|
|
||||||
th("Bezahlt"),
|
|
||||||
th()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tbody(
|
|
||||||
children <-- entryComponents.map(_.map(_.render)),
|
|
||||||
child(newEntryInput) <-- showNewEntryField
|
|
||||||
)
|
|
||||||
),
|
|
||||||
button(
|
|
||||||
cls := "button is-primary",
|
|
||||||
onClick --> { _ =>
|
|
||||||
showNewEntryField.set(true)
|
|
||||||
},
|
|
||||||
"Eintrag hinzufügen"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue