Is there any calendar widget I could use that coul...
# kvision
u
Is there any calendar widget I could use that could be presented on a full page?
r
No. You can use
DataTime
component with inline option, but it won't scale to fit the whole page.
You could play a bit with css styling to make it scale horizontally to 100%.
u
@Robert Jaros Thanks for the suggestion but that's now what I'm looking for, I wanted something more like an actual calendar where in each day cell I could embed some information, some text/color and etc. (text would be sufficient in my case). I'm looking into pulling in some react component but I've never used react with kvision or even react itself so I have no idea if its a good solution for me. Besides from what I see in the docs it'll probably require me to write a lot of boiler plate shim to interface with that component from kotlin.
r
It shouldn't be hard to use e.g. this one: https://github.com/jquense/react-big-calendar
The most important data (events) is just an array of JS objects.
It works.
If you want to try:
build.gradle.kts
Copy code
sourceSets["main"].dependencies {
        implementation(npm("react-big-calendar", "*"))
        implementation("io.kvision:kvision:$kvisionVersion")
        implementation("io.kvision:kvision-bootstrap:$kvisionVersion")
        implementation("io.kvision:kvision-bootstrap-css:$kvisionVersion")
        implementation("io.kvision:kvision-i18n:$kvisionVersion")
        implementation("io.kvision:kvision-moment:$kvisionVersion")
        implementation("io.kvision:kvision-react:$kvisionVersion")
    }
App.kt
Copy code
package com.example

import io.kvision.Application
import io.kvision.BootstrapCssModule
import io.kvision.BootstrapModule
import io.kvision.CoreModule
import io.kvision.module
import io.kvision.panel.root
import io.kvision.react.react
import io.kvision.require
import io.kvision.startApplication
import io.kvision.types.toDateF
import io.kvision.utils.obj
import react.ComponentClass
import react.PropsWithChildren

external interface BigCalendarProps : PropsWithChildren {
    var events: Array<dynamic>
    var localizer: dynamic
    var startAccessor: String
    var endAccessor: String
    var style: dynamic
}

val Calendar: ComponentClass<BigCalendarProps> = require("react-big-calendar").Calendar

val momentLocalizer = require("react-big-calendar").momentLocalizer
val moment = require("moment")

class App : Application() {
    init {
        require("react-big-calendar/lib/css/react-big-calendar.css")
    }

    override fun start() {
        root("kvapp") {
            val dayStart = "2021-09-19".toDateF("YYYY-MM-DD")
            val dayEnd = "2021-09-22".toDateF("YYYY-MM-DD")
            react {
                Calendar {
                    attrs.events = arrayOf(obj {
                        this.start = dayStart
                        this.end = dayEnd
                        this.title = "Some event title"
                        this.allDay = true
                    })
                    attrs.localizer = momentLocalizer(moment)
                    attrs.startAccessor = "start"
                    attrs.endAccessor = "end"
                    attrs.style = obj { this.height = 600 }
                }
            }
        }
    }
}

fun main() {
    startApplication(::App, module.hot, BootstrapModule, BootstrapCssModule, CoreModule)
}
What's good about integrating react components with KVision, is that's their API is quite similar, so even without much experience it's quite easy to do this.
u
Hm, so i tried applying your snippet, and I dont think I understand how to interface with this, I have something like this:
Copy code
external interface BigCalendarProps : PropsWithChildren {
    var events: Array<dynamic>
    var localizer: dynamic
    var startAccessor: String
    var endAccessor: String
    var style: dynamic
    var selectable: Boolean
    var onSelectEvent: (dynamic) -> Unit
    var onSelectSlot: (dynamic) -> Unit
}

val Calendar: ComponentClass<BigCalendarProps> = require("react-big-calendar").Calendar

val momentLocalizer = require("react-big-calendar").momentLocalizer
val moment = require("moment")

class CalendarTab: SimplePanel() {

    init {
        require("react-big-calendar/lib/css/react-big-calendar.css")
        this.marginTop = 10.px

        val events: List<dynamic> = DataAccess.DataStore.records.map {
            obj {
                this.start = it.eventDate
                this.end = it.eventDate
                this.title = "Test title"
                this.allDay = false
            }
        }

        react {
            Calendar {
                attrs.selectable = true
                attrs.events = events.toTypedArray()
                attrs.localizer = momentLocalizer(moment)
                attrs.startAccessor = "start"
                attrs.endAccessor = "end"
                attrs.style = obj { this.height = 600 }
                attrs.onSelectEvent = {selectedObject ->
                    println(selectedObject.title)
                }
            }
        }


    }
}
And the calendar doesnt show my events, also I'm not sure if I correctly understand how I should "import" or reimplement properties on this react component, am I doing it right in this example?
r
Looks ok. The same code works for me. Are you sure you have any records available in your
records
property when
init
is called?
u
Oh! You're right, the response was yet to come from the backend, now it displays everything as it should, thanks
r
This is how you can use KVision React component with extended state management to dynamically add events:
Copy code
root("kvapp") {
            val dayStart = "2021-09-19".toDateF("YYYY-MM-DD")
            val dayEnd = "2021-09-22".toDateF("YYYY-MM-DD")
            val events = arrayOf(obj {
                this.start = dayStart
                this.end = dayEnd
                this.title = "Some event title"
                this.allDay = true
            })
            val reactCalendar = react(events) { getState, _ ->
                Calendar {
                    attrs.events = getState()
                    attrs.localizer = momentLocalizer(moment)
                    attrs.startAccessor = "start"
                    attrs.endAccessor = "end"
                    attrs.style = obj { this.height = 600 }
                    attrs.selectable = true
                    attrs.onSelectEvent = {
                        println(it.title)
                    }
                    attrs.onSelectSlot = {
                        println(it)
                    }
                }
            }
            button("Add event").onClick {
                val dayStart = "2021-09-24".toDateF("YYYY-MM-DD")
                val dayEnd = "2021-09-25".toDateF("YYYY-MM-DD")
                reactCalendar.state = reactCalendar.state + arrayOf(obj {
                    this.start = dayStart
                    this.end = dayEnd
                    this.title = "Some other event"
                    this.allDay = true
                })
            }
        }
u
Hm, I'm using observable list and every component that has some data gets a listener that subscribes for updates
r
With observable list (and some external interface for more strict typing) you can work like this:
Copy code
external interface Event {
    var title: String
    var allDay: Boolean
    var start: Date
    var end: Date
}

val events = observableListOf<Event>()

class App : Application() {
    init {
        require("react-big-calendar/lib/css/react-big-calendar.css")
    }

    override fun start() {
        root("kvapp") {
            react(events.toTypedArray()) { getState, _ ->
                Calendar {
                    attrs.events = getState()
                    attrs.localizer = momentLocalizer(moment)
                    attrs.startAccessor = "start"
                    attrs.endAccessor = "end"
                    attrs.style = obj { this.height = 600 }
                    attrs.selectable = true
                    attrs.onSelectEvent = {
                        println(it.title)
                    }
                    attrs.onSelectSlot = {
                        println(it)
                    }
                }
            }.apply {
                events.subscribe {
                    this.state = it.toTypedArray()
                }
            }
            button("Add event").onClick {
                events.add(obj<Event> {
                    start = "2021-09-24".toDateF("YYYY-MM-DD")
                    end = "2021-09-24".toDateF("YYYY-MM-DD")
                    allDay = true
                    title = "Event title"
                })
            }
        }
    }
}
u
Yes, thats what I'm talking about 😛 I just mentioned my approach for anyone searching the conversation history with similar problem :p