/// Types and functions supporting htmx attributes in Giraffe View Engine module Giraffe.ViewEngine.Htmx /// Helpers to define hx-config attribute values /// Documentation [] module HxConfig = open Giraffe.Htmx.Common /// Configure the request with various options /// The options to configure /// A string with the configured options let Configure (opts: string list) = opts |> String.concat ", " |> sprintf "{ %s }" /// Set a timeout (in milliseconds) /// The milliseconds for the request timeout /// A string with the configured request timeout let Timeout (ms: int) = $"\"timeout\": {ms}" /// Include or exclude credentials from the request /// true if credentials should be sent, false if not /// A string with the configured credential options let Credentials send = (toLowerBool >> sprintf "\"credentials\": %s") send /// Exclude or include headers from the request /// /// true if no headers should be sent; false if headers should be sent /// /// A string with the configured header options let NoHeaders exclude = (toLowerBool >> sprintf "\"noHeaders\": %s") exclude /// Valid values for the hx-encoding attribute [] module HxEncoding = /// A standard HTTP form [] let Form = "application/x-www-form-urlencoded" /// A multipart form (used for file uploads) [] let MultipartForm = "multipart/form-data" #nowarn 44 // Obsolete elements still have entries in the conversion map; will be removed for v4 final /// The events recognized by htmx /// Documentation [] type HxEvent = /// Send this event to an element to abort a request | Abort /// Triggered after htmx disables an element or removes it from the DOM | AfterCleanup /// Triggered after content is saved to the history cache | AfterHistoryUpdate /// Triggered after a request has initialized | AfterInit /// Triggered after an AJAX request has completed processing a successful response | [] AfterOnLoad /// Triggered after htmx has initialized a node | [] AfterProcessNode /// Triggered after new content is saved to the history cache | AfterPushIntoHistory /// Triggered after updated content is saved to the history cache | AfterReplaceIntoHistory /// Triggered after an HTTP request has completed successfully | AfterRequest /// Triggered after the DOM has settled | [] AfterSettle /// Triggered after a Server Sent Events (SSE) message is read | AfterSseMessage /// Triggered after a Server Sent Events (SSE) stream is closed | AfterSseStream /// Triggered after new content has been swapped in | AfterSwap /// Triggered after a CSS view transition completes | AfterViewTransition /// Triggered before htmx disables an element or removes it from the DOM | BeforeCleanup /// 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 /// Triggered before htmx initializes a node | [] BeforeProcessNode /// Triggered before an HTTP request is made | BeforeRequest /// Triggered before a history restore request is made | BeforeRestoreHistory /// Triggered just before an ajax request is sent | [] BeforeSend /// Triggered before a Server Sent Events (SSE) message is read | BeforeSseMessage /// Triggered before a Server Sent Events (SSE) connection is reconnected | BeforeSseReconnect /// Triggered before a Server Sent Events (SSE) stream is opened | BeforeSseStream /// Triggered before a swap is done, allows you to configure the swap | BeforeSwap /// Triggered before a CSS view transition starts | BeforeViewTransition /// Triggered before the request, allows you to customize parameters, headers | ConfigRequest /// /// Triggered after a trigger occurs on an element, allows you to cancel (or delay) issuing the AJAX request /// | Confirm /// Triggered when an error occurs | Error /// Triggered after an HTTP request is made, whether it was successful or not | FinallyRequest /// Triggered on an error during cache writing | [] HistoryCacheError /// Triggered on a cache miss in the history subsystem | [] HistoryCacheMiss /// Triggered on a unsuccessful remote retrieval | [] HistoryCacheMissError /// Triggered on a successful remote retrieval | [] HistoryCacheMissLoad /// Triggered when htmx handles a history restoration action | [] HistoryRestore /// Triggered when new content is added to the DOM | [] Load /// /// Triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined /// | [] NoSseSourceError /// Triggered when an exception occurs during the onLoad handling in htmx | [] OnLoadError /// Triggered after an out of band element as been swapped in | [] OobAfterSwap /// Triggered before an out of band element swap is done, allows you to configure the swap | [] OobBeforeSwap /// Triggered when an out of band element does not have a matching ID in the current DOM | [] OobErrorNoTarget /// Triggered after a prompt is shown | [] Prompt /// Triggered after an url is pushed into history | [] PushedIntoHistory /// Triggered when an HTTP response error (non-200 or 300 response code) occurs | [] ResponseError /// Triggered when a network error prevents an HTTP request from happening | [] SendError /// Triggered when an error occurs with a SSE source | [] SseError /// Triggered when an SSE source is opened | [] SseOpen /// Triggered when an error occurs during the swap phase | [] SwapError /// Triggered when an invalid target is specified | [] TargetError /// Triggered when a request timeout occurs | [] Timeout /// Triggered before an element is validated | [] ValidationValidate /// Triggered when an element fails validation | [] ValidationFailed /// Triggered when a request is halted due to validation errors | [] ValidationHalted /// Triggered when an ajax request aborts | [] XhrAbort /// Triggered when an ajax request ends | [] XhrLoadEnd /// Triggered when an ajax request starts | [] XhrLoadStart /// Triggered periodically during an ajax request that supports progress events | [] XhrProgress /// The htmx event name (fst) and kebab-case name (snd, for use with hx-on) static member private Values = Map [ Abort, ("abort", "abort") AfterCleanup, ("afterCleanup", "after:cleanup") AfterHistoryUpdate, ("afterHistoryUpdate", "after:history:update") AfterInit, ("afterInit", "after:init") AfterOnLoad, ("afterOnLoad", "after:init") AfterProcessNode, ("afterProcessNode", "after:init") AfterPushIntoHistory, ("afterPushIntoHistory", "after:push:into:history") AfterReplaceIntoHistory, ("afterReplaceIntoHistory", "after:replace:into:history") AfterRequest, ("afterRequest", "after:request") AfterSettle, ("afterSettle", "after:swap") AfterSseMessage, ("afterSseMessage", "after:sse:message") AfterSseStream, ("afterSseStream", "after:sse:stream") 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") BeforeProcessNode, ("beforeProcessNode", "before:init") BeforeRestoreHistory, ("beforeRestoreHistory", "before:restore:history") BeforeRequest, ("beforeRequest", "before:request") 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") Error, ("error", "error") FinallyRequest, ("finallyRequest", "finally:request") HistoryCacheError, ("historyCacheError", "history-cache-error") HistoryCacheMiss, ("historyCacheMiss", "before:history:restore") HistoryCacheMissError, ("historyCacheMissError", "history-cache-miss-error") HistoryCacheMissLoad, ("historyCacheMissLoad", "before:history:restore") HistoryRestore, ("historyRestore", "before:history:restore") Load, ("load", "after:init") NoSseSourceError, ("noSSESourceError", "error") OnLoadError, ("onLoadError", "error") OobAfterSwap, ("oobAfterSwap", "after:swap") OobBeforeSwap, ("oobBeforeSwap", "before:swap") OobErrorNoTarget, ("oobErrorNoTarget", "error") Prompt, ("prompt", "prompt") PushedIntoHistory, ("pushedIntoHistory", "after:push:into:history") ResponseError, ("responseError", "error") SendError, ("sendError", "error") SseError, ("sseError", "error") SseOpen, ("sseOpen", "sse-open") SwapError, ("swapError", "error") TargetError, ("targetError", "error") Timeout, ("timeout", "error") ValidationValidate, ("validation:validate", "validation:validate") ValidationFailed, ("validation:failed", "validation:failed") ValidationHalted, ("validation:halted", "validation:halted") XhrAbort, ("xhr:abort", "xhr:abort") XhrLoadEnd, ("xhr:loadend", "xhr:loadend") XhrLoadStart, ("xhr:loadstart", "xhr:loadstart") XhrProgress, ("xhr:progress", "xhr:progress") ] /// The htmx event name override this.ToString() = fst HxEvent.Values[this] /// The hx-on variant of the htmx event name member this.ToHxOnString() = snd HxEvent.Values[this] #warn 44 // restore obsolete warning /// Helper to create the hx-headers attribute [] module HxHeaders = /// Create headers from a list of key/value pairs let From = Giraffe.Htmx.Common.toJson /// Values / helpers for the hx-params attribute /// Documentation [] [] module HxParams = /// Include all parameters [] let All = "*" /// Include no parameters [] let None = "none" /// Include the specified parameters /// One or more fields to include in the request /// The list of fields for the hx-params attribute value let With fields = match fields with [] -> "" | _ -> fields |> List.reduce (fun acc it -> $"{acc},{it}") /// Exclude the specified parameters /// One or more fields to exclude from the request /// The list of fields for the hx-params attribute value prefixed with "not" let Except fields = With fields |> sprintf "not %s" /// Helpers to define hx-request attribute values /// Documentation [] [] module HxRequest = open Giraffe.Htmx.Common /// Configure the request with various options /// The options to configure /// A string with the configured options let Configure (opts: string list) = opts |> String.concat ", " |> sprintf "{ %s }" /// Set a timeout (in milliseconds) /// The milliseconds for the request timeout /// A string with the configured request timeout let Timeout (ms: int) = $"\"timeout\": {ms}" /// Include or exclude credentials from the request /// true if credentials should be sent, false if not /// A string with the configured credential options let Credentials send = (toLowerBool >> sprintf "\"credentials\": %s") send /// Exclude or include headers from the request /// /// true if no headers should be sent; false if headers should be sent /// /// A string with the configured header options let NoHeaders exclude = (toLowerBool >> sprintf "\"noHeaders\": %s") exclude /// Helpers for the hx-sync attribute /// Documentation [] module HxSync = /// Drop (ignore) this request if a request is already in flight /// This is the default for hx-sync [] let Drop = "drop" /// /// Drop (ignore) this request if a request is already in flight, and if another request occurs while this one is in /// flight, abort this request /// [] let Abort = "abort" /// Abort any current in-flight request and replace it with this one [] let Replace = "replace" /// Place this request in an element-associated queue [] let Queue = "queue" /// Queue only the first request received while another request is in flight [] let QueueFirst = "queue first" /// Queue only the last request received while another request is in flight [] let QueueLast = "queue last" /// Queue all requests received while another request is in flight [] let QueueAll = "queue all" /// Helpers for the hx-trigger attribute /// Documentation [] module HxTrigger = /// Append a filter to a trigger let private appendFilter filter (trigger : string) = match trigger.Contains "[" with | true -> let parts = trigger.Split ('[', ']') $"{parts[0]}[{parts[1]}&&{filter}]" | false -> $"{trigger}[{filter}]" /// Trigger the event on a click [] let Click = "click" /// Trigger the event on page load [] let Load = "load" /// Trigger the event when the item is visible [] let Revealed = "revealed" /// Trigger this event every [timing declaration] /// The duration on which this trigger should fire (e.g., "1s", "500ms") /// A trigger timing specification let Every duration = $"every %s{duration}" /// Helpers for defining filters module Filter = /// Only trigger the event if the ALT key is pressed let Alt = appendFilter "altKey" /// Only trigger the event if the CTRL key is pressed let Ctrl = appendFilter "ctrlKey" /// Only trigger the event if the SHIFT key is pressed let Shift = appendFilter "shiftKey" /// Only trigger the event if CTRL and ALT are pressed let CtrlAlt = Ctrl >> Alt /// Only trigger the event if CTRL and SHIFT are pressed let CtrlShift = Ctrl >> Shift /// Only trigger the event if CTRL, ALT, and SHIFT are pressed let CtrlAltShift = CtrlAlt >> Shift /// Only trigger the event if ALT and SHIFT are pressed let AltShift = Alt >> Shift /// Append a modifier to the current trigger let private appendModifier modifier current = if current = "" then modifier else $"{current} {modifier}" /// Only trigger once /// The action which should only be fired once /// A trigger spec to fire the given action once let Once action = appendModifier "once" action /// Trigger when changed /// The element from which the onchange event will be emitted /// A trigger spec to fire when the given element changes let Changed elt = appendModifier "changed" elt /// Delay execution; resets every time the event is seen /// The duration for the delay (e.g., "1s", "500ms") /// The action which should be fired after the given delay /// A trigger spec to fire the given action after the specified delay let Delay duration action = appendModifier $"delay:%s{duration}" action /// Throttle execution; ignore other events, fire when duration passes /// The duration for the throttling (e.g., "1s", "500ms") /// The action which should be fired after the given duration /// A trigger spec to fire the given action after the specified duration let Throttle duration action = appendModifier $"throttle:%s{duration}" action /// Trigger this event from a CSS selector /// A CSS selector to identify elements which may fire this trigger /// The action to be fired /// A trigger spec to fire from the given element(s) let From selector action = appendModifier $"from:%s{selector}" action /// Trigger this event from the document object /// The action to be fired /// A trigger spec to fire from the document element let FromDocument action = From "document" action /// Trigger this event from the window object /// The action to be fired /// A trigger spec to fire from the window object let FromWindow action = From "window" action /// Trigger this event from the closest parent CSS selector /// The CSS selector from which the action should be fired /// The action to be fired /// A trigger spec to fire from the closest element let FromClosest selector action = From $"closest %s{selector}" action /// Trigger this event from the closest child CSS selector /// The child CSS selector from which the action should be fired /// The action to be fired /// A trigger spec to fire from the closest child element let FromFind selector action = From $"find %s{selector}" action /// Target the given CSS selector with the results of this event /// The CSS selector to which the result of the action will be applied /// The action to be fired /// A trigger spec to target the given selector let Target selector action = appendModifier $"target:%s{selector}" action /// Prevent any further events from occurring after this one fires /// The action to be fired /// A trigger spec to fire the given action and prevent further events let Consume action = appendModifier "consume" action /// /// Configure queueing when events fire when others are in flight; if unspecified, the default is last /// /// /// How the request should be queued (consider , , /// , and ) /// /// The action to be fired /// A trigger spec to queue the given action let Queue how action = let qSpec = if how = "" then "" else $":{how}" appendModifier $"queue{qSpec}" action /// Queue the first event, discard all others (i.e., a FIFO queue of 1) /// The action to be fired /// A trigger spec to queue the given action let QueueFirst action = Queue "first" action /// Queue the last event; discards current when another is received (i.e., a LIFO queue of 1) /// The action to be fired /// A trigger spec to queue the given action let QueueLast action = Queue "last" action /// Queue all events; discard none /// The action to be fired /// A trigger spec to queue the given action let QueueAll action = Queue "all" action /// Queue no events; discard all /// The action to be fired /// A trigger spec to queue the given action let QueueNone action = Queue "none" action /// Helper to create the hx-vals attribute /// Documentation [] module HxVals = /// Create values from a list of key/value pairs let From = Giraffe.Htmx.Common.toJson open System.Net.Http open Giraffe.Htmx /// Attributes and flags for 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 /// Documentation let _hxConfirm prompt = attr "hx-confirm" prompt /// Issues a DELETE to the specified URL /// The URL to which the DELETE request should be sent /// A configured hx-delete attribute /// Documentation let _hxDelete url = attr "hx-delete" url /// 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 /// Disinherit all ("*") or specific htmx attributes /// The htmx attributes to disinherit (should start with "hx-") /// A configured hx-disinherit attribute /// Documentation [] let _hxDisinherit hxAttrs = attr "hx-disinherit" hxAttrs /// Changes the request encoding type /// The encoding type (use HxEncoding constants) /// A configured hx-encoding attribute /// /// Documentation let _hxEncoding enc = attr "hx-encoding" enc /// Extensions to use for this element /// A list of extensions to apply to this element /// A configured hx-ext attribute /// Documentation [] let _hxExt exts = attr "hx-ext" exts /// Issues a GET to the specified URL /// The URL to which the GET request should be sent /// A configured hx-get attribute /// Documentation let _hxGet url = attr "hx-get" url /// Adds to the headers that will be submitted with the request /// The headers to include with the request /// A configured hx-headers attribute /// Documentation let _hxHeaders hdrs = attr "hx-headers" hdrs /// /// Set to "false" to prevent pages with sensitive information from being stored in the history cache /// /// 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 /// Documentation let _hxInclude spec = attr "hx-include" spec /// The element to put the htmx-request class on during the AJAX request /// The selector for the indicator element /// A configured hx-indicator attribute /// Documentation 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 = attr "hx-boost" "false" /// Attach an event handler for DOM events /// The name of the event /// The script to be executed when the event occurs /// A configured hx-on attribute /// Documentation let _hxOnEvent evtName handler = attr $"hx-on:%s{evtName}" handler /// Attach an event handler for htmx events /// The HxEvent to be handled /// The script to be executed when the event occurs /// A configured hx-on:: attribute /// /// Documentation let _hxOnHxEvent (hxEvent: HxEvent) handler = _hxOnEvent $":{hxEvent.ToHxOnString()}" handler /// Filters the parameters that will be submitted with a request /// The fields to include (use HxParams to generate this value) /// A configured hx-params attribute /// /// Documentation [] let _hxParams toInclude = attr "hx-params" toInclude /// Issues a PATCH to the specified URL /// The URL to which the request should be directed /// A configured hx-patch attribute /// Documentation let _hxPatch url = attr "hx-patch" url /// Issues a POST to the specified URL /// The URL to which the request should be directed /// A configured hx-post attribute /// Documentation let _hxPost url = attr "hx-post" url /// Preserves an element between requests /// Documentation let _hxPreserve = flag "hx-preserve" /// Shows a prompt() dialog before submitting a request /// The text for the prompt /// A configured hx-prompt attribute /// The value provided will be in the HX-Prompt request header /// Documentation [] let _hxPrompt text = attr "hx-prompt" text /// Pushes the URL into the location bar, creating a new history entry /// ///
    ///
  • "true" to push the fetched URL
  • ///
  • "false" to explicitly not push the fetched URL
  • ///
  • A specific URL to push
  • ///
/// /// A configured hx-push-url attribute /// Documentation let _hxPushUrl spec = attr "hx-push-url" spec /// Issues a PUT to the specified URL /// The URL to which the request should be directed /// A configured hx-put attribute /// Documentation let _hxPut url = attr "hx-put" url /// Replaces the current URL in the browser's history stack /// ///
    ///
  • "true" to replace the current URL with the fetched one
  • ///
  • "false" to explicitly replace nothing
  • ///
  • A specific URL to replace in the browser's history
  • ///
/// /// A configured hx-replace-url attribute /// Documentation let _hxReplaceUrl spec = attr "hx-replace-url" spec /// Configures various aspects of the request /// The configuration spec (use HxRequest.Configure to create value) /// A configured hx-request attribute /// /// Documentation [] let _hxRequest spec = attr "hx-request" spec /// Selects a subset of the server response to process /// A CSS selector for the content to be selected /// A configured hx-select attribute /// Documentation let _hxSelect selector = attr "hx-select" selector /// Selects a subset of an out-of-band server response /// One or more comma-delimited CSS selectors for the content to be selected /// A configured hx-select-oob attribute /// Documentation let _hxSelectOob selectors = attr "hx-select-oob" selectors /// /// Controls how the response content is swapped into the DOM (e.g. outerHTML or beforeEnd) /// /// The type of swap to perform (use HxSwap values) /// A configured hx-swap attribute /// Documentation let _hxSwap swap = attr "hx-swap" swap /// /// Controls how the response content is swapped into the DOM (e.g. outerHTML or beforeEnd), enabling /// CSS transitions /// /// The type of swap to perform (use HxSwap values) /// A configured hx-swap attribute /// Documentation let _hxSwapWithTransition swap = _hxSwap $"%s{swap} transition:true" /// /// Marks content in a response as being "Out of Band", i.e. swapped somewhere other than the target /// /// ///
    ///
  • "true" to mark this as an OOB swap
  • ///
  • Any HxSwap value
  • ///
  • Any HxSwap value, followed by a colon (:) and a CSS selector
  • ///
/// /// A configured hx-swap-oob attribute /// Documentation let _hxSwapOob swap = attr "hx-swap-oob" swap /// Synchronize events based on another element /// A CSS selector for the element with which this one should sync /// The request synchronization action to perform (use HxSync values) /// A configured hx-sync attribute /// /// Documentation let _hxSync selector action = attr "hx-sync" $"%s{selector}:%s{action}" /// Specifies the target element to be swapped /// A CSS selector or relative reference (or both) to identify the target /// A configured hx-target attribute /// Documentation let _hxTarget selector = attr "hx-target" selector /// Specifies the event that triggers the request /// The trigger specification (use HxTrigger to create) /// A configured hx-trigger attribute /// /// Documentation let _hxTrigger spec = attr "hx-trigger" spec /// Validate an input element (uses HTML5 validation API) /// Documentation let _hxValidate = flag "hx-validate" /// Adds to the parameters that will be submitted with the request /// The values for the parameters (use HxVals.From to create) /// A configured hx-vals attribute /// /// Documentation let _hxVals values = attr "hx-vals" values /// The URL of the SSE server /// The URL from which events will be received /// A configured sse-connect attribute /// Extension Docs let _sseConnect url = attr "sse-connect" url /// The name(s) of the message(s) to swap into the DOM /// The message names (comma-delimited) to swap (use "message" for unnamed events) /// A configured sse-swap attribute /// Extension Docs let _sseSwap messages = 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" /// Explicitly propagate inheritance for the value for this attribute /// The attribute whose value should be inherited /// The given attribute with :inherited applied let hxInherited 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 = open System /// Script tag to load the package-provided version of htmx let local = script [ _src htmxLocalScript ] [] /// Script tag to load the minified version from jsdelivr.net /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) let cdnMinified = script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha6/dist/htmx.min.js" _integrity "sha384-/5n21KGM472oBhvzUrvju8FRDq/4WNMS3TGw5RWFkZR/kq+sCevqNXFcakqRtaHu" _crossorigin "anonymous" ] [] /// Script tag to load the unminified version from jsdelivr.net /// Ensure cdn.jsdelivr.net is in your CSP script-src list (if applicable) let cdnUnminified = script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha6/dist/htmx.js" _integrity "sha384-I3uRy2RjSNcS8Oi2SNWZD9IrafyrrrBb4QaHNA9pWVWfJuKtL0p8qAwHSrEyXoKh" _crossorigin "anonymous" ] [] /// Script tag to load the minified version from jsdelivr.net [] let minified = cdnMinified /// Script tag to load the unminified version from jsdelivr.net [] let unminified = cdnUnminified /// Functions to extract and render an HTML fragment from a document [] module RenderFragment = /// Does this element have an ID matching the requested ID name? let private isIdElement nodeId (elt: XmlElement) = snd elt |> Array.exists (fun attr -> match attr with | KeyValue (name, value) -> name = "id" && value = nodeId | Boolean _ -> false) /// Generate a message if the requested ID node is not found let private nodeNotFound (nodeId: string) = $"– ID {nodeId} not found –" /// Find the node with the named ID /// The id attribute to find /// The node tree to search /// The node with the requested id attribute, or None if it was not found let rec findIdNode nodeId (node: XmlNode) : XmlNode option = match node with | Text _ -> None | VoidElement elt -> if isIdElement nodeId elt then Some node else None | ParentNode (elt, children) -> if isIdElement nodeId elt then Some node else children |> List.tryPick (findIdNode nodeId) /// Functions to render a fragment as a string [] module AsString = /// Render to HTML for the given ID /// The id attribute for the node to be rendered /// The node trees to search /// The HTML for the given id node, or an error message if it was not found let htmlFromNodes nodeId (nodes: XmlNode list) = match nodes |> List.tryPick (findIdNode nodeId) with | Some idNode -> RenderView.AsString.htmlNode idNode | None -> nodeNotFound nodeId /// Render to HTML for the given ID /// The id attribute for the node to be rendered /// The node tree to search /// The HTML for the given id node, or an error message if it was not found let htmlFromNode nodeId node = match findIdNode nodeId node with | Some idNode -> RenderView.AsString.htmlNode idNode | None -> nodeNotFound nodeId /// Functions to render a fragment as bytes [] module AsBytes = let private utf8 = System.Text.Encoding.UTF8 /// Render to bytes for the given ID /// The id attribute for the node to be rendered /// The node trees to search /// The bytes for the given id node, or an error message if it was not found let htmlFromNodes nodeId (nodes: XmlNode list) = match nodes |> List.tryPick (findIdNode nodeId) with | Some idNode -> RenderView.AsBytes.htmlNode idNode | None -> nodeNotFound nodeId |> utf8.GetBytes /// Render to bytes for the given ID /// The id attribute for the node to be rendered /// The node tree to search /// The bytes for the given id node, or an error message if it was not found let htmlFromNode nodeId node = match findIdNode nodeId node with | Some idNode -> RenderView.AsBytes.htmlNode idNode | None -> nodeNotFound nodeId |> utf8.GetBytes /// Functions to render a fragment into a StringBuilder [] module IntoStringBuilder = /// Render HTML into a StringBuilder for the given ID /// The StringBuilder into which the bytes will be rendered /// The id attribute for the node to be rendered /// The node trees to search let htmlFromNodes sb nodeId (nodes: XmlNode list) = match nodes |> List.tryPick (findIdNode nodeId) with | Some idNode -> RenderView.IntoStringBuilder.htmlNode sb idNode | None -> nodeNotFound nodeId |> sb.Append |> ignore /// Render HTML into a StringBuilder for the given ID /// The StringBuilder into which the bytes will be rendered /// The id attribute for the node to be rendered /// The node tree to search let htmlFromNode sb nodeId node = match findIdNode nodeId node with | Some idNode -> RenderView.IntoStringBuilder.htmlNode sb idNode | None -> nodeNotFound nodeId |> sb.Append |> ignore