|
|
|
|
@@ -6,9 +6,11 @@ module Giraffe.ViewEngine.Htmx
|
|
|
|
|
module HxEncoding =
|
|
|
|
|
|
|
|
|
|
/// <summary>A standard HTTP form</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Form = "application/x-www-form-urlencoded"
|
|
|
|
|
|
|
|
|
|
/// <summary>A multipart form (used for file uploads)</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let MultipartForm = "multipart/form-data"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -213,9 +215,11 @@ module HxHeaders =
|
|
|
|
|
module HxParams =
|
|
|
|
|
|
|
|
|
|
/// <summary>Include all parameters</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let All = "*"
|
|
|
|
|
|
|
|
|
|
/// <summary>Include no parameters</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let None = "none"
|
|
|
|
|
|
|
|
|
|
/// <summary>Include the specified parameters</summary>
|
|
|
|
|
@@ -267,7 +271,46 @@ module HxRequest =
|
|
|
|
|
(toLowerBool >> sprintf "\"noHeaders\": %s") exclude
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Helpers for the <c>hx-sync</c> attribute</summary>
|
|
|
|
|
/// <seealso href="https://htmx.org/attributes/hx-sync/">Documentation</seealso>
|
|
|
|
|
[<RequireQualifiedAccess>]
|
|
|
|
|
module HxSync =
|
|
|
|
|
|
|
|
|
|
/// <summary>Drop (ignore) this request if a request is already in flight</summary>
|
|
|
|
|
/// <remarks>This is the default for <c>hx-sync</c></remarks>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Drop = "drop"
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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
|
|
|
|
|
/// </summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Abort = "abort"
|
|
|
|
|
|
|
|
|
|
/// <summary>Abort any current in-flight request and replace it with this one</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Replace = "replace"
|
|
|
|
|
|
|
|
|
|
/// <summary>Place this request in an element-associated queue</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Queue = "queue"
|
|
|
|
|
|
|
|
|
|
/// <summary>Queue only the first request received while another request is in flight</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let QueueFirst = "queue first"
|
|
|
|
|
|
|
|
|
|
/// <summary>Queue only the last request received while another request is in flight</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let QueueLast = "queue last"
|
|
|
|
|
|
|
|
|
|
/// <summary>Queue all requests received while another request is in flight</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let QueueAll = "queue all"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Helpers for the <c>hx-trigger</c> attribute</summary>
|
|
|
|
|
/// <seealso href="https://htmx.org/attributes/hx-trigger/">Documentation</seealso>
|
|
|
|
|
[<RequireQualifiedAccess>]
|
|
|
|
|
module HxTrigger =
|
|
|
|
|
|
|
|
|
|
@@ -279,93 +322,160 @@ module HxTrigger =
|
|
|
|
|
$"{parts[0]}[{parts[1]}&&{filter}]"
|
|
|
|
|
| false -> $"{trigger}[{filter}]"
|
|
|
|
|
|
|
|
|
|
/// Trigger the event on a click
|
|
|
|
|
/// <summary>Trigger the event on a click</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Click = "click"
|
|
|
|
|
|
|
|
|
|
/// Trigger the event on page load
|
|
|
|
|
/// <summary>Trigger the event on page load</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Load = "load"
|
|
|
|
|
|
|
|
|
|
/// Trigger the event when the item is visible
|
|
|
|
|
/// <summary>Trigger the event when the item is visible</summary>
|
|
|
|
|
[<Literal>]
|
|
|
|
|
let Revealed = "revealed"
|
|
|
|
|
|
|
|
|
|
/// Trigger this event every [timing declaration]
|
|
|
|
|
let Every (duration : string) = $"every {duration}"
|
|
|
|
|
/// <summary>Trigger this event every [timing declaration]</summary>
|
|
|
|
|
/// <param name="duration">The duration on which this trigger should fire (e.g., "1s", "500ms")</param>
|
|
|
|
|
/// <returns>A trigger timing specification</returns>
|
|
|
|
|
let Every duration =
|
|
|
|
|
$"every %s{duration}"
|
|
|
|
|
|
|
|
|
|
/// Helpers for defining filters
|
|
|
|
|
/// <summary>Helpers for defining filters</summary>
|
|
|
|
|
module Filter =
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if the `ALT` key is pressed
|
|
|
|
|
/// <summary>Only trigger the event if the <c>ALT</c> key is pressed</summary>
|
|
|
|
|
let Alt = appendFilter "altKey"
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if the `CTRL` key is pressed
|
|
|
|
|
/// <summary>Only trigger the event if the <c>CTRL</c> key is pressed</summary>
|
|
|
|
|
let Ctrl = appendFilter "ctrlKey"
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if the `SHIFT` key is pressed
|
|
|
|
|
/// <summary>Only trigger the event if the <c>SHIFT</c> key is pressed</summary>
|
|
|
|
|
let Shift = appendFilter "shiftKey"
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if `CTRL+ALT` are pressed
|
|
|
|
|
/// <summary>Only trigger the event if <c>CTRL</c> and <c>ALT</c> are pressed</summary>
|
|
|
|
|
let CtrlAlt = Ctrl >> Alt
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if `CTRL+SHIFT` are pressed
|
|
|
|
|
/// <summary>Only trigger the event if <c>CTRL</c> and <c>SHIFT</c> are pressed</summary>
|
|
|
|
|
let CtrlShift = Ctrl >> Shift
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if `CTRL+ALT+SHIFT` are pressed
|
|
|
|
|
/// <summary>Only trigger the event if <c>CTRL</c>, <c>ALT</c>, and <c>SHIFT</c> are pressed</summary>
|
|
|
|
|
let CtrlAltShift = CtrlAlt >> Shift
|
|
|
|
|
|
|
|
|
|
/// Only trigger the event if `ALT+SHIFT` are pressed
|
|
|
|
|
/// <summary>Only trigger the event if <c>ALT</c> and <c>SHIFT</c> are pressed</summary>
|
|
|
|
|
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
|
|
|
|
|
let Once = appendModifier "once"
|
|
|
|
|
/// <summary>Only trigger once</summary>
|
|
|
|
|
/// <param name="action">The action which should only be fired once</param>
|
|
|
|
|
/// <returns>A trigger spec to fire the given action once</returns>
|
|
|
|
|
let Once action =
|
|
|
|
|
appendModifier "once" action
|
|
|
|
|
|
|
|
|
|
/// Trigger when changed
|
|
|
|
|
let Changed = appendModifier "changed"
|
|
|
|
|
/// <summary>Trigger when changed</summary>
|
|
|
|
|
/// <param name="elt">The element from which the <c>onchange</c> event will be emitted</param>
|
|
|
|
|
/// <returns>A trigger spec to fire when the given element changes</returns>
|
|
|
|
|
let Changed elt =
|
|
|
|
|
appendModifier "changed" elt
|
|
|
|
|
|
|
|
|
|
/// Delay execution; resets every time the event is seen
|
|
|
|
|
let Delay = sprintf "delay:%s" >> appendModifier
|
|
|
|
|
/// <summary>Delay execution; resets every time the event is seen</summary>
|
|
|
|
|
/// <param name="duration">The duration for the delay (e.g., "1s", "500ms")</param>
|
|
|
|
|
/// <param name="action">The action which should be fired after the given delay</param>
|
|
|
|
|
/// <returns>A trigger spec to fire the given action after the specified delay</returns>
|
|
|
|
|
let Delay duration action =
|
|
|
|
|
appendModifier $"delay:%s{duration}" action
|
|
|
|
|
|
|
|
|
|
/// Throttle execution; ignore other events, fire when duration passes
|
|
|
|
|
let Throttle = sprintf "throttle:%s" >> appendModifier
|
|
|
|
|
/// <summary>Throttle execution; ignore other events, fire when duration passes</summary>
|
|
|
|
|
/// <param name="duration">The duration for the throttling (e.g., "1s", "500ms")</param>
|
|
|
|
|
/// <param name="action">The action which should be fired after the given duration</param>
|
|
|
|
|
/// <returns>A trigger spec to fire the given action after the specified duration</returns>
|
|
|
|
|
let Throttle duration action =
|
|
|
|
|
appendModifier $"throttle:%s{duration}" action
|
|
|
|
|
|
|
|
|
|
/// Trigger this event from a CSS selector
|
|
|
|
|
let From = sprintf "from:%s" >> appendModifier
|
|
|
|
|
/// <summary>Trigger this event from a CSS selector</summary>
|
|
|
|
|
/// <param name="selector">A CSS selector to identify elements which may fire this trigger</param>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire from the given element(s)</returns>
|
|
|
|
|
let From selector action =
|
|
|
|
|
appendModifier $"from:%s{selector}" action
|
|
|
|
|
|
|
|
|
|
/// Trigger this event from the `document` object
|
|
|
|
|
let FromDocument = From "document"
|
|
|
|
|
/// <summary>Trigger this event from the <c>document</c> object</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire from the <c>document</c> element</returns>
|
|
|
|
|
let FromDocument action =
|
|
|
|
|
From "document" action
|
|
|
|
|
|
|
|
|
|
/// Trigger this event from the `window` object
|
|
|
|
|
let FromWindow = From "window"
|
|
|
|
|
/// <summary>Trigger this event from the <c>window</c> object</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire from the <c>window</c> object</returns>
|
|
|
|
|
let FromWindow action =
|
|
|
|
|
From "window" action
|
|
|
|
|
|
|
|
|
|
/// Trigger this event from the closest parent CSS selector
|
|
|
|
|
let FromClosest = sprintf "closest %s" >> From
|
|
|
|
|
/// <summary>Trigger this event from the closest parent CSS selector</summary>
|
|
|
|
|
/// <param name="selector">The CSS selector from which the action should be fired</param>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire from the closest element</returns>
|
|
|
|
|
let FromClosest selector action =
|
|
|
|
|
From $"closest %s{selector}" action
|
|
|
|
|
|
|
|
|
|
/// Trigger this event from the closest child CSS selector
|
|
|
|
|
let FromFind = sprintf "find %s" >> From
|
|
|
|
|
/// <summary>Trigger this event from the closest child CSS selector</summary>
|
|
|
|
|
/// <param name="selector">The child CSS selector from which the action should be fired</param>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire from the closest child element</returns>
|
|
|
|
|
let FromFind selector action =
|
|
|
|
|
From $"find %s{selector}" action
|
|
|
|
|
|
|
|
|
|
/// Target the given CSS selector with the results of this event
|
|
|
|
|
let Target = sprintf "target:%s" >> appendModifier
|
|
|
|
|
/// <summary>Target the given CSS selector with the results of this event</summary>
|
|
|
|
|
/// <param name="selector">The CSS selector to which the result of the action will be applied</param>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to target the given selector</returns>
|
|
|
|
|
let Target selector action =
|
|
|
|
|
appendModifier $"target:%s{selector}" action
|
|
|
|
|
|
|
|
|
|
/// Prevent any further events from occurring after this one fires
|
|
|
|
|
let Consume = appendModifier "consume"
|
|
|
|
|
/// <summary>Prevent any further events from occurring after this one fires</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to fire the given action and prevent further events</returns>
|
|
|
|
|
let Consume action =
|
|
|
|
|
appendModifier "consume" action
|
|
|
|
|
|
|
|
|
|
/// Configure queueing when events fire when others are in flight; if unspecified, the default is "last"
|
|
|
|
|
let Queue = sprintf "queue:%s" >> appendModifier
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Configure queueing when events fire when others are in flight; if unspecified, the default is <c>last</c>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="how">
|
|
|
|
|
/// How the request should be queued (consider <see cref="QueueFirst" />, <see cref="QueueLast" />,
|
|
|
|
|
/// <see cref="QueueAll" />, and <see cref="QueueNone" />)
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to queue the given action</returns>
|
|
|
|
|
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)
|
|
|
|
|
let QueueFirst = Queue "first"
|
|
|
|
|
/// <summary>Queue the first event, discard all others (i.e., a FIFO queue of 1)</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to queue the given action</returns>
|
|
|
|
|
let QueueFirst action =
|
|
|
|
|
Queue "first" action
|
|
|
|
|
|
|
|
|
|
/// Queue the last event; discards current when another is received (i.e., a LIFO queue of 1)
|
|
|
|
|
let QueueLast = Queue "last"
|
|
|
|
|
/// <summary>Queue the last event; discards current when another is received (i.e., a LIFO queue of 1)</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to queue the given action</returns>
|
|
|
|
|
let QueueLast action =
|
|
|
|
|
Queue "last" action
|
|
|
|
|
|
|
|
|
|
/// Queue all events; discard none
|
|
|
|
|
let QueueAll = Queue "all"
|
|
|
|
|
/// <summary>Queue all events; discard none</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to queue the given action</returns>
|
|
|
|
|
let QueueAll action =
|
|
|
|
|
Queue "all" action
|
|
|
|
|
|
|
|
|
|
/// Queue no events; discard all
|
|
|
|
|
let QueueNone = Queue "none"
|
|
|
|
|
/// <summary>Queue no events; discard all</summary>
|
|
|
|
|
/// <param name="action">The action to be fired</param>
|
|
|
|
|
/// <returns>A trigger spec to queue the given action</returns>
|
|
|
|
|
let QueueNone action =
|
|
|
|
|
Queue "none" action
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Helper to create the <c>hx-vals</c> attribute</summary>
|
|
|
|
|
@@ -623,8 +733,14 @@ module HtmxAttrs =
|
|
|
|
|
let _hxSwapOob swap =
|
|
|
|
|
attr "hx-swap-oob" swap
|
|
|
|
|
|
|
|
|
|
/// Synchronize events based on another element
|
|
|
|
|
let _hxSync = attr "hx-sync"
|
|
|
|
|
/// <summary>Synchronize events based on another element</summary>
|
|
|
|
|
/// <param name="selector">A CSS selector for the element with which this one should sync</param>
|
|
|
|
|
/// <param name="action">The request synchronization action to perform (use <c>HxSync</c> values)</param>
|
|
|
|
|
/// <returns>A configured <c>hx-sync</c> attribute</returns>
|
|
|
|
|
/// <seealso cref="HxSync" />
|
|
|
|
|
/// <seealso href="https://htmx.org/attributes/hx-sync/">Documentation</seealso>
|
|
|
|
|
let _hxSync selector action =
|
|
|
|
|
attr "hx-sync" $"%s{selector}:%s{action}"
|
|
|
|
|
|
|
|
|
|
/// <summary>Specifies the target element to be swapped</summary>
|
|
|
|
|
/// <param name="selector">A CSS selector or relative reference (or both) to identify the target</param>
|
|
|
|
|
@@ -633,8 +749,13 @@ module HtmxAttrs =
|
|
|
|
|
let _hxTarget selector =
|
|
|
|
|
attr "hx-target" selector
|
|
|
|
|
|
|
|
|
|
/// Specifies the event that triggers the request
|
|
|
|
|
let _hxTrigger = attr "hx-trigger"
|
|
|
|
|
/// <summary>Specifies the event that triggers the request</summary>
|
|
|
|
|
/// <param name="spec">The trigger specification (use <c>HxTrigger</c> to create)</param>
|
|
|
|
|
/// <returns>A configured <c>hx-trigger</c> attribute</returns>
|
|
|
|
|
/// <seealso cref="HxTrigger" />
|
|
|
|
|
/// <seealso href="https://htmx.org/attributes/hx-trigger/">Documentation</seealso>
|
|
|
|
|
let _hxTrigger spec =
|
|
|
|
|
attr "hx-trigger" spec
|
|
|
|
|
|
|
|
|
|
/// <summary>Validate an input element (uses HTML5 validation API)</summary>
|
|
|
|
|
/// <seealso href="https://htmx.org/attributes/hx-validate/">Documentation</seealso>
|
|
|
|
|
|