module Giraffe.Htmx open Microsoft.AspNetCore.Http open Microsoft.Extensions.Primitives open System /// Determine if the given header is present let private hdr (headers : IHeaderDictionary) hdr = match headers[hdr] with it when it = StringValues.Empty -> None | it -> Some it[0] /// Extensions to the header dictionary type IHeaderDictionary with /// Indicates that the request is via an element using hx-boost member this.HxBoosted with get () = hdr this "HX-Boosted" |> Option.map bool.Parse /// The current URL of the browser (note that this does not update until after settle) member this.HxCurrentUrl with get () = hdr this "HX-Current-URL" |> Option.map Uri /// true if the request is for history restoration after a miss in the local history cache member this.HxHistoryRestoreRequest 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" /// true if the request came from htmx 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" /// Extensions for the request object type HttpRequest with /// Whether this request was initiated from htmx member this.IsHtmx with get () = this.Headers.HxRequest |> Option.defaultValue false /// Whether this request is an htmx history-miss refresh request member this.IsHtmxRefresh with get () = this.IsHtmx && (this.Headers.HxHistoryRestoreRequest |> Option.defaultValue false) /// HTTP handlers for setting output headers [] module Handlers = open Giraffe.Htmx.Common /// Instruct htmx to perform a client-side redirect for content /// The path where the content should be found /// An HTTP handler with the HX-Location header set /// Documentation let withHxLocation (path: string) : HttpHandler = setHttpHeader "HX-Location" path /// Pushes a new url into the history stack /// The URL to be pushed /// An HTTP handler with the HX-Push-Url header set /// Use to explicitly not push a new URL /// Documentation let withHxPushUrl (url: string) : HttpHandler = setHttpHeader "HX-Push-Url" url /// Explicitly do not push a new URL into the history stack /// An HTTP handler with the HX-Push-Url header set to false /// Documentation let withHxNoPushUrl : HttpHandler = toLowerBool false |> withHxPushUrl /// Can be used to do a client-side redirect to a new location /// The URL to which the client should be redirected /// An HTTP handler with the HX-Redirect header set /// Documentation let withHxRedirect (url: string) : HttpHandler = setHttpHeader "HX-Redirect" url /// If set to true the client side will do a full refresh of the page /// Whether the client should refresh their page /// An HTTP handler with the HX-Refresh header set let withHxRefresh shouldRefresh : HttpHandler = (toLowerBool >> setHttpHeader "HX-Refresh") shouldRefresh /// Replaces the current URL in the history stack /// The URL to place in the history stack in place of the current one /// An HTTP handler with the HX-Replace-URL header set /// Use to explicitly not replace the current URL /// Documentation let withHxReplaceUrl url : HttpHandler = setHttpHeader "HX-Replace-Url" url /// Explicitly do not replace the current URL in the history stack /// An HTTP handler with the HX-Replace-URL header set to false /// Documentation let withHxNoReplaceUrl : HttpHandler = toLowerBool false |> withHxReplaceUrl /// Override which portion of the response will be swapped into the target document /// The selector for the new response target /// An HTTP handler with the HX-Reselect header set let withHxReselect (target: string) : HttpHandler = setHttpHeader "HX-Reselect" target /// Override the hx-swap attribute from the initiating element /// The swap value to override /// An HTTP handler with the HX-Reswap header set /// Use HxSwap constants for best results let withHxReswap (swap: string) : HttpHandler = setHttpHeader "HX-Reswap" swap /// Allows you to override the hx-target attribute /// The new target for the response /// An HTTP handler with the HX-Retarget header set let withHxRetarget (target: string) : HttpHandler = setHttpHeader "HX-Retarget" target /// Allows you to trigger a single client side event /// The call to the event that should be triggered /// An HTTP handler with the HX-Trigger header set /// Documentation let withHxTrigger (evt: string) : HttpHandler = setHttpHeader "HX-Trigger" evt /// Allows you to trigger multiple client side events /// The calls to events that should be triggered /// An HTTP handler with the HX-Trigger header set for all given events /// Documentation let withHxTriggerMany evts : HttpHandler = toJson evts |> setHttpHeader "HX-Trigger" /// Allows you to trigger a single client side event after changes have settled /// The call to the event that should be triggered /// An HTTP handler with the HX-Trigger-After-Settle header set /// Documentation let withHxTriggerAfterSettle (evt: string) : HttpHandler = setHttpHeader "HX-Trigger-After-Settle" evt /// Allows you to trigger multiple client side events after changes have settled /// The calls to events that should be triggered /// An HTTP handler with the HX-Trigger-After-Settle header set for all given events /// Documentation let withHxTriggerManyAfterSettle evts : HttpHandler = toJson evts |> setHttpHeader "HX-Trigger-After-Settle" /// Allows you to trigger a single client side event after DOM swapping occurs /// The call to the event that should be triggered /// An HTTP handler with the HX-Trigger-After-Swap header set /// Documentation let withHxTriggerAfterSwap (evt: string) : HttpHandler = setHttpHeader "HX-Trigger-After-Swap" evt /// Allows you to trigger multiple client side events after DOM swapping occurs /// The calls to events that should be triggered /// An HTTP handler with the HX-Trigger-After-Swap header set for all given events /// Documentation let withHxTriggerManyAfterSwap evts : HttpHandler = toJson evts |> setHttpHeader "HX-Trigger-After-Swap" /// Load the package-provided version of the htmx script [] module HtmxScript = open Giraffe.Htmx.Common open Microsoft.AspNetCore.Html /// script tag to load the package-provided version of the htmx script let local = HtmlString $""""""