Skip to main content

Host apps in HTML elements

You can also provide an HTML element when launching a top-level Web app. This allows you to host Doodle apps in non-Doodle contexts. The apps in this documentation are top-level within specific elements.

Closing the page cleans up any apps within it. Removing the element hosting an app or explicitly calling shutdown has the same effect.

package htmlelementapp import UsefulApp import io.nacular.doodle.application.application import org.w3c.dom.HTMLElement //sampleStart fun main(element: HTMLElement) { // launch app within element application(element) { UsefulApp() } } //sampleEnd

Nested apps

Doodle Web apps can be run within other Doodle Web apps. This is done by placing the nested app in a View that the host app manages. An app launched this way has the same functionality as a top-level one. Its lifecycle however, is tied to the host View.

You simply use an ApplicationViewFactory (available via the AppViewModule) to create nested apps.

package io.nacular.doodle.docs.apps.nesting import io.nacular.doodle.application.Application import io.nacular.doodle.controls.ColorPicker import io.nacular.doodle.core.Display import io.nacular.doodle.docs.utils.doodleColor import io.nacular.doodle.drawing.opacity import io.nacular.doodle.layout.Insets import io.nacular.doodle.layout.constraints.constrain import io.nacular.doodle.layout.constraints.fill //sampleStart class InnerApp(display: Display): Application { init { // Shows a color picker display += ColorPicker(doodleColor opacity 0.75f).apply { changed += { _,_,new -> println(new) } } // The picker grows with the display, but is inset a little display.layout = constrain(display.first(), fill(insets = Insets(2.0))) } override fun shutdown() {} } //sampleEnd
tip

Adding a nested app's View to the Display triggers the app's initialization. Shutdown the app by removing the host View from the Display.

Host arbitrary HTML elements

You can embed any HTML element into your app as a View. This means Doodle apps can host React and other web components and interop with a much larger part of the Web ecosystem out of the box!

Mon
Tue
Wed
Thu
Fri
Sat
Sun
package io.nacular.doodle.docs.apps import io.nacular.doodle.HtmlElementViewFactory import io.nacular.doodle.animation.Animator import io.nacular.doodle.application.Application import io.nacular.doodle.controls.text.Label import io.nacular.doodle.core.Display import io.nacular.doodle.core.then import io.nacular.doodle.docs.utils.DateRangeSelectionModel import io.nacular.doodle.docs.utils.HorizontalCalendar import io.nacular.doodle.docs.utils.ShadowCard import io.nacular.doodle.drawing.Font import io.nacular.doodle.geometry.PathMetrics import io.nacular.doodle.layout.constraints.constrain import io.nacular.doodle.theme.Theme import io.nacular.doodle.theme.ThemeManager import kotlinx.datetime.DatePeriod import kotlinx.datetime.LocalDate import kotlinx.datetime.plus import org.w3c.dom.HTMLElement class ReactCalendarApp( display : Display, font : Font, today : LocalDate, animate : Animator, pathMetrics : PathMetrics, themeManager : ThemeManager, theme : Theme, htmlElementView: HtmlElementViewFactory, reactCalendar : HTMLElement, appHeight : (Double) -> Unit ): Application { private val doodleCalendar = HorizontalCalendar( today, animate, pathMetrics, startDate = today, endDate = today + DatePeriod(years = 10), DateRangeSelectionModel() ).apply { this.font = font } init { themeManager.selected = theme //sampleStart display += Label("Doodle").apply { this.font = font } display += ShadowCard(doodleCalendar) display += Label("React").apply { this.font = font } display += htmlElementView(element = reactCalendar) //sampleEnd val spacing = 20 display.layout = constrain( display.children[0], display.children[1], display.children[2], display.children[3] ) { doodleLabel, doodle, reactLabel, react -> doodle.top eq doodleLabel.bottom + spacing doodle.height eq 280 react.height eq doodle.height doodleLabel.top eq spacing doodleLabel.centerX eq doodle.centerX doodleLabel.width.preserve doodleLabel.height.preserve reactLabel.centerX eq react.centerX reactLabel.width.preserve reactLabel.height.preserve when { parent.width.readOnly > 800 -> { doodle.width eq (parent.width - 3 * spacing) / 2 doodle.right eq parent.centerX - spacing / 2 react.top eq doodle.top react.width eq doodle.width react.left eq doodle.right + spacing reactLabel.top eq doodleLabel.top } else -> { doodle.left eq spacing doodle.right eq parent.right - spacing react.top eq reactLabel.bottom + spacing react.left eq spacing react.right eq parent.right - spacing reactLabel.top eq doodle.bottom + spacing } } }.then { container -> // signal to outer docs about height of the app appHeight(container.children.maxOf { it.bounds.bottom } + spacing) } } override fun shutdown() { // no-op } }
info

This app embeds a react-calendar.