Add dynamic resizing of calendar
This commit is contained in:
parent
990cdaf1a4
commit
dce2567160
|
@ -14,10 +14,14 @@ class WebSocketClient() {
|
|||
private val url = "ws://${window.location.host}/"
|
||||
|
||||
private lateinit var ws: WebSocket
|
||||
private var reconnect = false
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onOpen(event: Event) {
|
||||
console.log("Connected!")
|
||||
if (reconnect) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMessage(messageEvent: MessageEvent) {
|
||||
|
@ -37,6 +41,7 @@ class WebSocketClient() {
|
|||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onClose(event: Event) {
|
||||
console.log("Disconnected!")
|
||||
reconnect = true
|
||||
async(1000) {
|
||||
connect()
|
||||
}
|
||||
|
|
|
@ -6,31 +6,6 @@ import kotlin.coroutines.CoroutineContext
|
|||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
|
||||
|
||||
operator fun HTMLCollection.iterator() = object : Iterator<HTMLElement> {
|
||||
private var index = 0
|
||||
override fun hasNext(): Boolean {
|
||||
return index < this@iterator.length
|
||||
}
|
||||
|
||||
override fun next(): HTMLElement {
|
||||
return this@iterator.get(index++) as HTMLElement
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
operator fun NodeList.iterator() = object : Iterator<Node> {
|
||||
private var index = 0
|
||||
override fun hasNext(): Boolean {
|
||||
return index < this@iterator.length
|
||||
}
|
||||
|
||||
override fun next(): Node {
|
||||
return this@iterator.get(index++)!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun launch(context: CoroutineContext = EmptyCoroutineContext, block: suspend () -> Unit) =
|
||||
block.startCoroutine(Continuation(context) { result ->
|
||||
result.onFailure { exception ->
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package de.kif.frontend.views
|
||||
|
||||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.WorkGroupRepository
|
||||
import de.westermann.kobserve.event.EventListener
|
||||
|
@ -8,6 +7,7 @@ import de.westermann.kwebview.View
|
|||
import de.westermann.kwebview.async
|
||||
import de.westermann.kwebview.components.*
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import de.westermann.kwebview.iterator
|
||||
import org.w3c.dom.*
|
||||
import kotlin.browser.document
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package de.kif.frontend.views.board
|
||||
|
||||
import de.kif.common.formatDateTime
|
||||
import de.kif.frontend.iterator
|
||||
import de.westermann.kwebview.iterator
|
||||
import de.westermann.kwebview.interval
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.get
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
package de.kif.frontend.views.calendar
|
||||
|
||||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RoomRepository
|
||||
import de.kif.frontend.repository.ScheduleRepository
|
||||
import de.westermann.kwebview.View
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import de.westermann.kwebview.iterator
|
||||
import org.w3c.dom.*
|
||||
import kotlin.browser.document
|
||||
import kotlin.browser.window
|
||||
|
||||
|
||||
class Calendar(calendar: HTMLElement) : View(calendar) {
|
||||
var calendarEntries: List<CalendarEntry> = emptyList()
|
||||
var calendarCells: List<CalendarCell> = emptyList()
|
||||
|
||||
val day: Int
|
||||
val day: Int = calendar.dataset["day"]?.toIntOrNull() ?: -1
|
||||
|
||||
val htmlTag = document.body as HTMLElement
|
||||
private val htmlTag = document.body as HTMLElement
|
||||
val calendarTable = calendar.getElementsByClassName("calendar-table")[0] as HTMLElement
|
||||
val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement
|
||||
|
||||
fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
|
||||
htmlTag.scrollBy(ScrollToOptions(0.0, pixel, scrollBehavior))
|
||||
|
@ -34,58 +35,22 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
|
|||
calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, scrollBehavior))
|
||||
}
|
||||
|
||||
val editable = calendar.dataset["editable"]?.toBoolean() ?: false
|
||||
|
||||
val body = CalendarBody(this, calendar.getElementsByClassName("calendar-body")[0] as HTMLElement)
|
||||
|
||||
init {
|
||||
val editable = calendar.dataset["editable"]?.toBoolean() ?: false
|
||||
day = calendar.dataset["day"]?.toIntOrNull() ?: -1
|
||||
|
||||
calendarEntries = document.getElementsByClassName("calendar-entry")
|
||||
.iterator().asSequence().map { CalendarEntry(this, it) }.onEach { it.editable = editable }.toList()
|
||||
|
||||
calendarCells = document.getElementsByClassName("calendar-cell")
|
||||
.iterator().asSequence().filter { it.dataset["time"] != null }.map(::CalendarCell).toList()
|
||||
|
||||
if (editable) {
|
||||
CalendarEdit(this, calendar.querySelector(".calendar-edit") as HTMLElement)
|
||||
}
|
||||
|
||||
ScheduleRepository.onCreate {
|
||||
launch {
|
||||
val schedule = ScheduleRepository.get(it) ?: throw NoSuchElementException()
|
||||
calendarEntries += CalendarEntry.create(this, schedule).also { it.editable = editable }
|
||||
}
|
||||
}
|
||||
ScheduleRepository.onUpdate {
|
||||
launch {
|
||||
val schedule = ScheduleRepository.get(it) ?: throw NoSuchElementException()
|
||||
var found = false
|
||||
for (entry in calendarEntries) {
|
||||
if (entry.scheduleId == it) {
|
||||
entry.load(schedule)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
calendarEntries += CalendarEntry.create(this, schedule).also { it.editable = editable }
|
||||
}
|
||||
}
|
||||
}
|
||||
ScheduleRepository.onDelete {
|
||||
for (entry in calendarEntries) {
|
||||
if (entry.scheduleId == it) {
|
||||
entry.html.remove()
|
||||
calendarEntries -= entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(document.getElementById("calendar-check-constraints") as? HTMLElement)?.let { wrap(it) }?.onClick?.addListener {
|
||||
(document.getElementById("calendar-check-constraints") as? HTMLElement)?.let { wrap(it) }
|
||||
?.onClick?.addListener {
|
||||
launch {
|
||||
val errors = ScheduleRepository.checkConstraints()
|
||||
|
||||
println(errors)
|
||||
|
||||
for ((s, l) in errors.map) {
|
||||
for (entry in calendarEntries) {
|
||||
for (entry in body.calendarEntries) {
|
||||
if (entry.scheduleId == s) {
|
||||
entry.error = l.isNotEmpty()
|
||||
}
|
||||
|
@ -106,6 +71,29 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
|
|||
|
||||
it.preventDefault()
|
||||
}
|
||||
|
||||
RoomRepository.onCreate {
|
||||
val cell = createHtmlView<HTMLElement>()
|
||||
cell.dataset["room"] = it.toString()
|
||||
cell.classList.add("calendar-cell")
|
||||
calendarTableHeader.appendChild(cell)
|
||||
|
||||
launch {
|
||||
val room = RoomRepository.get(it) ?: return@launch
|
||||
val span = createHtmlView<HTMLSpanElement>()
|
||||
span.textContent = room.name
|
||||
cell.appendChild(span)
|
||||
}
|
||||
}
|
||||
|
||||
RoomRepository.onDelete {
|
||||
val str = it.toString()
|
||||
for (element in calendarTableHeader.children.iterator()) {
|
||||
if (element.dataset["room"] == str) {
|
||||
element.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
161
src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt
Normal file
161
src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt
Normal file
|
@ -0,0 +1,161 @@
|
|||
package de.kif.frontend.views.calendar
|
||||
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.ScheduleRepository
|
||||
import de.westermann.kwebview.ViewCollection
|
||||
import de.westermann.kwebview.interval
|
||||
import de.westermann.kwebview.iterator
|
||||
import org.w3c.dom.HTMLElement
|
||||
import kotlin.browser.document
|
||||
import kotlin.js.Date
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<CalendarRow>(view) {
|
||||
|
||||
val editable = calendar.editable
|
||||
val day = calendar.day
|
||||
|
||||
var calendarEntries: List<CalendarEntry> = emptyList()
|
||||
|
||||
val calendarCells: List<CalendarCell>
|
||||
get() = iterator().asSequence().flatten().toList()
|
||||
|
||||
private suspend fun updateRows(startTime: Int? = null, length: Int = 0) {
|
||||
if (calendarEntries.isEmpty() && startTime == null && !editable) {
|
||||
for (row in iterator().asSequence().toList()) {
|
||||
remove(row)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var max: Int
|
||||
var min: Int
|
||||
|
||||
if (startTime != null) {
|
||||
min = startTime
|
||||
max = startTime + length
|
||||
} else {
|
||||
min = calendarEntries.first().startTime
|
||||
max = calendarEntries.first().startTime + calendarEntries.first().length
|
||||
}
|
||||
|
||||
for (entry in calendarEntries) {
|
||||
max = max(max, entry.startTime + entry.length)
|
||||
min = min(min, entry.startTime)
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
val h1 = max
|
||||
max = min
|
||||
min = h1
|
||||
}
|
||||
|
||||
if (editable) {
|
||||
min = min(min, 0)
|
||||
max = max(max, 24 * 60)
|
||||
}
|
||||
|
||||
min = (min / 60 - 1) * 60
|
||||
max = (max / 60 + 2) * 60
|
||||
|
||||
while (isNotEmpty() && min > first().time) {
|
||||
remove(first())
|
||||
}
|
||||
|
||||
while (isNotEmpty() && max < last().time) {
|
||||
remove(last())
|
||||
}
|
||||
|
||||
if (isEmpty()) {
|
||||
+CalendarRow.create(this, min)
|
||||
}
|
||||
|
||||
while (min < first().time) {
|
||||
prepand(CalendarRow.create(this, first().time - 15))
|
||||
}
|
||||
|
||||
while (max > last().time + 15) {
|
||||
append(CalendarRow.create(this, last().time + 15))
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
calendarEntries = document.getElementsByClassName("calendar-entry")
|
||||
.iterator().asSequence().map { CalendarEntry(this, it) }.toList()
|
||||
|
||||
wrapContent {
|
||||
CalendarRow(this, it)
|
||||
}
|
||||
|
||||
ScheduleRepository.onCreate {
|
||||
launch {
|
||||
val schedule = ScheduleRepository.get(it) ?: throw NoSuchElementException()
|
||||
|
||||
updateRows(schedule.time, schedule.workGroup.length)
|
||||
|
||||
calendarEntries += CalendarEntry.create(this, schedule)
|
||||
}
|
||||
}
|
||||
ScheduleRepository.onUpdate {
|
||||
launch {
|
||||
val schedule = ScheduleRepository.get(it) ?: throw NoSuchElementException()
|
||||
|
||||
updateRows(schedule.time, schedule.workGroup.length)
|
||||
|
||||
var found = false
|
||||
for (entry in calendarEntries) {
|
||||
if (entry.scheduleId == it) {
|
||||
entry.load(schedule)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
calendarEntries += CalendarEntry.create(this, schedule)
|
||||
}
|
||||
|
||||
updateRows()
|
||||
}
|
||||
}
|
||||
ScheduleRepository.onDelete {
|
||||
for (entry in calendarEntries) {
|
||||
if (entry.scheduleId == it) {
|
||||
entry.html.remove()
|
||||
calendarEntries -= entry
|
||||
|
||||
launch {
|
||||
updateRows()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interval(1000) {
|
||||
val currentTime = Date().let {
|
||||
it.getHours() * 60 + it.getMinutes()
|
||||
}
|
||||
val rowTime = (currentTime / 15) * 15
|
||||
|
||||
for (row in this) {
|
||||
if (row.time == rowTime) {
|
||||
row.classList.clear()
|
||||
for (str in row.classList) {
|
||||
if ("now" in str) {
|
||||
row.classList -= str
|
||||
}
|
||||
}
|
||||
row.classList += "calendar-row"
|
||||
row.classList += "calendar-now"
|
||||
row.classList += "calendar-now-${currentTime - rowTime}"
|
||||
} else {
|
||||
for (str in row.classList) {
|
||||
if ("now" in str) {
|
||||
row.classList -= str
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,12 +3,17 @@ package de.kif.frontend.views.calendar
|
|||
import de.kif.common.model.Room
|
||||
import de.kif.frontend.repository.RoomRepository
|
||||
import de.westermann.kwebview.ViewCollection
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.get
|
||||
import org.w3c.dom.set
|
||||
import kotlin.browser.document
|
||||
|
||||
class CalendarCell(row: CalendarRow, view: HTMLElement) : ViewCollection<CalendarEntry>(view) {
|
||||
val day = row.day
|
||||
val time = row.time
|
||||
|
||||
class CalendarCell(view: HTMLElement) : ViewCollection<CalendarEntry>(view) {
|
||||
val day = dataset["day"]?.toIntOrNull() ?: 0
|
||||
val time = dataset["time"]?.toIntOrNull() ?: 0
|
||||
val roomId = dataset["room"]?.toLongOrNull() ?: 0
|
||||
|
||||
private lateinit var room: Room
|
||||
|
@ -22,7 +27,13 @@ class CalendarCell(view: HTMLElement) : ViewCollection<CalendarEntry>(view) {
|
|||
return room
|
||||
}
|
||||
|
||||
init {
|
||||
(view.getElementsByClassName("calendar-link")[0] as? HTMLElement)?.remove()
|
||||
companion object {
|
||||
fun create(row: CalendarRow, roomId: Long): CalendarCell {
|
||||
val view = createHtmlView<HTMLDivElement>()
|
||||
view.classList.add("calendar-cell")
|
||||
view.dataset["room"] = roomId.toString()
|
||||
|
||||
return CalendarCell(row, view)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package de.kif.frontend.views.calendar
|
||||
|
||||
import de.kif.common.CALENDAR_GRID_WIDTH
|
||||
import de.kif.common.model.Room
|
||||
import de.kif.common.model.Schedule
|
||||
import de.kif.common.model.WorkGroup
|
||||
import de.kif.frontend.iterator
|
||||
import de.westermann.kwebview.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RepositoryDelegate
|
||||
import de.kif.frontend.repository.ScheduleRepository
|
||||
|
@ -17,7 +16,7 @@ import kotlin.dom.appendText
|
|||
import kotlin.dom.isText
|
||||
import kotlin.js.Date
|
||||
|
||||
class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(view) {
|
||||
class CalendarEntry(private val calendar: CalendarBody, view: HTMLElement) : View(view) {
|
||||
|
||||
private lateinit var mouseDelta: Point
|
||||
private var newCell: CalendarCell? = null
|
||||
|
@ -31,11 +30,15 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
|
||||
private lateinit var workGroup: WorkGroup
|
||||
|
||||
var startTime = dataset["time"]?.toIntOrNull() ?: 0
|
||||
var length = dataset["length"]?.toIntOrNull() ?: 0
|
||||
|
||||
var pending by classList.property("pending")
|
||||
var error by classList.property("error")
|
||||
private var nextScroll = 0.0
|
||||
|
||||
var editable: Boolean = false
|
||||
val editable: Boolean
|
||||
get() = calendar.editable
|
||||
|
||||
var moveLookRoom: Long? = null
|
||||
var moveLookTime: Int? = null
|
||||
|
@ -48,7 +51,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
}
|
||||
|
||||
if (cell != null) {
|
||||
if (moveLookRoom != null && cell.roomId != moveLookRoom) {
|
||||
if (moveLookRoom != null && cell.roomId != moveLookRoom) {
|
||||
return
|
||||
}
|
||||
if (moveLookTime != null && cell.time != moveLookTime) {
|
||||
|
@ -70,29 +73,29 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
return@async
|
||||
}
|
||||
|
||||
val width = calendar.calendarTable.clientWidth
|
||||
val width = calendar.calendar.calendarTable.clientWidth
|
||||
val height = window.innerHeight
|
||||
val rect = html.getBoundingClientRect()
|
||||
|
||||
if (rect.left < 0.0) {
|
||||
nextScroll = now + 500.0
|
||||
calendar.scrollHorizontalBy(rect.left - 80.0)
|
||||
calendar.calendar.scrollHorizontalBy(rect.left - 80.0)
|
||||
} else if (rect.right > width) {
|
||||
nextScroll = now + 0.500
|
||||
calendar.scrollHorizontalBy(rect.right - width + 50.0)
|
||||
calendar.calendar.scrollHorizontalBy(rect.right - width + 50.0)
|
||||
}
|
||||
|
||||
if (rect.top < 20.0) {
|
||||
nextScroll = now + 500.0
|
||||
calendar.scrollVerticalBy(rect.top - 50.0)
|
||||
calendar.calendar.scrollVerticalBy(rect.top - 50.0)
|
||||
} else if (rect.bottom > height - 20.0) {
|
||||
nextScroll = now + 500.0
|
||||
calendar.scrollVerticalBy(rect.bottom - height + 50.0)
|
||||
calendar.calendar.scrollVerticalBy(rect.bottom - height + 50.0)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
calendarTools.setName(cell.getRoom(), cell.time)
|
||||
calendarTools?.setName(cell.getRoom(), cell.time)
|
||||
}
|
||||
|
||||
newCell = cell
|
||||
|
@ -153,7 +156,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
)
|
||||
}
|
||||
|
||||
private val calendarTools = CalendarTools(this)
|
||||
private val calendarTools = if (editable) CalendarTools(this) else null
|
||||
|
||||
init {
|
||||
onMouseDown { event ->
|
||||
|
@ -181,11 +184,13 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
event.stopPropagation()
|
||||
}
|
||||
|
||||
html.appendChild(calendarTools.html)
|
||||
if (calendarTools != null) {
|
||||
html.appendChild(calendarTools.html)
|
||||
|
||||
launch {
|
||||
val s = schedule.get()
|
||||
calendarTools.update(s)
|
||||
launch {
|
||||
val s = schedule.get()
|
||||
calendarTools.update(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,7 +210,10 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
}
|
||||
|
||||
load(schedule.workGroup)
|
||||
calendarTools.update(schedule)
|
||||
calendarTools?.update(schedule)
|
||||
|
||||
startTime = schedule.time
|
||||
length = schedule.workGroup.length
|
||||
|
||||
val time = schedule.time / CALENDAR_GRID_WIDTH * CALENDAR_GRID_WIDTH
|
||||
val cell = calendar.calendarCells.find {
|
||||
|
@ -247,7 +255,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun create(calendar: Calendar, schedule: Schedule): CalendarEntry {
|
||||
fun create(calendar: CalendarBody, schedule: Schedule): CalendarEntry {
|
||||
val entry = CalendarEntry(calendar, createHtmlView())
|
||||
|
||||
entry.load(schedule)
|
||||
|
@ -255,7 +263,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
|||
return entry
|
||||
}
|
||||
|
||||
fun create(calendar: Calendar, workGroup: WorkGroup): CalendarEntry {
|
||||
fun create(calendar: CalendarBody, workGroup: WorkGroup): CalendarEntry {
|
||||
val entry = CalendarEntry(calendar, createHtmlView())
|
||||
|
||||
entry.load(workGroup)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package de.kif.frontend.views.calendar
|
||||
|
||||
import de.kif.frontend.repository.RoomRepository
|
||||
import de.westermann.kwebview.ViewCollection
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLSpanElement
|
||||
import org.w3c.dom.set
|
||||
|
||||
class CalendarRow(calendar: CalendarBody, view: HTMLElement) : ViewCollection<CalendarCell>(view) {
|
||||
val day = calendar.day
|
||||
|
||||
val time = dataset["time"]?.toIntOrNull() ?: 0
|
||||
|
||||
init {
|
||||
wrapContent {
|
||||
CalendarCell(this, it)
|
||||
}
|
||||
|
||||
RoomRepository.onCreate {
|
||||
+CalendarCell.create(this, it)
|
||||
}
|
||||
RoomRepository.onDelete { id ->
|
||||
find { it.roomId == id }?.let(this@CalendarRow::remove)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
suspend fun create(calendar: CalendarBody, time: Int): CalendarRow {
|
||||
val view = createHtmlView<HTMLDivElement>()
|
||||
view.classList.add("calendar-row")
|
||||
view.dataset["time"] = time.toString()
|
||||
|
||||
val row = CalendarRow(calendar, view)
|
||||
|
||||
val rowHeader = createHtmlView<HTMLElement>()
|
||||
rowHeader.classList.add("calendar-cell")
|
||||
if (time % 60 == 0) {
|
||||
val span = createHtmlView<HTMLSpanElement>()
|
||||
|
||||
val t = (time % (60 * 24)).let {
|
||||
if (it < 0) it + 60 * 24 else it
|
||||
}
|
||||
val hours = (t / 60).toString().padStart(2, '0')
|
||||
span.textContent = "$hours:00"
|
||||
|
||||
rowHeader.appendChild(span)
|
||||
}
|
||||
row.html.appendChild(rowHeader)
|
||||
|
||||
row.html
|
||||
|
||||
val rooms = RoomRepository.all()
|
||||
|
||||
for (room in rooms) {
|
||||
if (room.id != null) {
|
||||
row += CalendarCell.create(row, room.id)
|
||||
}
|
||||
}
|
||||
|
||||
return row
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ class CalendarWorkGroup(
|
|||
}
|
||||
|
||||
onMouseDown {
|
||||
CalendarEntry.create(calendar, workGroup)
|
||||
CalendarEntry.create(calendar.body, workGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package de.kif.frontend.views.overview
|
||||
|
||||
import de.kif.frontend.iterator
|
||||
import de.westermann.kwebview.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.PostRepository
|
||||
import de.westermann.kobserve.event.subscribe
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package de.kif.frontend.views.table
|
||||
|
||||
import de.kif.common.SearchElement
|
||||
import de.kif.frontend.iterator
|
||||
import de.westermann.kwebview.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RepositoryDelegate
|
||||
import de.kif.frontend.repository.RoomRepository
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package de.kif.frontend.views.table
|
||||
|
||||
import de.kif.frontend.iterator
|
||||
import de.westermann.kwebview.iterator
|
||||
import de.westermann.kwebview.components.InputView
|
||||
import org.w3c.dom.HTMLFormElement
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
|
|
|
@ -3,7 +3,7 @@ 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.westermann.kwebview.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.RepositoryDelegate
|
||||
import de.kif.frontend.repository.TrackRepository
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package de.westermann.kwebview
|
||||
|
||||
import de.westermann.kobserve.event.EventListener
|
||||
import de.westermann.kobserve.Property
|
||||
import de.westermann.kobserve.ReadOnlyProperty
|
||||
import de.westermann.kobserve.event.EventListener
|
||||
import de.westermann.kobserve.property.property
|
||||
import org.w3c.dom.DOMTokenList
|
||||
import kotlin.collections.Iterable
|
||||
import kotlin.collections.Iterator
|
||||
import kotlin.collections.MutableMap
|
||||
import kotlin.collections.contains
|
||||
import kotlin.collections.minusAssign
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* Represents the css classes of an html element.
|
||||
|
@ -12,7 +19,7 @@ import org.w3c.dom.DOMTokenList
|
|||
* @author lars
|
||||
*/
|
||||
class ClassList(
|
||||
private val list: DOMTokenList
|
||||
private val list: DOMTokenList
|
||||
) : Iterable<String> {
|
||||
|
||||
private val bound: MutableMap<String, Bound> = mutableMapOf()
|
||||
|
@ -74,11 +81,11 @@ class ClassList(
|
|||
* Set css class present.
|
||||
*/
|
||||
operator fun set(clazz: String, present: Boolean) =
|
||||
if (present) {
|
||||
add(clazz)
|
||||
} else {
|
||||
remove(clazz)
|
||||
}
|
||||
if (present) {
|
||||
add(clazz)
|
||||
} else {
|
||||
remove(clazz)
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle css class.
|
||||
|
@ -92,9 +99,9 @@ class ClassList(
|
|||
|
||||
set(clazz, property.value)
|
||||
bound[clazz] = Bound(property,
|
||||
property.onChange.reference {
|
||||
list.toggle(clazz, property.value)
|
||||
}
|
||||
property.onChange.reference {
|
||||
list.toggle(clazz, property.value)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -130,8 +137,14 @@ class ClassList(
|
|||
|
||||
override fun toString(): String = list.value
|
||||
|
||||
fun clear() {
|
||||
for (element in this) {
|
||||
remove(element)
|
||||
}
|
||||
}
|
||||
|
||||
private data class Bound(
|
||||
val property: ReadOnlyProperty<Boolean>,
|
||||
val reference: EventListener<Unit>?
|
||||
val property: ReadOnlyProperty<Boolean>,
|
||||
val reference: EventListener<Unit>?
|
||||
)
|
||||
}
|
|
@ -8,7 +8,15 @@ import kotlin.dom.clear
|
|||
*/
|
||||
abstract class ViewCollection<V : View>(view: HTMLElement = createHtmlView()) : View(view), Collection<V> {
|
||||
|
||||
private val children: MutableList<V> = mutableListOf()
|
||||
protected val children: MutableList<V> = mutableListOf()
|
||||
|
||||
protected inline fun <reified T : HTMLElement> wrapContent(transform: (T) -> V) {
|
||||
for (element in html.children.iterator()) {
|
||||
children += transform(element as T)
|
||||
}
|
||||
}
|
||||
|
||||
protected inline fun wrapContent(transform: (HTMLElement) -> V) = wrapContent<HTMLElement>(transform)
|
||||
|
||||
fun append(view: V) {
|
||||
children += view
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package de.westermann.kwebview
|
||||
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
import org.w3c.dom.DOMRect
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.*
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.dom.events.EventListener
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
|
@ -11,6 +10,30 @@ import org.w3c.xhr.XMLHttpRequest
|
|||
import kotlin.browser.document
|
||||
import kotlin.browser.window
|
||||
|
||||
operator fun HTMLCollection.iterator() = object : Iterator<HTMLElement> {
|
||||
private var index = 0
|
||||
override fun hasNext(): Boolean {
|
||||
return index < this@iterator.length
|
||||
}
|
||||
|
||||
override fun next(): HTMLElement {
|
||||
return this@iterator.get(index++) as HTMLElement
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
operator fun NodeList.iterator() = object : Iterator<Node> {
|
||||
private var index = 0
|
||||
override fun hasNext(): Boolean {
|
||||
return index < this@iterator.length
|
||||
}
|
||||
|
||||
override fun next(): Node {
|
||||
return this@iterator.get(index++)!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified V : HTMLElement> createHtmlView(tag: String? = null): V {
|
||||
var tagName: String
|
||||
if (tag != null) {
|
||||
|
|
|
@ -384,7 +384,7 @@
|
|||
border-left: none;
|
||||
}
|
||||
|
||||
&:nth-child(4n + 2) .calendar-cell::before {
|
||||
&:nth-child(4n + 1) .calendar-cell::before {
|
||||
border-top: solid 1px var(--table-border-color);
|
||||
}
|
||||
|
||||
|
@ -424,6 +424,10 @@
|
|||
&.time-to-room {
|
||||
flex-direction: row;
|
||||
|
||||
.calendar-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.calendar-header, .calendar-row {
|
||||
flex-direction: column;
|
||||
line-height: 3rem;
|
||||
|
@ -468,7 +472,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 2) .calendar-cell::before {
|
||||
&:nth-child(4n + 1) .calendar-cell::before {
|
||||
content: '';
|
||||
height: 100%;
|
||||
left: 0;
|
||||
|
|
|
@ -40,10 +40,6 @@ fun Route.account() {
|
|||
val wikiSections = WikiImporter.loadSections()
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.ACCOUNT
|
||||
}
|
||||
content {
|
||||
h1 { +"Account" }
|
||||
div {
|
||||
|
|
|
@ -6,7 +6,6 @@ import de.kif.backend.Configuration
|
|||
import de.kif.backend.isAuthenticated
|
||||
import de.kif.backend.repository.RoomRepository
|
||||
import de.kif.backend.repository.ScheduleRepository
|
||||
import de.kif.backend.view.MenuTemplate
|
||||
import de.kif.backend.view.respondMain
|
||||
import de.kif.common.CALENDAR_GRID_WIDTH
|
||||
import de.kif.common.model.Permission
|
||||
|
@ -51,6 +50,8 @@ private fun DIV.calendarCell(schedule: Schedule?) {
|
|||
}.toString()
|
||||
attributes["data-language"] = schedule.workGroup.language.code
|
||||
attributes["data-id"] = schedule.id.toString()
|
||||
attributes["data-time"] = schedule.time.toString()
|
||||
attributes["data-length"] = schedule.workGroup.length.toString()
|
||||
|
||||
+schedule.workGroup.name
|
||||
}
|
||||
|
@ -81,6 +82,8 @@ private fun DIV.renderCalendar(
|
|||
|
||||
for (room in rooms) {
|
||||
div("calendar-cell") {
|
||||
attributes["data-room"] = room.id.toString()
|
||||
|
||||
span {
|
||||
+room.name
|
||||
}
|
||||
|
@ -88,43 +91,46 @@ private fun DIV.renderCalendar(
|
|||
}
|
||||
}
|
||||
|
||||
for (i in 0 until minutesOfDay / CALENDAR_GRID_WIDTH) {
|
||||
val time = ((i * CALENDAR_GRID_WIDTH + from) % MINUTES_OF_DAY).let {
|
||||
if (it < 0) it + MINUTES_OF_DAY else it
|
||||
}
|
||||
val minutes = (time % 60).toString().padStart(2, '0')
|
||||
val hours = (time / 60).toString().padStart(2, '0')
|
||||
val timeString = "$hours:$minutes"
|
||||
div("calendar-body") {
|
||||
for (i in 0 until minutesOfDay / CALENDAR_GRID_WIDTH) {
|
||||
val time = ((i * CALENDAR_GRID_WIDTH + from) % MINUTES_OF_DAY).let {
|
||||
if (it < 0) it + MINUTES_OF_DAY else it
|
||||
}
|
||||
val minutes = (time % 60).toString().padStart(2, '0')
|
||||
val hours = (time / 60).toString().padStart(2, '0')
|
||||
val timeString = "$hours:$minutes"
|
||||
|
||||
val start = i * CALENDAR_GRID_WIDTH + from
|
||||
val end = (i + 1) * CALENDAR_GRID_WIDTH + from - 1
|
||||
val start = i * CALENDAR_GRID_WIDTH + from
|
||||
val end = (i + 1) * CALENDAR_GRID_WIDTH + from - 1
|
||||
|
||||
var rowClass = "calendar-row"
|
||||
var rowClass = "calendar-row"
|
||||
|
||||
if (currentTime in start..end) {
|
||||
rowClass += " calendar-now calendar-now-${currentTime - start}"
|
||||
}
|
||||
|
||||
div(rowClass) {
|
||||
|
||||
div("calendar-cell") {
|
||||
if (time % gridLabelWidth == 0) {
|
||||
span {
|
||||
+timeString
|
||||
}
|
||||
}
|
||||
if (currentTime in start..end) {
|
||||
rowClass += " calendar-now calendar-now-${currentTime - start}"
|
||||
}
|
||||
|
||||
for (room in rooms) {
|
||||
div(rowClass) {
|
||||
attributes["data-time"] = start.toString()
|
||||
attributes["data-day"] = day.toString()
|
||||
|
||||
div("calendar-cell") {
|
||||
attributes["data-time"] = start.toString()
|
||||
attributes["data-room"] = room.id.toString()
|
||||
attributes["data-day"] = day.toString()
|
||||
title = room.name + " - " + timeString
|
||||
if (time % gridLabelWidth == 0) {
|
||||
span {
|
||||
+timeString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val schedule = (start..end).mapNotNull { schedules[room]?.get(it) }.firstOrNull()
|
||||
for (room in rooms) {
|
||||
div("calendar-cell") {
|
||||
attributes["data-room"] = room.id.toString()
|
||||
|
||||
calendarCell(schedule)
|
||||
title = room.name + " - " + timeString
|
||||
|
||||
val schedule = (start..end).mapNotNull { schedules[room]?.get(it) }.firstOrNull()
|
||||
|
||||
calendarCell(schedule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,9 +173,12 @@ fun Route.calendar() {
|
|||
val day = call.parameters["day"]?.toIntOrNull() ?: return@get
|
||||
|
||||
val range = ScheduleRepository.getDayRange()
|
||||
|
||||
/*
|
||||
if (!editable && day !in range) {
|
||||
return@get
|
||||
}
|
||||
*/
|
||||
|
||||
val rooms = RoomRepository.all()
|
||||
|
||||
|
@ -177,8 +186,8 @@ fun Route.calendar() {
|
|||
CalendarOrientation.values().find { it.name == name }
|
||||
} ?: CalendarOrientation.ROOM_TO_TIME
|
||||
|
||||
val h = ScheduleRepository.getByDay(day)
|
||||
val schedules = h.groupBy { it.room }.mapValues { (_, it) ->
|
||||
val list = ScheduleRepository.getByDay(day)
|
||||
val schedules = list.groupBy { it.room }.mapValues { (_, it) ->
|
||||
it.associateBy {
|
||||
it.time
|
||||
}
|
||||
|
@ -186,7 +195,7 @@ fun Route.calendar() {
|
|||
|
||||
var max = 0
|
||||
var min = 24 * 60
|
||||
for (s in h) {
|
||||
for (s in list) {
|
||||
max = max(max, s.time + s.workGroup.length)
|
||||
min = min(min, s.time)
|
||||
}
|
||||
|
@ -205,6 +214,11 @@ fun Route.calendar() {
|
|||
min = (min / 60 - 1) * 60
|
||||
max = (max / 60 + 2) * 60
|
||||
|
||||
if (!editable && list.isEmpty()) {
|
||||
min = 0
|
||||
max = 0
|
||||
}
|
||||
|
||||
val refDate = DateTime(Configuration.Schedule.referenceDate.time)
|
||||
val date = refDate + day.days
|
||||
val dateString = DateFormat("EEEE, d. MMMM")
|
||||
|
@ -212,15 +226,7 @@ fun Route.calendar() {
|
|||
.format(date)
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.CALENDAR
|
||||
}
|
||||
content {
|
||||
if (rooms.isEmpty()) {
|
||||
return@content
|
||||
}
|
||||
|
||||
div("header") {
|
||||
div("header-left") {
|
||||
if (editable || day - 1 > range.start) {
|
||||
|
|
|
@ -2,12 +2,10 @@ package de.kif.backend.route
|
|||
|
||||
import de.kif.backend.PortalSession
|
||||
import de.kif.backend.UserPrinciple
|
||||
import de.kif.backend.view.MainTemplate
|
||||
import de.kif.backend.view.respondMain
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.auth.principal
|
||||
import io.ktor.html.respondHtmlTemplate
|
||||
import io.ktor.response.respondRedirect
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.get
|
||||
|
|
|
@ -78,10 +78,6 @@ fun Route.overview() {
|
|||
val postList = PostRepository.all().asReversed()
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.BOARD
|
||||
}
|
||||
content {
|
||||
div("overview") {
|
||||
div("overview-main") {
|
||||
|
@ -118,10 +114,6 @@ fun Route.overview() {
|
|||
}
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.BOARD
|
||||
}
|
||||
content {
|
||||
div("overview") {
|
||||
createPost(post, editable)
|
||||
|
@ -135,10 +127,6 @@ fun Route.overview() {
|
|||
val postId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||
val editPost = PostRepository.get(postId) ?: return@get
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.BOARD
|
||||
}
|
||||
content {
|
||||
h1 { +"Edit post" }
|
||||
div("post-edit-container") {
|
||||
|
@ -365,10 +353,6 @@ fun Route.overview() {
|
|||
get("/post/new") {
|
||||
authenticateOrRedirect(Permission.POST) { user ->
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.BOARD
|
||||
}
|
||||
content {
|
||||
h1 { +"Create post" }
|
||||
div("post-edit-container") {
|
||||
|
|
|
@ -33,10 +33,6 @@ fun Route.room() {
|
|||
val list = RoomRepository.all()
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.ROOM
|
||||
}
|
||||
content {
|
||||
insert(TableTemplate()) {
|
||||
searchValue = search
|
||||
|
@ -113,10 +109,6 @@ fun Route.room() {
|
|||
val roomId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||
val editRoom = RoomRepository.get(roomId) ?: return@get
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.ROOM
|
||||
}
|
||||
content {
|
||||
h1 { +"Edit room" }
|
||||
form(method = FormMethod.post) {
|
||||
|
@ -276,10 +268,6 @@ fun Route.room() {
|
|||
get("/room/new") {
|
||||
authenticateOrRedirect(Permission.ROOM) { user ->
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.ROOM
|
||||
}
|
||||
content {
|
||||
h1 { +"Create room" }
|
||||
form(method = FormMethod.post) {
|
||||
|
|
|
@ -90,10 +90,6 @@ fun Route.track() {
|
|||
val search = call.parameters["search"] ?: ""
|
||||
val list = TrackRepository.all()
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
insert(TableTemplate()) {
|
||||
searchValue = search
|
||||
|
@ -150,10 +146,6 @@ fun Route.track() {
|
|||
val trackId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||
val editTrack = TrackRepository.get(trackId) ?: return@get
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
h1 { +"Edit track" }
|
||||
form(method = FormMethod.post) {
|
||||
|
@ -219,10 +211,6 @@ fun Route.track() {
|
|||
get("/track/new") {
|
||||
authenticateOrRedirect(Permission.WORK_GROUP) { user ->
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
h1 { +"Create track" }
|
||||
form(method = FormMethod.post) {
|
||||
|
|
|
@ -33,10 +33,6 @@ fun Route.user() {
|
|||
val search = call.parameters["search"] ?: ""
|
||||
val list = UserRepository.all()
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.USER
|
||||
}
|
||||
content {
|
||||
insert(TableTemplate()) {
|
||||
searchValue = search
|
||||
|
@ -92,10 +88,6 @@ fun Route.user() {
|
|||
val userId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||
val editUser = UserRepository.get(userId) ?: return@get
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.USER
|
||||
}
|
||||
content {
|
||||
h1 { +"Edit user" }
|
||||
form(method = FormMethod.post) {
|
||||
|
@ -185,10 +177,6 @@ fun Route.user() {
|
|||
get("/user/new") {
|
||||
authenticateOrRedirect(Permission.USER) { user ->
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.USER
|
||||
}
|
||||
content {
|
||||
h1 { +"Create user" }
|
||||
form(method = FormMethod.post) {
|
||||
|
|
|
@ -29,10 +29,6 @@ fun Route.workGroup() {
|
|||
val search = call.parameters["search"] ?: ""
|
||||
val list = WorkGroupRepository.all()
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
insert(TableTemplate()) {
|
||||
searchValue = search
|
||||
|
@ -166,10 +162,6 @@ fun Route.workGroup() {
|
|||
}
|
||||
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
h1 { +"Edit work group" }
|
||||
form(method = FormMethod.post) {
|
||||
|
@ -529,10 +521,6 @@ fun Route.workGroup() {
|
|||
authenticateOrRedirect(Permission.WORK_GROUP) { user ->
|
||||
val tracks = TrackRepository.all()
|
||||
respondMain {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
active = MenuTemplate.Tab.WORK_GROUP
|
||||
}
|
||||
content {
|
||||
h1 { +"Create work group" }
|
||||
form(method = FormMethod.post) {
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
package de.kif.backend.view
|
||||
|
||||
import de.kif.backend.PortalSession
|
||||
import de.kif.backend.Resources
|
||||
import de.kif.backend.authenticate
|
||||
import de.kif.common.model.User
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.html.*
|
||||
import io.ktor.request.path
|
||||
import io.ktor.request.uri
|
||||
import io.ktor.response.respondRedirect
|
||||
import io.ktor.sessions.get
|
||||
import io.ktor.sessions.sessions
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import kotlinx.html.*
|
||||
|
||||
class MainTemplate(
|
||||
private val theme: Theme,
|
||||
private val url: String,
|
||||
private val user: User?,
|
||||
private val noMenu: Boolean,
|
||||
private val stretch: Boolean
|
||||
) : Template<HTML> {
|
||||
val content = Placeholder<HtmlBlockTag>()
|
||||
val menuTemplate = TemplatePlaceholder<MenuTemplate>()
|
||||
|
||||
override fun HTML.apply() {
|
||||
head {
|
||||
|
@ -56,7 +63,7 @@ class MainTemplate(
|
|||
}
|
||||
body {
|
||||
if (!noMenu) {
|
||||
insert(MenuTemplate(), menuTemplate)
|
||||
insert(MenuTemplate(url, user)) {}
|
||||
}
|
||||
|
||||
val containerClasses = if (stretch) "container-full" else "container"
|
||||
|
@ -88,12 +95,16 @@ class MainTemplate(
|
|||
}
|
||||
|
||||
enum class Theme {
|
||||
LIGHT, DARK, PRINCESS
|
||||
}
|
||||
LIGHT, DARK, PRINCESS;
|
||||
|
||||
private fun String?.toTheme() = this?.let { str ->
|
||||
Theme.values().find { str == it.name }
|
||||
} ?: Theme.LIGHT
|
||||
companion object {
|
||||
private val loopup = values().toList().associateBy { it.name }
|
||||
|
||||
fun lookup(name: String?): Theme {
|
||||
return loopup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
||||
noMenu: Boolean = false,
|
||||
|
@ -101,11 +112,13 @@ suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
|||
body: MainTemplate.() -> Unit
|
||||
) {
|
||||
val param = call.request.queryParameters["theme"]
|
||||
val url = call.request.uri.substring(1)
|
||||
val user = call.sessions.get<PortalSession>()?.getUser(call)
|
||||
|
||||
if (param != null) {
|
||||
call.response.cookies.append(
|
||||
name = "theme",
|
||||
value = param.toTheme().name,
|
||||
value = Theme.lookup(param).name,
|
||||
maxAge = Int.MAX_VALUE,
|
||||
path = "/"
|
||||
)
|
||||
|
@ -113,7 +126,9 @@ suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
|||
} else {
|
||||
call.respondHtmlTemplate(
|
||||
MainTemplate(
|
||||
call.request.cookies["theme"].toTheme(),
|
||||
Theme.lookup(call.request.cookies["theme"]),
|
||||
url,
|
||||
user,
|
||||
noMenu,
|
||||
stretch
|
||||
),
|
||||
|
|
|
@ -5,19 +5,21 @@ import de.kif.common.model.User
|
|||
import io.ktor.html.Template
|
||||
import kotlinx.html.*
|
||||
|
||||
class MenuTemplate() : Template<FlowContent> {
|
||||
|
||||
var active: Tab = Tab.BOARD
|
||||
var user: User? = null
|
||||
class MenuTemplate(
|
||||
private val url: String,
|
||||
private val user: User?
|
||||
) : Template<FlowContent> {
|
||||
|
||||
override fun FlowContent.apply() {
|
||||
val tab = Tab.lookup(url)
|
||||
|
||||
nav("menu") {
|
||||
div("container") {
|
||||
div("menu-left") {
|
||||
a("/", classes = if (active == Tab.BOARD) "active" else null) {
|
||||
+"Dashboard"
|
||||
a("/", classes = if (tab == null) "active" else null) {
|
||||
+"News"
|
||||
}
|
||||
a("/calendar", classes = if (active == Tab.CALENDAR) "active" else null) {
|
||||
a("/calendar", classes = if (tab == Tab.CALENDAR) "active" else null) {
|
||||
+"Calendar"
|
||||
}
|
||||
}
|
||||
|
@ -28,26 +30,26 @@ class MenuTemplate() : Template<FlowContent> {
|
|||
val user = user
|
||||
div("menu-content") {
|
||||
if (user == null) {
|
||||
a("/account", classes = if (active == Tab.ACCOUNT) "active" else null) {
|
||||
a("/account", classes = if (tab == Tab.LOGIN) "active" else null) {
|
||||
+"Login"
|
||||
}
|
||||
} else {
|
||||
if (user.checkPermission(Permission.WORK_GROUP)) {
|
||||
a("/workgroups", classes = if (active == Tab.WORK_GROUP) "active" else null) {
|
||||
a("/workgroups", classes = if (tab == Tab.WORK_GROUP) "active" else null) {
|
||||
+"Work groups"
|
||||
}
|
||||
}
|
||||
if (user.checkPermission(Permission.ROOM)) {
|
||||
a("/rooms", classes = if (active == Tab.ROOM) "active" else null) {
|
||||
a("/rooms", classes = if (tab == Tab.ROOM) "active" else null) {
|
||||
+"Rooms"
|
||||
}
|
||||
}
|
||||
if (user.checkPermission(Permission.USER)) {
|
||||
a("/users", classes = if (active == Tab.USER) "active" else null) {
|
||||
a("/users", classes = if (tab == Tab.USER) "active" else null) {
|
||||
+"Users"
|
||||
}
|
||||
}
|
||||
a("/account", classes = if (active == Tab.ACCOUNT) "active" else null) {
|
||||
a("/account", classes = if (tab == Tab.ACCOUNT) "active" else null) {
|
||||
+user.username
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +60,14 @@ class MenuTemplate() : Template<FlowContent> {
|
|||
}
|
||||
|
||||
enum class Tab {
|
||||
BOARD, CALENDAR, ACCOUNT, WORK_GROUP, ROOM, PERSON, USER
|
||||
NEWS, CALENDAR, ACCOUNT, WORK_GROUP, ROOM, USER, LOGIN;
|
||||
|
||||
companion object {
|
||||
private val lookup = values().toList().associateBy { it.name.take(4).toLowerCase() }
|
||||
|
||||
fun lookup(url: String): Tab? {
|
||||
return lookup[url.take(4)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue