diff --git a/src/Common/Common.fs b/src/Common/Common.fs index 2ff982e..f288899 100644 --- a/src/Common/Common.fs +++ b/src/Common/Common.fs @@ -3,7 +3,7 @@ module Giraffe.Htmx.Common /// The version of htmx embedded in the package -let HtmxVersion = "2.0.8" +let HtmxVersion = "4.0.0-alpha6" /// The path for the provided htmx script let internal htmxLocalScript = $"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}" @@ -25,8 +25,8 @@ let internal toLowerBool (boolValue: bool) = /// Valid values for the hx-swap attribute / HX-Reswap header -/// May be combined with swap / settle / scroll / show config) -/// Documentation +/// May be combined with swap / scroll / show config) +/// Documentation [] module HxSwap = @@ -38,22 +38,60 @@ module HxSwap = [] let OuterHtml = "outerHTML" + /// Morph the inner HTML of the target to the new content + [] + let InnerMorph = "innerMorph" + + /// Morph the outer HTML of the target to the new content + [] + let OuterMorph = "innerMorph" + + /// Replace the text content of the target without parsing the response as HTML + [] + let TextContent = "textContent" + /// Insert the response before the target element [] - let BeforeBegin = "beforebegin" + let Before = "before" + + /// Insert the response before the target element (pre-v4 name) + [] + let BeforeBegin = Before /// Insert the response before the first child of the target element [] - let AfterBegin = "afterbegin" + let Prepend = "prepend" + + /// Insert the response before the first child of the target element (pre-v4 name) + [] + let AfterBegin = Prepend /// Insert the response after the last child of the target element [] - let BeforeEnd = "beforeend" + let Append = "append" + + /// Insert the response after the last child of the target element (pre-v4 name) + [] + let BeforeEnd = Append /// Insert the response after the target element [] - let AfterEnd = "afterend" + let After = "after" + + /// Insert the response after the target element (pre-v4 name) + [] + let AfterEnd = After - /// Does not append content from response (out of band items will still be processed). + /// Delete the target element regardless of response + [] + let Delete = "delete" + + /// Does not append content from response (out of band items will still be processed) [] let None = "none" + + /// Update existing elements by id and add new ones + /// This requires the upsert extension + /// Extension + [] + let Upsert = "upsert" diff --git a/src/Htmx/Htmx.fs b/src/Htmx/Htmx.fs index c1b100f..99b9d6d 100644 --- a/src/Htmx/Htmx.fs +++ b/src/Htmx/Htmx.fs @@ -24,6 +24,7 @@ type IHeaderDictionary with with get () = hdr this "HX-History-Restore-Request" |> Option.map bool.Parse /// The user response to an hx-prompt + [] member this.HxPrompt with get () = hdr this "HX-Prompt" @@ -31,15 +32,29 @@ type IHeaderDictionary with member this.HxRequest with get () = hdr this "HX-Request" |> Option.map bool.Parse + /// The tag name (fst) and id attribute (snd) of the element triggering this request + 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 + /// The id attribute of the target element if it exists member this.HxTarget with get () = hdr this "HX-Target" /// The id attribute of the triggered element if it exists + [] member this.HxTrigger with get () = hdr this "HX-Trigger" /// The name attribute of the triggered element if it exists + [] member this.HxTriggerName with get () = hdr this "HX-Trigger-Name" diff --git a/src/Tests/Common.fs b/src/Tests/Common.fs index 959596c..1d3972b 100644 --- a/src/Tests/Common.fs +++ b/src/Tests/Common.fs @@ -6,7 +6,7 @@ open Giraffe.Htmx /// Test to ensure the version was updated let version = test "HtmxVersion is correct" { - Expect.equal HtmxVersion "2.0.8" "htmx version incorrect" + Expect.equal HtmxVersion "4.0.0-alpha6" "htmx version incorrect" } /// Tests for the HxSwap module @@ -18,21 +18,48 @@ let swap = test "OuterHtml is correct" { Expect.equal HxSwap.OuterHtml "outerHTML" "Outer HTML swap value incorrect" } - test "BeforeBegin is correct" { - Expect.equal HxSwap.BeforeBegin "beforebegin" "Before Begin swap value incorrect" + test "InnerMorph is correct" { + Expect.equal HxSwap.InnerMorph "innerMorph" "Inner Morph swap value incorrect" } - test "BeforeEnd is correct" { - Expect.equal HxSwap.BeforeEnd "beforeend" "Before End swap value incorrect" + test "OuterMorph is correct" { + Expect.equal HxSwap.OuterMorph "innerMorph" "Outer Morph 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" { - 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" { - 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" { Expect.equal HxSwap.None "none" "None swap value incorrect" } + test "Upsert is correct" { + Expect.equal HxSwap.Upsert "upsert" "Upsert swap value incorrect" + } ] /// All tests for this module diff --git a/src/ViewEngine.Htmx/Htmx.fs b/src/ViewEngine.Htmx/Htmx.fs index c73a81e..8ea89bd 100644 --- a/src/ViewEngine.Htmx/Htmx.fs +++ b/src/ViewEngine.Htmx/Htmx.fs @@ -1,6 +1,8 @@ /// Types and functions supporting htmx attributes in Giraffe View Engine module Giraffe.ViewEngine.Htmx +open System.Net.Http + /// Valid values for the hx-encoding attribute [] module HxEncoding = @@ -21,17 +23,20 @@ type HxEvent = /// Send this event to an element to abort a request | Abort + /// Triggered after a request has initialized + | AfterInit + /// Triggered after an AJAX request has completed processing a successful response - | AfterOnLoad + | [] AfterOnLoad /// Triggered after htmx has initialized a node - | AfterProcessNode + | [] AfterProcessNode /// Triggered after an AJAX request has completed | AfterRequest /// Triggered after the DOM has settled - | AfterSettle + | [] AfterSettle /// Triggered after new content has been swapped in | AfterSwap @@ -39,20 +44,32 @@ type HxEvent = /// Triggered before htmx disables an element or removes it from the DOM | BeforeCleanupElement + /// Triggered before content is saved to the history cache + | [] BeforeHistorySave + + /// Triggered before content is saved to the history cache + | BeforeHistoryUpdate + + /// Triggered before htmx initializes a node + | BeforeInit + /// Triggered before any response processing occurs - | BeforeOnLoad + | [] BeforeOnLoad /// Triggered before htmx initializes a node | BeforeProcessNode - /// Triggered before an AJAX request is made + /// Triggered before an HTTP request is made | BeforeRequest + /// Triggered just before an ajax request is sent + | [] BeforeSend + /// Triggered before a swap is done, allows you to configure the swap | BeforeSwap - /// Triggered just before an ajax request is sent - | BeforeSend + /// Triggered before a CSS view transition starts + | BeforeTransition /// Triggered before the request, allows you to customize parameters, headers | ConfigRequest @@ -77,9 +94,6 @@ type HxEvent = /// Triggered when htmx handles a history restoration action | HistoryRestore - /// Triggered before content is saved to the history cache - | BeforeHistorySave - /// Triggered when new content is added to the DOM | Load @@ -151,25 +165,29 @@ type HxEvent = /// The htmx event name (fst) and kebab-case name (snd, for use with hx-on) static member private Values = Map [ Abort, ("abort", "abort") - AfterOnLoad, ("afterOnLoad", "after-on-load") - AfterProcessNode, ("afterProcessNode", "after-process-node") - AfterRequest, ("afterRequest", "after-request") - AfterSettle, ("afterSettle", "after-settle") - AfterSwap, ("afterSwap", "after-swap") - BeforeCleanupElement, ("beforeCleanupElement", "before-cleanup-element") - BeforeOnLoad, ("beforeOnLoad", "before-on-load") - BeforeProcessNode, ("beforeProcessNode", "before-process-node") - BeforeRequest, ("beforeRequest", "before-request") - BeforeSwap, ("beforeSwap", "before-swap") - BeforeSend, ("beforeSend", "before-send") - ConfigRequest, ("configRequest", "config-request") + AfterInit, ("afterInit", "after:init") + AfterOnLoad, ("afterOnLoad", "after:init") + AfterProcessNode, ("afterProcessNode", "after:init") + AfterRequest, ("afterRequest", "after:request") + AfterSettle, ("afterSettle", "after:swap") + AfterSwap, ("afterSwap", "after:swap") + BeforeCleanupElement, ("beforeCleanupElement", "before:cleanup:element") + BeforeHistorySave, ("beforeHistorySave", "before:history:update") + BeforeHistoryUpdate, ("beforeHistoryUpdate", "before:history:update") + BeforeInit, ("beforeInit", "before:init") + BeforeOnLoad, ("beforeOnLoad", "before:init") + BeforeProcessNode, ("beforeProcessNode", "before:process") + BeforeRequest, ("beforeRequest", "before:request") + BeforeSend, ("beforeSend", "before:send") + BeforeSwap, ("beforeSwap", "before:swap") + BeforeTransition, ("beforeTransition", "before:viewTransition") + ConfigRequest, ("configRequest", "config:request") Confirm, ("confirm", "confirm") HistoryCacheError, ("historyCacheError", "history-cache-error") HistoryCacheMiss, ("historyCacheMiss", "history-cache-miss") HistoryCacheMissError, ("historyCacheMissError", "history-cache-miss-error") HistoryCacheMissLoad, ("historyCacheMissLoad", "history-cache-miss-load") HistoryRestore, ("historyRestore", "history-restore") - BeforeHistorySave, ("beforeHistorySave", "before-history-save") Load, ("load", "load") NoSseSourceError, ("noSSESourceError", "no-sse-source-error") OnLoadError, ("onLoadError", "on-load-error") @@ -493,11 +511,23 @@ open Giraffe.Htmx [] module HtmxAttrs = + /// The URL which will receive the request (use with _hxMethod) + /// The URL for the attribute + /// Documentation + let _hxAction url = attr "hx-action" url + /// Progressively enhances anchors and forms to use AJAX requests /// Use _hxNoBoost to set to false /// Documentation let _hxBoost = attr "hx-boost" "true" + /// Configure request behavior + /// The configuration parameters to use for the request + /// A configured hx-config attribute + /// Documentation + let _hxConfig config = + attr "hx-config" (toJson config) + /// Shows a confirm() dialog before issuing a request /// The prompt to present to the user when seeking their confirmation /// A configured hx-confirm attribute @@ -512,14 +542,23 @@ module HtmxAttrs = let _hxDelete url = attr "hx-delete" url - /// Disables htmx processing for the given node and any children nodes - /// Documentation - let _hxDisable = flag "hx-disable" + /// Specifies elements that should be disabled when an htmx request is in flight + /// The element to disable when an htmx request is in flight + /// A configured hx-disable attribute + /// This behavior changed in v4 + ///
  • For the v2 behavior of hx-disable (which will manifest as compile errors), change instances to + /// _hxIgnore
  • + ///
  • For v4 behavior, changing _hxDisabledElt to _hxDisable should be sufficient
+ ///
+ /// Documentation + let _hxDisable elt = + attr "hx-disable" elt /// Specifies elements that should be disabled when an htmx request is in flight /// The element to disable when an htmx request is in flight /// A configured hx-disabled-elt attribute /// Documentation + [] let _hxDisabledElt elt = attr "hx-disabled-elt" elt @@ -527,6 +566,7 @@ module HtmxAttrs = /// The htmx attributes to disinherit (should start with "hx-") /// A configured hx-disinherit attribute /// Documentation + [] let _hxDisinherit hxAttrs = attr "hx-disinherit" hxAttrs @@ -542,6 +582,7 @@ module HtmxAttrs = /// A list of extensions to apply to this element /// A configured hx-ext attribute /// Documentation + [] let _hxExt exts = attr "hx-ext" exts @@ -565,14 +606,21 @@ module HtmxAttrs = /// Whether the page should be stored in the history cache /// A configured hx-history attribute /// Documentation + [] let _hxHistory shouldStore = attr "hx-history" (toLowerBool shouldStore) /// The element to snapshot and restore during history navigation /// Documentation + [] let _hxHistoryElt = flag "hx-history-elt" + /// Disables htmx processing for the given node and any children nodes + /// Documentation + let _hxIgnore = + flag "hx-ignore" + /// Includes additional data in AJAX requests /// The specification of what should be included in the request /// A configured hx-include attribute @@ -587,6 +635,12 @@ module HtmxAttrs = let _hxIndicator selector = attr "hx-indicator" selector + /// The HTTP method to use for the request(use with _hxAction) + /// The method to use + /// Documentation + let _hxMethod (method: HttpMethod) = + attr "hx-method" (method.Method.ToLowerInvariant()) + /// Overrides a previous hx-boost (hx-boost="false") /// Documentation let _hxNoBoost = @@ -614,6 +668,7 @@ module HtmxAttrs = /// A configured hx-params attribute /// /// Documentation + [] let _hxParams toInclude = attr "hx-params" toInclude @@ -641,6 +696,7 @@ module HtmxAttrs = /// A configured hx-prompt attribute /// The value provided will be in the HX-Prompt request header /// Documentation + [] let _hxPrompt text = attr "hx-prompt" text @@ -682,6 +738,7 @@ module HtmxAttrs = /// A configured hx-request attribute /// /// Documentation + [] let _hxRequest spec = attr "hx-request" spec @@ -785,6 +842,35 @@ module HtmxAttrs = attr "sse-swap" messages +/// Modifiers for htmx attributes +[] +module HxModifiers = + + /// Append this value to any inherited value of the attribute + /// The attribute to which :append should be applied + /// The given attribute with :append applied + let hxAppend attrib = + match attrib with + | KeyValue (name, value) -> attr $"{name}:append" value + | Boolean name -> flag $"{name}:append" + + /// Explicity inherit the value for this attribute + /// The attribute whose value should be inherited + /// The given attribute with :inherited applied + let hxInherit attrib = + match attrib with + | KeyValue (name, value) -> attr $"{name}:inherited" value + | Boolean name -> flag $"{name}:inherited" + + +/// htmx-specific HTML tags +[] +module HxTags = + + /// An hx-partial tag + let hxPartial = tag "hx-partial" + + /// Script tags to pull htmx into a web page module Script =