nove components to separate package

This commit is contained in:
Julian 2025-07-02 00:52:59 -07:00
parent 81bd1fa16b
commit 323d16b465
6 changed files with 211 additions and 175 deletions

View 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

View 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"))
)
)
)
}

View 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")
)
)
)
)

View file

@ -8,6 +8,8 @@ import scala.scalajs.js
import org.getshaka.nativeconverter.NativeConverter
import scala.concurrent.ExecutionContext.Implicits.global
import typings.dexie.mod.Observable
import com.raquo.airstream.core.Signal
import com.raquo.airstream.core.EventStream
object DexieDB {

View file

@ -6,7 +6,6 @@ import com.raquo.laminar.api.L.{*, given}
import com.raquo.laminar.api.features.unitArrows
import org.scalajs.dom.HTMLTableRowElement
import com.raquo.laminar.nodes.ReactiveHtmlElement
import fahrtenbuch.Main.editClickBus
import scala.annotation.threadUnsafe
import fahrtenbuch.Main.entryEditBus
import org.getshaka.nativeconverter.NativeConverter
@ -14,73 +13,6 @@ import fahrtenbuch.Main.entryPrinter
import org.getshaka.nativeconverter.ParseState
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(
id: Uid,
startKm: Double,

View file

@ -1,39 +1,31 @@
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.features.unitArrows
import scala.scalajs.js.Date
import rdts.base.Uid
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.Success
import scala.concurrent.ExecutionContext.Implicits.global
// import javascriptLogo from "/javascript.svg"
//@js.native @JSImport("/javascript.svg", JSImport.Default)
//val javascriptLogo: String = js.native
import components.AppComponent
@main
def Fahrtenbuch(): Unit =
val appComponent = AppComponent(Main.allEntries)
renderOnDomContentLoaded(
dom.document.getElementById("app"),
Main.appElement()
AppComponent(Main.allEntries).render()
)
println("Hello, Worlds!")
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
val entryEditBus = new EventBus[Entry]
val entryObserver =
@ -52,98 +44,11 @@ object Main {
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]) {
// case (acc, entry) =>
// acc + (entry.id -> entry)
// }
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"
)
)
}