From c1441e074190fac1cde58a7a619a516fb8d81305 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sun, 14 Jun 2026 17:23:41 -0400 Subject: [PATCH] Add outerSync swap; WIP on htmax view engine module --- src/Common/Common.fs | 5 + src/Directory.Build.props | 2 +- src/Tests/Common.fs | 3 + src/Tests/Program.fs | 2 +- src/Tests/Tests.fsproj | 1 + src/Tests/ViewEngine.fs | 162 ++++++++--------- src/Tests/ViewEngineMax.fs | 170 ++++++++++++++++++ .../Giraffe.ViewEngine.Htmx.fsproj | 1 + src/ViewEngine.Htmx/Htmax.fs | 105 +++++++++++ src/ViewEngine.Htmx/Htmx.fs | 66 +++---- 10 files changed, 404 insertions(+), 113 deletions(-) create mode 100644 src/Tests/ViewEngineMax.fs create mode 100644 src/ViewEngine.Htmx/Htmax.fs diff --git a/src/Common/Common.fs b/src/Common/Common.fs index aaaa3e6..1f08f4f 100644 --- a/src/Common/Common.fs +++ b/src/Common/Common.fs @@ -49,6 +49,11 @@ module HxSwap = [] let OuterMorph = "outerMorph" + /// Morph the outer HTML of the target to the new content, recreating all children + /// This is used internally by the new history extension, but can be used by others if desired + [] + let OuterSync = "outerSync" + /// Replace the text content of the target without parsing the response as HTML [] let TextContent = "textContent" diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d439fe3..3ec5f62 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -10,7 +10,7 @@ - [Common] Add htmax bundle to provided version - [Server] Update HX-Target header to return tag and ID (similar to HX-Source) - [Server] Add support for HX-Request-Type header -- [View Engine] Add hx-status attribute +- [View Engine] Add hx-status attribute, outerSync swap - [View Engine] Updated script tags to pull htmx 4.0.0-beta4, added "max" bundle links for local and CDN See package and prior alpha release READMEs; v2 to v4 is not an update-and-forget-it release diff --git a/src/Tests/Common.fs b/src/Tests/Common.fs index 093414b..3c318a6 100644 --- a/src/Tests/Common.fs +++ b/src/Tests/Common.fs @@ -24,6 +24,9 @@ let swap = test "OuterMorph is correct" { Expect.equal HxSwap.OuterMorph "outerMorph" "Outer Morph swap value incorrect" } + test "OuterSync is correct" { + Expect.equal HxSwap.OuterSync "outerSync" "Outer Sync swap value incorrect" + } test "TextContent is correct" { Expect.equal HxSwap.TextContent "textContent" "Text Content swap value incorrect" } diff --git a/src/Tests/Program.fs b/src/Tests/Program.fs index fba7983..e8fa719 100644 --- a/src/Tests/Program.fs +++ b/src/Tests/Program.fs @@ -1,6 +1,6 @@ open Expecto -let allTests = testList "Giraffe" [ Common.allTests; Htmx.allTests; ViewEngine.allTests ] +let allTests = testList "Giraffe" [ Common.allTests; Htmx.allTests; ViewEngine.allTests; ViewEngineMax.allTests ] [] let main args = runTestsWithCLIArgs [] args allTests diff --git a/src/Tests/Tests.fsproj b/src/Tests/Tests.fsproj index c0795fb..64638c3 100644 --- a/src/Tests/Tests.fsproj +++ b/src/Tests/Tests.fsproj @@ -8,6 +8,7 @@ + diff --git a/src/Tests/ViewEngine.fs b/src/Tests/ViewEngine.fs index 0e17a22..cb5f789 100644 --- a/src/Tests/ViewEngine.fs +++ b/src/Tests/ViewEngine.fs @@ -138,24 +138,6 @@ let hxEvent = Expect.equal (AfterSettle.ToHxOnString()) "after:settle" "AfterSettle hx-on event name not correct" } ] - testList "AfterSseMessage" [ - test "ToString succeeds" { - Expect.equal (string AfterSseMessage) "afterSseMessage" "AfterSseMessage event name not correct" - } - test "ToHxOnString succeeds" { - Expect.equal - (AfterSseMessage.ToHxOnString()) "after:sse:message" "AfterSseMessage hx-on event name not correct" - } - ] - testList "AfterSseStream" [ - test "ToString succeeds" { - Expect.equal (string AfterSseStream) "afterSseStream" "AfterSseStream event name not correct" - } - test "ToHxOnString succeeds" { - Expect.equal - (AfterSseStream.ToHxOnString()) "after:sse:stream" "AfterSseStream hx-on event name not correct" - } - ] testList "AfterSwap" [ test "ToString succeeds" { Expect.equal (string AfterSwap) "afterSwap" "AfterSwap event name not correct" @@ -232,38 +214,6 @@ let hxEvent = (BeforeResponse.ToHxOnString()) "before:response" "BeforeResponse hx-on event name not correct" } ] - testList "BeforeSseMessage" [ - test "ToString succeeds" { - Expect.equal (string BeforeSseMessage) "beforeSseMessage" "BeforeSseMessage event name not correct" - } - test "ToHxOnString succeeds" { - Expect.equal - (BeforeSseMessage.ToHxOnString()) - "before:sse:message" - "BeforeSseMessage hx-on event name not correct" - } - ] - testList "BeforeSseReconnect" [ - test "ToString succeeds" { - Expect.equal - (string BeforeSseReconnect) "beforeSseReconnect" "BeforeSseReconnect event name not correct" - } - test "ToHxOnString succeeds" { - Expect.equal - (BeforeSseReconnect.ToHxOnString()) - "before:sse:reconnect" - "BeforeSseReconnect hx-on event name not correct" - } - ] - testList "BeforeSseStream" [ - test "ToString succeeds" { - Expect.equal (string BeforeSseStream) "beforeSseStream" "BeforeSseStream event name not correct" - } - test "ToHxOnString succeeds" { - Expect.equal - (BeforeSseStream.ToHxOnString()) "before:sse:stream" "BeforeSseStream hx-on event name not correct" - } - ] testList "BeforeSwap" [ test "ToString succeeds" { Expect.equal (string BeforeSwap) "beforeSwap" "BeforeSwap event name not correct" @@ -673,12 +623,6 @@ let attributes = dt [ _hxVals """{ "extra": "values" }""" ] [] |> shouldRender """
""" } - test "_sseConnect succeeds" { - div [ _sseConnect "/gps/sse" ] [] |> shouldRender """
""" - } - test "_sseSwap succeeds" { - ul [ _sseSwap "sseMessageName" ] [] |> shouldRender """
    """ - } ] /// Tests for the HxModifiers module @@ -719,8 +663,6 @@ let hxTags = } ] -open Giraffe.Htmx.Common - /// Tests for the Script module let script = testList "Script" [ @@ -731,13 +673,6 @@ let script = $"""""" "Local script tag is incorrect" } - test "localMax succeeds" { - let html = RenderView.AsString.htmlNode Script.localMax - Expect.equal - html - $"""""" - "Local script tag is incorrect" - } test "cdnMinified succeeds" { let html = RenderView.AsString.htmlNode Script.cdnMinified Expect.equal @@ -752,20 +687,29 @@ let script = $"""""" "CDN unminified script tag is incorrect" } - test "cdnMaxMinified succeeds" { - let html = RenderView.AsString.htmlNode Script.cdnMaxMinified - Expect.equal - html - $"""""" - "CDN minified script tag is incorrect" - } - test "cdnMaxUnminified succeeds" { - let html = RenderView.AsString.htmlNode Script.cdnMaxUnminified - Expect.equal - html - $"""""" - "CDN unminified script tag is incorrect" - } + testList "Max" [ + test "localMax succeeds" { + let html = RenderView.AsString.htmlNode Script.Max.local + Expect.equal + html + $"""""" + "Local script tag is incorrect" + } + test "cdnMaxMinified succeeds" { + let html = RenderView.AsString.htmlNode Script.Max.cdnMinified + Expect.equal + html + $"""""" + "CDN minified script tag is incorrect" + } + test "cdnMaxUnminified succeeds" { + let html = RenderView.AsString.htmlNode Script.Max.cdnUnminified + Expect.equal + html + $"""""" + "CDN unminified script tag is incorrect" + } + ] ] open System.Text @@ -936,6 +880,24 @@ let hxEventObs = (AfterProcessNode.ToHxOnString()) "after:process" "AfterProcessNode hx-on event name not correct" } ] + testList "AfterSseMessage" [ + test "ToString succeeds" { + Expect.equal (string AfterSseMessage) "afterSseMessage" "AfterSseMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterSseMessage.ToHxOnString()) "after:sse:message" "AfterSseMessage hx-on event name not correct" + } + ] + testList "AfterSseStream" [ + test "ToString succeeds" { + Expect.equal (string AfterSseStream) "afterSseStream" "AfterSseStream event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterSseStream.ToHxOnString()) "after:sse:stream" "AfterSseStream hx-on event name not correct" + } + ] testList "BeforeCleanupElement" [ test "ToString succeeds" { Expect.equal @@ -984,6 +946,38 @@ let hxEventObs = Expect.equal (BeforeSend.ToHxOnString()) "before:request" "BeforeSend hx-on event name not correct" } ] + testList "BeforeSseMessage" [ + test "ToString succeeds" { + Expect.equal (string BeforeSseMessage) "beforeSseMessage" "BeforeSseMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeSseMessage.ToHxOnString()) + "before:sse:message" + "BeforeSseMessage hx-on event name not correct" + } + ] + testList "BeforeSseReconnect" [ + test "ToString succeeds" { + Expect.equal + (string BeforeSseReconnect) "beforeSseReconnect" "BeforeSseReconnect event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeSseReconnect.ToHxOnString()) + "before:sse:reconnect" + "BeforeSseReconnect hx-on event name not correct" + } + ] + testList "BeforeSseStream" [ + test "ToString succeeds" { + Expect.equal (string BeforeSseStream) "beforeSseStream" "BeforeSseStream event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeSseStream.ToHxOnString()) "before:sse:stream" "BeforeSseStream hx-on event name not correct" + } + ] testList "HistoryCacheError" [ test "ToString succeeds" { Expect.equal (string HistoryCacheError) "historyCacheError" "HistoryCacheError event name not correct" @@ -1123,7 +1117,7 @@ let hxEventObs = Expect.equal (string SseError) "sseError" "SseError event name not correct" } test "ToHxOnString succeeds" { - Expect.equal (SseError.ToHxOnString()) "error" "SseError hx-on event name not correct" + Expect.equal (SseError.ToHxOnString()) "sse:error" "SseError hx-on event name not correct" } ] testList "SseOpen" [ @@ -1131,7 +1125,7 @@ let hxEventObs = Expect.equal (string SseOpen) "sseOpen" "SseOpen event name not correct" } test "ToHxOnString succeeds" { - Expect.equal (SseOpen.ToHxOnString()) "sse-open" "SseOpen hx-on event name not correct" + Expect.equal (SseOpen.ToHxOnString()) "after:sse:connection" "SseOpen hx-on event name not correct" } ] testList "SwapError" [ @@ -1327,6 +1321,12 @@ let attributesObs = test "_hxRequest succeeds" { u [ _hxRequest "noHeaders" ] [] |> shouldRender """""" } + test "_sseConnect succeeds" { + div [ _sseConnect "/gps/sse" ] [] |> shouldRender """
    """ + } + test "_sseSwap succeeds" { + ul [ _sseSwap "sseMessageName" ] [] |> shouldRender """
      """ + } ] let obsolete = testList "Obsolete" [ hxEventObs; hxParamsObs; hxRequestObs; attributesObs ] diff --git a/src/Tests/ViewEngineMax.fs b/src/Tests/ViewEngineMax.fs new file mode 100644 index 0000000..17fa3d7 --- /dev/null +++ b/src/Tests/ViewEngineMax.fs @@ -0,0 +1,170 @@ +module ViewEngineMax + +open Expecto +open Giraffe.ViewEngine.Htmax + +/// Tests for the HxEvent module +let hxEvent = + testList "HxEvent" [ + testList "AfterSseConnection" [ + test "ToString succeeds" { + Expect.equal + (string AfterSseConnection) "afterSseConnection" "AfterSseConnection event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterSseConnection.ToHxOnString()) + "after:sse:connection" + "AfterSseConnection hx-on event name not correct" + } + ] + testList "AfterSseMessage" [ + test "ToString succeeds" { + Expect.equal (string AfterSseMessage) "afterSseMessage" "AfterSseMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterSseMessage.ToHxOnString()) "after:sse:message" "AfterSseMessage hx-on event name not correct" + } + ] + testList "AfterWsConnection" [ + test "ToString succeeds" { + Expect.equal + (string AfterWsConnection) "afterWsConnection" "AfterWsConnection event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterWsConnection.ToHxOnString()) + "after:ws:connection" + "AfterWsConnection hx-on event name not correct" + } + ] + testList "AfterWsMessage" [ + test "ToString succeeds" { + Expect.equal (string AfterWsMessage) "afterWsMessage" "AfterWsMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterWsMessage.ToHxOnString()) "after:ws:message" "AfterWsMessage hx-on event name not correct" + } + ] + testList "AfterWsRequest" [ + test "ToString succeeds" { + Expect.equal (string AfterWsRequest) "afterWsRequest" "AfterWsRequest event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (AfterWsRequest.ToHxOnString()) "after:ws:request" "AfterWsRequest hx-on event name not correct" + } + ] + testList "BeforeSseConnection" [ + test "ToString succeeds" { + Expect.equal + (string BeforeSseConnection) "beforeSseConnection" "BeforeSseConnection event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeSseConnection.ToHxOnString()) + "before:sse:connection" + "BeforeSseConnection hx-on event name not correct" + } + ] + testList "BeforeSseMessage" [ + test "ToString succeeds" { + Expect.equal (string BeforeSseMessage) "beforeSseMessage" "BeforeSseMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeSseMessage.ToHxOnString()) + "before:sse:message" + "BeforeSseMessage hx-on event name not correct" + } + ] + testList "BeforeWsConnection" [ + test "ToString succeeds" { + Expect.equal + (string BeforeWsConnection) "beforeWsConnection" "BeforeWsConnection event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeWsConnection.ToHxOnString()) + "before:ws:connection" + "BeforeWsConnection hx-on event name not correct" + } + ] + testList "BeforeWsMessage" [ + test "ToString succeeds" { + Expect.equal (string BeforeWsMessage) "beforeWsMessage" "BeforeWsMessage event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeWsMessage.ToHxOnString()) "before:ws:message" "BeforeWsMessage hx-on event name not correct" + } + ] + testList "BeforeWsRequest" [ + test "ToString succeeds" { + Expect.equal (string BeforeWsRequest) "beforeWsRequest" "BeforeWsRequest event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal + (BeforeWsRequest.ToHxOnString()) "before:ws:request" "BeforeWsRequest hx-on event name not correct" + } + ] + testList "SseClose" [ + test "ToString succeeds" { + Expect.equal (string SseClose) "sseClose" "SseClose event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal (SseClose.ToHxOnString()) "sse:close" "SseClose hx-on event name not correct" + } + ] + testList "SseError" [ + test "ToString succeeds" { + Expect.equal (string SseError) "sseError" "SseError event name not correct" + } + test "ToHxOnString succeeds" { + Expect.equal (SseError.ToHxOnString()) "sse:error" "SseError hx-on event name not correct" + } + ] + ] + +open Giraffe.ViewEngine + +/// Pipe-able assertion for a rendered node +let shouldRender expected node = + Expect.equal (RenderView.AsString.htmlNode node) expected "Rendered HTML incorrect" + +/// Tests for the HtmaxAttrs module +let attributes = + testList "Attributes" [ + + test "_hxOnMax succeeds" { + body [ _hxOnMax SseClose "alert(done)" ] [] + |> shouldRender """""" + } + test "_hxSseClose succeeds" { + form [ _hxSseClose "fin" ] [] |> shouldRender """
      """ + } + test "_hxSseConnect succeeds" { + div [ _hxSseConnect "/path/to/resource" ] [] + |> shouldRender """
      """ + } + test "_hxWsConnect succeeds" { + p [ _hxWsConnect "/over/here" ] [] |> shouldRender """

      """ + } + testList "_hxWsSend" [ + test "succeeds when given a URL" { + span [ _hxWsSend (Some "/a/new/place") ] [] + |> shouldRender """""" + } + test "succeeds when not given a URL" { + aside [ _hxWsSend None ] [] |> shouldRender """""" + } + ] + ] + +let allTests = + testList "ViewEngine.Htmax" [ + hxEvent + attributes + ] diff --git a/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj b/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj index bb8575e..0f2c456 100644 --- a/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj +++ b/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj @@ -9,6 +9,7 @@ + diff --git a/src/ViewEngine.Htmx/Htmax.fs b/src/ViewEngine.Htmx/Htmax.fs new file mode 100644 index 0000000..6df6d75 --- /dev/null +++ b/src/ViewEngine.Htmx/Htmax.fs @@ -0,0 +1,105 @@ +/// Types and functions supporting htmax-bundled extension attributes in Giraffe View Engine +module Giraffe.ViewEngine.Htmax + +/// The events recognized by htmax-bundled extensions +[] +type HxEvent = + + /// Fired after an SSE connection is established + | AfterSseConnection + + /// Fired after an SSE message is received + | AfterSseMessage + + /// Fired after a WebSocket connection is established + | AfterWsConnection + + /// Fired after a WebSocket message is received + | AfterWsMessage + + /// Fired after a WebSocket request is sent + | AfterWsRequest + + /// Fired before an SSE connection is established (cancelable) + | BeforeSseConnection + + /// Fired before a received SSE message is processed (cancelable) + | BeforeSseMessage + + /// Fired before a WebSocket connection is established (cancelable) + | BeforeWsConnection + + /// Fired before a received WebSocket message is processed (cancelable) + | BeforeWsMessage + + /// Fired before a WebSocket request is sent (cancelable) + | BeforeWsRequest + + /// Fired when an SSE connection is closed + | SseClose + + /// Fired when an SSE connection errors + | SseError + + /// The htmx event name (fst) and kebab-case name (snd, for use with hx-on) + static member private Values = Map [ + AfterSseConnection, ("afterSseConnection", "after:sse:connection") + AfterSseMessage, ("afterSseMessage", "after:sse:message") + AfterWsConnection, ("afterWsConnection", "after:ws:connection") + AfterWsMessage, ("afterWsMessage", "after:ws:message") + AfterWsRequest, ("afterWsRequest", "after:ws:request") + BeforeSseConnection, ("beforeSseConnection", "before:sse:connection") + BeforeSseMessage, ("beforeSseMessage", "before:sse:message") + BeforeWsConnection, ("beforeWsConnection", "before:ws:connection") + BeforeWsMessage, ("beforeWsMessage", "before:ws:message") + BeforeWsRequest, ("beforeWsRequest", "before:ws:request") + SseClose, ("sseClose", "sse:close") + SseError, ("sseError", "sse:error") + ] + + /// The htmx event name + override this.ToString() = fst HxEvent.Values[this] + + /// The hx-on variant of the htmx event name + member this.ToHxOnString() = snd HxEvent.Values[this] + + +/// Attributes and flags for htmax-bundled extensions +[] +module HtmaxAttrs = + + /// Generate an hx-on:htmx event for an htmax-bundled extension event + /// The HxEvent to be handled + /// The script to be executed when the event occurs + /// A configured hx-on:htmx: attribute + /// Documentation + let _hxOnMax (event: HxEvent) handler = + Htmx.HtmxAttrs._hxOnEvent $"htmx:{event.ToHxOnString()}" handler + + /// Define an SSE message which indicates the connection should be closed + /// The text of the message which indicates the SSE connection should be closed + /// A configured hx-sse:close attribute + /// Documentation + let _hxSseClose message = + attr "hx-sse:close" message + + /// Define a URL to which an SSE connection should be established + /// The URL to which an SSE connection should be established + /// A configured hx-sse:connect attribute + /// Documentation + let _hxSseConnect url = + attr "hx-sse:connect" url + + /// Connect to a WebSocket URL + /// The URL to which a WebSocket connection should be established + /// A configured hx-ws:connect attribute + /// Documentation + let _hxWsConnect url = + attr "hx-ws:connect" url + + /// Send data via a WebSocket connection + /// The URL to which the data should be sent (optional; default is connection URL) + /// A configured hx-ws:send attribute + /// Documentation + let _hxWsSend url = + match url with Some it -> attr "hx-ws:send" it | None -> flag "hx-ws:send" diff --git a/src/ViewEngine.Htmx/Htmx.fs b/src/ViewEngine.Htmx/Htmx.fs index 019de9c..e3d1fae 100644 --- a/src/ViewEngine.Htmx/Htmx.fs +++ b/src/ViewEngine.Htmx/Htmx.fs @@ -90,10 +90,10 @@ type HxEvent = | AfterSettle /// Triggered after a Server Sent Events (SSE) message is read - | AfterSseMessage + | [] AfterSseMessage /// Triggered after a Server Sent Events (SSE) stream is closed - | AfterSseStream + | [] AfterSseStream /// Triggered after new content has been swapped in | AfterSwap @@ -138,13 +138,13 @@ type HxEvent = | [] BeforeSend /// Triggered before a Server Sent Events (SSE) message is read - | BeforeSseMessage + | [] BeforeSseMessage /// Triggered before a Server Sent Events (SSE) connection is reconnected - | BeforeSseReconnect + | [] BeforeSseReconnect /// Triggered before a Server Sent Events (SSE) stream is opened - | BeforeSseStream + | [] BeforeSseStream /// Triggered before a swap is done, allows you to configure the swap | BeforeSwap @@ -214,10 +214,10 @@ type HxEvent = | [] SendError /// Triggered when an error occurs with a SSE source - | [] SseError + | [] SseError /// Triggered when an SSE source is opened - | [] SseOpen + | [] SseOpen /// Triggered when an error occurs during the swap phase | [] SwapError @@ -301,8 +301,8 @@ type HxEvent = PushedIntoHistory, ("pushedIntoHistory", "after:push:into:history") ResponseError, ("responseError", "response:error") SendError, ("sendError", "error") - SseError, ("sseError", "error") - SseOpen, ("sseOpen", "sse-open") + SseError, ("sseError", "sse:error") + SseOpen, ("sseOpen", "after:sse:connection") SwapError, ("swapError", "error") TargetError, ("targetError", "error") Timeout, ("timeout", "error") @@ -621,12 +621,14 @@ module HtmxAttrs = /// The URL which will receive the request (use with _hxMethod) /// The URL for the attribute /// Documentation - let _hxAction url = attr "hx-action" url + let _hxAction url = + attr "hx-action" url /// Progressively enhances anchors and forms to use AJAX requests /// Use _hxNoBoost to set to false /// Documentation - let _hxBoost = attr "hx-boost" "true" + let _hxBoost = + attr "hx-boost" "true" /// Configure request behavior /// The configuration parameters to use for the request @@ -764,7 +766,8 @@ module HtmxAttrs = /// Attach an event handler for htmx events /// The HxEvent to be handled /// The script to be executed when the event occurs - /// A configured hx-on:: attribute + /// A configured hx-on:htmx: attribute + /// For events defined in htmax, use _hxOnMax /// /// Documentation let _hxOnHxEvent (hxEvent: HxEvent) handler = @@ -963,14 +966,14 @@ module HtmxAttrs = /// The URL from which events will be received /// A configured sse-connect attribute /// Extension Docs - let _sseConnect url = + let [] _sseConnect url = attr "sse-connect" url /// The name(s) of the message(s) to swap into the DOM /// The message names (comma-delimited) to swap (use "message" for unnamed events) /// A configured sse-swap attribute /// Extension Docs - let _sseSwap messages = + let [] _sseSwap messages = attr "sse-swap" messages @@ -1011,9 +1014,6 @@ module Script = /// Script tag to load the package-provided version of htmx let local = script [ _src htmxLocalScript ] [] - /// Script tag to load the package-provided version of the htmx-plus-extensions bundle - let localMax = script [ _src htmaxLocalScript ] [] - /// Script tag to load the minified version from jsdelivr.net /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) let cdnMinified = @@ -1027,20 +1027,26 @@ module Script = script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js" _integrity "sha384-OFLRIZpuqI2wwFozxvDGcuF3TQ36ySMgp44WEthOiR4wFzRkhZbK72HFaBo2C3cx" _crossorigin "anonymous" ] [] + + /// Script tags to load the htmax bundle + module Max = + + /// Script tag to load the package-provided version of the htmx-plus-extensions bundle + let local = script [ _src htmaxLocalScript ] [] + + /// Script tag to load the minified htmx-plus-extensions bundle from jsdelivr.net + /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) + let cdnMinified = + script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js" + _integrity "sha384-Qoqie5IRtOE79SDFFRSb/yKi+pkzpSnfjgwr1KksyP14OaHkLHar0KrLVxUwlsJF" + _crossorigin "anonymous" ] [] - /// Script tag to load the minified htmx-plus-extensions bundle from jsdelivr.net - /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) - let cdnMaxMinified = - script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js" - _integrity "sha384-Qoqie5IRtOE79SDFFRSb/yKi+pkzpSnfjgwr1KksyP14OaHkLHar0KrLVxUwlsJF" - _crossorigin "anonymous" ] [] - - /// Script tag to load the unminified htmx-plus-extensions bundle from jsdelivr.net - /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) - let cdnMaxUnminified = - script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js" - _integrity "sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK" - _crossorigin "anonymous" ] [] + /// Script tag to load the unminified htmx-plus-extensions bundle from jsdelivr.net + /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) + let cdnUnminified = + script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js" + _integrity "sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK" + _crossorigin "anonymous" ] [] /// Script tag to load the minified version from jsdelivr.net []