diff --git a/src/Common.Tests/Giraffe.Htmx.Common.Tests.fsproj b/src/Common.Tests/Giraffe.Htmx.Common.Tests.fsproj
new file mode 100644
index 0000000..58aa294
--- /dev/null
+++ b/src/Common.Tests/Giraffe.Htmx.Common.Tests.fsproj
@@ -0,0 +1,30 @@
+
+
+
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/src/Common.Tests/Program.fs b/src/Common.Tests/Program.fs
new file mode 100644
index 0000000..fdc31cd
--- /dev/null
+++ b/src/Common.Tests/Program.fs
@@ -0,0 +1 @@
+module Program = let [] main _ = 0
diff --git a/src/Common.Tests/Tests.fs b/src/Common.Tests/Tests.fs
new file mode 100644
index 0000000..4172698
--- /dev/null
+++ b/src/Common.Tests/Tests.fs
@@ -0,0 +1,35 @@
+module Tests
+
+open Giraffe.Htmx
+open Xunit
+
+/// Tests for the HxSwap module
+module Swap =
+
+ []
+ let ``InnerHtml is correct`` () =
+ Assert.Equal ("innerHTML", HxSwap.InnerHtml)
+
+ []
+ let ``OuterHtml is correct`` () =
+ Assert.Equal ("outerHTML", HxSwap.OuterHtml)
+
+ []
+ let ``BeforeBegin is correct`` () =
+ Assert.Equal ("beforebegin", HxSwap.BeforeBegin)
+
+ []
+ let ``BeforeEnd is correct`` () =
+ Assert.Equal ("beforeend", HxSwap.BeforeEnd)
+
+ []
+ let ``AfterBegin is correct`` () =
+ Assert.Equal ("afterbegin", HxSwap.AfterBegin)
+
+ []
+ let ``AfterEnd is correct`` () =
+ Assert.Equal ("afterend", HxSwap.AfterEnd)
+
+ []
+ let ``None is correct`` () =
+ Assert.Equal ("none", HxSwap.None)
diff --git a/src/Common/Common.fs b/src/Common/Common.fs
new file mode 100644
index 0000000..9a75b8c
--- /dev/null
+++ b/src/Common/Common.fs
@@ -0,0 +1,28 @@
+/// Common definitions shared between attribute values and response headers
+[]
+module Giraffe.Htmx.Common
+
+/// Valid values for the `hx-swap` attribute / `HX-Reswap` header (may be combined with swap/settle/scroll/show config)
+[]
+module HxSwap =
+
+ /// The default, replace the inner html of the target element
+ let InnerHtml = "innerHTML"
+
+ /// Replace the entire target element with the response
+ let OuterHtml = "outerHTML"
+
+ /// Insert the response before the target element
+ let BeforeBegin = "beforebegin"
+
+ /// Insert the response before the first child of the target element
+ let AfterBegin = "afterbegin"
+
+ /// Insert the response after the last child of the target element
+ let BeforeEnd = "beforeend"
+
+ /// Insert the response after the target element
+ let AfterEnd = "afterend"
+
+ /// Does not append content from response (out of band items will still be processed).
+ let None = "none"
diff --git a/src/Common/Giraffe.Htmx.Common.fsproj b/src/Common/Giraffe.Htmx.Common.fsproj
new file mode 100644
index 0000000..112a122
--- /dev/null
+++ b/src/Common/Giraffe.Htmx.Common.fsproj
@@ -0,0 +1,11 @@
+
+
+
+ true
+
+
+
+
+
+
+
diff --git a/src/Giraffe.Htmx.sln b/src/Giraffe.Htmx.sln
new file mode 100644
index 0000000..c1ebafb
--- /dev/null
+++ b/src/Giraffe.Htmx.sln
@@ -0,0 +1,52 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30114.105
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx", "Htmx\Giraffe.Htmx.fsproj", "{8AB3085C-5236-485A-8565-A09106E72E1E}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx.Tests", "Htmx.Tests\Giraffe.Htmx.Tests.fsproj", "{D7CDD578-7A6F-4EF6-846A-80A55037E049}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.ViewEngine.Htmx", "ViewEngine.Htmx\Giraffe.ViewEngine.Htmx.fsproj", "{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.ViewEngine.Htmx.Tests", "ViewEngine.Htmx.Tests\Giraffe.ViewEngine.Htmx.Tests.fsproj", "{F21C28CE-1F18-4CB0-B2F7-10DABE84FB78}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx.Common", "Common\Giraffe.Htmx.Common.fsproj", "{75D66845-F93A-4463-AD29-A8B16E4D4BA9}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx.Common.Tests", "Common.Tests\Giraffe.Htmx.Common.Tests.fsproj", "{E261A653-68D5-4D7B-99A4-F09282B50F8A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8AB3085C-5236-485A-8565-A09106E72E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8AB3085C-5236-485A-8565-A09106E72E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8AB3085C-5236-485A-8565-A09106E72E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8AB3085C-5236-485A-8565-A09106E72E1E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D7CDD578-7A6F-4EF6-846A-80A55037E049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D7CDD578-7A6F-4EF6-846A-80A55037E049}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D7CDD578-7A6F-4EF6-846A-80A55037E049}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D7CDD578-7A6F-4EF6-846A-80A55037E049}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F21C28CE-1F18-4CB0-B2F7-10DABE84FB78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F21C28CE-1F18-4CB0-B2F7-10DABE84FB78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F21C28CE-1F18-4CB0-B2F7-10DABE84FB78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F21C28CE-1F18-4CB0-B2F7-10DABE84FB78}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E261A653-68D5-4D7B-99A4-F09282B50F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E261A653-68D5-4D7B-99A4-F09282B50F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E261A653-68D5-4D7B-99A4-F09282B50F8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E261A653-68D5-4D7B-99A4-F09282B50F8A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/src/Htmx.Tests/Tests.fs b/src/Htmx.Tests/Tests.fs
index d123f8b..213ac70 100644
--- a/src/Htmx.Tests/Tests.fs
+++ b/src/Htmx.Tests/Tests.fs
@@ -283,6 +283,17 @@ module HandlerTests =
Assert.Equal ("false", dic["HX-Replace-Url"][0])
}
+ []
+ let ``withHxReswap succeeds`` () =
+ let ctx = Substitute.For ()
+ let dic = HeaderDictionary ()
+ ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
+ task {
+ let! _ = withHxReswap HxSwap.BeforeEnd next ctx
+ Assert.True (dic.ContainsKey "HX-Reswap")
+ Assert.Equal (HxSwap.BeforeEnd, dic["HX-Reswap"][0])
+ }
+
[]
let ``withHxRetarget succeeds`` () =
let ctx = Substitute.For ()
diff --git a/src/Htmx/Giraffe.Htmx.fsproj b/src/Htmx/Giraffe.Htmx.fsproj
index cf71072..09928ad 100644
--- a/src/Htmx/Giraffe.Htmx.fsproj
+++ b/src/Htmx/Giraffe.Htmx.fsproj
@@ -15,4 +15,8 @@
+
+
+
+
diff --git a/src/Htmx/Htmx.fs b/src/Htmx/Htmx.fs
index e9324a2..ff7f1e0 100644
--- a/src/Htmx/Htmx.fs
+++ b/src/Htmx/Htmx.fs
@@ -6,7 +6,7 @@ open System
/// Determine if the given header is present
let private hdr (headers : IHeaderDictionary) hdr =
- match headers.[hdr] with it when it = StringValues.Empty -> None | it -> Some it[0]
+ match headers[hdr] with it when it = StringValues.Empty -> None | it -> Some it[0]
/// Extensions to the header dictionary
type IHeaderDictionary with
@@ -93,7 +93,11 @@ module Handlers =
/// Explicitly do not replace the current URL in the history stack
let withHxNoReplaceUrl : HttpHandler =
toLowerBool false |> withHxReplaceUrl
-
+
+ /// Override the `hx-swap` attribute from the initiating element
+ let withHxReswap : string -> HttpHandler =
+ setHttpHeader "HX-Reswap"
+
/// Allows you to override the `hx-target` attribute
let withHxRetarget : string -> HttpHandler =
setHttpHeader "HX-Retarget"
diff --git a/src/ViewEngine.Htmx.Tests/Tests.fs b/src/ViewEngine.Htmx.Tests/Tests.fs
index 8d50cfe..c9a4a5b 100644
--- a/src/ViewEngine.Htmx.Tests/Tests.fs
+++ b/src/ViewEngine.Htmx.Tests/Tests.fs
@@ -101,38 +101,6 @@ module Request =
Assert.Equal ("\"noHeaders\": false", HxRequest.NoHeaders false)
-/// Tests for the HxSwap module
-module Swap =
-
- []
- let ``InnerHtml is correct`` () =
- Assert.Equal ("innerHTML", HxSwap.InnerHtml)
-
- []
- let ``OuterHtml is correct`` () =
- Assert.Equal ("outerHTML", HxSwap.OuterHtml)
-
- []
- let ``BeforeBegin is correct`` () =
- Assert.Equal ("beforebegin", HxSwap.BeforeBegin)
-
- []
- let ``BeforeEnd is correct`` () =
- Assert.Equal ("beforeend", HxSwap.BeforeEnd)
-
- []
- let ``AfterBegin is correct`` () =
- Assert.Equal ("afterbegin", HxSwap.AfterBegin)
-
- []
- let ``AfterEnd is correct`` () =
- Assert.Equal ("afterend", HxSwap.AfterEnd)
-
- []
- let ``None is correct`` () =
- Assert.Equal ("none", HxSwap.None)
-
-
/// Tests for the HxTrigger module
module Trigger =
diff --git a/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj b/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj
index 19f5cd9..665b85c 100644
--- a/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj
+++ b/src/ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj
@@ -15,4 +15,8 @@
+
+
+
+
diff --git a/src/ViewEngine.Htmx/Htmx.fs b/src/ViewEngine.Htmx/Htmx.fs
index 706ccc8..51a948d 100644
--- a/src/ViewEngine.Htmx/Htmx.fs
+++ b/src/ViewEngine.Htmx/Htmx.fs
@@ -68,32 +68,6 @@ module HxRequest =
let NoHeaders = toLowerBool >> sprintf "\"noHeaders\": %s"
-/// Valid values for the `hx-swap` attribute (may be combined with swap/settle/scroll/show config)
-[]
-module HxSwap =
-
- /// The default, replace the inner html of the target element
- let InnerHtml = "innerHTML"
-
- /// Replace the entire target element with the response
- let OuterHtml = "outerHTML"
-
- /// Insert the response before the target element
- let BeforeBegin = "beforebegin"
-
- /// Insert the response before the first child of the target element
- let AfterBegin = "afterbegin"
-
- /// Insert the response after the last child of the target element
- let BeforeEnd = "beforeend"
-
- /// Insert the response after the target element
- let AfterEnd = "afterend"
-
- /// Does not append content from response (out of band items will still be processed).
- let None = "none"
-
-
/// Helpers for the `hx-trigger` attribute
[]
module HxTrigger =