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 index 7cac90a8e80..1c9d770ff32 100644 --- 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 @@ -21,49 +21,20 @@ public final class io/ktor/server/htmx/HXRequestHeaders { public final synthetic fun unbox-impl ()Lio/ktor/http/Headers; } -public final class io/ktor/server/htmx/HXResponseHeaders : io/ktor/server/htmx/StringMap { +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/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/String;)V -} - -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 static fun constructor-impl (Lio/ktor/server/routing/Route;)Lio/ktor/server/routing/Route; - 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 fun setRefresh (Ljava/lang/Boolean;)V } public final class io/ktor/server/htmx/HxAttributes : io/ktor/server/htmx/StringMapDelegate { @@ -123,6 +94,34 @@ public final class io/ktor/server/htmx/HxHeadersKt { 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/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts index 433887ae53f..36a53a421eb 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/build.gradle.kts @@ -3,6 +3,7 @@ kotlin.sourceSets { dependencies { api(project(":ktor-shared:ktor-htmx")) api(project(":ktor-server:ktor-server-plugins:ktor-server-html-builder")) + implementation(project(":ktor-utils")) } } } 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 index 28ea2b9b5b4..03c1570f444 100644 --- 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 @@ -4,6 +4,8 @@ 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 @@ -25,7 +27,7 @@ public value class HXRequestHeaders(private val headers: Headers) { 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) } + 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] @@ -41,12 +43,13 @@ public value class HXRequestHeaders(private val headers: Headers) { } @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: String? by HxResponseHeaders.Refresh // TODO boolean + public var refresh: Boolean? by HxResponseHeaders.Refresh.asBoolean() public val replaceUrl: String? by HxResponseHeaders.ReplaceUrl override fun set(key: String, value: String): Unit = 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 index 17ec2e50f48..b9806524997 100644 --- 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 @@ -9,14 +9,17 @@ 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(this) +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(this)) { +public fun Route.hx(configuration: HxRoute.() -> Unit): Route = with(HxRoute.wrap(this)) { header(HxRequestHeaders.Request, "true") { configuration() } @@ -28,7 +31,11 @@ public fun Route.hx(configuration: HXRoute.() -> Unit): Route = with(HXRoute(thi @ExperimentalHtmxApi @KtorDsl @JvmInline -public value class HXRoute(private val route: Route) : Route by route { +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. diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/MapDelegates.kt b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/MapDelegates.kt deleted file mode 100644 index f5d4a0189ae..00000000000 --- a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/MapDelegates.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 kotlin.reflect.KProperty - -internal interface StringMap { - operator fun set(key: String, value: String) - operator fun get(key: String): String? - fun remove(key: String): String? -} - -internal interface StringMapDelegate : StringMap { - 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 HxAttributes when setting attribute values from a string constant. - */ -internal operator fun String.getValue(thisRef: StringMap, property: KProperty<*>): String? = - thisRef[this] - -/** - * Simplifies property assignment delegation for HxAttributes when setting attribute values from a string constant. - */ -internal operator fun String.setValue(thisRef: StringMap, property: KProperty<*>, value: String?) { - if (value == null) { - thisRef.remove(this) - } else { - thisRef[this] = value - } -} diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HtmxTest.kt b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HtmxTest.kt index 1e2df16d89c..101e8499692 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HtmxTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-htmx/common/test/io/ktor/server/htmx/HtmxTest.kt @@ -8,6 +8,7 @@ import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.htmx.* import io.ktor.server.html.* +import io.ktor.server.response.respondText import io.ktor.server.routing.* import io.ktor.server.testing.* import kotlinx.html.body @@ -30,7 +31,7 @@ class HtmxTest { attributes.hx { get = "/?page=1" target = "#replaceMe" - swap = HxSwap.outerHTML + swap = HxSwap.outerHtml trigger = "click[console.log('Hello!')||true]" } } @@ -69,8 +70,11 @@ class HtmxTest { } } route("htmx") { + get { + call.respondText { "Not HTMX" } + } hx.get { - respondWith("No target") + respondWith("No target or trigger") } hx { target("#test") { @@ -87,7 +91,11 @@ class HtmxTest { } } assertEquals( - responseTemplate("No target"), + "Not HTMX", + client.get("htmx").bodyAsText().trim() + ) + assertEquals( + responseTemplate("No target or trigger"), client.get("htmx") { headers[HxRequestHeaders.Request] = "true" }.bodyAsText().trim() diff --git a/ktor-shared/ktor-htmx/api/ktor-htmx.api b/ktor-shared/ktor-htmx/api/ktor-htmx.api index b932706ceed..3daa4e06074 100644 --- a/ktor-shared/ktor-htmx/api/ktor-htmx.api +++ b/ktor-shared/ktor-htmx/api/ktor-htmx.api @@ -1,3 +1,6 @@ +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; @@ -69,7 +72,7 @@ public final class io/ktor/htmx/HxEvents { 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 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; @@ -77,9 +80,9 @@ public final class io/ktor/htmx/HxEvents { 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 SSEError Ljava/lang/String; - public static final field SSEOpen 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; @@ -94,7 +97,7 @@ public final class io/ktor/htmx/HxEvents { 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 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; @@ -126,9 +129,9 @@ public final class io/ktor/htmx/HxSwap { 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 innerHtml Ljava/lang/String; public static final field none Ljava/lang/String; - public static final field outerHTML Ljava/lang/String; + public static final field outerHtml Ljava/lang/String; public static final field textContent Ljava/lang/String; } diff --git a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/Annotations.kt b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt similarity index 62% rename from ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/Annotations.kt rename to ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt index 1fe3aa20f5d..a1fbae2c233 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/Annotations.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/Annotations.kt @@ -1,8 +1,8 @@ /* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * 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.server.htmx +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 index 17b5250610d..d4f4dbb3832 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxAttributeKeys.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxAttributeKeys.kt @@ -5,179 +5,184 @@ 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 + * Issues a GET to the specified URL. */ public const val Get: String = "hx-get" /** - * issues a POST to the specified URL + * Issues a POST to the specified URL. */ public const val Post: String = "hx-post" /** - * handle events with inline scripts on elements + * Handles events with inline scripts on elements. */ public const val On: String = "hx-on" /** - * push a URL into the browser location bar to create history + * Pushes a URL into the browser location bar to create history. */ public const val PushUrl: String = "hx-push-url" /** - * select content to swap in from a response + * Selects content to swap in from a response. */ public const val Select: String = "hx-select" /** - * select content to swap in from a response, somewhere other than the target (out of band) + * 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, …) + * Controls how content will swap in (outerHTML, beforeend, afterend, …). */ public const val Swap: String = "hx-swap" /** - * mark element to swap in from a response (out of band) + * 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 + * Specifies the target element to be swapped. */ public const val Target: String = "hx-target" /** - * specifies the event that triggers the request + * Specifies the event that triggers the request. */ public const val Trigger: String = "hx-trigger" /** - * add values to submit with the request (JSON format) + * Adds values to submit with the request (JSON format). */ public const val Vals: String = "hx-vals" /** - * add progressive enhancement for links and forms + * Adds progressive enhancement for links and forms. */ public const val Boost: String = "hx-boost" /** - * shows a confirm() dialog before issuing a request + * Shows a confirm() dialog before issuing a request. */ public const val Confirm: String = "hx-confirm" /** - * issues a DELETE to the specified URL + * 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 + * 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 + * Adds the disabled attribute to the specified elements while a request is in flight. */ public const val DisabledElt: String = "hx-disabled-elt" /** - * control and disable automatic attribute inheritance for child nodes + * Controls and disables automatic attribute inheritance for child nodes. */ public const val Disinherit: String = "hx-disinherit" /** - * changes the request encoding type + * Changes the request encoding type. */ public const val Encoding: String = "hx-encoding" /** - * extensions to use for this element + * Extensions to use for this element. */ public const val Ext: String = "hx-ext" /** - * adds to the headers that will be submitted with the request + * Adds to the headers that will be submitted with the request. */ public const val Headers: String = "hx-headers" /** - * prevent sensitive data being saved to the history cache + * Prevents sensitive data from being saved to the history cache. */ public const val History: String = "hx-history" /** - * the element to snapshot and restore during history navigation + * Specifies the element to snapshot and restore during history navigation. */ public const val HistoryElt: String = "hx-history-elt" /** - * include additional data in requests + * Includes additional data in requests. */ public const val Include: String = "hx-include" /** - * the element to put the htmx-request class on during the request + * Specifies the element to put the htmx-request class on during the request. */ public const val Indicator: String = "hx-indicator" /** - * control and enable automatic attribute inheritance for child nodes if it has been disabled by default + * 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 + * Filters the parameters that will be submitted with a request. */ public const val Params: String = "hx-params" /** - * issues a PATCH to the specified URL + * Issues a PATCH to the specified URL. */ public const val Patch: String = "hx-patch" /** - * specifies elements to keep unchanged between requests + * Specifies elements to keep unchanged between requests. */ public const val Preserve: String = "hx-preserve" /** - * shows a prompt() before submitting a request + * Shows a prompt() before submitting a request. */ public const val Prompt: String = "hx-prompt" /** - * issues a PUT to the specified URL + * Issues a PUT to the specified URL. */ public const val Put: String = "hx-put" /** - * replace the URL in the browser location bar + * Replaces the URL in the browser location bar. */ public const val ReplaceUrl: String = "hx-replace-url" /** - * configures various aspects of the request + * Configures various aspects of the request. */ public const val Request: String = "hx-request" /** - * control how requests made by different elements are synchronized + * Controls how requests made by different elements are synchronized. */ public const val Sync: String = "hx-sync" /** - * force elements to validate themselves before a request + * 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) + * 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 index dc9c2f3984b..0d130b38b25 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxCss.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxCss.kt @@ -5,6 +5,11 @@ 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. 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 index fa1c5d8ebd3..2bdebca0da3 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxEvents.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxEvents.kt @@ -4,214 +4,219 @@ 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 + * 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 + * 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 + * Triggered after htmx has initialized a node. */ public const val AfterProcessNode: String = "htmx:afterProcessNode" /** - * triggered after an AJAX request has completed + * Triggered after an AJAX request has completed. */ public const val AfterRequest: String = "htmx:afterRequest" /** - * triggered after the DOM has settled + * Triggered after the DOM has settled. */ public const val AfterSettle: String = "htmx:afterSettle" /** - * triggered after new content has been swapped in + * 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 + * 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 + * Triggered before any response processing occurs. */ public const val BeforeOnLoad: String = "htmx:beforeOnLoad" /** - * triggered before htmx initializes a node + * Triggered before htmx initializes a node. */ public const val BeforeProcessNode: String = "htmx:beforeProcessNode" /** - * triggered before an AJAX request is made + * 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 + * 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 + * 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 + * 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 + * 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 + * Triggered on an error during cache writing. */ public const val HistoryCacheError: String = "htmx:historyCacheError" /** - * triggered on a cache miss in the history subsystem + * Triggered on a cache miss in the history subsystem. */ public const val HistoryCacheMiss: String = "htmx:historyCacheMiss" /** - * triggered on a unsuccessful remote retrieval + * Triggered on an unsuccessful remote retrieval. */ public const val HistoryCacheMissError: String = "htmx:historyCacheMissError" /** - * triggered on a successful remote retrieval + * Triggered on a successful remote retrieval. */ public const val HistoryCacheMissLoad: String = "htmx:historyCacheMissLoad" /** - * triggered when htmx handles a history restoration action + * Triggered when htmx handles a history restoration action. */ public const val HistoryRestore: String = "htmx:historyRestore" /** - * triggered before content is saved to the history cache + * 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 + * 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 + * 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" + public const val NoSseSourceError: String = "htmx:noSSESourceError" /** - * triggered when an exception occurs during the onLoad handling in htmx + * 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 as been swapped in + * 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 + * 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 + * 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 + * Triggered after a prompt is shown. */ public const val Prompt: String = "htmx:prompt" /** - * triggered after an url is pushed into history + * 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 + * 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 + * 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 + * Triggered when an error occurs with a SSE source. */ - public const val SSEError: String = "htmx:sseError" + public const val SseError: String = "htmx:sseError" /** - * triggered when a SSE source is opened + * Triggered when a SSE source is opened. */ - public const val SSEOpen: String = "htmx:sseOpen" + public const val SseOpen: String = "htmx:sseOpen" /** - * triggered when an error occurs during the swap phase + * Triggered when an error occurs during the swap phase. */ public const val SwapError: String = "htmx:swapError" /** - * triggered when an invalid target is specified + * Triggered when an invalid target is specified. */ public const val TargetError: String = "htmx:targetError" /** - * triggered when a request timeout occurs + * Triggered when a request timeout occurs. */ public const val Timeout: String = "htmx:timeout" /** - * triggered before an element is validated + * Triggered before an element is validated. */ public const val ValidationValidate: String = "htmx:validation:validate" /** - * triggered when an element fails validation + * Triggered when an element fails validation. */ public const val ValidationFailed: String = "htmx:validation:failed" /** - * triggered when a request is halted due to validation errors + * Triggered when a request is halted due to validation errors. */ public const val ValidationHalted: String = "htmx:validation:halted" /** - * triggered when an ajax request aborts + * Triggered when an AJAX request aborts. */ public const val XhrAbort: String = "htmx:xhr:abort" /** - * triggered when an ajax request ends + * Triggered when an AJAX request ends. */ public const val XhrLoadend: String = "htmx:xhr:loadend" /** - * triggered when an ajax request starts + * Triggered when an AJAX request starts. */ public const val XhrLoadstart: String = "htmx:xhr:loadstart" /** - * triggered periodically during an ajax request that supports progress events + * 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 index c50d5fc33a5..efea5390b00 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxRequestHeaders.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxRequestHeaders.kt @@ -5,44 +5,49 @@ 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 + * Indicates that the request is via an element using hx-boost. */ public const val Boosted: String = "HX-Boosted" /** - * the current URL of the browser + * The current URL of the browser. */ - public const val CurrentURL: String = "HX-Current-URL" + public const val CurrentUrl: String = "HX-Current-URL" /** - * “true” if the request is for history restoration after a miss in the local history cache + * “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 + * The user response to an hx-prompt. */ public const val Prompt: String = "HX-Prompt" /** - * always “true” + * Always “true”. */ public const val Request: String = "HX-Request" /** - * the id of the target element if it exists + * 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 + * 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 + * 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 index 4ca7db88537..661a5a71b24 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxResponseHeaders.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxResponseHeaders.kt @@ -5,59 +5,64 @@ 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 index 837b2063d2c..804dc681b3c 100644 --- a/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxSwap.kt +++ b/ktor-shared/ktor-htmx/common/src/io/ktor/htmx/HxSwap.kt @@ -6,16 +6,21 @@ 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" + public const val innerHtml: String = "innerHtml" /** * Replace the entire target element with the response */ - public const val outerHTML: String = "outerHTML" + public const val outerHtml: String = "outerHTML" /** * Replace the text content of the target element, without parsing the response as HTML 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..eb8c1fa344c --- /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/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-fjYODpQ ()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/HxAttributes$On { + public static final synthetic fun box-impl (Ljava/util/Map;)Lio/ktor/htmx/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/HxAttributesKt { + public static final fun getHx (Lkotlinx/html/impl/DelegatingMap;)Lio/ktor/htmx/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/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-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxAttributes.kt b/ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/HxAttributes.kt similarity index 93% rename from ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxAttributes.kt rename to ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/HxAttributes.kt index 928c1dcf5e3..9b3770f13cf 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-htmx/common/src/io/ktor/server/htmx/HxAttributes.kt +++ b/ktor-shared/ktor-htmx/ktor-htmx-html/common/src/io/ktor/htmx/HxAttributes.kt @@ -2,9 +2,10 @@ * 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 +package io.ktor.htmx -import io.ktor.htmx.HxAttributeKeys +import io.ktor.util.collections.* +import io.ktor.utils.io.InternalAPI import kotlinx.html.HtmlTagMarker import kotlinx.html.impl.DelegatingMap import kotlin.jvm.JvmInline @@ -19,6 +20,7 @@ public inline fun DelegatingMap.hx(block: HxAttributes.() -> Unit) { @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 diff --git a/ktor-utils/api/ktor-utils.api b/ktor-utils/api/ktor-utils.api index e9ff2665b3e..7af0fbc6397 100644 --- a/ktor-utils/api/ktor-utils.api +++ b/ktor-utils/api/ktor-utils.api @@ -587,6 +587,41 @@ 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/SerializedKey { + public fun (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)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/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..b63924d7125 --- /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 HxAttributes when setting attribute values from a string constant. + */ +@InternalAPI +public operator fun String.getValue(thisRef: StringMap, property: KProperty<*>): String? = + thisRef[this] + +/** + * Simplifies property assignment delegation for HxAttributes when setting attribute values from 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 c918be844ff..31f5ba09be6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -138,5 +138,6 @@ 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")