Add direct edit for work groups and rooms
This commit is contained in:
parent
d08caf7b8b
commit
e88db9c75c
11 changed files with 468 additions and 73 deletions
src/jsMain
kotlin/de
kif/frontend/views
westermann/kwebview/components
resources/style
|
@ -1,8 +1,9 @@
|
|||
package de.kif.frontend.views
|
||||
|
||||
import de.kif.common.Search
|
||||
import de.kif.common.SearchElement
|
||||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.views.table.RoomTableLine
|
||||
import de.kif.frontend.views.table.TableLine
|
||||
import de.kif.frontend.views.table.WorkGroupTableLine
|
||||
import de.westermann.kwebview.components.InputView
|
||||
import org.w3c.dom.HTMLFormElement
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
|
@ -17,16 +18,20 @@ fun initTableLayout() {
|
|||
val table = document.getElementsByClassName("table-layout-table")[0] as HTMLTableElement
|
||||
|
||||
val list = table.getElementsByTagName("tr").iterator().asSequence().filter {
|
||||
it.dataset.get("search") != null
|
||||
}.associateWith {
|
||||
SearchElement.parse(it.dataset.get("search")!!)
|
||||
}
|
||||
it.dataset["search"] != null
|
||||
}.map {
|
||||
when (it.dataset["edit"]) {
|
||||
"workgroup" -> WorkGroupTableLine(it)
|
||||
"room" -> RoomTableLine(it)
|
||||
else -> TableLine(it)
|
||||
}
|
||||
}.toList()
|
||||
|
||||
val input = form.getElementsByTagName("input")[0] as HTMLInputElement
|
||||
val search = InputView.wrap(input)
|
||||
search.valueProperty.onChange {
|
||||
for ((row, s) in list) {
|
||||
row.style.display = if (Search.match(search.value, s)) "table-row" else "none"
|
||||
for (row in list) {
|
||||
row.search(search.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package de.kif.frontend.views.table
|
||||
|
||||
import de.kif.common.SearchElement
|
||||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RepositoryDelegate
|
||||
import de.kif.frontend.repository.RoomRepository
|
||||
import de.westermann.kwebview.components.TextView
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLSpanElement
|
||||
import org.w3c.dom.get
|
||||
|
||||
class RoomTableLine(view: HTMLElement) : TableLine(view) {
|
||||
|
||||
var lineId = dataset["id"]?.toLongOrNull() ?: -1
|
||||
|
||||
private val room =
|
||||
RepositoryDelegate(RoomRepository, lineId)
|
||||
|
||||
private val spanRoomName: TextView
|
||||
private val spanRoomPlaces: TextView
|
||||
private val spanRoomProjector: TextView
|
||||
|
||||
override var searchElement: SearchElement = super.searchElement
|
||||
|
||||
init {
|
||||
val spans = view.getElementsByTagName("span").iterator().asSequence().toList()
|
||||
|
||||
spanRoomName =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "room-name" } as HTMLSpanElement)
|
||||
spanRoomPlaces =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "room-places" } as HTMLSpanElement)
|
||||
spanRoomProjector =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "room-projector" } as HTMLSpanElement)
|
||||
|
||||
setupEditable(spanRoomName) {
|
||||
launch {
|
||||
val wg = room.get()
|
||||
if (wg.name != it) {
|
||||
RoomRepository.update(wg.copy(name = it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupEditable(spanRoomPlaces, "\\d+".toRegex()) {
|
||||
val number = it.toIntOrNull() ?: return@setupEditable
|
||||
launch {
|
||||
val wg = room.get()
|
||||
if (wg.places != number) {
|
||||
RoomRepository.update(wg.copy(places = number))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupBoolean(spanRoomProjector) {
|
||||
launch {
|
||||
val wg = room.get()
|
||||
RoomRepository.update(wg.copy(projector = !wg.projector))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RoomRepository.onUpdate {
|
||||
if (it != lineId) return@onUpdate
|
||||
|
||||
launch {
|
||||
val wg = RoomRepository.get(it) ?: return@launch
|
||||
room.set(wg)
|
||||
searchElement = wg.createSearch()
|
||||
|
||||
spanRoomName.text = wg.name
|
||||
spanRoomPlaces.text = wg.places.toString()
|
||||
spanRoomProjector.text = wg.projector.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt
Normal file
79
src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt
Normal file
|
@ -0,0 +1,79 @@
|
|||
package de.kif.frontend.views.table
|
||||
|
||||
import de.kif.common.Search
|
||||
import de.kif.common.SearchElement
|
||||
import de.westermann.kwebview.View
|
||||
import de.westermann.kwebview.components.ListView
|
||||
import de.westermann.kwebview.components.TextView
|
||||
import de.westermann.kwebview.components.textView
|
||||
import org.w3c.dom.HTMLElement
|
||||
|
||||
open class TableLine(line: HTMLElement) : View(line) {
|
||||
|
||||
open val searchElement: SearchElement = SearchElement.parse(dataset["search"]!!)
|
||||
|
||||
fun search(value: String) {
|
||||
style.display = if (Search.match(value, searchElement)) "table-row" else "none"
|
||||
}
|
||||
|
||||
protected fun setupEditable(view: TextView, regex: Regex = ".*".toRegex(), onSave: (String) -> Unit) {
|
||||
view.contentEditable = true
|
||||
|
||||
view.onKeyDown {
|
||||
if (it.keyCode == 13) {
|
||||
it.preventDefault()
|
||||
|
||||
view.blur()
|
||||
return@onKeyDown
|
||||
}
|
||||
}
|
||||
|
||||
view.onKeyUp {
|
||||
view.classList["error"] = !regex.matches(view.text)
|
||||
}
|
||||
|
||||
view.onBlur {
|
||||
view.classList["error"] = !regex.matches(view.text)
|
||||
if (!view.classList["error"]) {
|
||||
onSave(view.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setupBoolean(view: TextView, onSave: () -> Unit) {
|
||||
view.classList += "no-select"
|
||||
view.tabIndex = 0
|
||||
view.onDblClick {
|
||||
onSave()
|
||||
}
|
||||
view.onKeyDown {
|
||||
if (it.keyCode != 32) return@onKeyDown
|
||||
onSave()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun <T : Any> setupList(view: TextView, list: List<T?>, transform: (T) -> String, onSave: (T?) -> Unit) {
|
||||
view.classList += "no-select"
|
||||
view.tabIndex = 0
|
||||
|
||||
val listView = ListView<TextView>()
|
||||
listView.classList += "table-select-box"
|
||||
|
||||
for (elem in list) {
|
||||
val text = if (elem == null) "" else transform(elem)
|
||||
listView.textView(text) {
|
||||
onClick {
|
||||
onSave(elem)
|
||||
view.blur()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view.onFocus {
|
||||
view.html.appendChild(listView.html)
|
||||
}
|
||||
view.onBlur {
|
||||
listView.html.remove()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package de.kif.frontend.views.table
|
||||
|
||||
import de.kif.common.SearchElement
|
||||
import de.kif.common.model.Language
|
||||
import de.kif.common.model.Track
|
||||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RepositoryDelegate
|
||||
import de.kif.frontend.repository.TrackRepository
|
||||
import de.kif.frontend.repository.WorkGroupRepository
|
||||
import de.westermann.kwebview.components.TextView
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLSpanElement
|
||||
import org.w3c.dom.get
|
||||
|
||||
class WorkGroupTableLine(view: HTMLElement) : TableLine(view) {
|
||||
|
||||
var lineId = dataset["id"]?.toLongOrNull() ?: -1
|
||||
|
||||
private val workGroup =
|
||||
RepositoryDelegate(WorkGroupRepository, lineId)
|
||||
|
||||
private val spanWorkGroupName: TextView
|
||||
private val spanWorkGroupLength: TextView
|
||||
private val spanWorkGroupInterested: TextView
|
||||
private val spanWorkGroupTrack: TextView
|
||||
private val spanWorkGroupProjector: TextView
|
||||
private val spanWorkGroupResolution: TextView
|
||||
private val spanWorkGroupLanguage: TextView
|
||||
|
||||
override var searchElement: SearchElement = super.searchElement
|
||||
|
||||
init {
|
||||
val spans = view.getElementsByTagName("span").iterator().asSequence().toList()
|
||||
|
||||
spanWorkGroupName =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-name" } as HTMLSpanElement)
|
||||
spanWorkGroupLength =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-length" } as HTMLSpanElement)
|
||||
spanWorkGroupInterested =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-interested" } as HTMLSpanElement)
|
||||
spanWorkGroupTrack =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-track" } as HTMLSpanElement)
|
||||
spanWorkGroupProjector =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-projector" } as HTMLSpanElement)
|
||||
spanWorkGroupResolution =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-resolution" } as HTMLSpanElement)
|
||||
spanWorkGroupLanguage =
|
||||
TextView.wrap(spans.first { it.dataset["editType"] == "workgroup-language" } as HTMLSpanElement)
|
||||
|
||||
setupEditable(spanWorkGroupName) {
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
if (wg.name != it) {
|
||||
WorkGroupRepository.update(wg.copy(name = it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupEditable(spanWorkGroupLength, "\\d+".toRegex()) {
|
||||
val number = it.toIntOrNull() ?: return@setupEditable
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
if (wg.length != number) {
|
||||
WorkGroupRepository.update(wg.copy(length = number))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupEditable(spanWorkGroupInterested, "\\d+".toRegex()) {
|
||||
val number = it.toIntOrNull() ?: return@setupEditable
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
if (wg.interested != number) {
|
||||
WorkGroupRepository.update(wg.copy(interested = number))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupBoolean(spanWorkGroupProjector) {
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
WorkGroupRepository.update(wg.copy(projector = !wg.projector))
|
||||
}
|
||||
}
|
||||
|
||||
setupBoolean(spanWorkGroupResolution) {
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
WorkGroupRepository.update(wg.copy(resolution = !wg.resolution))
|
||||
}
|
||||
}
|
||||
|
||||
setupList(spanWorkGroupLanguage, Language.values().sortedBy { it.localeName }, { it.localeName }) {
|
||||
if (it == null) return@setupList
|
||||
launch {
|
||||
val wg = workGroup.get()
|
||||
if (wg.language == it) return@launch
|
||||
WorkGroupRepository.update(wg.copy(language = it))
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
val tracks = listOf<Track?>(null) + TrackRepository.all()
|
||||
|
||||
setupList(spanWorkGroupTrack, tracks, { it.name }) {
|
||||
launch x@{
|
||||
val wg = workGroup.get()
|
||||
if (wg.track == it) return@x
|
||||
WorkGroupRepository.update(wg.copy(track = it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorkGroupRepository.onUpdate {
|
||||
if (it != lineId) return@onUpdate
|
||||
|
||||
launch {
|
||||
val wg = WorkGroupRepository.get(it) ?: return@launch
|
||||
workGroup.set(wg)
|
||||
searchElement = wg.createSearch()
|
||||
|
||||
spanWorkGroupName.text = wg.name
|
||||
spanWorkGroupLength.text = wg.length.toString()
|
||||
spanWorkGroupInterested.text = wg.interested.toString()
|
||||
spanWorkGroupTrack.text = wg.track?.name ?: ""
|
||||
spanWorkGroupProjector.text = wg.projector.toString()
|
||||
spanWorkGroupResolution.text = wg.resolution.toString()
|
||||
spanWorkGroupLanguage.text = wg.language.localeName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,10 +3,7 @@ package de.westermann.kwebview.components
|
|||
import de.westermann.kobserve.Property
|
||||
import de.westermann.kobserve.ReadOnlyProperty
|
||||
import de.westermann.kobserve.property.property
|
||||
import de.westermann.kwebview.KWebViewDsl
|
||||
import de.westermann.kwebview.View
|
||||
import de.westermann.kwebview.ViewCollection
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import de.westermann.kwebview.*
|
||||
import org.w3c.dom.HTMLSpanElement
|
||||
|
||||
/**
|
||||
|
@ -15,8 +12,9 @@ import org.w3c.dom.HTMLSpanElement
|
|||
* @author lars
|
||||
*/
|
||||
class TextView(
|
||||
value: String = ""
|
||||
) : View(createHtmlView<HTMLSpanElement>()) {
|
||||
value: String = "",
|
||||
view: HTMLSpanElement = createHtmlView()
|
||||
) : View(view) {
|
||||
|
||||
override val html = super.html as HTMLSpanElement
|
||||
|
||||
|
@ -37,9 +35,26 @@ class TextView(
|
|||
|
||||
val textProperty: Property<String> = property(this::text)
|
||||
|
||||
var contentEditable: Boolean
|
||||
get() = html.isContentEditable
|
||||
set(value) {
|
||||
html.contentEditable = value.toString()
|
||||
}
|
||||
|
||||
private var internalTabIndex by AttributeDelegate("tabIndex")
|
||||
var tabIndex: Int?
|
||||
get() = internalTabIndex?.toIntOrNull()
|
||||
set(value) {
|
||||
internalTabIndex = value?.toString()
|
||||
}
|
||||
|
||||
init {
|
||||
text = value
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun wrap(view: HTMLSpanElement) = TextView(view.textContent ?: "", view)
|
||||
}
|
||||
}
|
||||
|
||||
@KWebViewDsl
|
||||
|
|
|
@ -27,6 +27,8 @@ $bg-enabled-color: rgba($primary-color, .5);
|
|||
$lever-disabled-color: $background-primary-color;
|
||||
$lever-enabled-color: $primary-color;
|
||||
|
||||
$error-background-color: #FFCDD2;
|
||||
|
||||
body, html {
|
||||
color: $text-primary-color;
|
||||
background: $background-secondary-color;
|
||||
|
@ -40,6 +42,10 @@ body, html {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.no-select {
|
||||
@include no-select()
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
|
@ -248,6 +254,10 @@ a {
|
|||
tr {
|
||||
border-top: solid 1px rgba($text-primary-color, 0.1);
|
||||
|
||||
&:nth-child(odd) {
|
||||
//background-color: rgba($text-primary-color, 0.01);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
background-color: rgba($text-primary-color, 0.06);
|
||||
height: 2.5rem;
|
||||
|
@ -263,6 +273,34 @@ a {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:empty:before {
|
||||
content: "\200b";
|
||||
}
|
||||
|
||||
&.error {
|
||||
background-color: $error-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
.table-select-box {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background: $background-primary-color;
|
||||
width: 100%;
|
||||
border: solid 1px rgba($text-primary-color, 0.1);
|
||||
|
||||
span {
|
||||
padding: 0 0.5rem;
|
||||
&:hover {
|
||||
background-color: rgba($text-primary-color, 0.06);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue