Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c12ad7cef | |||
| d4a7e0c9ce | |||
| de75672bb3 |
+60
-8
@@ -3,11 +3,14 @@
|
|||||||
module Giraffe.Htmx.Common
|
module Giraffe.Htmx.Common
|
||||||
|
|
||||||
/// <summary>The version of htmx embedded in the package</summary>
|
/// <summary>The version of htmx embedded in the package</summary>
|
||||||
let HtmxVersion = "2.0.8"
|
let HtmxVersion = "4.0.0-beta4"
|
||||||
|
|
||||||
/// <summary>The path for the provided htmx script</summary>
|
/// <summary>The path for the provided htmx script</summary>
|
||||||
let internal htmxLocalScript = $"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"
|
let internal htmxLocalScript = $"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"
|
||||||
|
|
||||||
|
/// <summary>The path for the provided htmax script</summary>
|
||||||
|
let internal htmaxLocalScript = $"/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"
|
||||||
|
|
||||||
/// <summary>Serialize a list of key/value pairs to JSON (very rudimentary)</summary>
|
/// <summary>Serialize a list of key/value pairs to JSON (very rudimentary)</summary>
|
||||||
/// <param name="pairs">The key/value pairs to be serialized to JSON</param>
|
/// <param name="pairs">The key/value pairs to be serialized to JSON</param>
|
||||||
/// <returns>A string with the key/value pairs serialized to JSON</returns>
|
/// <returns>A string with the key/value pairs serialized to JSON</returns>
|
||||||
@@ -25,8 +28,8 @@ let internal toLowerBool (boolValue: bool) =
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>Valid values for the <c>hx-swap</c> attribute / <c>HX-Reswap</c> header</summary>
|
/// <summary>Valid values for the <c>hx-swap</c> attribute / <c>HX-Reswap</c> header</summary>
|
||||||
/// <remarks>May be combined with <c>swap</c> / <c>settle</c> / <c>scroll</c> / <c>show</c> config)</remarks>
|
/// <remarks>May be combined with <c>swap</c> / <c>scroll</c> / <c>show</c> config)</remarks>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-swap/">Documentation</seealso>
|
/// <seealso href="https://four.htmx.org/attributes/hx-swap/">Documentation</seealso>
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module HxSwap =
|
module HxSwap =
|
||||||
|
|
||||||
@@ -38,22 +41,71 @@ module HxSwap =
|
|||||||
[<Literal>]
|
[<Literal>]
|
||||||
let OuterHtml = "outerHTML"
|
let OuterHtml = "outerHTML"
|
||||||
|
|
||||||
|
/// <summary>Morph the inner HTML of the target to the new content</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let InnerMorph = "innerMorph"
|
||||||
|
|
||||||
|
/// <summary>Morph the outer HTML of the target to the new content</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let OuterMorph = "outerMorph"
|
||||||
|
|
||||||
|
/// <summary>Morph the outer HTML of the target to the new content, recreating all children</summary>
|
||||||
|
/// <remarks>This is used internally by the new history extension, but can be used by others if desired</remarks>
|
||||||
|
[<Literal>]
|
||||||
|
let OuterSync = "outerSync"
|
||||||
|
|
||||||
|
/// <summary>Replace the text content of the target without parsing the response as HTML</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let TextContent = "textContent"
|
||||||
|
|
||||||
/// <summary>Insert the response before the target element</summary>
|
/// <summary>Insert the response before the target element</summary>
|
||||||
[<Literal>]
|
[<Literal>]
|
||||||
let BeforeBegin = "beforebegin"
|
let Before = "before"
|
||||||
|
|
||||||
|
/// <summary>Insert the response before the target element (pre-v4 name)</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let BeforeBegin = Before
|
||||||
|
|
||||||
/// <summary>Insert the response before the first child of the target element</summary>
|
/// <summary>Insert the response before the first child of the target element</summary>
|
||||||
[<Literal>]
|
[<Literal>]
|
||||||
let AfterBegin = "afterbegin"
|
let Prepend = "prepend"
|
||||||
|
|
||||||
|
/// <summary>Insert the response before the first child of the target element (pre-v4 name)</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let AfterBegin = Prepend
|
||||||
|
|
||||||
/// <summary>Insert the response after the last child of the target element</summary>
|
/// <summary>Insert the response after the last child of the target element</summary>
|
||||||
[<Literal>]
|
[<Literal>]
|
||||||
let BeforeEnd = "beforeend"
|
let Append = "append"
|
||||||
|
|
||||||
|
/// <summary>Insert the response after the last child of the target element (pre-v4 name)</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let BeforeEnd = Append
|
||||||
|
|
||||||
/// <summary>Insert the response after the target element</summary>
|
/// <summary>Insert the response after the target element</summary>
|
||||||
[<Literal>]
|
[<Literal>]
|
||||||
let AfterEnd = "afterend"
|
let After = "after"
|
||||||
|
|
||||||
/// <summary>Does not append content from response (out of band items will still be processed).</summary>
|
/// <summary>Insert the response after the target element (pre-v4 name)</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let AfterEnd = After
|
||||||
|
|
||||||
|
/// <summary>Delete the target element regardless of response</summary>
|
||||||
|
[<Literal>]
|
||||||
|
let Delete = "delete"
|
||||||
|
|
||||||
|
/// <summary>Does not append content from response (out of band items will still be processed)</summary>
|
||||||
[<Literal>]
|
[<Literal>]
|
||||||
let None = "none"
|
let None = "none"
|
||||||
|
|
||||||
|
/// <summary>Update existing elements by <c>id</c> and add new ones</summary>
|
||||||
|
/// <remarks>This requires the <c>upsert</c> extension</remarks>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/upsert">Extension</seealso>
|
||||||
|
[<Literal>]
|
||||||
|
let Upsert = "upsert"
|
||||||
|
|
||||||
|
/// <summary>Specify that the target of the htmx request should be downloaded</summary>
|
||||||
|
/// <remarks>This requires the <c>hx-download</c> extension (included in the htmax bundle)</remarks>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-download#explicit-swap-style">Documentation</seealso>
|
||||||
|
[<Literal>]
|
||||||
|
let Download = "download"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
<PackageReference Include="FSharp.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,4 +2,6 @@
|
|||||||
|
|
||||||
This package contains common code shared between [`Giraffe.Htmx`](https://www.nuget.org/packages/Giraffe.Htmx) and [`Giraffe.ViewEngine.Htmx`](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx), and will be automatically installed when you install either one. It also contains htmx as a static web asset, allowing it to be loaded from your local (or published) project.
|
This package contains common code shared between [`Giraffe.Htmx`](https://www.nuget.org/packages/Giraffe.Htmx) and [`Giraffe.ViewEngine.Htmx`](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx), and will be automatically installed when you install either one. It also contains htmx as a static web asset, allowing it to be loaded from your local (or published) project.
|
||||||
|
|
||||||
**htmx version: 2.0.8**
|
**htmx version: 4.0.0-beta4**
|
||||||
|
|
||||||
|
_**NOTE:** Pay special attention to breaking changes highlighted in the packages listed above._
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -2,16 +2,18 @@
|
|||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||||
<VersionPrefix>2.0.8</VersionPrefix>
|
<VersionPrefix>4.0.0</VersionPrefix>
|
||||||
|
<VersionSuffix>beta4</VersionSuffix>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PackageReleaseNotes>- Adds package-provided script (available via Giraffe.Htmx.Common); use app.MapStaticAssets() for publish support, use builder.UseStaticWebAssets() for non-development/non-published execution
|
<PackageReleaseNotes>Update htmx 4 to beta4
|
||||||
- [View Engine] Deprecates Script.minified and Script.unminified (use Script.cdnMinified and Script.cdnUnminified instead)
|
- [Common] Update provided htmx 4 to 4.0.0-beta4
|
||||||
- Updates script tags to pull htmx 2.0.8 (no header or attribute changes)
|
- [Common] Add htmax bundle to provided version
|
||||||
- Adds .NET 10 support
|
- [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, outerSync swap
|
||||||
|
- [View Engine] Updated script tags to pull htmx 4.0.0-beta4, added "max" bundle links for local and CDN
|
||||||
|
|
||||||
See full release notes, including more info about the package-provided script, at https://git.bitbadger.solutions/bit-badger/Giraffe.Htmx/releases/tag/v2.0.8
|
See package and prior alpha release READMEs; v2 to v4 is not an update-and-forget-it release
|
||||||
|
|
||||||
NOTE: As of 2.0.6, the CDN for htmx changed from unpkg.com to cdn.jsdelivr.net; sites with Content-Security-Policy headers will want to update their allowed script-src domains accordingly
|
|
||||||
</PackageReleaseNotes>
|
</PackageReleaseNotes>
|
||||||
<Authors>danieljsummers</Authors>
|
<Authors>danieljsummers</Authors>
|
||||||
<Company>Bit Badger Solutions</Company>
|
<Company>Bit Badger Solutions</Company>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="Expecto" Version="11.0.0" />
|
||||||
|
<PackageVersion Include="Giraffe" Version="8.2.0" />
|
||||||
|
<PackageVersion Include="Giraffe.ViewEngine" Version="1.4.0" />
|
||||||
|
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||||
|
<PackageVersion Include="FSharp.Core" Version="10.1.301" />
|
||||||
|
<!-- <PackageVersion Update="FSharp.Core" Version="10.0.0" /> -->
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
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.ViewEngine.Htmx", "ViewEngine.Htmx\Giraffe.ViewEngine.Htmx.fsproj", "{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}"
|
|
||||||
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}") = "Tests", "Tests\Tests.fsproj", "{39823773-4311-4E79-9CA0-F9DDC40CAF6A}"
|
|
||||||
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
|
|
||||||
{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
|
|
||||||
{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
|
|
||||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="Common/Giraffe.Htmx.Common.fsproj" />
|
||||||
|
<Project Path="Htmx/Giraffe.Htmx.fsproj" />
|
||||||
|
<Project Path="Tests/Tests.fsproj" />
|
||||||
|
<Project Path="ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj" />
|
||||||
|
</Solution>
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Giraffe" Version="6.4.0" />
|
<PackageReference Include="Giraffe" />
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
<PackageReference Include="FSharp.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
+75
-5
@@ -4,6 +4,14 @@ open Microsoft.AspNetCore.Http
|
|||||||
open Microsoft.Extensions.Primitives
|
open Microsoft.Extensions.Primitives
|
||||||
open System
|
open System
|
||||||
|
|
||||||
|
/// <summary>The request types which may be set in the <c>HX-Request</c> header</summary>
|
||||||
|
type HxRequestTypes =
|
||||||
|
/// <summary>A request targeting the <c>body</c> tag or using an <c>hx-select</c> attribute</summary>
|
||||||
|
| HxFullRequest
|
||||||
|
|
||||||
|
/// <summary>A request for partial content</summary>
|
||||||
|
| HxPartialRequest
|
||||||
|
|
||||||
/// Determine if the given header is present
|
/// Determine if the given header is present
|
||||||
let private hdr (headers : IHeaderDictionary) hdr =
|
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]
|
||||||
@@ -23,7 +31,13 @@ type IHeaderDictionary with
|
|||||||
member this.HxHistoryRestoreRequest
|
member this.HxHistoryRestoreRequest
|
||||||
with get () = hdr this "HX-History-Restore-Request" |> Option.map bool.Parse
|
with get () = hdr this "HX-History-Restore-Request" |> Option.map bool.Parse
|
||||||
|
|
||||||
|
/// <summary><c>true</c> if the request has been fired by the preload extension</summary>
|
||||||
|
/// <remarks><c>preload</c> is part of the htmax htmx-plus-extensions bundle</remarks>
|
||||||
|
member this.HxPreloaded
|
||||||
|
with get () = hdr this "HX-Preloaded" |> Option.map bool.Parse
|
||||||
|
|
||||||
/// <summary>The user response to an <c>hx-prompt</c></summary>
|
/// <summary>The user response to an <c>hx-prompt</c></summary>
|
||||||
|
[<Obsolete "hx-prompt is removed in v4">]
|
||||||
member this.HxPrompt
|
member this.HxPrompt
|
||||||
with get () = hdr this "HX-Prompt"
|
with get () = hdr this "HX-Prompt"
|
||||||
|
|
||||||
@@ -31,15 +45,59 @@ type IHeaderDictionary with
|
|||||||
member this.HxRequest
|
member this.HxRequest
|
||||||
with get () = hdr this "HX-Request" |> Option.map bool.Parse
|
with get () = hdr this "HX-Request" |> Option.map bool.Parse
|
||||||
|
|
||||||
|
/// <summary>The ID of the request (WebSocket extension requests only)</summary>
|
||||||
|
/// <remarks><c>hx-ws</c> is part of the htmax htmx-plus-extensions bundle</remarks>
|
||||||
|
member this.HxRequestId
|
||||||
|
with get () = hdr this "HX-Request-ID"
|
||||||
|
|
||||||
|
/// <summary>The request type sent by htmx</summary>
|
||||||
|
/// <seealso cref="HxRequestTypes" />
|
||||||
|
member this.HxRequestType
|
||||||
|
with get () =
|
||||||
|
match hdr this "HX-Request-Type" with
|
||||||
|
| Some typ when typ = "full" -> Some HxFullRequest
|
||||||
|
| Some typ when typ = "partial" -> Some HxPartialRequest
|
||||||
|
| Some _ -> None
|
||||||
|
| None -> None
|
||||||
|
|
||||||
|
/// <summary>The tag name (fst) and <c>id</c> attribute (snd) of the element triggering this request</summary>
|
||||||
|
member this.HxSource
|
||||||
|
with get () =
|
||||||
|
match hdr this "HX-Source" with
|
||||||
|
| Some src ->
|
||||||
|
let parts = src.Split "#"
|
||||||
|
if parts.Length = 1 then
|
||||||
|
Some (parts[0], None)
|
||||||
|
else
|
||||||
|
Some (parts[0], if parts[1] <> "" then Some parts[1] else None)
|
||||||
|
| None -> None
|
||||||
|
|
||||||
/// <summary>The <c>id</c> attribute of the target element if it exists</summary>
|
/// <summary>The <c>id</c> attribute of the target element if it exists</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In v4, this changed to tag name (fst) and <c>id</c> (snd); to resolve build errors, and restore the prior
|
||||||
|
/// behavior of <c>[id] option</c>, replace
|
||||||
|
/// <code>ctx.HxTarget</code>
|
||||||
|
/// with
|
||||||
|
/// <code>ctx.HxTarget |> Option.iter snd</code>
|
||||||
|
/// </remarks>
|
||||||
member this.HxTarget
|
member this.HxTarget
|
||||||
with get () = hdr this "HX-Target"
|
with get () =
|
||||||
|
match hdr this "HX-Target" with
|
||||||
|
| Some src ->
|
||||||
|
let parts = src.Split "#"
|
||||||
|
if parts.Length = 1 then
|
||||||
|
Some (parts[0], None)
|
||||||
|
else
|
||||||
|
Some (parts[0], if parts[1] <> "" then Some parts[1] else None)
|
||||||
|
| None -> None
|
||||||
|
|
||||||
/// <summary>The <c>id</c> attribute of the triggered element if it exists</summary>
|
/// <summary>The <c>id</c> attribute of the triggered element if it exists</summary>
|
||||||
|
[<Obsolete "HX-Trigger is removed in v4; use the second item of HX-Source">]
|
||||||
member this.HxTrigger
|
member this.HxTrigger
|
||||||
with get () = hdr this "HX-Trigger"
|
with get () = hdr this "HX-Trigger"
|
||||||
|
|
||||||
/// <summary>The <c>name</c> attribute of the triggered element if it exists</summary>
|
/// <summary>The <c>name</c> attribute of the triggered element if it exists</summary>
|
||||||
|
[<Obsolete "HX-Trigger-Name is removed in v4; may be available via extension, but will be removed from this library">]
|
||||||
member this.HxTriggerName
|
member this.HxTriggerName
|
||||||
with get () = hdr this "HX-Trigger-Name"
|
with get () = hdr this "HX-Trigger-Name"
|
||||||
|
|
||||||
@@ -62,6 +120,14 @@ module Handlers =
|
|||||||
|
|
||||||
open Giraffe.Htmx.Common
|
open Giraffe.Htmx.Common
|
||||||
|
|
||||||
|
/// <summary>Instruct htmx to download a response from another path / URL</summary>
|
||||||
|
/// <param name="path">The path or URL where the downloadable content is found</param>
|
||||||
|
/// <returns>An HTTP handler with the <c>HX-Download</c> header set</returns>
|
||||||
|
/// <remarks>This requires the client-side <c>hx-download</c> extension (included in the htmax bundle)</remarks>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-download#hx-download-header">Documentation</seealso>
|
||||||
|
let withHxDownload (path: string) : HttpHandler =
|
||||||
|
setHttpHeader "HX-Download" path
|
||||||
|
|
||||||
/// <summary>Instruct htmx to perform a client-side redirect for content</summary>
|
/// <summary>Instruct htmx to perform a client-side redirect for content</summary>
|
||||||
/// <param name="path">The path where the content should be found</param>
|
/// <param name="path">The path where the content should be found</param>
|
||||||
/// <returns>An HTTP handler with the <c>HX-Location</c> header set</returns>
|
/// <returns>An HTTP handler with the <c>HX-Location</c> header set</returns>
|
||||||
@@ -147,29 +213,33 @@ module Handlers =
|
|||||||
/// <param name="evt">The call to the event that should be triggered</param>
|
/// <param name="evt">The call to the event that should be triggered</param>
|
||||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set</returns>
|
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set</returns>
|
||||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||||
|
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||||
let withHxTriggerAfterSettle (evt: string) : HttpHandler =
|
let withHxTriggerAfterSettle (evt: string) : HttpHandler =
|
||||||
setHttpHeader "HX-Trigger-After-Settle" evt
|
setHttpHeader "HX-Trigger" evt
|
||||||
|
|
||||||
/// <summary>Allows you to trigger multiple client side events after changes have settled</summary>
|
/// <summary>Allows you to trigger multiple client side events after changes have settled</summary>
|
||||||
/// <param name="evts">The calls to events that should be triggered</param>
|
/// <param name="evts">The calls to events that should be triggered</param>
|
||||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set for all given events</returns>
|
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set for all given events</returns>
|
||||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||||
|
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||||
let withHxTriggerManyAfterSettle evts : HttpHandler =
|
let withHxTriggerManyAfterSettle evts : HttpHandler =
|
||||||
toJson evts |> setHttpHeader "HX-Trigger-After-Settle"
|
toJson evts |> setHttpHeader "HX-Trigger"
|
||||||
|
|
||||||
/// <summary>Allows you to trigger a single client side event after DOM swapping occurs</summary>
|
/// <summary>Allows you to trigger a single client side event after DOM swapping occurs</summary>
|
||||||
/// <param name="evt">The call to the event that should be triggered</param>
|
/// <param name="evt">The call to the event that should be triggered</param>
|
||||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set</returns>
|
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set</returns>
|
||||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||||
|
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||||
let withHxTriggerAfterSwap (evt: string) : HttpHandler =
|
let withHxTriggerAfterSwap (evt: string) : HttpHandler =
|
||||||
setHttpHeader "HX-Trigger-After-Swap" evt
|
setHttpHeader "HX-Trigger" evt
|
||||||
|
|
||||||
/// <summary>Allows you to trigger multiple client side events after DOM swapping occurs</summary>
|
/// <summary>Allows you to trigger multiple client side events after DOM swapping occurs</summary>
|
||||||
/// <param name="evts">The calls to events that should be triggered</param>
|
/// <param name="evts">The calls to events that should be triggered</param>
|
||||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set for all given events</returns>
|
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set for all given events</returns>
|
||||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||||
|
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||||
let withHxTriggerManyAfterSwap evts : HttpHandler =
|
let withHxTriggerManyAfterSwap evts : HttpHandler =
|
||||||
toJson evts |> setHttpHeader "HX-Trigger-After-Swap"
|
toJson evts |> setHttpHeader "HX-Trigger"
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Load the package-provided version of the htmx script</summary>
|
/// <summary>Load the package-provided version of the htmx script</summary>
|
||||||
|
|||||||
+7
-5
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
This package enables server-side support for [htmx](https://htmx.org) within [Giraffe](https://giraffe.wiki) and ASP.NET's `HttpContext`.
|
This package enables server-side support for [htmx](https://htmx.org) within [Giraffe](https://giraffe.wiki) and ASP.NET's `HttpContext`.
|
||||||
|
|
||||||
**htmx version: 2.0.8**
|
**htmx version: 4.0.0-beta4**
|
||||||
|
|
||||||
_Upgrading from v1.x: the [migration guide](https://htmx.org/migration-guide-htmx-1/) does not currently specify any request or response header changes. This means that there are no required code changes in moving from v1.* to v2.*._
|
_Upgrading from v2.x: the [migration guide](https://four.htmx.org/docs/get-started/migration) lists changes for v4. For this package, the `HX-Trigger` and `HX-Trigger-Name` headers are marked obsolete. They are replaced by `HX-Source`, which provides the triggering tag name and `id` attribute. The `HX-Prompt` header has also been marked as obsolete, as the `hx-prompt` attribute which generated its content has been removed._
|
||||||
|
|
||||||
|
_Obsolete elements will be removed in the first production v4 release._
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
@@ -18,9 +20,9 @@ To obtain a request header, using the `IHeaderDictionary` extension properties:
|
|||||||
```fsharp
|
```fsharp
|
||||||
let myHandler : HttpHander =
|
let myHandler : HttpHander =
|
||||||
fun next ctx ->
|
fun next ctx ->
|
||||||
match ctx.HxPrompt with
|
match ctx.Target with
|
||||||
| Some prompt -> ... // do something with the text the user provided
|
| Some elt -> ... // do something with id of the target element
|
||||||
| None -> ... // no text provided
|
| None -> ... // no target element provided
|
||||||
```
|
```
|
||||||
|
|
||||||
To set a response header:
|
To set a response header:
|
||||||
|
|||||||
+40
-7
@@ -6,7 +6,7 @@ open Giraffe.Htmx
|
|||||||
/// Test to ensure the version was updated
|
/// Test to ensure the version was updated
|
||||||
let version =
|
let version =
|
||||||
test "HtmxVersion is correct" {
|
test "HtmxVersion is correct" {
|
||||||
Expect.equal HtmxVersion "2.0.8" "htmx version incorrect"
|
Expect.equal HtmxVersion "4.0.0-beta4" "htmx version incorrect"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests for the HxSwap module
|
/// Tests for the HxSwap module
|
||||||
@@ -18,21 +18,54 @@ let swap =
|
|||||||
test "OuterHtml is correct" {
|
test "OuterHtml is correct" {
|
||||||
Expect.equal HxSwap.OuterHtml "outerHTML" "Outer HTML swap value incorrect"
|
Expect.equal HxSwap.OuterHtml "outerHTML" "Outer HTML swap value incorrect"
|
||||||
}
|
}
|
||||||
test "BeforeBegin is correct" {
|
test "InnerMorph is correct" {
|
||||||
Expect.equal HxSwap.BeforeBegin "beforebegin" "Before Begin swap value incorrect"
|
Expect.equal HxSwap.InnerMorph "innerMorph" "Inner Morph swap value incorrect"
|
||||||
}
|
}
|
||||||
test "BeforeEnd is correct" {
|
test "OuterMorph is correct" {
|
||||||
Expect.equal HxSwap.BeforeEnd "beforeend" "Before End swap value incorrect"
|
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"
|
||||||
|
}
|
||||||
|
test "Before is correct" {
|
||||||
|
Expect.equal HxSwap.Before "before" "Before swap value incorrect"
|
||||||
|
}
|
||||||
|
test "BeforeBegin is correct" {
|
||||||
|
Expect.equal HxSwap.BeforeBegin HxSwap.Before "Before Begin swap value incorrect"
|
||||||
|
}
|
||||||
|
test "Prepend is correct" {
|
||||||
|
Expect.equal HxSwap.Prepend "prepend" "Prepend swap value incorrect"
|
||||||
}
|
}
|
||||||
test "AfterBegin is correct" {
|
test "AfterBegin is correct" {
|
||||||
Expect.equal HxSwap.AfterBegin "afterbegin" "After Begin swap value incorrect"
|
Expect.equal HxSwap.AfterBegin HxSwap.Prepend "Prepend swap value incorrect"
|
||||||
|
}
|
||||||
|
test "Append is correct" {
|
||||||
|
Expect.equal HxSwap.Append "append" "Append swap value incorrect"
|
||||||
|
}
|
||||||
|
test "BeforeEnd is correct" {
|
||||||
|
Expect.equal HxSwap.BeforeEnd HxSwap.Append "Before End swap value incorrect"
|
||||||
|
}
|
||||||
|
test "After is correct" {
|
||||||
|
Expect.equal HxSwap.After "after" "After swap value incorrect"
|
||||||
}
|
}
|
||||||
test "AfterEnd is correct" {
|
test "AfterEnd is correct" {
|
||||||
Expect.equal HxSwap.AfterEnd "afterend" "After End swap value incorrect"
|
Expect.equal HxSwap.AfterEnd HxSwap.After "After End swap value incorrect"
|
||||||
|
}
|
||||||
|
test "Delete is correct" {
|
||||||
|
Expect.equal HxSwap.Delete "delete" "Delete swap value incorrect"
|
||||||
}
|
}
|
||||||
test "None is correct" {
|
test "None is correct" {
|
||||||
Expect.equal HxSwap.None "none" "None swap value incorrect"
|
Expect.equal HxSwap.None "none" "None swap value incorrect"
|
||||||
}
|
}
|
||||||
|
test "Upsert is correct" {
|
||||||
|
Expect.equal HxSwap.Upsert "upsert" "Upsert swap value incorrect"
|
||||||
|
}
|
||||||
|
test "Download is correct" {
|
||||||
|
Expect.equal HxSwap.Download "download" "Download swap value incorrect"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
/// All tests for this module
|
/// All tests for this module
|
||||||
|
|||||||
+212
-72
@@ -3,7 +3,6 @@ module Htmx
|
|||||||
open System
|
open System
|
||||||
open Expecto
|
open Expecto
|
||||||
open Giraffe.Htmx
|
open Giraffe.Htmx
|
||||||
open Microsoft.AspNetCore.Html
|
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
open NSubstitute
|
open NSubstitute
|
||||||
|
|
||||||
@@ -74,19 +73,28 @@ let dictExtensions =
|
|||||||
ctx.Request.Headers.HxHistoryRestoreRequest.Value "The header should have been false"
|
ctx.Request.Headers.HxHistoryRestoreRequest.Value "The header should have been false"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "HxPrompt" [
|
testList "HxPreloaded" [
|
||||||
test "succeeds when the header is not present" {
|
test "succeeds when the header is not present" {
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
Expect.isNone ctx.Request.Headers.HxPrompt "There should not have been a header returned"
|
Expect.isNone ctx.Request.Headers.HxPreloaded "There should not have been a header returned"
|
||||||
}
|
}
|
||||||
test "succeeds when the header is present" {
|
test "succeeds when the header is present and true" {
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
let dic = HeaderDictionary()
|
let dic = HeaderDictionary()
|
||||||
dic.Add("HX-Prompt", "of course")
|
dic.Add("HX-Preloaded", "true")
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
Expect.isSome ctx.Request.Headers.HxPrompt "There should be a header present"
|
Expect.isSome ctx.Request.Headers.HxPreloaded "There should be a header present"
|
||||||
Expect.equal ctx.Request.Headers.HxPrompt.Value "of course" "The header value was incorrect"
|
Expect.isTrue ctx.Request.Headers.HxPreloaded.Value "The header should have been true"
|
||||||
|
}
|
||||||
|
// This is not a condition fired by the extension; the header will be either absent or true
|
||||||
|
test "succeeds when the header is present and false" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Preloaded", "false")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxPreloaded "There should be a header present"
|
||||||
|
Expect.isFalse ctx.Request.Headers.HxPreloaded.Value "The header should have been false"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "HxRequest" [
|
testList "HxRequest" [
|
||||||
@@ -112,49 +120,125 @@ let dictExtensions =
|
|||||||
Expect.isFalse ctx.Request.Headers.HxRequest.Value "The header should have been false"
|
Expect.isFalse ctx.Request.Headers.HxRequest.Value "The header should have been false"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "HxRequestId" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxRequestId "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Request-ID", "abcd1234")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxRequestId "There should be a header present"
|
||||||
|
Expect.equal ctx.Request.Headers.HxRequestId.Value "abcd1234" "The header value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "HxRequestType" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxRequestType "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is invalid" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Request-Type", "relaxed")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxRequestType "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds for a full request" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Request-Type", "full")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxRequestType "There have been a header returned"
|
||||||
|
Expect.equal ctx.Request.Headers.HxRequestType.Value HxFullRequest "The header value is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a partial request" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Request-Type", "partial")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxRequestType "There have been a header returned"
|
||||||
|
Expect.equal ctx.Request.Headers.HxRequestType.Value HxPartialRequest "The header value is incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "HxSource" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxSource "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present and both parts exist" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Source", "button#theId")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let hdr = ctx.Request.Headers.HxSource
|
||||||
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "button" "The source tag was incorrect"
|
||||||
|
Expect.isSome (snd hdr.Value) "There should be a source ID present"
|
||||||
|
Expect.equal (snd hdr.Value).Value "theId" "The source ID was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present and ID is blank" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Source", "a#")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let hdr = ctx.Request.Headers.HxSource
|
||||||
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "a" "The source tag was incorrect"
|
||||||
|
Expect.isNone (snd hdr.Value) "There should not be a source ID present"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present and ID is missing" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Source", "form")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let hdr = ctx.Request.Headers.HxSource
|
||||||
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "form" "The source tag was incorrect"
|
||||||
|
Expect.isNone (snd hdr.Value) "There should not be a source ID present"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "HxTarget" [
|
testList "HxTarget" [
|
||||||
test "succeeds when the header is not present" {
|
test "succeeds when the header is not present" {
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
Expect.isNone ctx.Request.Headers.HxTarget "There should not have been a header returned"
|
Expect.isNone ctx.Request.Headers.HxTarget "There should not have been a header returned"
|
||||||
}
|
}
|
||||||
test "succeeds when the header is present" {
|
test "succeeds when the header is present and both parts exist" {
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
let dic = HeaderDictionary()
|
let dic = HeaderDictionary()
|
||||||
dic.Add("HX-Target", "#leItem")
|
dic.Add("HX-Target", "div#leItem")
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
Expect.isSome ctx.Request.Headers.HxTarget "There should be a header present"
|
let hdr = ctx.Request.Headers.HxTarget
|
||||||
Expect.equal ctx.Request.Headers.HxTarget.Value "#leItem" "The header value was incorrect"
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "div" "The target tag was incorrect"
|
||||||
|
Expect.isSome (snd hdr.Value) "There should be a target ID present"
|
||||||
|
Expect.equal (snd hdr.Value).Value "leItem" "The header value was incorrect"
|
||||||
}
|
}
|
||||||
]
|
test "succeeds when the header is present and ID is blank" {
|
||||||
testList "HxTrigger" [
|
|
||||||
test "succeeds when the header is not present" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore
|
|
||||||
Expect.isNone ctx.Request.Headers.HxTrigger "There should not have been a header returned"
|
|
||||||
}
|
|
||||||
test "succeeds when the header is present" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
let dic = HeaderDictionary()
|
let dic = HeaderDictionary()
|
||||||
dic.Add("HX-Trigger", "#trig")
|
dic.Add("HX-Target", "span#")
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
Expect.isSome ctx.Request.Headers.HxTrigger "There should be a header present"
|
let hdr = ctx.Request.Headers.HxTarget
|
||||||
Expect.equal ctx.Request.Headers.HxTrigger.Value "#trig" "The header value was incorrect"
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "span" "The target tag was incorrect"
|
||||||
|
Expect.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||||
}
|
}
|
||||||
]
|
test "succeeds when the header is present and ID is missing" {
|
||||||
testList "HxTriggerName" [
|
|
||||||
test "succeeds when the header is not present" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
|
||||||
Expect.isNone ctx.Request.Headers.HxTriggerName "There should not have been a header returned"
|
|
||||||
}
|
|
||||||
test "HxTriggerName succeeds when the header is present" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
let dic = HeaderDictionary()
|
let dic = HeaderDictionary()
|
||||||
dic.Add("HX-Trigger-Name", "click")
|
dic.Add("HX-Target", "aside")
|
||||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
Expect.isSome ctx.Request.Headers.HxTriggerName "There should be a header present"
|
let hdr = ctx.Request.Headers.HxTarget
|
||||||
Expect.equal ctx.Request.Headers.HxTriggerName.Value "click" "The header value was incorrect"
|
Expect.isSome hdr "There should be a header present"
|
||||||
|
Expect.equal (fst hdr.Value) "aside" "The target tag was incorrect"
|
||||||
|
Expect.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -208,6 +292,14 @@ let next (ctx: HttpContext) = Task.FromResult(Some ctx)
|
|||||||
/// Tests for the HttpHandler functions provided in the Handlers module
|
/// Tests for the HttpHandler functions provided in the Handlers module
|
||||||
let handlers =
|
let handlers =
|
||||||
testList "HandlerTests" [
|
testList "HandlerTests" [
|
||||||
|
testTask "withHxDownload succeeds" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let! _ = withHxDownload "/files/stuff.pdf" next ctx
|
||||||
|
Expect.isTrue (dic.ContainsKey "HX-Download") "The HX-Download header should be present"
|
||||||
|
Expect.equal dic["HX-Download"].[0] "/files/stuff.pdf" "The HX-Download value was incorrect"
|
||||||
|
}
|
||||||
testTask "withHxLocation succeeds" {
|
testTask "withHxLocation succeeds" {
|
||||||
let ctx = Substitute.For<HttpContext>()
|
let ctx = Substitute.For<HttpContext>()
|
||||||
let dic = HeaderDictionary()
|
let dic = HeaderDictionary()
|
||||||
@@ -315,44 +407,6 @@ let handlers =
|
|||||||
Expect.equal
|
Expect.equal
|
||||||
dic["HX-Trigger"].[0] """{ "blah": "foo", "bleh": "bar" }""" "The HX-Trigger value was incorrect"
|
dic["HX-Trigger"].[0] """{ "blah": "foo", "bleh": "bar" }""" "The HX-Trigger value was incorrect"
|
||||||
}
|
}
|
||||||
testTask "withHxTriggerAfterSettle succeeds" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
let dic = HeaderDictionary()
|
|
||||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
|
||||||
let! _ = withHxTriggerAfterSettle "byTheWay" next ctx
|
|
||||||
Expect.isTrue
|
|
||||||
(dic.ContainsKey "HX-Trigger-After-Settle") "The HX-Trigger-After-Settle header should be present"
|
|
||||||
Expect.equal dic["HX-Trigger-After-Settle"].[0] "byTheWay" "The HX-Trigger-After-Settle value was incorrect"
|
|
||||||
}
|
|
||||||
testTask "withHxTriggerManyAfterSettle succeeds" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
let dic = HeaderDictionary()
|
|
||||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
|
||||||
let! _ = withHxTriggerManyAfterSettle [ "oof", "ouch"; "hmm", "uh" ] next ctx
|
|
||||||
Expect.isTrue
|
|
||||||
(dic.ContainsKey "HX-Trigger-After-Settle") "The HX-Trigger-After-Settle header should be present"
|
|
||||||
Expect.equal
|
|
||||||
dic["HX-Trigger-After-Settle"].[0] """{ "oof": "ouch", "hmm": "uh" }"""
|
|
||||||
"The HX-Trigger-After-Settle value was incorrect"
|
|
||||||
}
|
|
||||||
testTask "withHxTriggerAfterSwap succeeds" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
let dic = HeaderDictionary()
|
|
||||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
|
||||||
let! _ = withHxTriggerAfterSwap "justASec" next ctx
|
|
||||||
Expect.isTrue (dic.ContainsKey "HX-Trigger-After-Swap") "The HX-Trigger-After-Swap header should be present"
|
|
||||||
Expect.equal dic["HX-Trigger-After-Swap"].[0] "justASec" "The HX-Trigger-After-Swap value was incorrect"
|
|
||||||
}
|
|
||||||
testTask "withHxTriggerManyAfterSwap succeeds" {
|
|
||||||
let ctx = Substitute.For<HttpContext>()
|
|
||||||
let dic = HeaderDictionary()
|
|
||||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
|
||||||
let! _ = withHxTriggerManyAfterSwap [ "this", "1"; "that", "2" ] next ctx
|
|
||||||
Expect.isTrue (dic.ContainsKey "HX-Trigger-After-Swap") "The HX-Trigger-After-Swap header should be present"
|
|
||||||
Expect.equal
|
|
||||||
dic["HX-Trigger-After-Swap"].[0] """{ "this": "1", "that": "2" }"""
|
|
||||||
"The HX-Trigger-After-Swap value was incorrect"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/// Tests for the HtmxScript module
|
/// Tests for the HtmxScript module
|
||||||
@@ -366,5 +420,91 @@ let script =
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#nowarn 44 // Obsolete items still have tests
|
||||||
|
let dictExtensionsObs =
|
||||||
|
testList "IHeaderDictionaryExtensions (Obsolete)" [
|
||||||
|
testList "HxPrompt" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxPrompt "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Prompt", "of course")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxPrompt "There should be a header present"
|
||||||
|
Expect.equal ctx.Request.Headers.HxPrompt.Value "of course" "The header value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "HxTrigger" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxTrigger "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "succeeds when the header is present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Trigger", "#trig")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxTrigger "There should be a header present"
|
||||||
|
Expect.equal ctx.Request.Headers.HxTrigger.Value "#trig" "The header value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "HxTriggerName" [
|
||||||
|
test "succeeds when the header is not present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||||
|
Expect.isNone ctx.Request.Headers.HxTriggerName "There should not have been a header returned"
|
||||||
|
}
|
||||||
|
test "HxTriggerName succeeds when the header is present" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
dic.Add("HX-Trigger-Name", "click")
|
||||||
|
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
Expect.isSome ctx.Request.Headers.HxTriggerName "There should be a header present"
|
||||||
|
Expect.equal ctx.Request.Headers.HxTriggerName.Value "click" "The header value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
let handlerObs =
|
||||||
|
testList "Handler Tests (Obsolete)" [
|
||||||
|
testTask "withHxTriggerAfterSettle succeeds" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let! _ = withHxTriggerAfterSettle "byTheWay" next ctx
|
||||||
|
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||||
|
Expect.equal dic["HX-Trigger"].[0] "byTheWay" "The HX-Trigger value was incorrect"
|
||||||
|
}
|
||||||
|
testTask "withHxTriggerManyAfterSettle succeeds" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let! _ = withHxTriggerManyAfterSettle [ "oof", "ouch"; "hmm", "uh" ] next ctx
|
||||||
|
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||||
|
Expect.equal dic["HX-Trigger"].[0] """{ "oof": "ouch", "hmm": "uh" }""" "The HX-Trigger value was incorrect"
|
||||||
|
}
|
||||||
|
testTask "withHxTriggerAfterSwap succeeds" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let! _ = withHxTriggerAfterSwap "justASec" next ctx
|
||||||
|
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||||
|
Expect.equal dic["HX-Trigger"].[0] "justASec" "The HX-Trigger value was incorrect"
|
||||||
|
}
|
||||||
|
testTask "withHxTriggerManyAfterSwap succeeds" {
|
||||||
|
let ctx = Substitute.For<HttpContext>()
|
||||||
|
let dic = HeaderDictionary()
|
||||||
|
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||||
|
let! _ = withHxTriggerManyAfterSwap [ "this", "1"; "that", "2" ] next ctx
|
||||||
|
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||||
|
Expect.equal dic["HX-Trigger"].[0] """{ "this": "1", "that": "2" }""" "The HX-Trigger value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// All tests for this module
|
/// All tests for this module
|
||||||
let allTests = testList "Htmx" [ dictExtensions; reqExtensions; handlers; script ]
|
let allTests = testList "Htmx" [ dictExtensions; reqExtensions; handlers; script; dictExtensionsObs; handlerObs ]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
open Expecto
|
open Expecto
|
||||||
|
|
||||||
let allTests = testList "Giraffe" [ Common.allTests; Htmx.allTests; ViewEngine.allTests ]
|
let allTests = testList "Giraffe" [ Common.allTests; Htmx.allTests; ViewEngine.allTests; ViewEngineMax.allTests ]
|
||||||
|
|
||||||
[<EntryPoint>]
|
[<EntryPoint>]
|
||||||
let main args = runTestsWithCLIArgs [] args allTests
|
let main args = runTestsWithCLIArgs [] args allTests
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<Compile Include="Common.fs" />
|
<Compile Include="Common.fs" />
|
||||||
<Compile Include="Htmx.fs" />
|
<Compile Include="Htmx.fs" />
|
||||||
<Compile Include="ViewEngine.fs" />
|
<Compile Include="ViewEngine.fs" />
|
||||||
|
<Compile Include="ViewEngineMax.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -18,9 +19,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Expecto" Version="10.2.1" />
|
<PackageReference Include="Expecto" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="NSubstitute" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
<PackageReference Include="FSharp.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+717
-402
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,335 @@
|
|||||||
|
module ViewEngineMax
|
||||||
|
|
||||||
|
open Expecto
|
||||||
|
open Giraffe.ViewEngine.Htmax
|
||||||
|
|
||||||
|
/// Tests for the HxBrowserIndicatorConfigItem type
|
||||||
|
let hxBrowserIndicatorConfigItem =
|
||||||
|
testList "HxBrowserIndicatorConfigItem" [
|
||||||
|
testList "ToHcon" [
|
||||||
|
test "succeeds for BoostBrowserIndicator" {
|
||||||
|
Expect.equal
|
||||||
|
((BoostBrowserIndicator false).ToHcon())
|
||||||
|
"boostBrowserIndicator:false"
|
||||||
|
"The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Tests for the HxPreloadConfigItem type
|
||||||
|
let hxPreloadConfigItem =
|
||||||
|
testList "HxPreloadConfigItem" [
|
||||||
|
testList "ToHcon" [
|
||||||
|
test "succeeds for AutoBoost" {
|
||||||
|
Expect.equal ((AutoBoost false).ToHcon()) "autoBoost:false" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for BoostEvent" {
|
||||||
|
Expect.equal ((BoostEvent "mouseover").ToHcon()) "boostEvent:mouseover" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for BoostTimeout" {
|
||||||
|
Expect.equal ((BoostTimeout 30_000).ToHcon()) "boostTimeout:30000" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Tests for the HxSseWsConfigItem type
|
||||||
|
let hxSseWsConfigItem =
|
||||||
|
testList "HxSseWsConfigItem" [
|
||||||
|
testList "ToHcon" [
|
||||||
|
test "succeeds for Reconnect" {
|
||||||
|
Expect.equal ((Reconnect true).ToHcon()) "reconnect:true" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
testList "ReconnectDelay" [
|
||||||
|
test "succeeds for numeric value" {
|
||||||
|
Expect.equal ((ReconnectDelay "3000").ToHcon()) "reconnectDelay:3000" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for value with units" {
|
||||||
|
Expect.equal ((ReconnectDelay "3s").ToHcon()) "reconnectDelay:3s" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "ReconnectMaxDelay" [
|
||||||
|
test "succeeds for numeric value" {
|
||||||
|
Expect.equal
|
||||||
|
((ReconnectMaxDelay "30000").ToHcon()) "reconnectMaxDelay:30000" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for value with units" {
|
||||||
|
Expect.equal
|
||||||
|
((ReconnectMaxDelay "50s").ToHcon()) "reconnectMaxDelay:50s" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "ReconnectMaxAttempts" [
|
||||||
|
test "succeeds for negative 1" {
|
||||||
|
Expect.equal
|
||||||
|
((ReconnectMaxAttempts -1).ToHcon())
|
||||||
|
"reconnectMaxAttempts:Infinity"
|
||||||
|
"The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for zero" {
|
||||||
|
Expect.equal
|
||||||
|
((ReconnectMaxAttempts 0).ToHcon()) "reconnectMaxAttempts:0" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for positive number" {
|
||||||
|
Expect.equal
|
||||||
|
((ReconnectMaxAttempts 7).ToHcon()) "reconnectMaxAttempts:7" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
test "succeeds for ReconnectJitter" {
|
||||||
|
Expect.equal ((ReconnectJitter 0.7).ToHcon()) "reconnectJitter:0.7" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for PauseOnBackground" {
|
||||||
|
Expect.equal
|
||||||
|
((PauseOnBackground false).ToHcon()) "pauseOnBackground:false" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for PendingRequestTtl" {
|
||||||
|
Expect.equal ((PendingRequestTtl 5000).ToHcon()) "pendingRequestTTL:5000" "The HCON value was incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Tests for the HxConfig module
|
||||||
|
let hxConfig =
|
||||||
|
testList "HxConfig" [
|
||||||
|
test "hxBrowserIndicatorConfig succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(hxBrowserIndicatorConfig [ BoostBrowserIndicator true ])
|
||||||
|
"browser-indicator.boostBrowserIndicator:true"
|
||||||
|
"Config settings not correct"
|
||||||
|
}
|
||||||
|
test "hxPreloadConfig succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(hxPreloadConfig [ AutoBoost true; BoostTimeout 3_000 ])
|
||||||
|
"preload.autoBoost:true preload.boostTimeout:3000"
|
||||||
|
"Config settings not correct"
|
||||||
|
}
|
||||||
|
test "hxSseConfig succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(hxSseConfig [ ReconnectDelay "5s"; PauseOnBackground false ])
|
||||||
|
"sse.reconnectDelay:5s sse.pauseOnBackground:false"
|
||||||
|
"Config settings not correct"
|
||||||
|
}
|
||||||
|
test "hxWsConfig succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(hxWsConfig [ Reconnect false; PendingRequestTtl 40_000 ])
|
||||||
|
"ws.reconnect:false ws.pendingRequestTTL:40000"
|
||||||
|
"Config settings not correct"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/// 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 "DownloadComplete" [
|
||||||
|
test "ToString succeeds" {
|
||||||
|
Expect.equal (string DownloadComplete) "downloadComplete" "DownloadComplete event name not correct"
|
||||||
|
}
|
||||||
|
test "ToHxOnString succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(DownloadComplete.ToHxOnString())
|
||||||
|
"download:complete"
|
||||||
|
"DownloadComplete hx-on event name not correct"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "DownloadProgress" [
|
||||||
|
test "ToString succeeds" {
|
||||||
|
Expect.equal (string DownloadProgress) "downloadProgress" "DownloadProgress event name not correct"
|
||||||
|
}
|
||||||
|
test "ToHxOnString succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(DownloadProgress.ToHxOnString())
|
||||||
|
"download:progress"
|
||||||
|
"DownloadProgress hx-on event name not correct"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "DownloadStart" [
|
||||||
|
test "ToString succeeds" {
|
||||||
|
Expect.equal (string DownloadStart) "downloadStart" "DownloadStart event name not correct"
|
||||||
|
}
|
||||||
|
test "ToHxOnString succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(DownloadStart.ToHxOnString()) "download:start" "DownloadStart 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 "_hxBrowserIndicator succeeds" {
|
||||||
|
a [ _hxBrowserIndicator ] [] |> shouldRender """<a hx-browser-indicator="true"></a>"""
|
||||||
|
}
|
||||||
|
test "_hxLive succeeds" {
|
||||||
|
main [ _hxLive "q().doStuff()" ] [] |> shouldRender """<main hx-live="q().doStuff()"></main>"""
|
||||||
|
}
|
||||||
|
test "_hxOnMax succeeds" {
|
||||||
|
body [ _hxOnMax SseClose "alert(done)" ] []
|
||||||
|
|> shouldRender """<body hx-on:htmx:sse:close="alert(done)"></body>"""
|
||||||
|
}
|
||||||
|
testList "_hxPreload" [
|
||||||
|
test "succeeds when given an event" {
|
||||||
|
blockquote [ _hxPreload (Some "focus") ] []
|
||||||
|
|> shouldRender """<blockquote hx-preload="focus"></blockquote>"""
|
||||||
|
}
|
||||||
|
test "succeeds when not given an event" {
|
||||||
|
em [ _hxPreload None ] [] |> shouldRender """<em hx-preload></em>"""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
test "_hxSseClose succeeds" {
|
||||||
|
form [ _hxSseClose "fin" ] [] |> shouldRender """<form hx-sse:close="fin"></form>"""
|
||||||
|
}
|
||||||
|
test "_hxSseConnect succeeds" {
|
||||||
|
div [ _hxSseConnect "/path/to/resource" ] []
|
||||||
|
|> shouldRender """<div hx-sse:connect="/path/to/resource"></div>"""
|
||||||
|
}
|
||||||
|
test "_hxTargets succeeds" {
|
||||||
|
dl [ _hxTargets ".ephemeral" ] [] |> shouldRender """<dl hx-targets=".ephemeral"></dl>"""
|
||||||
|
}
|
||||||
|
test "_hxWsConnect succeeds" {
|
||||||
|
p [ _hxWsConnect "/over/here" ] [] |> shouldRender """<p hx-ws:connect="/over/here"></p>"""
|
||||||
|
}
|
||||||
|
testList "_hxWsSend" [
|
||||||
|
test "succeeds when given a URL" {
|
||||||
|
span [ _hxWsSend (Some "/a/new/place") ] []
|
||||||
|
|> shouldRender """<span hx-ws:send="/a/new/place"></span>"""
|
||||||
|
}
|
||||||
|
test "succeeds when not given a URL" {
|
||||||
|
aside [ _hxWsSend None ] [] |> shouldRender """<aside hx-ws:send></aside>"""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
let allTests =
|
||||||
|
testList "ViewEngine.Htmax" [
|
||||||
|
hxBrowserIndicatorConfigItem
|
||||||
|
hxPreloadConfigItem
|
||||||
|
hxSseWsConfigItem
|
||||||
|
hxConfig
|
||||||
|
hxEvent
|
||||||
|
attributes
|
||||||
|
]
|
||||||
@@ -9,13 +9,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Htmx.fs" />
|
<Compile Include="Htmx.fs" />
|
||||||
|
<Compile Include="Htmax.fs" />
|
||||||
<None Include="README.md" Pack="true" PackagePath="\" />
|
<None Include="README.md" Pack="true" PackagePath="\" />
|
||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Giraffe.ViewEngine" Version="1.4.0" />
|
<PackageReference Include="Giraffe.ViewEngine" />
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
<PackageReference Include="FSharp.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
/// <summary>Types and functions supporting htmax-bundled extension attributes in Giraffe View Engine</summary>
|
||||||
|
module Giraffe.ViewEngine.Htmax
|
||||||
|
|
||||||
|
/// <summary>Items which can be configured for the <c>hx-browser-indicator</c> extension</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-browser-indicator#boosted-elements">Documentation</seealso>
|
||||||
|
type HxBrowserIndicatorConfigItem =
|
||||||
|
/// <summary>Whether to show the browser's native loading indicator for <c>hx-boost</c>ed links</summary>
|
||||||
|
| BoostBrowserIndicator of bool
|
||||||
|
|
||||||
|
/// <summary>Get the HCON representation of this configuration value</summary>
|
||||||
|
member this.ToHcon() =
|
||||||
|
match this with
|
||||||
|
| BoostBrowserIndicator it -> "boostBrowserIndicator:" + (string it).ToLowerInvariant()
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Items which can be configured for the <c>hx-preload</c> extension</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-preload#configuration">Documentation</seealso>
|
||||||
|
type HxPreloadConfigItem =
|
||||||
|
/// <summary>Whether items subject to <c>hx-boost</c> are automatically preloaded (default <c>true</c>)</summary>
|
||||||
|
| AutoBoost of bool
|
||||||
|
|
||||||
|
/// <summary>The event to use for <c>hx-boost</c> auto-preloaded content (default "mousedown")</summary>
|
||||||
|
| BoostEvent of string
|
||||||
|
|
||||||
|
/// <summary>The timeout (in ms) for <c>hx-boost</c> auto-preloaded requests (default 50_000)</summary>
|
||||||
|
| BoostTimeout of int
|
||||||
|
|
||||||
|
/// <summary>Get the HCON representation of this configuration value</summary>
|
||||||
|
member this.ToHcon() =
|
||||||
|
match this with
|
||||||
|
| AutoBoost it -> "autoBoost:" + (string it).ToLowerInvariant()
|
||||||
|
| BoostEvent it -> $"boostEvent:{it}"
|
||||||
|
| BoostTimeout it -> $"boostTimeout:{it}"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Items which can be configured for the <c>hx-sse</c> or <c>hx-ws</c> extensions</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-sse#configuration">SSE Documentation</seealso>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-ws#configuration">WebSockets Documentation</seealso>
|
||||||
|
type HxSseWsConfigItem =
|
||||||
|
/// <summary>Whether to automatically reconnect on stream end (default <c>true</c>)</summary>
|
||||||
|
| Reconnect of bool
|
||||||
|
|
||||||
|
/// <summary>Delay for reconnect attempts (ms, or numbers with units "1s", "2m"; default "500")</summary>
|
||||||
|
| ReconnectDelay of string
|
||||||
|
|
||||||
|
/// <summary>Maximum delay for reconnect attempts (ms, or numbers with units "1s, "2m"; default "60000")</summary>
|
||||||
|
| ReconnectMaxDelay of string
|
||||||
|
|
||||||
|
/// <summary>Reconnect maximum attempts (use -1 for Infinity; default Infinity)</summary>
|
||||||
|
| ReconnectMaxAttempts of int
|
||||||
|
|
||||||
|
/// <summary>Jitter to use when reconnecting (decimal between 0.0 and 1.0; default 0.3)</summary>
|
||||||
|
| ReconnectJitter of double
|
||||||
|
|
||||||
|
/// <summary>Whether to pause when the current tab is in the background (default <c>true</c>)</summary>
|
||||||
|
| PauseOnBackground of bool
|
||||||
|
|
||||||
|
/// <summary>Time-to-Live (TTL) for pending requests (<c>hx-ws</c> only)</summary>
|
||||||
|
| PendingRequestTtl of int
|
||||||
|
|
||||||
|
/// <summary>Get the HCON representation of this configuration value</summary>
|
||||||
|
member this.ToHcon() =
|
||||||
|
match this with
|
||||||
|
| Reconnect it -> "reconnect:" + (string it).ToLowerInvariant()
|
||||||
|
| ReconnectDelay it -> $"reconnectDelay:{it}"
|
||||||
|
| ReconnectMaxDelay it -> $"reconnectMaxDelay:{it}"
|
||||||
|
| ReconnectMaxAttempts it -> "reconnectMaxAttempts:" + if it = -1 then "Infinity" else string it
|
||||||
|
| ReconnectJitter it -> $"reconnectJitter:{it}"
|
||||||
|
| PauseOnBackground it -> "pauseOnBackground:" + (string it).ToLowerInvariant()
|
||||||
|
| PendingRequestTtl it -> $"pendingRequestTTL:{it}"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Helpers for generating extension configuration parameters</summary>
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HxConfig =
|
||||||
|
|
||||||
|
/// Create an HCON section suitable for hx-config
|
||||||
|
let private hconSettings title values =
|
||||||
|
values |> Seq.map (sprintf "%s.%s" title) |> String.concat " "
|
||||||
|
|
||||||
|
/// <summary>Generate configuration items for preload suitable for <c>hx-config</c></summary>
|
||||||
|
/// <param name="items">The configuration items</param>
|
||||||
|
let hxBrowserIndicatorConfig (items: HxBrowserIndicatorConfigItem seq) =
|
||||||
|
items |> Seq.map _.ToHcon() |> hconSettings "browser-indicator"
|
||||||
|
|
||||||
|
/// <summary>Generate configuration items for preload suitable for <c>hx-config</c></summary>
|
||||||
|
/// <param name="items">The configuration items</param>
|
||||||
|
let hxPreloadConfig (items: HxPreloadConfigItem seq) =
|
||||||
|
items |> Seq.map _.ToHcon() |> hconSettings "preload"
|
||||||
|
|
||||||
|
/// <summary>Generate configuration items for SSE suitable for <c>hx-config</c></summary>
|
||||||
|
/// <param name="items">The configuration items</param>
|
||||||
|
let hxSseConfig (items: HxSseWsConfigItem seq) =
|
||||||
|
items |> Seq.map _.ToHcon() |> hconSettings "sse"
|
||||||
|
|
||||||
|
/// <summary>Generate configuration items for WebSockets suitable for <c>hx-config</c></summary>
|
||||||
|
/// <param name="items">The configuration items</param>
|
||||||
|
let hxWsConfig (items: HxSseWsConfigItem seq) =
|
||||||
|
items |> Seq.map _.ToHcon() |> hconSettings "ws"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>The events recognized by htmax-bundled extensions</summary>
|
||||||
|
[<Struct>]
|
||||||
|
type HxEvent =
|
||||||
|
|
||||||
|
/// <summary>Fired after an SSE connection is established</summary>
|
||||||
|
| AfterSseConnection
|
||||||
|
|
||||||
|
/// <summary>Fired after an SSE message is received</summary>
|
||||||
|
| AfterSseMessage
|
||||||
|
|
||||||
|
/// <summary>Fired after a WebSocket connection is established</summary>
|
||||||
|
| AfterWsConnection
|
||||||
|
|
||||||
|
/// <summary>Fired after a WebSocket message is received</summary>
|
||||||
|
| AfterWsMessage
|
||||||
|
|
||||||
|
/// <summary>Fired after a WebSocket request is sent</summary>
|
||||||
|
| AfterWsRequest
|
||||||
|
|
||||||
|
/// <summary>Fired before an SSE connection is established (cancelable)</summary>
|
||||||
|
| BeforeSseConnection
|
||||||
|
|
||||||
|
/// <summary>Fired before a received SSE message is processed (cancelable)</summary>
|
||||||
|
| BeforeSseMessage
|
||||||
|
|
||||||
|
/// <summary>Fired before a WebSocket connection is established (cancelable)</summary>
|
||||||
|
| BeforeWsConnection
|
||||||
|
|
||||||
|
/// <summary>Fired when a download handled by <c>hx-download</c> is complete</summary>
|
||||||
|
| DownloadComplete
|
||||||
|
|
||||||
|
/// <summary>Fired when a download handled by <c>hx-download</c> is complete</summary>
|
||||||
|
| DownloadProgress
|
||||||
|
|
||||||
|
/// <summary>Fired when a chunk is received for a download handled by <c>hx-download</c></summary>
|
||||||
|
| DownloadStart
|
||||||
|
|
||||||
|
/// <summary>Fired before a received WebSocket message is processed (cancelable)</summary>
|
||||||
|
| BeforeWsMessage
|
||||||
|
|
||||||
|
/// <summary>Fired before a WebSocket request is sent (cancelable)</summary>
|
||||||
|
| BeforeWsRequest
|
||||||
|
|
||||||
|
/// <summary>Fired when an SSE connection is closed</summary>
|
||||||
|
| SseClose
|
||||||
|
|
||||||
|
/// <summary>Fired when an SSE connection errors</summary>
|
||||||
|
| 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")
|
||||||
|
DownloadComplete, ("downloadComplete", "download:complete")
|
||||||
|
DownloadProgress, ("downloadProgress", "download:progress")
|
||||||
|
DownloadStart, ("downloadStart", "download:start")
|
||||||
|
SseClose, ("sseClose", "sse:close")
|
||||||
|
SseError, ("sseError", "sse:error")
|
||||||
|
]
|
||||||
|
|
||||||
|
/// <summary>The htmx event name</summary>
|
||||||
|
override this.ToString() = fst HxEvent.Values[this]
|
||||||
|
|
||||||
|
/// <summary>The <c>hx-on</c> variant of the htmx event name</summary>
|
||||||
|
member this.ToHxOnString() = snd HxEvent.Values[this]
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Attributes and flags for htmax-bundled extensions</summary>
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HtmaxAttrs =
|
||||||
|
|
||||||
|
/// <summary>Display the browser's native loading indicator when an htmx request is in flight</summary>
|
||||||
|
/// <returns>A configured <c>hx-browser-indicator</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-browser-indicator#usage">Documentation</seealso>
|
||||||
|
let _hxBrowserIndicator =
|
||||||
|
attr "hx-browser-indicator" "true"
|
||||||
|
|
||||||
|
/// <summary>Run script each time the DOM changes</summary>
|
||||||
|
/// <param name="script">The script to be run each time the DOM changes (may use <c>q</c> helper)</param>
|
||||||
|
/// <returns>A configured <c>hx-live</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-live#the-hx-live-attribute">Documentation</seealso>
|
||||||
|
let _hxLive script =
|
||||||
|
attr "hx-live" script
|
||||||
|
|
||||||
|
/// <summary>Generate an <c>hx-on:htmx</c> event for an htmax-bundled extension event</summary>
|
||||||
|
/// <param name="event">The <c>HxEvent</c> to be handled</param>
|
||||||
|
/// <param name="handler">The script to be executed when the event occurs</param>
|
||||||
|
/// <returns>A configured <c>hx-on:htmx:</c> attribute</returns>
|
||||||
|
/// <seealso href="https://htmx.org/attributes/hx-on/">Documentation</seealso>
|
||||||
|
let _hxOnMax (event: HxEvent) handler =
|
||||||
|
Htmx.HtmxAttrs._hxOnEvent $"htmx:{event.ToHxOnString()}" handler
|
||||||
|
|
||||||
|
/// <summary>Identify a resource as one that should be preloaded</summary>
|
||||||
|
/// <param name="evt">The DOM event or htmx trigger which should cause the preload (optional; default behavior is
|
||||||
|
/// preloading on <c>mousedown</c>)</param>
|
||||||
|
/// <returns>A configured <c>hx-preload</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-preload#usage">Documentation</seealso>
|
||||||
|
let _hxPreload evt =
|
||||||
|
match evt with Some it -> attr "hx-preload" it | None -> flag "hx-preload"
|
||||||
|
|
||||||
|
/// <summary>Define an SSE message which indicates the connection should be closed</summary>
|
||||||
|
/// <param name="message">The text of the message which indicates the SSE connection should be closed</param>
|
||||||
|
/// <returns>A configured <c>hx-sse:close</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-sse#hx-sseclose">Documentation</seealso>
|
||||||
|
let _hxSseClose message =
|
||||||
|
attr "hx-sse:close" message
|
||||||
|
|
||||||
|
/// <summary>Define a URL to which an SSE connection should be established</summary>
|
||||||
|
/// <param name="url">The URL to which an SSE connection should be established</param>
|
||||||
|
/// <returns>A configured <c>hx-sse:connect</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-sse#hx-sseconnect">Documentation</seealso>
|
||||||
|
let _hxSseConnect url =
|
||||||
|
attr "hx-sse:connect" url
|
||||||
|
|
||||||
|
/// <summary>Replace multiple targets with the same content</summary>
|
||||||
|
/// <param name="selector">The CSS selector to identify targets for the replacement</param>
|
||||||
|
/// <returns>A configured <c>hx-targets</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-targets#usage">Documentation</seealso>
|
||||||
|
let _hxTargets selector =
|
||||||
|
attr "hx-targets" selector
|
||||||
|
|
||||||
|
/// <summary>Connect to a WebSocket URL</summary>
|
||||||
|
/// <param name="url">The URL to which a WebSocket connection should be established</param>
|
||||||
|
/// <returns>A configured <c>hx-ws:connect</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-ws#usage">Documentation</seealso>
|
||||||
|
let _hxWsConnect url =
|
||||||
|
attr "hx-ws:connect" url
|
||||||
|
|
||||||
|
/// <summary>Send data via a WebSocket connection</summary>
|
||||||
|
/// <param name="url">The URL to which the data should be sent (optional; default is connection URL)</param>
|
||||||
|
/// <returns>A configured <c>hx-ws:send</c> attribute</returns>
|
||||||
|
/// <seealso href="https://four.htmx.org/extensions/hx-ws#usage">Documentation</seealso>
|
||||||
|
let _hxWsSend url =
|
||||||
|
match url with Some it -> attr "hx-ws:send" it | None -> flag "hx-ws:send"
|
||||||
+326
-85
@@ -1,6 +1,42 @@
|
|||||||
/// <summary>Types and functions supporting htmx attributes in Giraffe View Engine</summary>
|
/// <summary>Types and functions supporting htmx attributes in Giraffe View Engine</summary>
|
||||||
module Giraffe.ViewEngine.Htmx
|
module Giraffe.ViewEngine.Htmx
|
||||||
|
|
||||||
|
/// <summary>Helpers to define <c>hx-config</c> attribute values</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/attributes/hx-config/">Documentation</seealso>
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module HxConfig =
|
||||||
|
|
||||||
|
open Giraffe.Htmx.Common
|
||||||
|
|
||||||
|
/// <summary>Configure the request with various options</summary>
|
||||||
|
/// <param name="opts">The options to configure</param>
|
||||||
|
/// <returns>A string with the configured options</returns>
|
||||||
|
let Configure (opts: string list) =
|
||||||
|
opts
|
||||||
|
|> String.concat ", "
|
||||||
|
|> sprintf "{ %s }"
|
||||||
|
|
||||||
|
/// <summary>Set a timeout (in milliseconds)</summary>
|
||||||
|
/// <param name="ms">The milliseconds for the request timeout</param>
|
||||||
|
/// <returns>A string with the configured request timeout</returns>
|
||||||
|
let Timeout (ms: int) =
|
||||||
|
$"\"timeout\": {ms}"
|
||||||
|
|
||||||
|
/// <summary>Include or exclude credentials from the request</summary>
|
||||||
|
/// <param name="send"><c>true</c> if credentials should be sent, <c>false</c> if not</param>
|
||||||
|
/// <returns>A string with the configured credential options</returns>
|
||||||
|
let Credentials send =
|
||||||
|
(toLowerBool >> sprintf "\"credentials\": %s") send
|
||||||
|
|
||||||
|
/// <summary>Exclude or include headers from the request</summary>
|
||||||
|
/// <param name="exclude">
|
||||||
|
/// <c>true</c> if no headers should be sent; <c>false</c> if headers should be sent
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A string with the configured header options</returns>
|
||||||
|
let NoHeaders exclude =
|
||||||
|
(toLowerBool >> sprintf "\"noHeaders\": %s") exclude
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Valid values for the <c>hx-encoding</c> attribute</summary>
|
/// <summary>Valid values for the <c>hx-encoding</c> attribute</summary>
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module HxEncoding =
|
module HxEncoding =
|
||||||
@@ -14,45 +50,107 @@ module HxEncoding =
|
|||||||
let MultipartForm = "multipart/form-data"
|
let MultipartForm = "multipart/form-data"
|
||||||
|
|
||||||
|
|
||||||
|
#nowarn 44 // Obsolete elements still have entries in the conversion map; will be removed for v4 final
|
||||||
/// <summary>The events recognized by htmx</summary>
|
/// <summary>The events recognized by htmx</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/events/">Documentation</seealso>
|
||||||
[<Struct>]
|
[<Struct>]
|
||||||
type HxEvent =
|
type HxEvent =
|
||||||
|
|
||||||
/// <summary>Send this event to an element to abort a request</summary>
|
/// <summary>Send this event to an element to abort a request</summary>
|
||||||
| Abort
|
| Abort
|
||||||
|
|
||||||
|
/// <summary>Triggered after htmx disables an element or removes it from the DOM</summary>
|
||||||
|
| AfterCleanup
|
||||||
|
|
||||||
|
/// <summary>Triggered after content is saved to the history cache</summary>
|
||||||
|
| AfterHistoryUpdate
|
||||||
|
|
||||||
|
/// <summary>Triggered after a request has initialized</summary>
|
||||||
|
| AfterInit
|
||||||
|
|
||||||
/// <summary>Triggered after an AJAX request has completed processing a successful response</summary>
|
/// <summary>Triggered after an AJAX request has completed processing a successful response</summary>
|
||||||
| AfterOnLoad
|
| [<System.Obsolete "Removed in v4; use AfterInit">] AfterOnLoad
|
||||||
|
|
||||||
|
/// <summary>Triggered after htmx has initialized a DOM node or subtree</summary>
|
||||||
|
| AfterProcess
|
||||||
|
|
||||||
/// <summary>Triggered after htmx has initialized a node</summary>
|
/// <summary>Triggered after htmx has initialized a node</summary>
|
||||||
| AfterProcessNode
|
| [<System.Obsolete "Removed in v4; use AfterProcess">] AfterProcessNode
|
||||||
|
|
||||||
/// <summary>Triggered after an AJAX request has completed</summary>
|
/// <summary>Triggered after new content is saved to the history cache</summary>
|
||||||
|
| AfterPushIntoHistory
|
||||||
|
|
||||||
|
/// <summary>Triggered after updated content is saved to the history cache</summary>
|
||||||
|
| AfterReplaceIntoHistory
|
||||||
|
|
||||||
|
/// <summary>Triggered after an HTTP request has completed successfully</summary>
|
||||||
| AfterRequest
|
| AfterRequest
|
||||||
|
|
||||||
/// <summary>Triggered after the DOM has settled</summary>
|
/// <summary>Triggered after the DOM has settled</summary>
|
||||||
| AfterSettle
|
| AfterSettle
|
||||||
|
|
||||||
|
/// <summary>Triggered after a Server Sent Events (SSE) message is read</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use Htmax.HxEvents.AfterSseMessage">] AfterSseMessage
|
||||||
|
|
||||||
|
/// <summary>Triggered after a Server Sent Events (SSE) stream is closed</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; SSE is assumed to be a stream">] AfterSseStream
|
||||||
|
|
||||||
/// <summary>Triggered after new content has been swapped in</summary>
|
/// <summary>Triggered after new content has been swapped in</summary>
|
||||||
| AfterSwap
|
| AfterSwap
|
||||||
|
|
||||||
/// <summary>Triggered before htmx disables an element or removes it from the DOM</summary>
|
/// <summary>Triggered after a CSS view transition completes</summary>
|
||||||
| BeforeCleanupElement
|
| AfterViewTransition
|
||||||
|
|
||||||
/// <summary>Triggered before any response processing occurs</summary>
|
/// <summary>Triggered before htmx disables an element or removes it from the DOM</summary>
|
||||||
| BeforeOnLoad
|
| BeforeCleanup
|
||||||
|
|
||||||
|
/// <summary>Triggered before htmx disables an element or removes it from the DOM</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use BeforeCleanup">] BeforeCleanupElement
|
||||||
|
|
||||||
|
/// <summary>Triggered before content is saved to the history cache</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use BeforeHistoryUpdate">] BeforeHistorySave
|
||||||
|
|
||||||
|
/// <summary>Triggered before content is saved to the history cache</summary>
|
||||||
|
| BeforeHistoryUpdate
|
||||||
|
|
||||||
/// <summary>Triggered before htmx initializes a node</summary>
|
/// <summary>Triggered before htmx initializes a node</summary>
|
||||||
| BeforeProcessNode
|
| BeforeInit
|
||||||
|
|
||||||
/// <summary>Triggered before an AJAX request is made</summary>
|
/// <summary>Triggered before any response processing occurs</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use BeforeInit">] BeforeOnLoad
|
||||||
|
|
||||||
|
/// <summary>Triggered before htmx begins processing a DOM node or subtree</summary>
|
||||||
|
| BeforeProcess
|
||||||
|
|
||||||
|
/// <summary>Triggered before htmx initializes a node</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use BeforeProcess">] BeforeProcessNode
|
||||||
|
|
||||||
|
/// <summary>Triggered before an HTTP request is made</summary>
|
||||||
| BeforeRequest
|
| BeforeRequest
|
||||||
|
|
||||||
|
/// <summary>Triggered before an HTTP response is processed (cancelable)</summary>
|
||||||
|
| BeforeResponse
|
||||||
|
|
||||||
|
/// <summary>Triggered before a history restore request is made</summary>
|
||||||
|
| BeforeRestoreHistory
|
||||||
|
|
||||||
|
/// <summary>Triggered just before an ajax request is sent</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use BeforeRequest">] BeforeSend
|
||||||
|
|
||||||
|
/// <summary>Triggered before a Server Sent Events (SSE) message is read</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use Htmax.HxEvents.BeforeSseMessage">] BeforeSseMessage
|
||||||
|
|
||||||
|
/// <summary>Triggered before a Server Sent Events (SSE) connection is reconnected</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; use fetch event handlers">] BeforeSseReconnect
|
||||||
|
|
||||||
|
/// <summary>Triggered before a Server Sent Events (SSE) stream is opened</summary>
|
||||||
|
| [<System.Obsolete "Removed in v4; SSE is assumed to be a stream">] BeforeSseStream
|
||||||
|
|
||||||
/// <summary>Triggered before a swap is done, allows you to configure the swap</summary>
|
/// <summary>Triggered before a swap is done, allows you to configure the swap</summary>
|
||||||
| BeforeSwap
|
| BeforeSwap
|
||||||
|
|
||||||
/// <summary>Triggered just before an ajax request is sent</summary>
|
/// <summary>Triggered before a CSS view transition starts</summary>
|
||||||
| BeforeSend
|
| BeforeViewTransition
|
||||||
|
|
||||||
/// <summary>Triggered before the request, allows you to customize parameters, headers</summary>
|
/// <summary>Triggered before the request, allows you to customize parameters, headers</summary>
|
||||||
| ConfigRequest
|
| ConfigRequest
|
||||||
@@ -62,134 +160,157 @@ type HxEvent =
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
| Confirm
|
| Confirm
|
||||||
|
|
||||||
|
/// <summary>Triggered when an error occurs</summary>
|
||||||
|
| Error
|
||||||
|
|
||||||
|
/// <summary>Triggered after an HTTP request is made, whether it was successful or not</summary>
|
||||||
|
| FinallyRequest
|
||||||
|
|
||||||
/// <summary>Triggered on an error during cache writing</summary>
|
/// <summary>Triggered on an error during cache writing</summary>
|
||||||
| HistoryCacheError
|
| [<System.Obsolete "Removed in v4; history no longer uses localStorage">] HistoryCacheError
|
||||||
|
|
||||||
/// <summary>Triggered on a cache miss in the history subsystem</summary>
|
/// <summary>Triggered on a cache miss in the history subsystem</summary>
|
||||||
| HistoryCacheMiss
|
| [<System.Obsolete "Removed in v4; use BeforeRestoreHistory">] HistoryCacheMiss
|
||||||
|
|
||||||
/// <summary>Triggered on a unsuccessful remote retrieval</summary>
|
/// <summary>Triggered on a unsuccessful remote retrieval</summary>
|
||||||
| HistoryCacheMissError
|
| [<System.Obsolete "Removed in v4; history no longer uses localStorage">] HistoryCacheMissError
|
||||||
|
|
||||||
/// <summary>Triggered on a successful remote retrieval</summary>
|
/// <summary>Triggered on a successful remote retrieval</summary>
|
||||||
| HistoryCacheMissLoad
|
| [<System.Obsolete "Removed in v4; use BeforeRestoreHistory">] HistoryCacheMissLoad
|
||||||
|
|
||||||
/// <summary>Triggered when htmx handles a history restoration action</summary>
|
/// <summary>Triggered when htmx handles a history restoration action</summary>
|
||||||
| HistoryRestore
|
| [<System.Obsolete "Removed in v4; use BeforeRestoreHistory">] HistoryRestore
|
||||||
|
|
||||||
/// <summary>Triggered before content is saved to the history cache</summary>
|
|
||||||
| BeforeHistorySave
|
|
||||||
|
|
||||||
/// <summary>Triggered when new content is added to the DOM</summary>
|
/// <summary>Triggered when new content is added to the DOM</summary>
|
||||||
| Load
|
| [<System.Obsolete "Removed in v4; use AfterInit">] Load
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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
|
||||||
/// </summary>
|
/// </summary>
|
||||||
| NoSseSourceError
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] NoSseSourceError
|
||||||
|
|
||||||
/// <summary>Triggered when an exception occurs during the onLoad handling in htmx</summary>
|
/// <summary>Triggered when an exception occurs during the onLoad handling in htmx</summary>
|
||||||
| OnLoadError
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] OnLoadError
|
||||||
|
|
||||||
/// <summary>Triggered after an out of band element as been swapped in</summary>
|
/// <summary>Triggered after an out of band element as been swapped in</summary>
|
||||||
| OobAfterSwap
|
| [<System.Obsolete "Removed in v4; use AfterSwap (no separate OOB event)">] OobAfterSwap
|
||||||
|
|
||||||
/// <summary>Triggered before an out of band element swap is done, allows you to configure the swap</summary>
|
/// <summary>Triggered before an out of band element swap is done, allows you to configure the swap</summary>
|
||||||
| OobBeforeSwap
|
| [<System.Obsolete "Removed in v4; use BeforeSwap (no separate OOB event)">] OobBeforeSwap
|
||||||
|
|
||||||
/// <summary>Triggered when an out of band element does not have a matching ID in the current DOM</summary>
|
/// <summary>Triggered when an out of band element does not have a matching ID in the current DOM</summary>
|
||||||
| OobErrorNoTarget
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] OobErrorNoTarget
|
||||||
|
|
||||||
/// <summary>Triggered after a prompt is shown</summary>
|
/// <summary>Triggered after a prompt is shown</summary>
|
||||||
| Prompt
|
| [<System.Obsolete "Removed in v4; use Confirm with custom JavaScript">] Prompt
|
||||||
|
|
||||||
/// <summary>Triggered after an url is pushed into history</summary>
|
/// <summary>Triggered after an url is pushed into history</summary>
|
||||||
| PushedIntoHistory
|
| [<System.Obsolete "Removed in v4; use AfterPushIntoHistory (or AfterHistoryUpdate to catch new and updated content)">] PushedIntoHistory
|
||||||
|
|
||||||
/// <summary>Triggered when an HTTP response error (non-200 or 300 response code) occurs</summary>
|
/// <summary>Triggered when an HTTP response error (non-200 or 300 response code) occurs</summary>
|
||||||
| ResponseError
|
| ResponseError
|
||||||
|
|
||||||
/// <summary>Triggered when a network error prevents an HTTP request from happening</summary>
|
/// <summary>Triggered when a network error prevents an HTTP request from happening</summary>
|
||||||
| SendError
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] SendError
|
||||||
|
|
||||||
/// <summary>Triggered when an error occurs with a SSE source</summary>
|
/// <summary>Triggered when an error occurs with a SSE source</summary>
|
||||||
| SseError
|
| [<System.Obsolete "Removed in v4; use Htmax.HxEvents.SseError)">] SseError
|
||||||
|
|
||||||
/// <summary>Triggered when a SSE source is opened</summary>
|
/// <summary>Triggered when an SSE source is opened</summary>
|
||||||
| SseOpen
|
| [<System.Obsolete "Removed in v4; use Htmax.HxEvents.AfterSseConnection">] SseOpen
|
||||||
|
|
||||||
/// <summary>Triggered when an error occurs during the swap phase</summary>
|
/// <summary>Triggered when an error occurs during the swap phase</summary>
|
||||||
| SwapError
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] SwapError
|
||||||
|
|
||||||
/// <summary>Triggered when an invalid target is specified</summary>
|
/// <summary>Triggered when an invalid target is specified</summary>
|
||||||
| TargetError
|
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] TargetError
|
||||||
|
|
||||||
/// <summary>Triggered when a request timeout occurs</summary>
|
/// <summary>Triggered when a request timeout occurs</summary>
|
||||||
| Timeout
|
| [<System.Obsolete "Removed in v4; use Error (timeout is exposed as an error)">] Timeout
|
||||||
|
|
||||||
/// <summary>Triggered before an element is validated</summary>
|
/// <summary>Triggered before an element is validated</summary>
|
||||||
| ValidationValidate
|
| [<System.Obsolete "Removed in v4; use native validation API">] ValidationValidate
|
||||||
|
|
||||||
/// <summary>Triggered when an element fails validation</summary>
|
/// <summary>Triggered when an element fails validation</summary>
|
||||||
| ValidationFailed
|
| [<System.Obsolete "Removed in v4; use native validation API">] ValidationFailed
|
||||||
|
|
||||||
/// <summary>Triggered when a request is halted due to validation errors</summary>
|
/// <summary>Triggered when a request is halted due to validation errors</summary>
|
||||||
| ValidationHalted
|
| [<System.Obsolete "Removed in v4; use native validation API">] ValidationHalted
|
||||||
|
|
||||||
/// <summary>Triggered when an ajax request aborts</summary>
|
/// <summary>Triggered when an ajax request aborts</summary>
|
||||||
| XhrAbort
|
| [<System.Obsolete "Removed in v4; use fetch event listeners or Error event">] XhrAbort
|
||||||
|
|
||||||
/// <summary>Triggered when an ajax request ends</summary>
|
/// <summary>Triggered when an ajax request ends</summary>
|
||||||
| XhrLoadEnd
|
| [<System.Obsolete "Removed in v4; use fetch event listeners or FinallyRequest event">] XhrLoadEnd
|
||||||
|
|
||||||
/// <summary>Triggered when an ajax request starts</summary>
|
/// <summary>Triggered when an ajax request starts</summary>
|
||||||
| XhrLoadStart
|
| [<System.Obsolete "Removed in v4; use fetch event listeners">] XhrLoadStart
|
||||||
|
|
||||||
/// <summary>Triggered periodically during an ajax request that supports progress events</summary>
|
/// <summary>Triggered periodically during an ajax request that supports progress events</summary>
|
||||||
| XhrProgress
|
| [<System.Obsolete "Removed in v4; use fetch event listeners">] XhrProgress
|
||||||
|
|
||||||
/// The htmx event name (fst) and kebab-case name (snd, for use with hx-on)
|
/// The htmx event name (fst) and kebab-case name (snd, for use with hx-on)
|
||||||
static member private Values = Map [
|
static member private Values = Map [
|
||||||
Abort, ("abort", "abort")
|
Abort, ("abort", "abort")
|
||||||
AfterOnLoad, ("afterOnLoad", "after-on-load")
|
AfterCleanup, ("afterCleanup", "after:cleanup")
|
||||||
AfterProcessNode, ("afterProcessNode", "after-process-node")
|
AfterHistoryUpdate, ("afterHistoryUpdate", "after:history:update")
|
||||||
AfterRequest, ("afterRequest", "after-request")
|
AfterInit, ("afterInit", "after:init")
|
||||||
AfterSettle, ("afterSettle", "after-settle")
|
AfterOnLoad, ("afterOnLoad", "after:init")
|
||||||
AfterSwap, ("afterSwap", "after-swap")
|
AfterProcessNode, ("afterProcessNode", "after:process")
|
||||||
BeforeCleanupElement, ("beforeCleanupElement", "before-cleanup-element")
|
AfterPushIntoHistory, ("afterPushIntoHistory", "after:push:into:history")
|
||||||
BeforeOnLoad, ("beforeOnLoad", "before-on-load")
|
AfterReplaceIntoHistory, ("afterReplaceIntoHistory", "after:replace:into:history")
|
||||||
BeforeProcessNode, ("beforeProcessNode", "before-process-node")
|
AfterRequest, ("afterRequest", "after:request")
|
||||||
BeforeRequest, ("beforeRequest", "before-request")
|
AfterSettle, ("afterSettle", "after:settle")
|
||||||
BeforeSwap, ("beforeSwap", "before-swap")
|
AfterSseMessage, ("afterSseMessage", "after:sse:message")
|
||||||
BeforeSend, ("beforeSend", "before-send")
|
AfterSseStream, ("afterSseStream", "after:sse:stream")
|
||||||
ConfigRequest, ("configRequest", "config-request")
|
AfterSwap, ("afterSwap", "after:swap")
|
||||||
|
AfterViewTransition, ("afterViewTransition", "after:viewTransition")
|
||||||
|
BeforeCleanup, ("beforeCleanup", "before:cleanup")
|
||||||
|
BeforeCleanupElement, ("beforeCleanupElement", "before:cleanup")
|
||||||
|
BeforeHistorySave, ("beforeHistorySave", "before:history:update")
|
||||||
|
BeforeHistoryUpdate, ("beforeHistoryUpdate", "before:history:update")
|
||||||
|
BeforeInit, ("beforeInit", "before:init")
|
||||||
|
BeforeOnLoad, ("beforeOnLoad", "before:init")
|
||||||
|
BeforeProcess, ("beforeProcess", "before:process")
|
||||||
|
BeforeProcessNode, ("beforeProcessNode", "before:process")
|
||||||
|
BeforeRequest, ("beforeRequest", "before:request")
|
||||||
|
BeforeResponse, ("beforeResponse", "before:response")
|
||||||
|
BeforeRestoreHistory, ("beforeRestoreHistory", "before:restore:history")
|
||||||
|
BeforeSend, ("beforeSend", "before:request")
|
||||||
|
BeforeSseMessage, ("beforeSseMessage", "before:sse:message")
|
||||||
|
BeforeSseReconnect, ("beforeSseReconnect", "before:sse:reconnect")
|
||||||
|
BeforeSseStream, ("beforeSseStream", "before:sse:stream")
|
||||||
|
BeforeSwap, ("beforeSwap", "before:swap")
|
||||||
|
BeforeViewTransition, ("beforeTransition", "before:viewTransition")
|
||||||
|
ConfigRequest, ("configRequest", "config:request")
|
||||||
Confirm, ("confirm", "confirm")
|
Confirm, ("confirm", "confirm")
|
||||||
|
Error, ("error", "error")
|
||||||
|
FinallyRequest, ("finallyRequest", "finally:request")
|
||||||
HistoryCacheError, ("historyCacheError", "history-cache-error")
|
HistoryCacheError, ("historyCacheError", "history-cache-error")
|
||||||
HistoryCacheMiss, ("historyCacheMiss", "history-cache-miss")
|
HistoryCacheMiss, ("historyCacheMiss", "before:history:restore")
|
||||||
HistoryCacheMissError, ("historyCacheMissError", "history-cache-miss-error")
|
HistoryCacheMissError, ("historyCacheMissError", "history-cache-miss-error")
|
||||||
HistoryCacheMissLoad, ("historyCacheMissLoad", "history-cache-miss-load")
|
HistoryCacheMissLoad, ("historyCacheMissLoad", "before:history:restore")
|
||||||
HistoryRestore, ("historyRestore", "history-restore")
|
HistoryRestore, ("historyRestore", "before:history:restore")
|
||||||
BeforeHistorySave, ("beforeHistorySave", "before-history-save")
|
Load, ("load", "after:init")
|
||||||
Load, ("load", "load")
|
NoSseSourceError, ("noSSESourceError", "error")
|
||||||
NoSseSourceError, ("noSSESourceError", "no-sse-source-error")
|
OnLoadError, ("onLoadError", "error")
|
||||||
OnLoadError, ("onLoadError", "on-load-error")
|
OobAfterSwap, ("oobAfterSwap", "after:swap")
|
||||||
OobAfterSwap, ("oobAfterSwap", "oob-after-swap")
|
OobBeforeSwap, ("oobBeforeSwap", "before:swap")
|
||||||
OobBeforeSwap, ("oobBeforeSwap", "oob-before-swap")
|
OobErrorNoTarget, ("oobErrorNoTarget", "error")
|
||||||
OobErrorNoTarget, ("oobErrorNoTarget", "oob-error-no-target")
|
|
||||||
Prompt, ("prompt", "prompt")
|
Prompt, ("prompt", "prompt")
|
||||||
PushedIntoHistory, ("pushedIntoHistory", "pushed-into-history")
|
PushedIntoHistory, ("pushedIntoHistory", "after:push:into:history")
|
||||||
ResponseError, ("responseError", "response-error")
|
ResponseError, ("responseError", "response:error")
|
||||||
SendError, ("sendError", "send-error")
|
SendError, ("sendError", "error")
|
||||||
SseError, ("sseError", "sse-error")
|
SseError, ("sseError", "sse:error")
|
||||||
SseOpen, ("sseOpen", "sse-open")
|
SseOpen, ("sseOpen", "after:sse:connection")
|
||||||
SwapError, ("swapError", "swap-error")
|
SwapError, ("swapError", "error")
|
||||||
TargetError, ("targetError", "target-error")
|
TargetError, ("targetError", "error")
|
||||||
Timeout, ("timeout", "timeout")
|
Timeout, ("timeout", "error")
|
||||||
ValidationValidate, ("validation:validate", "validation:validate")
|
ValidationValidate, ("validation:validate", "validation:validate")
|
||||||
ValidationFailed, ("validation:failed", "validation:failed")
|
ValidationFailed, ("validation:failed", "validation:failed")
|
||||||
ValidationHalted, ("validation:halted", "validation:halted")
|
ValidationHalted, ("validation:halted", "validation:halted")
|
||||||
XhrAbort, ("xhr:abort", "xhr:abort")
|
XhrAbort, ("xhr:abort", "error")
|
||||||
XhrLoadEnd, ("xhr:loadend", "xhr:loadend")
|
XhrLoadEnd, ("xhr:loadend", "finally:request")
|
||||||
XhrLoadStart, ("xhr:loadstart", "xhr:loadstart")
|
XhrLoadStart, ("xhr:loadstart", "xhr:loadstart")
|
||||||
XhrProgress, ("xhr:progress", "xhr:progress")
|
XhrProgress, ("xhr:progress", "xhr:progress")
|
||||||
]
|
]
|
||||||
@@ -199,6 +320,7 @@ type HxEvent =
|
|||||||
|
|
||||||
/// <summary>The <c>hx-on</c> variant of the htmx event name</summary>
|
/// <summary>The <c>hx-on</c> variant of the htmx event name</summary>
|
||||||
member this.ToHxOnString() = snd HxEvent.Values[this]
|
member this.ToHxOnString() = snd HxEvent.Values[this]
|
||||||
|
#warn 44 // restore obsolete warning
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Helper to create the <c>hx-headers</c> attribute</summary>
|
/// <summary>Helper to create the <c>hx-headers</c> attribute</summary>
|
||||||
@@ -212,6 +334,7 @@ module HxHeaders =
|
|||||||
/// <summary>Values / helpers for the <c>hx-params</c> attribute</summary>
|
/// <summary>Values / helpers for the <c>hx-params</c> attribute</summary>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-params/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-params/">Documentation</seealso>
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
[<System.Obsolete "Removed in v4; filter parameters via JavaScript in configRequest event">]
|
||||||
module HxParams =
|
module HxParams =
|
||||||
|
|
||||||
/// <summary>Include all parameters</summary>
|
/// <summary>Include all parameters</summary>
|
||||||
@@ -238,6 +361,7 @@ module HxParams =
|
|||||||
/// <summary>Helpers to define <c>hx-request</c> attribute values</summary>
|
/// <summary>Helpers to define <c>hx-request</c> attribute values</summary>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-request/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-request/">Documentation</seealso>
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
[<System.Obsolete "Removed in v4; change to HxConfig">]
|
||||||
module HxRequest =
|
module HxRequest =
|
||||||
|
|
||||||
open Giraffe.Htmx.Common
|
open Giraffe.Htmx.Common
|
||||||
@@ -487,16 +611,31 @@ module HxVals =
|
|||||||
let From = Giraffe.Htmx.Common.toJson
|
let From = Giraffe.Htmx.Common.toJson
|
||||||
|
|
||||||
|
|
||||||
|
open System.Net.Http
|
||||||
open Giraffe.Htmx
|
open Giraffe.Htmx
|
||||||
|
|
||||||
/// <summary>Attributes and flags for htmx</summary>
|
/// <summary>Attributes and flags for htmx</summary>
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module HtmxAttrs =
|
module HtmxAttrs =
|
||||||
|
|
||||||
|
/// <summary>The URL which will receive the request (use with <c>_hxMethod</c>)</summary>
|
||||||
|
/// <param name="url">The URL for the attribute</param>
|
||||||
|
/// <seealso href="https://four.htmx.org/attributes/hx-action/">Documentation</seealso>
|
||||||
|
let _hxAction url =
|
||||||
|
attr "hx-action" url
|
||||||
|
|
||||||
/// <summary>Progressively enhances anchors and forms to use AJAX requests</summary>
|
/// <summary>Progressively enhances anchors and forms to use AJAX requests</summary>
|
||||||
/// <remarks>Use <c>_hxNoBoost</c> to set to false</remarks>
|
/// <remarks>Use <c>_hxNoBoost</c> to set to false</remarks>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-boost/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-boost/">Documentation</seealso>
|
||||||
let _hxBoost = attr "hx-boost" "true"
|
let _hxBoost =
|
||||||
|
attr "hx-boost" "true"
|
||||||
|
|
||||||
|
/// <summary>Configure request behavior</summary>
|
||||||
|
/// <param name="config">The configuration parameters to use for the request</param>
|
||||||
|
/// <returns>A configured <c>hx-config</c> attribute</returns>
|
||||||
|
/// <seealso cref="https://four.htmx.org/attributes/hx-config/">Documentation</seealso>
|
||||||
|
let _hxConfig config =
|
||||||
|
attr "hx-config" (toJson config)
|
||||||
|
|
||||||
/// <summary>Shows a <c>confirm()</c> dialog before issuing a request</summary>
|
/// <summary>Shows a <c>confirm()</c> dialog before issuing a request</summary>
|
||||||
/// <param name="prompt">The prompt to present to the user when seeking their confirmation</param>
|
/// <param name="prompt">The prompt to present to the user when seeking their confirmation</param>
|
||||||
@@ -512,14 +651,23 @@ module HtmxAttrs =
|
|||||||
let _hxDelete url =
|
let _hxDelete url =
|
||||||
attr "hx-delete" url
|
attr "hx-delete" url
|
||||||
|
|
||||||
/// <summary>Disables htmx processing for the given node and any children nodes</summary>
|
/// <summary>Specifies elements that should be disabled when an htmx request is in flight</summary>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-disable/">Documentation</seealso>
|
/// <param name="elt">The element to disable when an htmx request is in flight</param>
|
||||||
let _hxDisable = flag "hx-disable"
|
/// <returns>A configured <c>hx-disable</c> attribute</returns>
|
||||||
|
/// <remarks>This behavior changed in v4
|
||||||
|
/// <ul><li>For the v2 behavior of <c>hx-disable</c> (which will manifest as compile errors), change instances to
|
||||||
|
/// <c>_hxIgnore</c></li>
|
||||||
|
/// <li>For v4 behavior, changing <c>_hxDisabledElt</c> to <c>_hxDisable</c> should be sufficient</li></ul>
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso href="https://four.htmx.org/attributes/hx-disable/">Documentation</seealso>
|
||||||
|
let _hxDisable elt =
|
||||||
|
attr "hx-disable" elt
|
||||||
|
|
||||||
/// <summary>Specifies elements that should be disabled when an htmx request is in flight</summary>
|
/// <summary>Specifies elements that should be disabled when an htmx request is in flight</summary>
|
||||||
/// <param name="elt">The element to disable when an htmx request is in flight</param>
|
/// <param name="elt">The element to disable when an htmx request is in flight</param>
|
||||||
/// <returns>A configured <c>hx-disabled-elt</c> attribute</returns>
|
/// <returns>A configured <c>hx-disabled-elt</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-disabled-elt/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-disabled-elt/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-disabled-elt has become hx-disable in v4">]
|
||||||
let _hxDisabledElt elt =
|
let _hxDisabledElt elt =
|
||||||
attr "hx-disabled-elt" elt
|
attr "hx-disabled-elt" elt
|
||||||
|
|
||||||
@@ -527,6 +675,7 @@ module HtmxAttrs =
|
|||||||
/// <param name="hxAttrs">The htmx attributes to disinherit (should start with "hx-")</param>
|
/// <param name="hxAttrs">The htmx attributes to disinherit (should start with "hx-")</param>
|
||||||
/// <returns>A configured <c>hx-disinherit</c> attribute</returns>
|
/// <returns>A configured <c>hx-disinherit</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-disinherit/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-disinherit/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-disinherit removed in v4; use explicit inheritance">]
|
||||||
let _hxDisinherit hxAttrs =
|
let _hxDisinherit hxAttrs =
|
||||||
attr "hx-disinherit" hxAttrs
|
attr "hx-disinherit" hxAttrs
|
||||||
|
|
||||||
@@ -542,6 +691,7 @@ module HtmxAttrs =
|
|||||||
/// <param name="exts">A list of extensions to apply to this element</param>
|
/// <param name="exts">A list of extensions to apply to this element</param>
|
||||||
/// <returns>A configured <c>hx-ext</c> attribute</returns>
|
/// <returns>A configured <c>hx-ext</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-ext/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-ext/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-ext removed in v4; extensions register via JavaScript">]
|
||||||
let _hxExt exts =
|
let _hxExt exts =
|
||||||
attr "hx-ext" exts
|
attr "hx-ext" exts
|
||||||
|
|
||||||
@@ -565,14 +715,21 @@ module HtmxAttrs =
|
|||||||
/// <param name="shouldStore">Whether the page should be stored in the history cache</param>
|
/// <param name="shouldStore">Whether the page should be stored in the history cache</param>
|
||||||
/// <returns>A configured <c>hx-history</c> attribute</returns>
|
/// <returns>A configured <c>hx-history</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-history/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-history/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-history removed in v4; history does not use localStorage">]
|
||||||
let _hxHistory shouldStore =
|
let _hxHistory shouldStore =
|
||||||
attr "hx-history" (toLowerBool shouldStore)
|
attr "hx-history" (toLowerBool shouldStore)
|
||||||
|
|
||||||
/// <summary>The element to snapshot and restore during history navigation</summary>
|
/// <summary>The element to snapshot and restore during history navigation</summary>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-history-elt/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-history-elt/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-history-elt removed in v4; history based on target element">]
|
||||||
let _hxHistoryElt =
|
let _hxHistoryElt =
|
||||||
flag "hx-history-elt"
|
flag "hx-history-elt"
|
||||||
|
|
||||||
|
/// <summary>Disables htmx processing for the given node and any children nodes</summary>
|
||||||
|
/// <seealso href="https://four.htmx.org/attributes/hx-ignore/">Documentation</seealso>
|
||||||
|
let _hxIgnore =
|
||||||
|
flag "hx-ignore"
|
||||||
|
|
||||||
/// <summary>Includes additional data in AJAX requests</summary>
|
/// <summary>Includes additional data in AJAX requests</summary>
|
||||||
/// <param name="spec">The specification of what should be included in the request</param>
|
/// <param name="spec">The specification of what should be included in the request</param>
|
||||||
/// <returns>A configured <c>hx-include</c> attribute</returns>
|
/// <returns>A configured <c>hx-include</c> attribute</returns>
|
||||||
@@ -587,6 +744,12 @@ module HtmxAttrs =
|
|||||||
let _hxIndicator selector =
|
let _hxIndicator selector =
|
||||||
attr "hx-indicator" selector
|
attr "hx-indicator" selector
|
||||||
|
|
||||||
|
/// <summary>The HTTP method to use for the request(use with <c>_hxAction</c>)</summary>
|
||||||
|
/// <param name="method">The method to use</param>
|
||||||
|
/// <seealso href="https://four.htmx.org/attributes/hx-method/">Documentation</seealso>
|
||||||
|
let _hxMethod (method: HttpMethod) =
|
||||||
|
attr "hx-method" (method.Method.ToLowerInvariant())
|
||||||
|
|
||||||
/// <summary>Overrides a previous <c>hx-boost</c> (hx-boost="false")</summary>
|
/// <summary>Overrides a previous <c>hx-boost</c> (hx-boost="false")</summary>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-boost/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-boost/">Documentation</seealso>
|
||||||
let _hxNoBoost =
|
let _hxNoBoost =
|
||||||
@@ -603,17 +766,19 @@ module HtmxAttrs =
|
|||||||
/// <summary>Attach an event handler for htmx events</summary>
|
/// <summary>Attach an event handler for htmx events</summary>
|
||||||
/// <param name="hxEvent">The <c>HxEvent</c> to be handled</param>
|
/// <param name="hxEvent">The <c>HxEvent</c> to be handled</param>
|
||||||
/// <param name="handler">The script to be executed when the event occurs</param>
|
/// <param name="handler">The script to be executed when the event occurs</param>
|
||||||
/// <returns>A configured <c>hx-on::</c> attribute</returns>
|
/// <returns>A configured <c>hx-on:htmx:</c> attribute</returns>
|
||||||
|
/// <remarks>For events defined in htmax, use <c>_hxOnMax</c></remarks>
|
||||||
/// <seealso cref="HxEvent" />
|
/// <seealso cref="HxEvent" />
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-on/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-on/">Documentation</seealso>
|
||||||
let _hxOnHxEvent (hxEvent: HxEvent) handler =
|
let _hxOnHxEvent (hxEvent: HxEvent) handler =
|
||||||
_hxOnEvent $":{hxEvent.ToHxOnString()}" handler
|
_hxOnEvent $"htmx:{hxEvent.ToHxOnString()}" handler
|
||||||
|
|
||||||
/// <summary>Filters the parameters that will be submitted with a request</summary>
|
/// <summary>Filters the parameters that will be submitted with a request</summary>
|
||||||
/// <param name="toInclude">The fields to include (use <c>HxParams</c> to generate this value)</param>
|
/// <param name="toInclude">The fields to include (use <c>HxParams</c> to generate this value)</param>
|
||||||
/// <returns>A configured <c>hx-params</c> attribute</returns>
|
/// <returns>A configured <c>hx-params</c> attribute</returns>
|
||||||
/// <seealso cref="HxParams" />
|
/// <seealso cref="HxParams" />
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-params/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-params/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-params removed in v4; use htmx:config:request to filter parameter">]
|
||||||
let _hxParams toInclude =
|
let _hxParams toInclude =
|
||||||
attr "hx-params" toInclude
|
attr "hx-params" toInclude
|
||||||
|
|
||||||
@@ -641,6 +806,7 @@ module HtmxAttrs =
|
|||||||
/// <returns>A configured <c>hx-prompt</c> attribute</returns>
|
/// <returns>A configured <c>hx-prompt</c> attribute</returns>
|
||||||
/// <remarks>The value provided will be in the <c>HX-Prompt</c> request header</remarks>
|
/// <remarks>The value provided will be in the <c>HX-Prompt</c> request header</remarks>
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-prompt/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-prompt/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-prompt removed in v4; use hx-confirm with async JavaScript function">]
|
||||||
let _hxPrompt text =
|
let _hxPrompt text =
|
||||||
attr "hx-prompt" text
|
attr "hx-prompt" text
|
||||||
|
|
||||||
@@ -682,6 +848,7 @@ module HtmxAttrs =
|
|||||||
/// <returns>A configured <c>hx-request</c> attribute</returns>
|
/// <returns>A configured <c>hx-request</c> attribute</returns>
|
||||||
/// <seealso cref="HxRequest.Configure" />
|
/// <seealso cref="HxRequest.Configure" />
|
||||||
/// <seealso href="https://htmx.org/attributes/hx-request/">Documentation</seealso>
|
/// <seealso href="https://htmx.org/attributes/hx-request/">Documentation</seealso>
|
||||||
|
[<System.Obsolete "hx-request removed in v4; use hx-config instead">]
|
||||||
let _hxRequest spec =
|
let _hxRequest spec =
|
||||||
attr "hx-request" spec
|
attr "hx-request" spec
|
||||||
|
|
||||||
@@ -699,6 +866,31 @@ module HtmxAttrs =
|
|||||||
let _hxSelectOob selectors =
|
let _hxSelectOob selectors =
|
||||||
attr "hx-select-oob" selectors
|
attr "hx-select-oob" selectors
|
||||||
|
|
||||||
|
/// <summary>Define a response to an HTTP status code or code range</summary>
|
||||||
|
/// <param name="code">
|
||||||
|
/// The code or code range; for a code, pass the full code as a string (ex. <c>404</c>), and for a range, pass a
|
||||||
|
/// string with one or two characters (ex. <c>5</c> for all 500s, <c>42</c> for 420-429)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="swap">The swap style to use for this action(use <c>HxSwap</c> values)</param>
|
||||||
|
/// <param name="action">
|
||||||
|
/// The action which should be taken for responses matching the status code; can be a combination of the following,
|
||||||
|
/// with each item separated by a space:
|
||||||
|
/// <ul>
|
||||||
|
/// <li><c>target:</c> and a CSS selector for the swap target (overrides original <c>hx-target</c>)</li>
|
||||||
|
/// <li><c>select:</c> and a CSS selector for the content to be swapped (overrides original <c>hx-select</c>)</li>
|
||||||
|
/// <li><c>push:</c> and <c>true</c>, <c>false</c>, or a URL; <c>false</c> does not update URL history, <c>true</c>
|
||||||
|
/// updates with the current URL, and a string URL will be pushed into history as-is</li>
|
||||||
|
/// <li><c>replace:</c> and <c>true</c>, <c>false</c>, or a URL; same as <c>push:</c>, but replaces in history
|
||||||
|
/// instead</li>
|
||||||
|
/// <li><c>transition:</c> and <c>true</c> or <c>false</c>; whether to use view transitions</li>
|
||||||
|
/// </ul>
|
||||||
|
/// If only the type of swap is being changed, pass an empty string to this parameter.
|
||||||
|
/// </param>
|
||||||
|
/// <seealso href="https://four.htmx.org/reference/attributes/hx-status">Documentation</seealso>
|
||||||
|
let _hxStatus code swap action =
|
||||||
|
let range = $"%s{code}xx".Substring(0, 3)
|
||||||
|
attr $"hx-status:{range}" ($"swap:%s{swap} %s{action}".Trim())
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controls how the response content is swapped into the DOM (e.g. <c>outerHTML</c> or <c>beforeEnd</c>)
|
/// Controls how the response content is swapped into the DOM (e.g. <c>outerHTML</c> or <c>beforeEnd</c>)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -774,17 +966,46 @@ module HtmxAttrs =
|
|||||||
/// <param name="url">The URL from which events will be received</param>
|
/// <param name="url">The URL from which events will be received</param>
|
||||||
/// <returns>A configured <c>sse-connect</c> attribute</returns>
|
/// <returns>A configured <c>sse-connect</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/extensions/sse/#usage">Extension Docs</seealso>
|
/// <seealso href="https://htmx.org/extensions/sse/#usage">Extension Docs</seealso>
|
||||||
let _sseConnect url =
|
let [<System.Obsolete "Removed in v4; use Htmax._hxSseConnect">] _sseConnect url =
|
||||||
attr "sse-connect" url
|
attr "sse-connect" url
|
||||||
|
|
||||||
/// <summary>The name(s) of the message(s) to swap into the DOM</summary>
|
/// <summary>The name(s) of the message(s) to swap into the DOM</summary>
|
||||||
/// <param name="messages">The message names (comma-delimited) to swap (use "message" for unnamed events)</param>
|
/// <param name="messages">The message names (comma-delimited) to swap (use "message" for unnamed events)</param>
|
||||||
/// <returns>A configured <c>sse-swap</c> attribute</returns>
|
/// <returns>A configured <c>sse-swap</c> attribute</returns>
|
||||||
/// <seealso href="https://htmx.org/extensions/sse/#usage">Extension Docs</seealso>
|
/// <seealso href="https://htmx.org/extensions/sse/#usage">Extension Docs</seealso>
|
||||||
let _sseSwap messages =
|
let [<System.Obsolete "Removed in v4; use hx-swap on target element">] _sseSwap messages =
|
||||||
attr "sse-swap" messages
|
attr "sse-swap" messages
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Modifiers for htmx attributes</summary>
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HxModifiers =
|
||||||
|
|
||||||
|
/// <summary>Append this value to any inherited value of the attribute</summary>
|
||||||
|
/// <param name="attrib">The attribute to which <c>:append</c> should be applied</param>
|
||||||
|
/// <returns>The given attribute with <c>:append</c> applied</returns>
|
||||||
|
let hxAppend attrib =
|
||||||
|
match attrib with
|
||||||
|
| KeyValue (name, value) -> attr $"{name}:append" value
|
||||||
|
| Boolean name -> flag $"{name}:append"
|
||||||
|
|
||||||
|
/// <summary>Explicitly propagate inheritance for the value for this attribute</summary>
|
||||||
|
/// <param name="attrib">The attribute whose value should be inherited</param>
|
||||||
|
/// <returns>The given attribute with <c>:inherited</c> applied</returns>
|
||||||
|
let hxInherited attrib =
|
||||||
|
match attrib with
|
||||||
|
| KeyValue (name, value) -> attr $"{name}:inherited" value
|
||||||
|
| Boolean name -> flag $"{name}:inherited"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>htmx-specific HTML tags</summary>
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HxTags =
|
||||||
|
|
||||||
|
/// <summary>An <c>hx-partial</c> tag</summary>
|
||||||
|
let hxPartial = tag "hx-partial"
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Script tags to pull htmx into a web page</summary>
|
/// <summary>Script tags to pull htmx into a web page</summary>
|
||||||
module Script =
|
module Script =
|
||||||
|
|
||||||
@@ -796,17 +1017,37 @@ module Script =
|
|||||||
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
||||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||||
let cdnMinified =
|
let cdnMinified =
|
||||||
script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"
|
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.min.js"
|
||||||
_integrity "sha256-Iig+9oy3VFkU8KiKG97cclanA9HVgMHSVSF9ClDTExM="
|
_integrity "sha384-aWZK1NtOs/aWb/+YZdTM8q2JkWEshlMc9mgZ189numT9bwFhyAyYEoO4nO/2dTXt"
|
||||||
_crossorigin "anonymous" ] []
|
_crossorigin "anonymous" ] []
|
||||||
|
|
||||||
/// <summary>Script tag to load the unminified version from jsdelivr.net</summary>
|
/// <summary>Script tag to load the unminified version from jsdelivr.net</summary>
|
||||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||||
let cdnUnminified =
|
let cdnUnminified =
|
||||||
script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.js"
|
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js"
|
||||||
_integrity "sha256-upUwYnay6R+DA68rROTAP+EdfO3NvOqtE513PgDyAYM="
|
_integrity "sha384-OFLRIZpuqI2wwFozxvDGcuF3TQ36ySMgp44WEthOiR4wFzRkhZbK72HFaBo2C3cx"
|
||||||
_crossorigin "anonymous" ] []
|
_crossorigin "anonymous" ] []
|
||||||
|
|
||||||
|
/// <summary>Script tags to load the htmax bundle</summary>
|
||||||
|
module Max =
|
||||||
|
|
||||||
|
/// <summary>Script tag to load the package-provided version of the htmx-plus-extensions bundle</summary>
|
||||||
|
let local = script [ _src htmaxLocalScript ] []
|
||||||
|
|
||||||
|
/// <summary>Script tag to load the minified htmx-plus-extensions bundle from jsdelivr.net</summary>
|
||||||
|
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||||
|
let cdnMinified =
|
||||||
|
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js"
|
||||||
|
_integrity "sha384-Qoqie5IRtOE79SDFFRSb/yKi+pkzpSnfjgwr1KksyP14OaHkLHar0KrLVxUwlsJF"
|
||||||
|
_crossorigin "anonymous" ] []
|
||||||
|
|
||||||
|
/// <summary>Script tag to load the unminified htmx-plus-extensions bundle from jsdelivr.net</summary>
|
||||||
|
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||||
|
let cdnUnminified =
|
||||||
|
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js"
|
||||||
|
_integrity "sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK"
|
||||||
|
_crossorigin "anonymous" ] []
|
||||||
|
|
||||||
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
||||||
[<Obsolete "Use cdnMinified instead; this will be removed in v4">]
|
[<Obsolete "Use cdnMinified instead; this will be removed in v4">]
|
||||||
let minified = cdnMinified
|
let minified = cdnMinified
|
||||||
|
|||||||
@@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
This package enables [htmx](https://htmx.org) support within the [Giraffe](https://giraffe.wiki) view engine.
|
This package enables [htmx](https://htmx.org) support within the [Giraffe](https://giraffe.wiki) view engine.
|
||||||
|
|
||||||
**htmx version: 2.0.8**
|
**htmx version: 4.0.0-beta4**
|
||||||
|
|
||||||
_Upgrading from v1.x: see [the migration guide](https://htmx.org/migration-guide-htmx-1/) for changes_
|
_Upgrading from v2.x: see [the migration guide](https://four.htmx.org/docs/get-started/migration) for changes, which are plentiful. htmx switches from `XMLHTTPRequest` to `fetch`, and many changes are related to the new event cycle._
|
||||||
|
|
||||||
|
_Inheritance is now explicit; to have an attribute's value inherited to its children, wrap the attribute in `hxInherited` (ex. `hxInherited (_hxTarget "#main")`). Values can be appended to inherited values as well using the `hxAppend` modifier._
|
||||||
|
|
||||||
|
_Several constructs have been marked obsolete in this release, and will be removed from the first production release of v4. With the exception of `_hxDisable`, though (which now functions as the deprecated `_hxDisabledElt` did), this should not introduce compile errors. Rather, this package will raise warnings for deprecated constructs, along with suggestions of what to use instead._
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
1. Install the package.
|
1. Install the package.
|
||||||
2. Prior to using the attribute or support modules, `open Giraffe.ViewEngine.Htmx`.
|
1. Prior to using the attribute or support modules, `open Giraffe.ViewEngine.Htmx`.
|
||||||
|
1. If the client is using the `htmax` bundle, also `open Giraffe.ViewEngine.Htmax`.
|
||||||
|
|
||||||
### Use
|
### Use
|
||||||
|
|
||||||
@@ -21,15 +26,18 @@ let autoload =
|
|||||||
```
|
```
|
||||||
|
|
||||||
Support modules include:
|
Support modules include:
|
||||||
|
- `HxConfig` _(new in v4)_
|
||||||
- `HxEncoding`
|
- `HxEncoding`
|
||||||
- `HxHeaders`
|
- `HxHeaders`
|
||||||
- `HxParams`
|
- ~~`HxParams`~~ _(removed in v4)_
|
||||||
- `HxRequest`
|
- ~~`HxRequest`~~ _(renamed to `HxConfig`)_
|
||||||
- `HxSwap` (requires `open Giraffe.Htmx`)
|
- `HxSwap` (requires `open Giraffe.Htmx`)
|
||||||
- `HxTrigger`
|
- `HxTrigger`
|
||||||
- `HxVals`
|
- `HxVals`
|
||||||
|
|
||||||
`Htmx.Script.local` creates an `XmlNode` to load the package-provided htmx library. There are also two `XmlNode`s that will load the htmx script from jsdelivr; `Htmx.Script.cdnMinified` loads the minified version, and `Htmx.Script.cdnUnminified` loads the unminified version (useful for debugging). When using the CDN nodes and a Content Security Policy (CSP) header, `cdn.jsdelivr.net` needs to be listed as an allowable `script-src`.
|
`Htmx.Script.local` creates an `XmlNode` to load the package-provided htmx library. There are also two `XmlNode`s that will load the htmx script from jsdelivr; `Htmx.Script.cdnMinified` loads the minified version, and `Htmx.Script.cdnUnminified` loads the unminified version (useful for debugging). htmx v4 also distributes a "max" bundle which contains some common extensions; these are available with the same names under `Htmx.Script.Max`.
|
||||||
|
|
||||||
|
_NOTE: When using the CDN nodes and a Content Security Policy (CSP) header, `cdn.jsdelivr.net` needs to be listed as an allowable `script-src`._
|
||||||
|
|
||||||
This also supports [fragment rendering](https://bitbadger.solutions/blog/2022/fragment-rendering-in-giraffe-view-engine.html), providing the flexibility to render an entire template, or only a portion of it (based on the element's `id` attribute).
|
This also supports [fragment rendering](https://bitbadger.solutions/blog/2022/fragment-rendering-in-giraffe-view-engine.html), providing the flexibility to render an entire template, or only a portion of it (based on the element's `id` attribute).
|
||||||
|
|
||||||
@@ -42,10 +50,10 @@ htmx's attributes and these attribute functions map one-to-one. There are two ex
|
|||||||
The htmx `hx-on` attribute supports multiple events if they are separated with a newline (`\n`) character. The value provided to this attribute will be attribute-escaped, but in testing, it was interpreted correctly.
|
The htmx `hx-on` attribute supports multiple events if they are separated with a newline (`\n`) character. The value provided to this attribute will be attribute-escaped, but in testing, it was interpreted correctly.
|
||||||
|
|
||||||
The support modules contain named properties for known values (as illustrated with `HxTrigger.Load` above). A few of the modules are more than collections of names, though:
|
The support modules contain named properties for known values (as illustrated with `HxTrigger.Load` above). A few of the modules are more than collections of names, though:
|
||||||
- `HxRequest` has a `Configure` function, which takes a list of strings; the other functions in the module allow for configuring the request.
|
- `HxConfig` has a `Configure` function, which takes a list of strings; the other functions in the module allow for configuring the request.
|
||||||
|
|
||||||
```fsharp
|
```fsharp
|
||||||
HxRequest.Configure [ HxRequest.Timeout 500 ] |> _hxRequest
|
HxConfig.Configure [ HxRequest.Timeout 500 ] |> _hxConfig
|
||||||
```
|
```
|
||||||
- `HxTrigger` is _(by far)_ the most complex of these modules. Most uses won't need that complexity; however, complex triggers can be defined by piping into or composing with other functions. For example, to define an event that responds to a shift-click anywhere on the document, with a delay of 3 seconds before firing:
|
- `HxTrigger` is _(by far)_ the most complex of these modules. Most uses won't need that complexity; however, complex triggers can be defined by piping into or composing with other functions. For example, to define an event that responds to a shift-click anywhere on the document, with a delay of 3 seconds before firing:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user