diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.api b/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.api new file mode 100644 index 00000000000..37d5431fcae --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.api @@ -0,0 +1,75 @@ +public final class io/ktor/server/htmx/HXRequestHeaders { + public static final synthetic fun box-impl (Lio/ktor/http/Headers;)Lio/ktor/server/htmx/HXRequestHeaders; + public static fun constructor-impl (Lio/ktor/http/Headers;)Lio/ktor/http/Headers; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Lio/ktor/http/Headers;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Lio/ktor/http/Headers;Lio/ktor/http/Headers;)Z + public static final fun getCurrentUrl-impl (Lio/ktor/http/Headers;)Lio/ktor/http/Url; + public static final fun getPrompt-impl (Lio/ktor/http/Headers;)Ljava/lang/String; + public static final fun getTargetId-impl (Lio/ktor/http/Headers;)Ljava/lang/String; + public static final fun getTriggerId-impl (Lio/ktor/http/Headers;)Ljava/lang/String; + public static final fun getTriggerName-impl (Lio/ktor/http/Headers;)Ljava/lang/String; + public fun hashCode ()I + public static fun hashCode-impl (Lio/ktor/http/Headers;)I + public static final fun isBoosted-impl (Lio/ktor/http/Headers;)Z + public static final fun isHistoryRestore-impl (Lio/ktor/http/Headers;)Z + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Lio/ktor/http/Headers;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Lio/ktor/http/Headers; +} + +public final class io/ktor/server/htmx/HXResponseHeaders : io/ktor/util/collections/StringMap { + public fun (Lio/ktor/server/response/ResponseHeaders;)V + public fun get (Ljava/lang/String;)Ljava/lang/String; + public final fun getLocation ()Ljava/lang/String; + public final fun getPushUrl ()Ljava/lang/String; + public final fun getRedirect ()Ljava/lang/String; + public final fun getRefresh ()Ljava/lang/Boolean; + public final fun getReplaceUrl ()Ljava/lang/String; + public fun remove (Ljava/lang/String;)Ljava/lang/String; + public fun set (Ljava/lang/String;Ljava/lang/String;)V + public final fun setLocation (Ljava/lang/String;)V + public final fun setPushUrl (Ljava/lang/String;)V + public final fun setRedirect (Ljava/lang/String;)V + public final fun setRefresh (Ljava/lang/Boolean;)V +} + +public final class io/ktor/server/htmx/HxHeadersKt { + public static final fun getHx (Lio/ktor/server/routing/RoutingRequest;)Lio/ktor/http/Headers; + public static final fun getHx (Lio/ktor/server/routing/RoutingResponse;)Lio/ktor/server/htmx/HXResponseHeaders; + public static final fun isHtmx (Lio/ktor/server/routing/RoutingRequest;)Z +} + +public final class io/ktor/server/htmx/HxRoute : io/ktor/server/routing/Route { + public static final synthetic fun box-impl (Lio/ktor/server/routing/Route;)Lio/ktor/server/htmx/HxRoute; + public fun createChild (Lio/ktor/server/routing/RouteSelector;)Lio/ktor/server/routing/Route; + public static fun createChild-impl (Lio/ktor/server/routing/Route;Lio/ktor/server/routing/RouteSelector;)Lio/ktor/server/routing/Route; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Lio/ktor/server/routing/Route;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Lio/ktor/server/routing/Route;Lio/ktor/server/routing/Route;)Z + public fun getAttributes ()Lio/ktor/util/Attributes; + public static fun getAttributes-impl (Lio/ktor/server/routing/Route;)Lio/ktor/util/Attributes; + public fun getEnvironment ()Lio/ktor/server/application/ApplicationEnvironment; + public static fun getEnvironment-impl (Lio/ktor/server/routing/Route;)Lio/ktor/server/application/ApplicationEnvironment; + public fun getParent ()Lio/ktor/server/routing/Route; + public static fun getParent-impl (Lio/ktor/server/routing/Route;)Lio/ktor/server/routing/Route; + public fun handle (Lkotlin/jvm/functions/Function2;)V + public static fun handle-impl (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function2;)V + public fun hashCode ()I + public static fun hashCode-impl (Lio/ktor/server/routing/Route;)I + public fun install (Lio/ktor/server/application/Plugin;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static fun install-impl (Lio/ktor/server/routing/Route;Lio/ktor/server/application/Plugin;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun plugin (Lio/ktor/server/application/Plugin;)Ljava/lang/Object; + public static fun plugin-impl (Lio/ktor/server/routing/Route;Lio/ktor/server/application/Plugin;)Ljava/lang/Object; + public static final fun target-impl (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lio/ktor/server/routing/Route; + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Lio/ktor/server/routing/Route;)Ljava/lang/String; + public static final fun trigger-impl (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lio/ktor/server/routing/Route; + public final synthetic fun unbox-impl ()Lio/ktor/server/routing/Route; +} + +public final class io/ktor/server/htmx/HxRoutingKt { + public static final fun getHx (Lio/ktor/server/routing/Route;)Lio/ktor/server/routing/Route; + public static final fun hx (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;)Lio/ktor/server/routing/Route; +} + diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.klib.api b/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.klib.api new file mode 100644 index 00000000000..7d7fcfb7d3e --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/api/ktor-server-htmx.klib.api @@ -0,0 +1,84 @@ +// Klib ABI Dump +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class io.ktor.server.htmx/HXResponseHeaders : io.ktor.util.collections/StringMap { // io.ktor.server.htmx/HXResponseHeaders|null[0] + constructor (io.ktor.server.response/ResponseHeaders) // io.ktor.server.htmx/HXResponseHeaders.|(io.ktor.server.response.ResponseHeaders){}[0] + + final val replaceUrl // io.ktor.server.htmx/HXResponseHeaders.replaceUrl|{}replaceUrl[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.replaceUrl.|(){}[0] + + final var location // io.ktor.server.htmx/HXResponseHeaders.location|{}location[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.location.|(){}[0] + final fun (kotlin/String?) // io.ktor.server.htmx/HXResponseHeaders.location.|(kotlin.String?){}[0] + final var pushUrl // io.ktor.server.htmx/HXResponseHeaders.pushUrl|{}pushUrl[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.pushUrl.|(){}[0] + final fun (kotlin/String?) // io.ktor.server.htmx/HXResponseHeaders.pushUrl.|(kotlin.String?){}[0] + final var redirect // io.ktor.server.htmx/HXResponseHeaders.redirect|{}redirect[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.redirect.|(){}[0] + final fun (kotlin/String?) // io.ktor.server.htmx/HXResponseHeaders.redirect.|(kotlin.String?){}[0] + final var refresh // io.ktor.server.htmx/HXResponseHeaders.refresh|{}refresh[0] + final fun (): kotlin/Boolean? // io.ktor.server.htmx/HXResponseHeaders.refresh.|(){}[0] + final fun (kotlin/Boolean?) // io.ktor.server.htmx/HXResponseHeaders.refresh.|(kotlin.Boolean?){}[0] + + final fun get(kotlin/String): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.get|get(kotlin.String){}[0] + final fun remove(kotlin/String): kotlin/String? // io.ktor.server.htmx/HXResponseHeaders.remove|remove(kotlin.String){}[0] + final fun set(kotlin/String, kotlin/String) // io.ktor.server.htmx/HXResponseHeaders.set|set(kotlin.String;kotlin.String){}[0] +} + +final value class io.ktor.server.htmx/HXRequestHeaders { // io.ktor.server.htmx/HXRequestHeaders|null[0] + constructor (io.ktor.http/Headers) // io.ktor.server.htmx/HXRequestHeaders.|(io.ktor.http.Headers){}[0] + + final val currentUrl // io.ktor.server.htmx/HXRequestHeaders.currentUrl|{}currentUrl[0] + final fun (): io.ktor.http/Url? // io.ktor.server.htmx/HXRequestHeaders.currentUrl.|(){}[0] + final val isBoosted // io.ktor.server.htmx/HXRequestHeaders.isBoosted|{}isBoosted[0] + final fun (): kotlin/Boolean // io.ktor.server.htmx/HXRequestHeaders.isBoosted.|(){}[0] + final val isHistoryRestore // io.ktor.server.htmx/HXRequestHeaders.isHistoryRestore|{}isHistoryRestore[0] + final fun (): kotlin/Boolean // io.ktor.server.htmx/HXRequestHeaders.isHistoryRestore.|(){}[0] + final val prompt // io.ktor.server.htmx/HXRequestHeaders.prompt|{}prompt[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXRequestHeaders.prompt.|(){}[0] + final val targetId // io.ktor.server.htmx/HXRequestHeaders.targetId|{}targetId[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXRequestHeaders.targetId.|(){}[0] + final val triggerId // io.ktor.server.htmx/HXRequestHeaders.triggerId|{}triggerId[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXRequestHeaders.triggerId.|(){}[0] + final val triggerName // io.ktor.server.htmx/HXRequestHeaders.triggerName|{}triggerName[0] + final fun (): kotlin/String? // io.ktor.server.htmx/HXRequestHeaders.triggerName.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.server.htmx/HXRequestHeaders.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.ktor.server.htmx/HXRequestHeaders.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.ktor.server.htmx/HXRequestHeaders.toString|toString(){}[0] +} + +final value class io.ktor.server.htmx/HxRoute : io.ktor.server.routing/Route { // io.ktor.server.htmx/HxRoute|null[0] + final val attributes // io.ktor.server.htmx/HxRoute.attributes|{}attributes[0] + final fun (): io.ktor.util/Attributes // io.ktor.server.htmx/HxRoute.attributes.|(){}[0] + final val environment // io.ktor.server.htmx/HxRoute.environment|{}environment[0] + final fun (): io.ktor.server.application/ApplicationEnvironment // io.ktor.server.htmx/HxRoute.environment.|(){}[0] + final val parent // io.ktor.server.htmx/HxRoute.parent|{}parent[0] + final fun (): io.ktor.server.routing/Route? // io.ktor.server.htmx/HxRoute.parent.|(){}[0] + + final fun <#A1: kotlin/Any, #B1: kotlin/Any> install(io.ktor.server.application/Plugin, kotlin/Function1<#A1, kotlin/Unit>): #B1 // io.ktor.server.htmx/HxRoute.install|install(io.ktor.server.application.Plugin;kotlin.Function1<0:0,kotlin.Unit>){0§;1§}[0] + final fun <#A1: kotlin/Any> plugin(io.ktor.server.application/Plugin<*, *, #A1>): #A1 // io.ktor.server.htmx/HxRoute.plugin|plugin(io.ktor.server.application.Plugin<*,*,0:0>){0§}[0] + final fun createChild(io.ktor.server.routing/RouteSelector): io.ktor.server.routing/Route // io.ktor.server.htmx/HxRoute.createChild|createChild(io.ktor.server.routing.RouteSelector){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.server.htmx/HxRoute.equals|equals(kotlin.Any?){}[0] + final fun handle(kotlin.coroutines/SuspendFunction1) // io.ktor.server.htmx/HxRoute.handle|handle(kotlin.coroutines.SuspendFunction1){}[0] + final fun hashCode(): kotlin/Int // io.ktor.server.htmx/HxRoute.hashCode|hashCode(){}[0] + final fun target(kotlin/String, kotlin/Function1): io.ktor.server.routing/Route // io.ktor.server.htmx/HxRoute.target|target(kotlin.String;kotlin.Function1){}[0] + final fun toString(): kotlin/String // io.ktor.server.htmx/HxRoute.toString|toString(){}[0] + final fun trigger(kotlin/String, kotlin/Function1): io.ktor.server.routing/Route // io.ktor.server.htmx/HxRoute.trigger|trigger(kotlin.String;kotlin.Function1){}[0] +} + +final val io.ktor.server.htmx/hx // io.ktor.server.htmx/hx|@io.ktor.server.routing.Route{}hx[0] + final fun (io.ktor.server.routing/Route).(): io.ktor.server.htmx/HxRoute // io.ktor.server.htmx/hx.|@io.ktor.server.routing.Route(){}[0] +final val io.ktor.server.htmx/hx // io.ktor.server.htmx/hx|@io.ktor.server.routing.RoutingRequest{}hx[0] + final fun (io.ktor.server.routing/RoutingRequest).(): io.ktor.server.htmx/HXRequestHeaders // io.ktor.server.htmx/hx.|@io.ktor.server.routing.RoutingRequest(){}[0] +final val io.ktor.server.htmx/hx // io.ktor.server.htmx/hx|@io.ktor.server.routing.RoutingResponse{}hx[0] + final fun (io.ktor.server.routing/RoutingResponse).(): io.ktor.server.htmx/HXResponseHeaders // io.ktor.server.htmx/hx.|@io.ktor.server.routing.RoutingResponse(){}[0] +final val io.ktor.server.htmx/isHtmx // io.ktor.server.htmx/isHtmx|@io.ktor.server.routing.RoutingRequest{}isHtmx[0] + final fun (io.ktor.server.routing/RoutingRequest).(): kotlin/Boolean // io.ktor.server.htmx/isHtmx.|@io.ktor.server.routing.RoutingRequest(){}[0] + +final fun (io.ktor.server.routing/Route).io.ktor.server.htmx/hx(kotlin/Function1): io.ktor.server.routing/Route // io.ktor.server.htmx/hx|hx@io.ktor.server.routing.Route(kotlin.Function1){}[0] diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts new file mode 100644 index 00000000000..eab9b9bb280 --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts @@ -0,0 +1,14 @@ +kotlin.sourceSets { + commonMain { + dependencies { + api(project(":ktor-shared:ktor-htmx")) + implementation(project(":ktor-utils")) + } + } + commonTest { + dependencies { + implementation(project(":ktor-server:ktor-server-plugins:ktor-server-html-builder")) + implementation(project(":ktor-shared:ktor-htmx:ktor-htmx-html")) + } + } +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxHeaders.kt b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxHeaders.kt new file mode 100644 index 00000000000..03c1570f444 --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxHeaders.kt @@ -0,0 +1,63 @@ +package io.ktor.server.htmx + +import io.ktor.htmx.* +import io.ktor.http.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.util.collections.* +import io.ktor.utils.io.InternalAPI +import kotlin.jvm.JvmInline + +@ExperimentalHtmxApi +public val RoutingRequest.hx: HXRequestHeaders get() = HXRequestHeaders(headers) + +@ExperimentalHtmxApi +public val RoutingResponse.hx: HXResponseHeaders get() = HXResponseHeaders(headers) + +public val RoutingRequest.isHtmx: Boolean get() = headers[HxRequestHeaders.Request] == "true" + +@ExperimentalHtmxApi +@JvmInline +public value class HXRequestHeaders(private val headers: Headers) { + + /** Indicates that the request is via an element using hx-boost */ + public val isBoosted: Boolean get() = headers[HxRequestHeaders.Boosted]?.toBoolean() == true + + /** "true" if the request is for history restoration after a miss in the local history cache */ + public val isHistoryRestore: Boolean get() = headers[HxRequestHeaders.HistoryRestoreRequest]?.toBoolean() == true + + /** The current URL of the browser */ + public val currentUrl: Url? get() = headers[HxRequestHeaders.CurrentUrl]?.let { Url(it) } + + /** The user response to an hx-prompt */ + public val prompt: String? get() = headers[HxRequestHeaders.Prompt] + + /** The id of the target element if it exists */ + public val targetId: String? get() = headers[HxRequestHeaders.Target] + + /** The id of the triggered element if it exists */ + public val triggerId: String? get() = headers[HxRequestHeaders.Trigger] + + /** The name of the triggered element if it exists */ + public val triggerName: String? get() = headers[HxRequestHeaders.TriggerName] +} + +@ExperimentalHtmxApi +@OptIn(InternalAPI::class) +public class HXResponseHeaders(private val headers: ResponseHeaders) : StringMap { + + public var location: String? by HxResponseHeaders.Location + public var pushUrl: String? by HxResponseHeaders.PushUrl + public var redirect: String? by HxResponseHeaders.Redirect + public var refresh: Boolean? by HxResponseHeaders.Refresh.asBoolean() + public val replaceUrl: String? by HxResponseHeaders.ReplaceUrl + + override fun set(key: String, value: String): Unit = + headers.append(key, value) + + override fun get(key: String): String? = + headers[key] + + override fun remove(key: String): String? = + throw IllegalStateException("Not implemented") +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxRouting.kt b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxRouting.kt new file mode 100644 index 00000000000..eda6e8e9404 --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxRouting.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.server.htmx + +import io.ktor.htmx.* +import io.ktor.server.routing.* +import io.ktor.utils.io.* +import kotlin.jvm.JvmInline + +/** + * Property for scoping routes to HTMX (e.g., `hx.get { ... }` + */ +@ExperimentalHtmxApi +public val Route.hx: HxRoute get() = HxRoute.wrap(this) + +/** + * Scope child routes to apply when `HX-Request` header is supplied. + */ +@ExperimentalHtmxApi +public fun Route.hx(configuration: HxRoute.() -> Unit): Route = with(HxRoute.wrap(this)) { + header(HxRequestHeaders.Request, "true") { + configuration() + } +} + +/** + * Provides custom routes based on common HTMX headers. + */ +@ExperimentalHtmxApi +@KtorDsl +@JvmInline +public value class HxRoute internal constructor(private val route: Route) : Route by route { + internal companion object { + internal fun wrap(route: Route) = + HxRoute(route.createChild(HttpHeaderRouteSelector(HxRequestHeaders.Request, "true"))) + } + + /** + * Sub-routes only apply to a specific HX-Target header. + */ + public fun target(expectedTarget: String, body: Route.() -> Unit): Route { + return header(HxRequestHeaders.Target, expectedTarget, body) + } + + /** + * Sub-routes only apply to a specific HX-Trigger header. + */ + public fun trigger(expectedTrigger: String, body: Route.() -> Unit): Route = + header(HxRequestHeaders.Trigger, expectedTrigger, body) +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HxRoutingTest.kt b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HxRoutingTest.kt new file mode 100644 index 00000000000..99c0bffe02e --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HxRoutingTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.server.htmx + +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.htmx.* +import io.ktor.server.html.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.testing.* +import kotlinx.html.body +import kotlinx.html.h1 +import kotlin.test.Test +import kotlin.test.assertEquals + +class HxRoutingTest { + + @OptIn(ExperimentalHtmxApi::class) + @Test + fun routing() = testApplication { + val responseTemplate: (String) -> String = { header -> + """ + + + +

$header

+ + + """.trimIndent() + } + + routing { + val respondWith: suspend RoutingContext.(String) -> Unit = { text -> + call.respondHtml { + body { + h1 { +text } + } + } + } + route("htmx") { + get { + call.respondText { "Not HTMX" } + } + hx.get { + respondWith("No target or trigger") + } + hx { + target("#test") { + get { + respondWith("With target") + } + } + trigger("#button") { + get { + respondWith("With trigger") + } + } + } + } + } + assertEquals( + "Not HTMX", + client.get("htmx").bodyAsText().trim() + ) + assertEquals( + responseTemplate("No target or trigger"), + client.get("htmx") { + headers[HxRequestHeaders.Request] = "true" + }.bodyAsText().trim() + ) + assertEquals( + responseTemplate("With target"), + client.get("htmx") { + headers[HxRequestHeaders.Request] = "true" + headers[HxRequestHeaders.Target] = "#test" + }.bodyAsText().trim() + ) + assertEquals( + responseTemplate("With trigger"), + client.get("htmx") { + headers[HxRequestHeaders.Request] = "true" + headers[HxRequestHeaders.Trigger] = "#button" + }.bodyAsText().trim() + ) + } +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/gradle.properties b/ktor-server/ktor-server-plugins/ktor-server-htmx/gradle.properties new file mode 100644 index 00000000000..9733d692363 --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/gradle.properties @@ -0,0 +1,6 @@ +# +# Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# + +# Needs support in kotlinx-html: https://github.com/Kotlin/kotlinx.html/pull/288 +target.androidNative=false diff --git a/ktor-shared/ktor-htmx/api/ktor-htmx.api b/ktor-shared/ktor-htmx/api/ktor-htmx.api new file mode 100644 index 00000000000..3daa4e06074 --- /dev/null +++ b/ktor-shared/ktor-htmx/api/ktor-htmx.api @@ -0,0 +1,137 @@ +public abstract interface annotation class io/ktor/htmx/ExperimentalHtmxApi : java/lang/annotation/Annotation { +} + +public final class io/ktor/htmx/HxAttributeKeys { + public static final field Boost Ljava/lang/String; + public static final field Confirm Ljava/lang/String; + public static final field Delete Ljava/lang/String; + public static final field Disable Ljava/lang/String; + public static final field DisabledElt Ljava/lang/String; + public static final field Disinherit Ljava/lang/String; + public static final field Encoding Ljava/lang/String; + public static final field Ext Ljava/lang/String; + public static final field Get Ljava/lang/String; + public static final field Headers Ljava/lang/String; + public static final field History Ljava/lang/String; + public static final field HistoryElt Ljava/lang/String; + public static final field INSTANCE Lio/ktor/htmx/HxAttributeKeys; + public static final field Include Ljava/lang/String; + public static final field Indicator Ljava/lang/String; + public static final field Inherit Ljava/lang/String; + public static final field On Ljava/lang/String; + public static final field Params Ljava/lang/String; + public static final field Patch Ljava/lang/String; + public static final field Post Ljava/lang/String; + public static final field Preserve Ljava/lang/String; + public static final field Prompt Ljava/lang/String; + public static final field PushUrl Ljava/lang/String; + public static final field Put Ljava/lang/String; + public static final field ReplaceUrl Ljava/lang/String; + public static final field Request Ljava/lang/String; + public static final field Select Ljava/lang/String; + public static final field SelectOob Ljava/lang/String; + public static final field Swap Ljava/lang/String; + public static final field SwapOob Ljava/lang/String; + public static final field Sync Ljava/lang/String; + public static final field Target Ljava/lang/String; + public static final field Trigger Ljava/lang/String; + public static final field Validate Ljava/lang/String; + public static final field Vals Ljava/lang/String; + public static final field Vars Ljava/lang/String; +} + +public final class io/ktor/htmx/HxCss { + public static final field Added Ljava/lang/String; + public static final field INSTANCE Lio/ktor/htmx/HxCss; + public static final field Indicator Ljava/lang/String; + public static final field Request Ljava/lang/String; + public static final field Settling Ljava/lang/String; + public static final field Swapping Ljava/lang/String; +} + +public final class io/ktor/htmx/HxEvents { + public static final field Abort Ljava/lang/String; + public static final field AfterOnLoad Ljava/lang/String; + public static final field AfterProcessNode Ljava/lang/String; + public static final field AfterRequest Ljava/lang/String; + public static final field AfterSettle Ljava/lang/String; + public static final field AfterSwap Ljava/lang/String; + public static final field BeforeCleanupElement Ljava/lang/String; + public static final field BeforeHistorySave Ljava/lang/String; + public static final field BeforeOnLoad Ljava/lang/String; + public static final field BeforeProcessNode Ljava/lang/String; + public static final field BeforeRequest Ljava/lang/String; + public static final field BeforeSend Ljava/lang/String; + public static final field BeforeSwap Ljava/lang/String; + public static final field ConfigRequest Ljava/lang/String; + public static final field Confirm Ljava/lang/String; + public static final field HistoryCacheError Ljava/lang/String; + public static final field HistoryCacheMiss Ljava/lang/String; + public static final field HistoryCacheMissError Ljava/lang/String; + public static final field HistoryCacheMissLoad Ljava/lang/String; + public static final field HistoryRestore Ljava/lang/String; + public static final field INSTANCE Lio/ktor/htmx/HxEvents; + public static final field Load Ljava/lang/String; + public static final field NoSseSourceError Ljava/lang/String; + public static final field OnLoadError Ljava/lang/String; + public static final field OobAfterSwap Ljava/lang/String; + public static final field OobBeforeSwap Ljava/lang/String; + public static final field OobErrorNoTarget Ljava/lang/String; + public static final field Prompt Ljava/lang/String; + public static final field PushedIntoHistory Ljava/lang/String; + public static final field ResponseError Ljava/lang/String; + public static final field SendError Ljava/lang/String; + public static final field SseError Ljava/lang/String; + public static final field SseOpen Ljava/lang/String; + public static final field SwapError Ljava/lang/String; + public static final field TargetError Ljava/lang/String; + public static final field Timeout Ljava/lang/String; + public static final field ValidationFailed Ljava/lang/String; + public static final field ValidationHalted Ljava/lang/String; + public static final field ValidationValidate Ljava/lang/String; + public static final field XhrAbort Ljava/lang/String; + public static final field XhrLoadend Ljava/lang/String; + public static final field XhrLoadstart Ljava/lang/String; + public static final field XhrProgress Ljava/lang/String; +} + +public final class io/ktor/htmx/HxRequestHeaders { + public static final field Boosted Ljava/lang/String; + public static final field CurrentUrl Ljava/lang/String; + public static final field HistoryRestoreRequest Ljava/lang/String; + public static final field INSTANCE Lio/ktor/htmx/HxRequestHeaders; + public static final field Prompt Ljava/lang/String; + public static final field Request Ljava/lang/String; + public static final field Target Ljava/lang/String; + public static final field Trigger Ljava/lang/String; + public static final field TriggerName Ljava/lang/String; +} + +public final class io/ktor/htmx/HxResponseHeaders { + public static final field INSTANCE Lio/ktor/htmx/HxResponseHeaders; + public static final field Location Ljava/lang/String; + public static final field PushUrl Ljava/lang/String; + public static final field Redirect Ljava/lang/String; + public static final field Refresh Ljava/lang/String; + public static final field ReplaceUrl Ljava/lang/String; + public static final field Reselect Ljava/lang/String; + public static final field Reswap Ljava/lang/String; + public static final field Retarget Ljava/lang/String; + public static final field Trigger Ljava/lang/String; + public static final field TriggerAfterSettle Ljava/lang/String; + public static final field TriggerAfterSwap Ljava/lang/String; +} + +public final class io/ktor/htmx/HxSwap { + public static final field INSTANCE Lio/ktor/htmx/HxSwap; + public static final field afterBegin Ljava/lang/String; + public static final field afterEnd Ljava/lang/String; + public static final field beforeBegin Ljava/lang/String; + public static final field beforeEnd Ljava/lang/String; + public static final field delete Ljava/lang/String; + public static final field innerHtml Ljava/lang/String; + public static final field none Ljava/lang/String; + public static final field outerHtml Ljava/lang/String; + public static final field textContent Ljava/lang/String; +} + diff --git a/ktor-shared/ktor-htmx/api/ktor-htmx.klib.api b/ktor-shared/ktor-htmx/api/ktor-htmx.klib.api new file mode 100644 index 00000000000..1e90e21b32f --- /dev/null +++ b/ktor-shared/ktor-htmx/api/ktor-htmx.klib.api @@ -0,0 +1,249 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +open annotation class io.ktor.htmx/ExperimentalHtmxApi : kotlin/Annotation { // io.ktor.htmx/ExperimentalHtmxApi|null[0] + constructor () // io.ktor.htmx/ExperimentalHtmxApi.|(){}[0] +} + +final object io.ktor.htmx/HxAttributeKeys { // io.ktor.htmx/HxAttributeKeys|null[0] + final const val Boost // io.ktor.htmx/HxAttributeKeys.Boost|{}Boost[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Boost.|(){}[0] + final const val Confirm // io.ktor.htmx/HxAttributeKeys.Confirm|{}Confirm[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Confirm.|(){}[0] + final const val Delete // io.ktor.htmx/HxAttributeKeys.Delete|{}Delete[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Delete.|(){}[0] + final const val Disable // io.ktor.htmx/HxAttributeKeys.Disable|{}Disable[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Disable.|(){}[0] + final const val DisabledElt // io.ktor.htmx/HxAttributeKeys.DisabledElt|{}DisabledElt[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.DisabledElt.|(){}[0] + final const val Disinherit // io.ktor.htmx/HxAttributeKeys.Disinherit|{}Disinherit[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Disinherit.|(){}[0] + final const val Encoding // io.ktor.htmx/HxAttributeKeys.Encoding|{}Encoding[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Encoding.|(){}[0] + final const val Ext // io.ktor.htmx/HxAttributeKeys.Ext|{}Ext[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Ext.|(){}[0] + final const val Get // io.ktor.htmx/HxAttributeKeys.Get|{}Get[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Get.|(){}[0] + final const val Headers // io.ktor.htmx/HxAttributeKeys.Headers|{}Headers[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Headers.|(){}[0] + final const val History // io.ktor.htmx/HxAttributeKeys.History|{}History[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.History.|(){}[0] + final const val HistoryElt // io.ktor.htmx/HxAttributeKeys.HistoryElt|{}HistoryElt[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.HistoryElt.|(){}[0] + final const val Include // io.ktor.htmx/HxAttributeKeys.Include|{}Include[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Include.|(){}[0] + final const val Indicator // io.ktor.htmx/HxAttributeKeys.Indicator|{}Indicator[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Indicator.|(){}[0] + final const val Inherit // io.ktor.htmx/HxAttributeKeys.Inherit|{}Inherit[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Inherit.|(){}[0] + final const val On // io.ktor.htmx/HxAttributeKeys.On|{}On[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.On.|(){}[0] + final const val Params // io.ktor.htmx/HxAttributeKeys.Params|{}Params[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Params.|(){}[0] + final const val Patch // io.ktor.htmx/HxAttributeKeys.Patch|{}Patch[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Patch.|(){}[0] + final const val Post // io.ktor.htmx/HxAttributeKeys.Post|{}Post[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Post.|(){}[0] + final const val Preserve // io.ktor.htmx/HxAttributeKeys.Preserve|{}Preserve[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Preserve.|(){}[0] + final const val Prompt // io.ktor.htmx/HxAttributeKeys.Prompt|{}Prompt[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Prompt.|(){}[0] + final const val PushUrl // io.ktor.htmx/HxAttributeKeys.PushUrl|{}PushUrl[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.PushUrl.|(){}[0] + final const val Put // io.ktor.htmx/HxAttributeKeys.Put|{}Put[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Put.|(){}[0] + final const val ReplaceUrl // io.ktor.htmx/HxAttributeKeys.ReplaceUrl|{}ReplaceUrl[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.ReplaceUrl.|(){}[0] + final const val Request // io.ktor.htmx/HxAttributeKeys.Request|{}Request[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Request.|(){}[0] + final const val Select // io.ktor.htmx/HxAttributeKeys.Select|{}Select[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Select.|(){}[0] + final const val SelectOob // io.ktor.htmx/HxAttributeKeys.SelectOob|{}SelectOob[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.SelectOob.|(){}[0] + final const val Swap // io.ktor.htmx/HxAttributeKeys.Swap|{}Swap[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Swap.|(){}[0] + final const val SwapOob // io.ktor.htmx/HxAttributeKeys.SwapOob|{}SwapOob[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.SwapOob.|(){}[0] + final const val Sync // io.ktor.htmx/HxAttributeKeys.Sync|{}Sync[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Sync.|(){}[0] + final const val Target // io.ktor.htmx/HxAttributeKeys.Target|{}Target[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Target.|(){}[0] + final const val Trigger // io.ktor.htmx/HxAttributeKeys.Trigger|{}Trigger[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Trigger.|(){}[0] + final const val Validate // io.ktor.htmx/HxAttributeKeys.Validate|{}Validate[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Validate.|(){}[0] + final const val Vals // io.ktor.htmx/HxAttributeKeys.Vals|{}Vals[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Vals.|(){}[0] + final const val Vars // io.ktor.htmx/HxAttributeKeys.Vars|{}Vars[0] + final fun (): kotlin/String // io.ktor.htmx/HxAttributeKeys.Vars.|(){}[0] +} + +final object io.ktor.htmx/HxCss { // io.ktor.htmx/HxCss|null[0] + final const val Added // io.ktor.htmx/HxCss.Added|{}Added[0] + final fun (): kotlin/String // io.ktor.htmx/HxCss.Added.|(){}[0] + final const val Indicator // io.ktor.htmx/HxCss.Indicator|{}Indicator[0] + final fun (): kotlin/String // io.ktor.htmx/HxCss.Indicator.|(){}[0] + final const val Request // io.ktor.htmx/HxCss.Request|{}Request[0] + final fun (): kotlin/String // io.ktor.htmx/HxCss.Request.|(){}[0] + final const val Settling // io.ktor.htmx/HxCss.Settling|{}Settling[0] + final fun (): kotlin/String // io.ktor.htmx/HxCss.Settling.|(){}[0] + final const val Swapping // io.ktor.htmx/HxCss.Swapping|{}Swapping[0] + final fun (): kotlin/String // io.ktor.htmx/HxCss.Swapping.|(){}[0] +} + +final object io.ktor.htmx/HxEvents { // io.ktor.htmx/HxEvents|null[0] + final const val Abort // io.ktor.htmx/HxEvents.Abort|{}Abort[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.Abort.|(){}[0] + final const val AfterOnLoad // io.ktor.htmx/HxEvents.AfterOnLoad|{}AfterOnLoad[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.AfterOnLoad.|(){}[0] + final const val AfterProcessNode // io.ktor.htmx/HxEvents.AfterProcessNode|{}AfterProcessNode[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.AfterProcessNode.|(){}[0] + final const val AfterRequest // io.ktor.htmx/HxEvents.AfterRequest|{}AfterRequest[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.AfterRequest.|(){}[0] + final const val AfterSettle // io.ktor.htmx/HxEvents.AfterSettle|{}AfterSettle[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.AfterSettle.|(){}[0] + final const val AfterSwap // io.ktor.htmx/HxEvents.AfterSwap|{}AfterSwap[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.AfterSwap.|(){}[0] + final const val BeforeCleanupElement // io.ktor.htmx/HxEvents.BeforeCleanupElement|{}BeforeCleanupElement[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeCleanupElement.|(){}[0] + final const val BeforeHistorySave // io.ktor.htmx/HxEvents.BeforeHistorySave|{}BeforeHistorySave[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeHistorySave.|(){}[0] + final const val BeforeOnLoad // io.ktor.htmx/HxEvents.BeforeOnLoad|{}BeforeOnLoad[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeOnLoad.|(){}[0] + final const val BeforeProcessNode // io.ktor.htmx/HxEvents.BeforeProcessNode|{}BeforeProcessNode[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeProcessNode.|(){}[0] + final const val BeforeRequest // io.ktor.htmx/HxEvents.BeforeRequest|{}BeforeRequest[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeRequest.|(){}[0] + final const val BeforeSend // io.ktor.htmx/HxEvents.BeforeSend|{}BeforeSend[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeSend.|(){}[0] + final const val BeforeSwap // io.ktor.htmx/HxEvents.BeforeSwap|{}BeforeSwap[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.BeforeSwap.|(){}[0] + final const val ConfigRequest // io.ktor.htmx/HxEvents.ConfigRequest|{}ConfigRequest[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.ConfigRequest.|(){}[0] + final const val Confirm // io.ktor.htmx/HxEvents.Confirm|{}Confirm[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.Confirm.|(){}[0] + final const val HistoryCacheError // io.ktor.htmx/HxEvents.HistoryCacheError|{}HistoryCacheError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.HistoryCacheError.|(){}[0] + final const val HistoryCacheMiss // io.ktor.htmx/HxEvents.HistoryCacheMiss|{}HistoryCacheMiss[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.HistoryCacheMiss.|(){}[0] + final const val HistoryCacheMissError // io.ktor.htmx/HxEvents.HistoryCacheMissError|{}HistoryCacheMissError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.HistoryCacheMissError.|(){}[0] + final const val HistoryCacheMissLoad // io.ktor.htmx/HxEvents.HistoryCacheMissLoad|{}HistoryCacheMissLoad[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.HistoryCacheMissLoad.|(){}[0] + final const val HistoryRestore // io.ktor.htmx/HxEvents.HistoryRestore|{}HistoryRestore[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.HistoryRestore.|(){}[0] + final const val Load // io.ktor.htmx/HxEvents.Load|{}Load[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.Load.|(){}[0] + final const val NoSseSourceError // io.ktor.htmx/HxEvents.NoSseSourceError|{}NoSseSourceError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.NoSseSourceError.|(){}[0] + final const val OnLoadError // io.ktor.htmx/HxEvents.OnLoadError|{}OnLoadError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.OnLoadError.|(){}[0] + final const val OobAfterSwap // io.ktor.htmx/HxEvents.OobAfterSwap|{}OobAfterSwap[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.OobAfterSwap.|(){}[0] + final const val OobBeforeSwap // io.ktor.htmx/HxEvents.OobBeforeSwap|{}OobBeforeSwap[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.OobBeforeSwap.|(){}[0] + final const val OobErrorNoTarget // io.ktor.htmx/HxEvents.OobErrorNoTarget|{}OobErrorNoTarget[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.OobErrorNoTarget.|(){}[0] + final const val Prompt // io.ktor.htmx/HxEvents.Prompt|{}Prompt[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.Prompt.|(){}[0] + final const val PushedIntoHistory // io.ktor.htmx/HxEvents.PushedIntoHistory|{}PushedIntoHistory[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.PushedIntoHistory.|(){}[0] + final const val ResponseError // io.ktor.htmx/HxEvents.ResponseError|{}ResponseError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.ResponseError.|(){}[0] + final const val SendError // io.ktor.htmx/HxEvents.SendError|{}SendError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.SendError.|(){}[0] + final const val SseError // io.ktor.htmx/HxEvents.SseError|{}SseError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.SseError.|(){}[0] + final const val SseOpen // io.ktor.htmx/HxEvents.SseOpen|{}SseOpen[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.SseOpen.|(){}[0] + final const val SwapError // io.ktor.htmx/HxEvents.SwapError|{}SwapError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.SwapError.|(){}[0] + final const val TargetError // io.ktor.htmx/HxEvents.TargetError|{}TargetError[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.TargetError.|(){}[0] + final const val Timeout // io.ktor.htmx/HxEvents.Timeout|{}Timeout[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.Timeout.|(){}[0] + final const val ValidationFailed // io.ktor.htmx/HxEvents.ValidationFailed|{}ValidationFailed[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.ValidationFailed.|(){}[0] + final const val ValidationHalted // io.ktor.htmx/HxEvents.ValidationHalted|{}ValidationHalted[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.ValidationHalted.|(){}[0] + final const val ValidationValidate // io.ktor.htmx/HxEvents.ValidationValidate|{}ValidationValidate[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.ValidationValidate.|(){}[0] + final const val XhrAbort // io.ktor.htmx/HxEvents.XhrAbort|{}XhrAbort[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.XhrAbort.|(){}[0] + final const val XhrLoadend // io.ktor.htmx/HxEvents.XhrLoadend|{}XhrLoadend[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.XhrLoadend.|(){}[0] + final const val XhrLoadstart // io.ktor.htmx/HxEvents.XhrLoadstart|{}XhrLoadstart[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.XhrLoadstart.|(){}[0] + final const val XhrProgress // io.ktor.htmx/HxEvents.XhrProgress|{}XhrProgress[0] + final fun (): kotlin/String // io.ktor.htmx/HxEvents.XhrProgress.|(){}[0] +} + +final object io.ktor.htmx/HxRequestHeaders { // io.ktor.htmx/HxRequestHeaders|null[0] + final const val Boosted // io.ktor.htmx/HxRequestHeaders.Boosted|{}Boosted[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.Boosted.|(){}[0] + final const val CurrentUrl // io.ktor.htmx/HxRequestHeaders.CurrentUrl|{}CurrentUrl[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.CurrentUrl.|(){}[0] + final const val HistoryRestoreRequest // io.ktor.htmx/HxRequestHeaders.HistoryRestoreRequest|{}HistoryRestoreRequest[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.HistoryRestoreRequest.|(){}[0] + final const val Prompt // io.ktor.htmx/HxRequestHeaders.Prompt|{}Prompt[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.Prompt.|(){}[0] + final const val Request // io.ktor.htmx/HxRequestHeaders.Request|{}Request[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.Request.|(){}[0] + final const val Target // io.ktor.htmx/HxRequestHeaders.Target|{}Target[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.Target.|(){}[0] + final const val Trigger // io.ktor.htmx/HxRequestHeaders.Trigger|{}Trigger[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.Trigger.|(){}[0] + final const val TriggerName // io.ktor.htmx/HxRequestHeaders.TriggerName|{}TriggerName[0] + final fun (): kotlin/String // io.ktor.htmx/HxRequestHeaders.TriggerName.|(){}[0] +} + +final object io.ktor.htmx/HxResponseHeaders { // io.ktor.htmx/HxResponseHeaders|null[0] + final const val Location // io.ktor.htmx/HxResponseHeaders.Location|{}Location[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Location.|(){}[0] + final const val PushUrl // io.ktor.htmx/HxResponseHeaders.PushUrl|{}PushUrl[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.PushUrl.|(){}[0] + final const val Redirect // io.ktor.htmx/HxResponseHeaders.Redirect|{}Redirect[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Redirect.|(){}[0] + final const val Refresh // io.ktor.htmx/HxResponseHeaders.Refresh|{}Refresh[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Refresh.|(){}[0] + final const val ReplaceUrl // io.ktor.htmx/HxResponseHeaders.ReplaceUrl|{}ReplaceUrl[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.ReplaceUrl.|(){}[0] + final const val Reselect // io.ktor.htmx/HxResponseHeaders.Reselect|{}Reselect[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Reselect.|(){}[0] + final const val Reswap // io.ktor.htmx/HxResponseHeaders.Reswap|{}Reswap[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Reswap.|(){}[0] + final const val Retarget // io.ktor.htmx/HxResponseHeaders.Retarget|{}Retarget[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Retarget.|(){}[0] + final const val Trigger // io.ktor.htmx/HxResponseHeaders.Trigger|{}Trigger[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.Trigger.|(){}[0] + final const val TriggerAfterSettle // io.ktor.htmx/HxResponseHeaders.TriggerAfterSettle|{}TriggerAfterSettle[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.TriggerAfterSettle.|(){}[0] + final const val TriggerAfterSwap // io.ktor.htmx/HxResponseHeaders.TriggerAfterSwap|{}TriggerAfterSwap[0] + final fun (): kotlin/String // io.ktor.htmx/HxResponseHeaders.TriggerAfterSwap.|(){}[0] +} + +final object io.ktor.htmx/HxSwap { // io.ktor.htmx/HxSwap|null[0] + final const val afterBegin // io.ktor.htmx/HxSwap.afterBegin|{}afterBegin[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.afterBegin.|(){}[0] + final const val afterEnd // io.ktor.htmx/HxSwap.afterEnd|{}afterEnd[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.afterEnd.|(){}[0] + final const val beforeBegin // io.ktor.htmx/HxSwap.beforeBegin|{}beforeBegin[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.beforeBegin.|(){}[0] + final const val beforeEnd // io.ktor.htmx/HxSwap.beforeEnd|{}beforeEnd[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.beforeEnd.|(){}[0] + final const val delete // io.ktor.htmx/HxSwap.delete|{}delete[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.delete.|(){}[0] + final const val innerHtml // io.ktor.htmx/HxSwap.innerHtml|{}innerHtml[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.innerHtml.|(){}[0] + final const val none // io.ktor.htmx/HxSwap.none|{}none[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.none.|(){}[0] + final const val outerHtml // io.ktor.htmx/HxSwap.outerHtml|{}outerHtml[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.outerHtml.|(){}[0] + final const val textContent // io.ktor.htmx/HxSwap.textContent|{}textContent[0] + final fun (): kotlin/String // io.ktor.htmx/HxSwap.textContent.|(){}[0] +} diff --git a/ktor-shared/ktor-htmx/build.gradle.kts b/ktor-shared/ktor-htmx/build.gradle.kts new file mode 100644 index 00000000000..1b9815283da --- /dev/null +++ b/ktor-shared/ktor-htmx/build.gradle.kts @@ -0,0 +1,5 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +description = "Common HTMX constants for use in client and server" diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt new file mode 100644 index 00000000000..a1fbae2c233 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.htmx + +@MustBeDocumented @Retention @RequiresOptIn +public annotation class ExperimentalHtmxApi diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxAttributeKeys.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxAttributeKeys.kt new file mode 100644 index 00000000000..d4f4dbb3832 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxAttributeKeys.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("ConstPropertyName") + +package io.ktor.htmx + +/** + * Attribute constants that are used with HTMX. + * + * @see [Official documentation](https://htmx.org/reference/#attributes-additional) + */ +public object HxAttributeKeys { + /** + * Issues a GET to the specified URL. + */ + public const val Get: String = "hx-get" + + /** + * Issues a POST to the specified URL. + */ + public const val Post: String = "hx-post" + + /** + * Handles events with inline scripts on elements. + */ + public const val On: String = "hx-on" + + /** + * Pushes a URL into the browser location bar to create history. + */ + public const val PushUrl: String = "hx-push-url" + + /** + * Selects content to swap in from a response. + */ + public const val Select: String = "hx-select" + + /** + * Selects content to swap in from a response, somewhere other than the target (out of band). + */ + public const val SelectOob: String = "hx-select-oob" + + /** + * Controls how content will swap in (outerHTML, beforeend, afterend, …). + */ + public const val Swap: String = "hx-swap" + + /** + * Marks element to swap in from a response (out of band). + */ + public const val SwapOob: String = "hx-swap-oob" + + /** + * Specifies the target element to be swapped. + */ + public const val Target: String = "hx-target" + + /** + * Specifies the event that triggers the request. + */ + public const val Trigger: String = "hx-trigger" + + /** + * Adds values to submit with the request (JSON format). + */ + public const val Vals: String = "hx-vals" + + /** + * Adds progressive enhancement for links and forms. + */ + public const val Boost: String = "hx-boost" + + /** + * Shows a confirm() dialog before issuing a request. + */ + public const val Confirm: String = "hx-confirm" + + /** + * Issues a DELETE to the specified URL. + */ + public const val Delete: String = "hx-delete" + + /** + * Disables htmx processing for the given node and any children nodes. + */ + public const val Disable: String = "hx-disable" + + /** + * Adds the disabled attribute to the specified elements while a request is in flight. + */ + public const val DisabledElt: String = "hx-disabled-elt" + + /** + * Controls and disables automatic attribute inheritance for child nodes. + */ + public const val Disinherit: String = "hx-disinherit" + + /** + * Changes the request encoding type. + */ + public const val Encoding: String = "hx-encoding" + + /** + * Extensions to use for this element. + */ + public const val Ext: String = "hx-ext" + + /** + * Adds to the headers that will be submitted with the request. + */ + public const val Headers: String = "hx-headers" + + /** + * Prevents sensitive data from being saved to the history cache. + */ + public const val History: String = "hx-history" + + /** + * Specifies the element to snapshot and restore during history navigation. + */ + public const val HistoryElt: String = "hx-history-elt" + + /** + * Includes additional data in requests. + */ + public const val Include: String = "hx-include" + + /** + * Specifies the element to put the htmx-request class on during the request. + */ + public const val Indicator: String = "hx-indicator" + + /** + * Controls and enables automatic attribute inheritance for child nodes if it has been disabled by default. + */ + public const val Inherit: String = "hx-inherit" + + /** + * Filters the parameters that will be submitted with a request. + */ + public const val Params: String = "hx-params" + + /** + * Issues a PATCH to the specified URL. + */ + public const val Patch: String = "hx-patch" + + /** + * Specifies elements to keep unchanged between requests. + */ + public const val Preserve: String = "hx-preserve" + + /** + * Shows a prompt() before submitting a request. + */ + public const val Prompt: String = "hx-prompt" + + /** + * Issues a PUT to the specified URL. + */ + public const val Put: String = "hx-put" + + /** + * Replaces the URL in the browser location bar. + */ + public const val ReplaceUrl: String = "hx-replace-url" + + /** + * Configures various aspects of the request. + */ + public const val Request: String = "hx-request" + + /** + * Controls how requests made by different elements are synchronized. + */ + public const val Sync: String = "hx-sync" + + /** + * Forces elements to validate themselves before a request. + */ + public const val Validate: String = "hx-validate" + + /** + * Adds values dynamically to the parameters to submit with the request (deprecated, please use hx-vals). + */ + public const val Vars: String = "hx-vars" +} diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxCss.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxCss.kt new file mode 100644 index 00000000000..0d130b38b25 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxCss.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("ConstPropertyName") + +package io.ktor.htmx + +/** + * Constants for HTMX CSS classes. + * + * @see [Official documentation](https://htmx.org/reference/#classes) + */ +public object HxCss { + /** + * Applied to a new piece of content before it is swapped, removed after it is settled. + */ + public const val Added: String = "htmx-added" + + /** + * A dynamically generated class that will toggle visible (opacity:1) when a htmx-request class is present + */ + public const val Indicator: String = "htmx-indicator" + + /** + * Applied to either the element or the element specified with hx-indicator while a request is ongoing + */ + public const val Request: String = "htmx-request" + + /** + * Applied to a target after content is swapped, removed after it is settled. The duration can be modified via hx-swap. + */ + public const val Settling: String = "htmx-settling" + + /** + * Applied to a target before any content is swapped, removed after it is swapped. The duration can be modified via hx-swap. + */ + public const val Swapping: String = "htmx-swapping" +} diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxEvents.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxEvents.kt new file mode 100644 index 00000000000..2bdebca0da3 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxEvents.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.htmx + +/** + * Constants for HTMX events. + * + * @see [Official documentation](https://htmx.org/events/) + */ +public object HxEvents { + /** + * Send this event to an element to abort a request. + */ + public const val Abort: String = "htmx:abort" + + /** + * Triggered after an AJAX request has completed processing a successful response. + */ + public const val AfterOnLoad: String = "htmx:afterOnLoad" + + /** + * Triggered after htmx has initialized a node. + */ + public const val AfterProcessNode: String = "htmx:afterProcessNode" + + /** + * Triggered after an AJAX request has completed. + */ + public const val AfterRequest: String = "htmx:afterRequest" + + /** + * Triggered after the DOM has settled. + */ + public const val AfterSettle: String = "htmx:afterSettle" + + /** + * Triggered after new content has been swapped in. + */ + public const val AfterSwap: String = "htmx:afterSwap" + + /** + * Triggered before htmx disables an element or removes it from the DOM. + */ + public const val BeforeCleanupElement: String = "htmx:beforeCleanupElement" + + /** + * Triggered before any response processing occurs. + */ + public const val BeforeOnLoad: String = "htmx:beforeOnLoad" + + /** + * Triggered before htmx initializes a node. + */ + public const val BeforeProcessNode: String = "htmx:beforeProcessNode" + + /** + * Triggered before an AJAX request is made. + */ + public const val BeforeRequest: String = "htmx:beforeRequest" + + /** + * Triggered before a swap is done, allows you to configure the swap. + */ + public const val BeforeSwap: String = "htmx:beforeSwap" + + /** + * Triggered just before an AJAX request is sent. + */ + public const val BeforeSend: String = "htmx:beforeSend" + + /** + * Triggered before the request, allows you to customize parameters, headers. + */ + public const val ConfigRequest: String = "htmx:configRequest" + + /** + * Triggered after a trigger occurs on an element, allows you to cancel (or delay) issuing the AJAX request. + */ + public const val Confirm: String = "htmx:confirm" + + /** + * Triggered on an error during cache writing. + */ + public const val HistoryCacheError: String = "htmx:historyCacheError" + + /** + * Triggered on a cache miss in the history subsystem. + */ + public const val HistoryCacheMiss: String = "htmx:historyCacheMiss" + + /** + * Triggered on an unsuccessful remote retrieval. + */ + public const val HistoryCacheMissError: String = "htmx:historyCacheMissError" + + /** + * Triggered on a successful remote retrieval. + */ + public const val HistoryCacheMissLoad: String = "htmx:historyCacheMissLoad" + + /** + * Triggered when htmx handles a history restoration action. + */ + public const val HistoryRestore: String = "htmx:historyRestore" + + /** + * Triggered before content is saved to the history cache. + */ + public const val BeforeHistorySave: String = "htmx:beforeHistorySave" + + /** + * Triggered when new content is added to the DOM. + */ + public const val Load: String = "htmx:load" + + /** + * Triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined. + */ + public const val NoSseSourceError: String = "htmx:noSSESourceError" + + /** + * Triggered when an exception occurs during the onLoad handling in htmx. + */ + public const val OnLoadError: String = "htmx:onLoadError" + + /** + * Triggered after an out of band element has been swapped in. + */ + public const val OobAfterSwap: String = "htmx:oobAfterSwap" + + /** + * Triggered before an out of band element swap is done, allows you to configure the swap. + */ + public const val OobBeforeSwap: String = "htmx:oobBeforeSwap" + + /** + * Triggered when an out of band element does not have a matching ID in the current DOM. + */ + public const val OobErrorNoTarget: String = "htmx:oobErrorNoTarget" + + /** + * Triggered after a prompt is shown. + */ + public const val Prompt: String = "htmx:prompt" + + /** + * Triggered after a URL is pushed into history. + */ + public const val PushedIntoHistory: String = "htmx:pushedIntoHistory" + + /** + * Triggered when an HTTP response error (non-200 or 300 response code) occurs. + */ + public const val ResponseError: String = "htmx:responseError" + + /** + * Triggered when a network error prevents an HTTP request from happening. + */ + public const val SendError: String = "htmx:sendError" + + /** + * Triggered when an error occurs with a SSE source. + */ + public const val SseError: String = "htmx:sseError" + + /** + * Triggered when a SSE source is opened. + */ + public const val SseOpen: String = "htmx:sseOpen" + + /** + * Triggered when an error occurs during the swap phase. + */ + public const val SwapError: String = "htmx:swapError" + + /** + * Triggered when an invalid target is specified. + */ + public const val TargetError: String = "htmx:targetError" + + /** + * Triggered when a request timeout occurs. + */ + public const val Timeout: String = "htmx:timeout" + + /** + * Triggered before an element is validated. + */ + public const val ValidationValidate: String = "htmx:validation:validate" + + /** + * Triggered when an element fails validation. + */ + public const val ValidationFailed: String = "htmx:validation:failed" + + /** + * Triggered when a request is halted due to validation errors. + */ + public const val ValidationHalted: String = "htmx:validation:halted" + + /** + * Triggered when an AJAX request aborts. + */ + public const val XhrAbort: String = "htmx:xhr:abort" + + /** + * Triggered when an AJAX request ends. + */ + public const val XhrLoadend: String = "htmx:xhr:loadend" + + /** + * Triggered when an AJAX request starts. + */ + public const val XhrLoadstart: String = "htmx:xhr:loadstart" + + /** + * Triggered periodically during an AJAX request that supports progress events. + */ + public const val XhrProgress: String = "htmx:xhr:progress" +} diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxRequestHeaders.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxRequestHeaders.kt new file mode 100644 index 00000000000..efea5390b00 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxRequestHeaders.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("ConstPropertyName") + +package io.ktor.htmx + +/** + * Constants for HTMX request headers. + * + * @see [Official documentation](https://htmx.org/events/) + */ +public object HxRequestHeaders { + /** + * Indicates that the request is via an element using hx-boost. + */ + public const val Boosted: String = "HX-Boosted" + + /** + * The current URL of the browser. + */ + public const val CurrentUrl: String = "HX-Current-URL" + + /** + * “True” if the request is for history restoration after a miss in the local history cache. + */ + public const val HistoryRestoreRequest: String = "HX-History-Restore-Request" + + /** + * The user response to an hx-prompt. + */ + public const val Prompt: String = "HX-Prompt" + + /** + * Always “true”. + */ + public const val Request: String = "HX-Request" + + /** + * The id of the target element if it exists. + */ + public const val Target: String = "HX-Target" + + /** + * The name of the triggered element if it exists. + */ + public const val TriggerName: String = "HX-Trigger-Name" + + /** + * The id of the triggered element if it exists. + */ + public const val Trigger: String = "HX-Trigger" +} diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxResponseHeaders.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxResponseHeaders.kt new file mode 100644 index 00000000000..661a5a71b24 --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxResponseHeaders.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("ConstPropertyName") + +package io.ktor.htmx + +/** + * Constants for HTMX response headers. + * + * @see [Official documentation](https://htmx.org/reference/#response_headers) + */ +public object HxResponseHeaders { + /** + * Allows you to do a client-side redirect that does not do a full page reload. + */ + public const val Location: String = "HX-Location" + + /** + * Pushes a new URL into the history stack. + */ + public const val PushUrl: String = "HX-Push-Url" + + /** + * Can be used to do a client-side redirect to a new location. + */ + public const val Redirect: String = "HX-Redirect" + + /** + * If set to “true” the client-side will do a full refresh of the page. + */ + public const val Refresh: String = "HX-Refresh" + + /** + * Replaces the current URL in the location bar. + */ + public const val ReplaceUrl: String = "HX-Replace-Url" + + /** + * Allows you to specify how the response will be swapped. See hx-swap for possible values. + */ + public const val Reswap: String = "HX-Reswap" + + /** + * A CSS selector that updates the target of the content update to a different element on the page. + */ + public const val Retarget: String = "HX-Retarget" + + /** + * A CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing hx-select on the triggering element. + */ + public const val Reselect: String = "HX-Reselect" + + /** + * Allows you to trigger client-side events. + */ + public const val Trigger: String = "HX-Trigger" + + /** + * Allows you to trigger client-side events after the settle step. + */ + public const val TriggerAfterSettle: String = "HX-Trigger-After-Settle" + + /** + * Allows you to trigger client-side events after the swap step. + */ + public const val TriggerAfterSwap: String = "HX-Trigger-After-Swap" +} diff --git a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxSwap.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxSwap.kt new file mode 100644 index 00000000000..804dc681b3c --- /dev/null +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxSwap.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("ConstPropertyName") + +package io.ktor.htmx + +/** + * Constants for "hx-swap" values. + * + * @see [Official documentation](https://htmx.org/attributes/hx-swap/) + */ +public object HxSwap { + /** + * Replace the inner HTML of the target element + */ + public const val innerHtml: String = "innerHtml" + + /** + * Replace the entire target element with the response + */ + public const val outerHtml: String = "outerHTML" + + /** + * Replace the text content of the target element, without parsing the response as HTML + */ + public const val textContent: String = "textContent" + + /** + * Insert the response before the target element + */ + public const val beforeBegin: String = "beforebegin" + + /** + * Insert the response before the first child of the target element + */ + public const val afterBegin: String = "afterbegin" + + /** + * Insert the response after the last child of the target element + */ + public const val beforeEnd: String = "beforeend" + + /** + * Insert the response after the target element + */ + public const val afterEnd: String = "afterend" + + /** + * Deletes the target element regardless of the response + */ + public const val delete: String = "delete" + + /** + * Does not append content from response (out of band items will still be processed) + */ + public const val none: String = "none" +} diff --git a/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.api b/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.api new file mode 100644 index 00000000000..5e5ef10700a --- /dev/null +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.api @@ -0,0 +1,51 @@ +public final class io/ktor/htmx/html/HxAttributes : io/ktor/util/collections/StringMapDelegate { + public fun (Lkotlinx/html/impl/DelegatingMap;)V + public fun get (Ljava/lang/String;)Ljava/lang/String; + public final fun getGet ()Ljava/lang/String; + public synthetic fun getMap ()Ljava/util/Map; + public fun getMap ()Lkotlinx/html/impl/DelegatingMap; + public final fun getOn-6sA4otk ()Ljava/util/Map; + public final fun getPost ()Ljava/lang/String; + public final fun getPushUrl ()Ljava/lang/String; + public final fun getSelect ()Ljava/lang/String; + public final fun getSelectOob ()Ljava/lang/String; + public final fun getSwap ()Ljava/lang/String; + public final fun getSwapOob ()Ljava/lang/String; + public final fun getTarget ()Ljava/lang/String; + public final fun getTrigger ()Ljava/lang/String; + public final fun getVals ()Ljava/lang/String; + public final fun on (Ljava/lang/String;Ljava/lang/String;)V + public fun remove (Ljava/lang/String;)Ljava/lang/String; + public fun set (Ljava/lang/String;Ljava/lang/String;)V + public final fun setGet (Ljava/lang/String;)V + public final fun setPost (Ljava/lang/String;)V + public final fun setPushUrl (Ljava/lang/String;)V + public final fun setSelect (Ljava/lang/String;)V + public final fun setSelectOob (Ljava/lang/String;)V + public final fun setSwap (Ljava/lang/String;)V + public final fun setSwapOob (Ljava/lang/String;)V + public final fun setTarget (Ljava/lang/String;)V + public final fun setTrigger (Ljava/lang/String;)V + public final fun setVals (Ljava/lang/String;)V +} + +public final class io/ktor/htmx/html/HxAttributes$On { + public static final synthetic fun box-impl (Ljava/util/Map;)Lio/ktor/htmx/html/HxAttributes$On; + public static fun constructor-impl (Ljava/util/Map;)Ljava/util/Map; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/util/Map;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/util/Map;Ljava/util/Map;)Z + public static final fun get-impl (Ljava/util/Map;Ljava/lang/String;)Ljava/lang/String; + public fun hashCode ()I + public static fun hashCode-impl (Ljava/util/Map;)I + public static final fun set-impl (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;)V + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ljava/util/Map;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/util/Map; +} + +public final class io/ktor/htmx/html/HxAttributesKt { + public static final fun getHx (Lkotlinx/html/impl/DelegatingMap;)Lio/ktor/htmx/html/HxAttributes; + public static final fun hx (Lkotlinx/html/impl/DelegatingMap;Lkotlin/jvm/functions/Function1;)V +} + diff --git a/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.klib.api b/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.klib.api new file mode 100644 index 00000000000..4b7d507dca9 --- /dev/null +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/api/ktor-htmx-html.klib.api @@ -0,0 +1,64 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class io.ktor.htmx.html/HxAttributes : io.ktor.util.collections/StringMapDelegate { // io.ktor.htmx.html/HxAttributes|null[0] + constructor (kotlinx.html.impl/DelegatingMap) // io.ktor.htmx.html/HxAttributes.|(kotlinx.html.impl.DelegatingMap){}[0] + + final val map // io.ktor.htmx.html/HxAttributes.map|{}map[0] + final fun (): kotlinx.html.impl/DelegatingMap // io.ktor.htmx.html/HxAttributes.map.|(){}[0] + final val on // io.ktor.htmx.html/HxAttributes.on|{}on[0] + final fun (): io.ktor.htmx.html/HxAttributes.On // io.ktor.htmx.html/HxAttributes.on.|(){}[0] + + final var get // io.ktor.htmx.html/HxAttributes.get|{}get[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.get.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.get.|(kotlin.String?){}[0] + final var post // io.ktor.htmx.html/HxAttributes.post|{}post[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.post.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.post.|(kotlin.String?){}[0] + final var pushUrl // io.ktor.htmx.html/HxAttributes.pushUrl|{}pushUrl[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.pushUrl.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.pushUrl.|(kotlin.String?){}[0] + final var select // io.ktor.htmx.html/HxAttributes.select|{}select[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.select.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.select.|(kotlin.String?){}[0] + final var selectOob // io.ktor.htmx.html/HxAttributes.selectOob|{}selectOob[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.selectOob.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.selectOob.|(kotlin.String?){}[0] + final var swap // io.ktor.htmx.html/HxAttributes.swap|{}swap[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.swap.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.swap.|(kotlin.String?){}[0] + final var swapOob // io.ktor.htmx.html/HxAttributes.swapOob|{}swapOob[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.swapOob.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.swapOob.|(kotlin.String?){}[0] + final var target // io.ktor.htmx.html/HxAttributes.target|{}target[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.target.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.target.|(kotlin.String?){}[0] + final var trigger // io.ktor.htmx.html/HxAttributes.trigger|{}trigger[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.trigger.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.trigger.|(kotlin.String?){}[0] + final var vals // io.ktor.htmx.html/HxAttributes.vals|{}vals[0] + final fun (): kotlin/String? // io.ktor.htmx.html/HxAttributes.vals.|(){}[0] + final fun (kotlin/String?) // io.ktor.htmx.html/HxAttributes.vals.|(kotlin.String?){}[0] + + final fun on(kotlin/String, kotlin/String) // io.ktor.htmx.html/HxAttributes.on|on(kotlin.String;kotlin.String){}[0] + + final value class On { // io.ktor.htmx.html/HxAttributes.On|null[0] + constructor (kotlin.collections/MutableMap) // io.ktor.htmx.html/HxAttributes.On.|(kotlin.collections.MutableMap){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.htmx.html/HxAttributes.On.equals|equals(kotlin.Any?){}[0] + final fun get(kotlin/String): kotlin/String? // io.ktor.htmx.html/HxAttributes.On.get|get(kotlin.String){}[0] + final fun hashCode(): kotlin/Int // io.ktor.htmx.html/HxAttributes.On.hashCode|hashCode(){}[0] + final fun set(kotlin/String, kotlin/String?) // io.ktor.htmx.html/HxAttributes.On.set|set(kotlin.String;kotlin.String?){}[0] + final fun toString(): kotlin/String // io.ktor.htmx.html/HxAttributes.On.toString|toString(){}[0] + } +} + +final val io.ktor.htmx.html/hx // io.ktor.htmx.html/hx|@kotlinx.html.impl.DelegatingMap{}hx[0] + final fun (kotlinx.html.impl/DelegatingMap).(): io.ktor.htmx.html/HxAttributes // io.ktor.htmx.html/hx.|@kotlinx.html.impl.DelegatingMap(){}[0] + +final inline fun (kotlinx.html.impl/DelegatingMap).io.ktor.htmx.html/hx(kotlin/Function1) // io.ktor.htmx.html/hx|hx@kotlinx.html.impl.DelegatingMap(kotlin.Function1){}[0] diff --git a/ktor-shared/ktor-htmx/ktor-htmx-html/build.gradle.kts b/ktor-shared/ktor-htmx/ktor-htmx-html/build.gradle.kts new file mode 100644 index 00000000000..cbeed0c2b3c --- /dev/null +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/build.gradle.kts @@ -0,0 +1,16 @@ +/* + * Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +description = "HTMX support for the Kotlin HTML DSL" + +kotlin.sourceSets { + commonMain { + dependencies { + api(libs.kotlinx.html) + api(project(":ktor-shared:ktor-htmx")) + implementation(project(":ktor-utils")) + } + } +} + diff --git a/ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/html/HxAttributes.kt b/ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/html/HxAttributes.kt new file mode 100644 index 00000000000..b9b7b38a561 --- /dev/null +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/html/HxAttributes.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.htmx.html + +import io.ktor.htmx.* +import io.ktor.util.collections.* +import io.ktor.utils.io.* +import kotlinx.html.HtmlTagMarker +import kotlinx.html.impl.DelegatingMap +import kotlin.jvm.JvmInline + +@ExperimentalHtmxApi +public val DelegatingMap.hx: HxAttributes get() = HxAttributes(this) + +@ExperimentalHtmxApi +public inline fun DelegatingMap.hx(block: HxAttributes.() -> Unit) { + hx.block() +} + +@ExperimentalHtmxApi +@HtmlTagMarker +@OptIn(InternalAPI::class) +public class HxAttributes(override val map: DelegatingMap) : StringMapDelegate { + public var get: String? by HxAttributeKeys.Get + public var post: String? by HxAttributeKeys.Post + public var pushUrl: String? by HxAttributeKeys.PushUrl + public var select: String? by HxAttributeKeys.Select + public var selectOob: String? by HxAttributeKeys.SelectOob + public var swap: String? by HxAttributeKeys.Swap + public var swapOob: String? by HxAttributeKeys.SwapOob + public var target: String? by HxAttributeKeys.Target + public var trigger: String? by HxAttributeKeys.Trigger + public var vals: String? by HxAttributeKeys.Vals + + public val on: On + get() = On(map) + + public fun on(event: String, script: String) { + map["hx-on:$event"] = script + } + + @JvmInline + public value class On(private val attributes: MutableMap) { + public operator fun set(event: String, script: String?) { + if (script == null) { + attributes.remove("${HxAttributeKeys.On}:$event") + } else { + attributes["${HxAttributeKeys.On}:$event"] = script + } + } + + public operator fun get(event: String): String? = attributes["${HxAttributeKeys.On}:$event"] + } +} diff --git a/ktor-shared/ktor-htmx/ktor-htmx-html/common/test/io/ktor/htmx/html/HxAttributesTest.kt b/ktor-shared/ktor-htmx/ktor-htmx-html/common/test/io/ktor/htmx/html/HxAttributesTest.kt new file mode 100644 index 00000000000..13cc1eaeb75 --- /dev/null +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/common/test/io/ktor/htmx/html/HxAttributesTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.htmx.html + +import io.ktor.htmx.ExperimentalHtmxApi +import io.ktor.htmx.HxSwap +import kotlinx.html.body +import kotlinx.html.button +import kotlinx.html.html +import kotlinx.html.stream.appendHTML +import kotlin.test.Test +import kotlin.test.assertEquals + +class HxAttributesTest { + + @OptIn(ExperimentalHtmxApi::class) + @Test + fun htmxAttributes() { + val actual = buildString { + appendHTML(prettyPrint = true).html { + body { + button { + attributes.hx { + get = "/?page=1" + target = "#replaceMe" + swap = HxSwap.outerHtml + trigger = "click[console.log('Hello!')||true]" + } + } + } + } + } + assertEquals( + """ + + + + """.trimIndent(), + actual.trim() + ) + } +} diff --git a/ktor-utils/api/ktor-utils.api b/ktor-utils/api/ktor-utils.api index e9ff2665b3e..30491e807d6 100644 --- a/ktor-utils/api/ktor-utils.api +++ b/ktor-utils/api/ktor-utils.api @@ -587,6 +587,37 @@ public final class io/ktor/util/collections/CopyOnWriteHashMap { public final fun set (Ljava/lang/Object;Ljava/lang/Object;)V } +public final class io/ktor/util/collections/MapDelegatesKt { + public static final fun asBoolean (Ljava/lang/String;)Lio/ktor/util/collections/SerializedMapValue; + public static final fun getValue (Lio/ktor/util/collections/SerializedMapValue;Lio/ktor/util/collections/StringMap;Lkotlin/reflect/KProperty;)Ljava/lang/Object; + public static final fun getValue (Ljava/lang/String;Lio/ktor/util/collections/StringMap;Lkotlin/reflect/KProperty;)Ljava/lang/String; + public static final fun setValue (Lio/ktor/util/collections/SerializedMapValue;Lio/ktor/util/collections/StringMap;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V + public static final fun setValue (Ljava/lang/String;Lio/ktor/util/collections/StringMap;Lkotlin/reflect/KProperty;Ljava/lang/String;)V +} + +public final class io/ktor/util/collections/SerializedMapValue { + public fun (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V +} + +public abstract interface class io/ktor/util/collections/StringMap { + public abstract fun get (Ljava/lang/String;)Ljava/lang/String; + public abstract fun remove (Ljava/lang/String;)Ljava/lang/String; + public abstract fun set (Ljava/lang/String;Ljava/lang/String;)V +} + +public abstract interface class io/ktor/util/collections/StringMapDelegate : io/ktor/util/collections/StringMap { + public abstract fun get (Ljava/lang/String;)Ljava/lang/String; + public abstract fun getMap ()Ljava/util/Map; + public abstract fun remove (Ljava/lang/String;)Ljava/lang/String; + public abstract fun set (Ljava/lang/String;Ljava/lang/String;)V +} + +public final class io/ktor/util/collections/StringMapDelegate$DefaultImpls { + public static fun get (Lio/ktor/util/collections/StringMapDelegate;Ljava/lang/String;)Ljava/lang/String; + public static fun remove (Lio/ktor/util/collections/StringMapDelegate;Ljava/lang/String;)Ljava/lang/String; + public static fun set (Lio/ktor/util/collections/StringMapDelegate;Ljava/lang/String;Ljava/lang/String;)V +} + public abstract interface class io/ktor/util/converters/ConversionService { public abstract fun fromValues (Ljava/util/List;Lio/ktor/util/reflect/TypeInfo;)Ljava/lang/Object; public abstract fun toValues (Ljava/lang/Object;)Ljava/util/List; diff --git a/ktor-utils/api/ktor-utils.klib.api b/ktor-utils/api/ktor-utils.klib.api index 7d3bdf0f4da..8219bd27571 100644 --- a/ktor-utils/api/ktor-utils.klib.api +++ b/ktor-utils/api/ktor-utils.klib.api @@ -75,6 +75,21 @@ final enum class io.ktor.util.logging/LogLevel : kotlin/Enum(): kotlin.collections/MutableMap // io.ktor.util.collections/StringMapDelegate.map.|(){}[0] + + open fun get(kotlin/String): kotlin/String? // io.ktor.util.collections/StringMapDelegate.get|get(kotlin.String){}[0] + open fun remove(kotlin/String): kotlin/String? // io.ktor.util.collections/StringMapDelegate.remove|remove(kotlin.String){}[0] + open fun set(kotlin/String, kotlin/String) // io.ktor.util.collections/StringMapDelegate.set|set(kotlin.String;kotlin.String){}[0] +} + abstract interface io.ktor.util.converters/ConversionService { // io.ktor.util.converters/ConversionService|null[0] abstract fun fromValues(kotlin.collections/List, io.ktor.util.reflect/TypeInfo): kotlin/Any? // io.ktor.util.converters/ConversionService.fromValues|fromValues(kotlin.collections.List;io.ktor.util.reflect.TypeInfo){}[0] abstract fun toValues(kotlin/Any?): kotlin.collections/List // io.ktor.util.converters/ConversionService.toValues|toValues(kotlin.Any?){}[0] @@ -293,6 +308,10 @@ final class <#A: kotlin/Any?, #B: kotlin/Any?> io.ktor.util.collections/Concurre final fun toString(): kotlin/String // io.ktor.util.collections/ConcurrentMap.toString|toString(){}[0] } +final class <#A: kotlin/Any?> io.ktor.util.collections/SerializedMapValue { // io.ktor.util.collections/SerializedMapValue|null[0] + constructor (kotlin/String, kotlin/Function1<#A, kotlin/String>, kotlin/Function1) // io.ktor.util.collections/SerializedMapValue.|(kotlin.String;kotlin.Function1<1:0,kotlin.String>;kotlin.Function1){}[0] +} + final class io.ktor.util.cio/ChannelReadException : io.ktor.util.cio/ChannelIOException { // io.ktor.util.cio/ChannelReadException|null[0] constructor (kotlin/String = ..., kotlin/Throwable) // io.ktor.util.cio/ChannelReadException.|(kotlin.String;kotlin.Throwable){}[0] } @@ -928,6 +947,9 @@ final fun (kotlin/Any).io.ktor.util.reflect/instanceOf(kotlin.reflect/KClass<*>) final fun (kotlin/ByteArray).io.ktor.util/encodeBase64(): kotlin/String // io.ktor.util/encodeBase64|encodeBase64@kotlin.ByteArray(){}[0] final fun (kotlin/ByteArray).io.ktor.util/readShort(kotlin/Int): kotlin/Short // io.ktor.util/readShort|readShort@kotlin.ByteArray(kotlin.Int){}[0] final fun (kotlin/Char).io.ktor.util/isLowerCase(): kotlin/Boolean // io.ktor.util/isLowerCase|isLowerCase@kotlin.Char(){}[0] +final fun (kotlin/String).io.ktor.util.collections/asBoolean(): io.ktor.util.collections/SerializedMapValue // io.ktor.util.collections/asBoolean|asBoolean@kotlin.String(){}[0] +final fun (kotlin/String).io.ktor.util.collections/getValue(io.ktor.util.collections/StringMap, kotlin.reflect/KProperty<*>): kotlin/String? // io.ktor.util.collections/getValue|getValue@kotlin.String(io.ktor.util.collections.StringMap;kotlin.reflect.KProperty<*>){}[0] +final fun (kotlin/String).io.ktor.util.collections/setValue(io.ktor.util.collections/StringMap, kotlin.reflect/KProperty<*>, kotlin/String?) // io.ktor.util.collections/setValue|setValue@kotlin.String(io.ktor.util.collections.StringMap;kotlin.reflect.KProperty<*>;kotlin.String?){}[0] final fun (kotlin/String).io.ktor.util/decodeBase64Bytes(): kotlin/ByteArray // io.ktor.util/decodeBase64Bytes|decodeBase64Bytes@kotlin.String(){}[0] final fun (kotlin/String).io.ktor.util/decodeBase64String(): kotlin/String // io.ktor.util/decodeBase64String|decodeBase64String@kotlin.String(){}[0] final fun (kotlin/String).io.ktor.util/encodeBase64(): kotlin/String // io.ktor.util/encodeBase64|encodeBase64@kotlin.String(){}[0] @@ -942,6 +964,8 @@ final fun (kotlinx.io/Source).io.ktor.util/encodeBase64(): kotlin/String // io.k final fun <#A: kotlin/Any, #B: kotlin/Any> io.ktor.util.collections/sharedMap(kotlin/Int = ...): kotlin.collections/MutableMap<#A, #B> // io.ktor.util.collections/sharedMap|sharedMap(kotlin.Int){0§;1§}[0] final fun <#A: kotlin/Any> io.ktor.util.collections/ConcurrentSet(): kotlin.collections/MutableSet<#A> // io.ktor.util.collections/ConcurrentSet|ConcurrentSet(){0§}[0] final fun <#A: kotlin/Any> io.ktor.util/caseInsensitiveMap(): kotlin.collections/MutableMap // io.ktor.util/caseInsensitiveMap|caseInsensitiveMap(){0§}[0] +final fun <#A: kotlin/Any?> (io.ktor.util.collections/SerializedMapValue<#A>).io.ktor.util.collections/getValue(io.ktor.util.collections/StringMap, kotlin.reflect/KProperty<*>): #A? // io.ktor.util.collections/getValue|getValue@io.ktor.util.collections.SerializedMapValue<0:0>(io.ktor.util.collections.StringMap;kotlin.reflect.KProperty<*>){0§}[0] +final fun <#A: kotlin/Any?> (io.ktor.util.collections/SerializedMapValue<#A>).io.ktor.util.collections/setValue(io.ktor.util.collections/StringMap, kotlin.reflect/KProperty<*>, #A?) // io.ktor.util.collections/setValue|setValue@io.ktor.util.collections.SerializedMapValue<0:0>(io.ktor.util.collections.StringMap;kotlin.reflect.KProperty<*>;0:0?){0§}[0] final fun <#A: kotlin/Any?> (kotlin.collections/Set<#A>).io.ktor.util/unmodifiable(): kotlin.collections/Set<#A> // io.ktor.util/unmodifiable|unmodifiable@kotlin.collections.Set<0:0>(){0§}[0] final fun <#A: kotlin/Any?> io.ktor.util.collections/sharedList(): kotlin.collections/MutableList<#A> // io.ktor.util.collections/sharedList|sharedList(){0§}[0] final fun <#A: kotlin/Any?> io.ktor.util.collections/sharedListOf(kotlin/Array...): kotlin.collections/MutableList<#A> // io.ktor.util.collections/sharedListOf|sharedListOf(kotlin.Array...){0§}[0] diff --git a/ktor-utils/common/src/io/ktor/util/collections/MapDelegates.kt b/ktor-utils/common/src/io/ktor/util/collections/MapDelegates.kt new file mode 100644 index 00000000000..9ee9e75df15 --- /dev/null +++ b/ktor-utils/common/src/io/ktor/util/collections/MapDelegates.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.util.collections + +import io.ktor.utils.io.InternalAPI +import kotlin.reflect.KProperty + +@InternalAPI +public interface StringMap { + public operator fun set(key: String, value: String) + public operator fun get(key: String): String? + public fun remove(key: String): String? +} + +@InternalAPI +public interface StringMapDelegate : StringMap { + public val map: MutableMap + + override fun set(key: String, value: String): Unit = map.set(key, value) + override fun get(key: String): String? = map[key] + override fun remove(key: String): String? = map.remove(key) +} + +/** + * Simplifies property access delegation for string maps when using a string constant. + */ +@InternalAPI +public operator fun String.getValue(thisRef: StringMap, property: KProperty<*>): String? = + thisRef[this] + +/** + * Simplifies property assignment delegation for string maps when using a string constant. + */ +@InternalAPI +public operator fun String.setValue(thisRef: StringMap, property: KProperty<*>, value: String?) { + if (value == null) { + thisRef.remove(this) + } else { + thisRef[this] = value + } +} + +/** + * Simplifies property access delegation for HxAttributes when setting attribute values from a string constant. + */ +@InternalAPI +public operator fun SerializedMapValue.getValue(thisRef: StringMap, property: KProperty<*>): T? = + thisRef[key]?.let(deserialize) + +/** + * Simplifies property assignment delegation for HxAttributes when setting attribute values from a string constant. + */ +@InternalAPI +public operator fun SerializedMapValue.setValue(thisRef: StringMap, property: KProperty<*>, value: T?) { + if (value == null) { + thisRef.remove(key) + } else { + thisRef[key] = serialize(value) + } +} + +/** + * Treat the map key properties as a [Boolean] + */ +@InternalAPI +public fun String.asBoolean(): SerializedMapValue = + SerializedMapValue(this, Boolean::toString, String::toBoolean) + +/** + * Simple type for handling serialization with [StringMap] delegation. + */ +@InternalAPI +public class SerializedMapValue( + internal val key: String, + internal val serialize: (T) -> String, + internal val deserialize: (String) -> T +) diff --git a/settings.gradle.kts b/settings.gradle.kts index 38cb1258345..31f5ba09be6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -88,6 +88,7 @@ include(":ktor-server:ktor-server-plugins:ktor-server-forwarded-header") include(":ktor-server:ktor-server-plugins:ktor-server-freemarker") include(":ktor-server:ktor-server-plugins:ktor-server-hsts") include(":ktor-server:ktor-server-plugins:ktor-server-html-builder") +include(":ktor-server:ktor-server-plugins:ktor-server-htmx") include(":ktor-server:ktor-server-plugins:ktor-server-http-redirect") include(":ktor-server:ktor-server-plugins:ktor-server-jte") include(":ktor-server:ktor-server-plugins:ktor-server-metrics") @@ -136,5 +137,7 @@ include(":ktor-shared:ktor-events") include(":ktor-shared:ktor-websocket-serialization") include(":ktor-shared:ktor-websockets") include(":ktor-shared:ktor-sse") +include(":ktor-shared:ktor-htmx") +include(":ktor-shared:ktor-htmx:ktor-htmx-html") include(":ktor-shared:ktor-test-base") include(":ktor-java-modules-test")