diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e25865..737286c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,9 +32,9 @@ Keep formatting changes limited to the code you're working on. ## Working on scripts and documentation -### Companion script +### Toolkit script -Install Node.js and pnpm when working on the companion script. +Install Node.js and pnpm when working on the Toolkit script. Edit `src/Ramstack.HtmxToolkit/wwwroot/htmx-toolkit.js`, then rebuild the script assets: @@ -111,7 +111,7 @@ Put ticket references and implementation details in the message body when needed | `tag-helpers` | Razor Tag Helpers | | `config` | Application, version-specific, and antiforgery configuration | | `hosting` | Endpoint mapping, dependency registration, and server-side asset delivery | -| `script` | Browser-side companion script behavior that does not belong to another product area | +| `script` | Browser-side Toolkit script behavior that does not belong to another product area | | `internals` | Shared collection, serialization, debugging, and compatibility code | | `docs` | Documentation covering multiple product areas and DocFX tooling | | `demo` | Demo-only application structure and presentation | @@ -131,7 +131,7 @@ use the first rule that applies: 1. **It changes behavior or public API.** Use the scope of the area where the change is visible, even if the implementation spans other areas. Antiforgery behavior belongs to `config`, even when - its implementation spans Tag Helpers and the companion script. If several areas change, use the one + its implementation spans Tag Helpers and the Toolkit script. If several areas change, use the one whose documentation describes the change. 2. **It introduces or updates one thing that the rest of the commit adopts.** Use the scope of that thing: `internals: validate arguments with the Argument helper`, diff --git a/README.md b/README.md index 205c8f6..fe2b3ef 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ By default, the toolkit targets HTMX 2.x. If you use HTMX 1.9.x or 4.x, select i ``` `` renders your HTMX settings and the antiforgery token. The Toolkit script comes -with the package. It adds the token to HTMX requests and, with HTMX 1.x and 2.x, tells the server +with the package. It adds the token to HTMX requests and, with HTMX 1.9.x and 2.x, tells the server whether a request expects a fragment or a full page (see [Handling requests](#handling-requests)). The script is served like any other static file, so make sure the application calls `UseStaticFiles()` or `MapStaticAssets()`. @@ -119,7 +119,7 @@ or a request with `hx-select`, comes from HTMX too, but expects a full page. the response into an element inside the page, and `full` when the response replaces the whole `` or goes through `hx-select`. A history restore always counts as a full page. -HTMX 4 sends this header itself. HTMX 1.x and 2.x don't, so the Toolkit script adds it the same way. +HTMX 4 sends this header itself. HTMX 1.9.x and 2.x don't, so the Toolkit script adds it the same way. That's why the example above works with every supported version, as long as the script is on the page. If you cache such responses, add a `Vary` header so that the page and the fragment are cached separately. See [Full pages and fragments](https://rameel.github.io/ramstack.htmxtoolkit/articles/pages-and-fragments.html). @@ -207,7 +207,7 @@ Other Tag Helpers build HTMX attributes that otherwise need hand-written JSON: |----------------|-----------------------------------------------------------------------------------------| | `hx-val-*` | `hx-vals`, extra values sent with the request | | `hx-header-*` | `hx-headers`, extra request headers | -| `hx-request-*` | `hx-request` (HTMX 1.x, 2.x) or `hx-config` (HTMX 4), request options such as a timeout | +| `hx-request-*` | `hx-request` (HTMX 1.9.x, 2.x) or `hx-config` (HTMX 4), request options such as a timeout | The toolkit builds and escapes the JSON for you. See [Tag Helpers](https://rameel.github.io/ramstack.htmxtoolkit/articles/tag-helpers.html) for the full list of attributes. diff --git a/docs/articles/antiforgery.md b/docs/articles/antiforgery.md index cbc05d9..6f7fe28 100644 --- a/docs/articles/antiforgery.md +++ b/docs/articles/antiforgery.md @@ -12,7 +12,7 @@ see [Static assets and deployment](static-assets.md) for the URL and caching. | Feature | HTMX versions | Without the script | |---|---|---| | Adds the antiforgery token to non-GET HTMX requests and refreshes it after boosted navigation | All | Non-GET requests without a token field fail server-side antiforgery validation, usually with HTTP 400 | -| Sends `HX-Request-Type`; see [Check full or partial requests](requests.md#check-full-or-partial-requests) | 1.x and 2.x; HTMX 4.x sends it itself | `IsHtmxPartialRequest()` and `IsHtmxFullRequest()` return `false`, and `[HtmxRequest]` with a `RequestType` does not match | +| Sends `HX-Request-Type`; see [Check full or partial requests](requests.md#check-full-or-partial-requests) | 1.9.x and 2.x; HTMX 4.x sends it itself | `IsHtmxPartialRequest()` and `IsHtmxFullRequest()` return `false`, and `[HtmxRequest]` with a `RequestType` does not match | | Replays events whose name repeats in one response header; see [Timing and repeated events](responses.md#timing-and-repeated-events) | All | None of the events in that header fire | | Adds the `ramstack-swap` extension with `innerMorph`, `outerMorph`, and a simplified `outerSync`, plus `textContent` for HTMX 1.9.x; see [Morph swaps](version-compatibility.md#morph-swaps) | 1.9.x and 2.x; HTMX 4.x supports these swap styles natively | These swap styles are not available | @@ -107,7 +107,7 @@ render a token, as shown in the [forms tutorial](forms-and-validation.md). ## Token refresh after boosted navigation After a boosted navigation, the Toolkit script looks for antiforgery metadata in the response. If it contains -a nonempty request token, the script updates the saved token and associated names. A fragment without token +a non-empty request token, the script updates the saved token and associated names. A fragment without token metadata leaves the existing values in place. Keep `` in full document layouts. After signing in, signing out, or changing identity, diff --git a/docs/articles/choosing-version.md b/docs/articles/choosing-version.md index 83fc222..cb25851 100644 --- a/docs/articles/choosing-version.md +++ b/docs/articles/choosing-version.md @@ -36,7 +36,7 @@ trying to select a different version causes startup validation to fail. ## What the selection changes -| Behavior | HTMX 1.x | HTMX 2.x | HTMX 4.x | +| Behavior | HTMX 1.9.x | HTMX 2.x | HTMX 4.x | |---|---|---|---| | Default when omitted | No | Yes | No | | Global config type | `HtmxV1Config` | `HtmxV2Config` | `HtmxV4Config` | @@ -73,5 +73,5 @@ For a new application with no version-specific requirements, start with HTMX 2.x tutorials without changes. It is HtmxToolkit's default target. Choose V4 when you want its native morphing or Fetch API options, and check that the extensions you plan to use support it. -Continue with [Application configuration](configuration.md), then use the dedicated [HTMX 1.x](configuration-v1.md), +Continue with [Application configuration](configuration.md), then use the dedicated [HTMX 1.9.x](configuration-v1.md), [HTMX 2.x](configuration-v2.md), or [HTMX 4.x](configuration-v4.md) reference guide. diff --git a/docs/articles/configuration-v1.md b/docs/articles/configuration-v1.md index 3d53dcd..e777e0c 100644 --- a/docs/articles/configuration-v1.md +++ b/docs/articles/configuration-v1.md @@ -1,6 +1,6 @@ -# HTMX 1.x configuration +# HTMX 1.9.x configuration -Select HTMX 1.x when the application loads an HTMX 1.9.x release: +Call `UseHtmxV1` when the application loads HTMX 1.9.x: ```csharp using Ramstack.HtmxToolkit; diff --git a/docs/articles/configuration-v4.md b/docs/articles/configuration-v4.md index 03e6a5a..ecede10 100644 --- a/docs/articles/configuration-v4.md +++ b/docs/articles/configuration-v4.md @@ -57,7 +57,7 @@ config.History = HtmxHistoryMode.Reload; // Perform a full page reload on hist Choose one value; the three assignments above illustrate alternatives. -Unlike HTMX 1.x and 2.x, HTMX 4.x does not keep local DOM snapshots in `localStorage` by default. +Unlike HTMX 1.9.x and 2.x, HTMX 4.x does not keep local DOM snapshots in `localStorage` by default. `Enabled` requests the history URL from the server and swaps the response. The optional HTMX 4 `hx-history-cache` extension restores local snapshot behavior; HtmxToolkit does not enable that extension automatically. @@ -86,7 +86,7 @@ The parent inputs become `hx-config:inherited` and `hx-vals:inherited`. The butt The same inputs are available for `hx-headers`; see [Apply attribute modifiers](tag-helpers.md#apply-attribute-modifiers) for the full list. -Alternatively, enable the HTMX 1.x/2.x inheritance behavior globally: +Alternatively, enable the HTMX 1.9.x/2.x inheritance behavior globally: ```csharp config.ImplicitInheritance = true; diff --git a/docs/articles/configuration.md b/docs/articles/configuration.md index e2f9325..98825e3 100644 --- a/docs/articles/configuration.md +++ b/docs/articles/configuration.md @@ -99,7 +99,7 @@ See [Antiforgery and Toolkit script](antiforgery.md) before disabling this behav ## Version-specific settings -- [HTMX 1.x configuration](configuration-v1.md) +- [HTMX 1.9.x configuration](configuration-v1.md) - [HTMX 2.x configuration](configuration-v2.md) - [HTMX 4.x configuration](configuration-v4.md) - [Version compatibility](version-compatibility.md) diff --git a/docs/articles/index.md b/docs/articles/index.md index 9ccd60e..6ee2311 100644 --- a/docs/articles/index.md +++ b/docs/articles/index.md @@ -36,7 +36,7 @@ For an existing integration, browse the task guides below or [run the demo](samp Decision Choose an HTMX version - Target the correct markup for HTMX 1.x, 2.x, or 4.x. + Target the correct markup for HTMX 1.9.x, 2.x, or 4.x. diff --git a/docs/articles/overview.md b/docs/articles/overview.md index ad16c64..41ff724 100644 --- a/docs/articles/overview.md +++ b/docs/articles/overview.md @@ -97,7 +97,7 @@ registration, and layout needed to run it. | Generate URLs or JSON attributes in Razor | Register the toolkit Tag Helpers in `_ViewImports.cshtml` | | Attach antiforgery tokens automatically | Render configuration metadata, load the Toolkit script, and enable server validation | | Use compatibility morph swaps or replay duplicate event names | Load the Toolkit script; follow the relevant feature's setup | -| Tell full-page and fragment requests apart with HTMX 1.x or 2.x | Load the Toolkit script | +| Tell full-page and fragment requests apart with HTMX 1.9.x or 2.x | Load the Toolkit script | For HTMX 4.x, register the target version even if you only use response events, so the toolkit chooses the correct trigger headers. In a pure Minimal API application, HTML rendering and token metadata are supplied diff --git a/docs/articles/pages-and-fragments.md b/docs/articles/pages-and-fragments.md index b112ecb..133d367 100644 --- a/docs/articles/pages-and-fragments.md +++ b/docs/articles/pages-and-fragments.md @@ -39,7 +39,7 @@ A boosted link that replaces the ``, an `hx-select` request, and a history get the complete document. The method reads the `HX-Request-Type` header; see [Check full or partial requests](requests.md#check-full-or-partial-requests) for how it is computed. -With HTMX 1.x and 2.x, the header comes from the Toolkit script, so load the script on every page that uses HTMX. +With HTMX 1.9.x and 2.x, the header comes from the Toolkit script, so load the script on every page that uses HTMX. Without it, the endpoints above return the full page to every request. ## Use a dedicated fragment endpoint @@ -65,7 +65,7 @@ This style works well for validation, polling, autocomplete, dialogs, and inline ## Avoid rendering a layout into a target If a response unexpectedly inserts navigation, ``, or the whole page into a component, -the endpoint returned `View()` or `Page()` instead of a partial. With the shared endpoint above and HTMX 1.x or 2.x, +the endpoint returned `View()` or `Page()` instead of a partial. With the shared endpoint above and HTMX 1.9.x or 2.x, the usual reason is a page without the Toolkit script: the request has no `HX-Request-Type` header, so `IsHtmxPartialRequest()` returns `false`. Inspect the request headers and the response body in browser developer tools before changing `hx-target` or `hx-swap`. diff --git a/docs/articles/requests.md b/docs/articles/requests.md index 06d49e0..205c14c 100644 --- a/docs/articles/requests.md +++ b/docs/articles/requests.md @@ -50,9 +50,9 @@ var htmx = Request.GetHtmxHeaders(); | `HistoryRestoreRequest` | Whether HTMX marked the request for history restoration | | `Target` | Target element; its format differs in HTMX 4 | | `Prompt` | Result of `hx-prompt`; in HTMX 4.x, sent only by the `hx-prompt` extension | -| `Trigger`, `TriggerName` | Triggering element ID and name in HTMX 1.x and 2.x | +| `Trigger`, `TriggerName` | Triggering element ID and name in HTMX 1.9.x and 2.x | | `Source` | Triggering `tag#id` in HTMX 4.x | -| `RequestType` | `HtmxRequestType.Partial` or `HtmxRequestType.Full`; added by the Toolkit script in HTMX 1.x and 2.x | +| `RequestType` | `HtmxRequestType.Partial` or `HtmxRequestType.Full`; added by the Toolkit script in HTMX 1.9.x and 2.x | Missing or version-inapplicable string headers return `null`. `Request` is `true` whenever the `HX-Request` header is present, whatever its value. @@ -68,10 +68,10 @@ See [Version compatibility](version-compatibility.md) before using version-speci When `HistoryRestoreRequest` is true, return the full document rather than a target fragment. `RequestType` already accounts for it: HTMX 4.x sends `HX-Request-Type: full` with such a request, -and HTMX 1.x and 2.x send no `HX-Request-Type`, which `RequestType` reports as `HtmxRequestType.Full`. +and HTMX 1.9.x and 2.x send no `HX-Request-Type`, which `RequestType` reports as `HtmxRequestType.Full`. So `IsHtmxPartialRequest()` returns `false`, and the example in [Detect a request](#detect-a-request) returns the page. -HTMX 1.x and 2.x send this request after a miss in their local snapshot cache. +HTMX 1.9.x and 2.x send this request after a miss in their local snapshot cache. HTMX 4.x requests history entries from the server by default; see [History behavior](configuration-v4.md#history-behavior). @@ -100,14 +100,14 @@ The `HX-Request-Type` header tells the server what the client does with the resp - `partial` when HTMX swaps it into an element inside the page; - `full` when it replaces the whole ``, or when `hx-select` takes part of it. -HTMX 4.x sends this header itself. HTMX 1.x and 2.x do not, so the Toolkit script adds it, +HTMX 4.x sends this header itself. HTMX 1.9.x and 2.x do not, so the Toolkit script adds it, computed the same way from the final target and selector, including the `select` option of `htmx.ajax()` and `HX-Location`. The script respects `noHeaders` in `hx-request`; a `js:` value of `hx-request` is not evaluated, so it cannot turn the header off. Use `Request.IsHtmxPartialRequest()` to check for a fragment, or `Request.IsHtmxFullRequest()` for a full page. -A history restoration request counts as a full page even though HTMX 1.x and 2.x send it without the header. -For any other request without the header, both methods return `false`; with HTMX 1.x and 2.x, +A history restoration request counts as a full page even though HTMX 1.9.x and 2.x send it without the header. +For any other request without the header, both methods return `false`; with HTMX 1.9.x and 2.x, this means the page does not load the [Toolkit script](antiforgery.md#what-the-toolkit-script-does). The type comes from the target, not from `HX-Boosted`: a boosted link with `hx-target` is a partial request. @@ -136,7 +136,7 @@ boosted navigation that replaces the ``, and history restoration, goes to `RequestType = HtmxRequestType.Unspecified`, the default, does not restrict the request type. `Full` and `Partial` require a matching `RequestType`. History restoration requests match `Full`. -Other requests without the `HX-Request-Type` header, such as HTMX 1.x and 2.x requests from a page +Other requests without the `HX-Request-Type` header, such as HTMX 1.9.x and 2.x requests from a page without the Toolkit script, match neither. Without `RequestType`, the attribute accepts any HTMX request. That suits an endpoint that only HTMX calls, diff --git a/docs/articles/responses.md b/docs/articles/responses.md index c1b698b..6fa0a5c 100644 --- a/docs/articles/responses.md +++ b/docs/articles/responses.md @@ -204,7 +204,7 @@ The `hx-trigger` listener can still refresh a component while JavaScript handles ### Timing and repeated events -HTMX 1.x and 2.x distinguish receive, after-swap, and after-settle response headers. +HTMX 1.9.x and 2.x distinguish receive, after-swap, and after-settle response headers. HTMX 4.x delivers these Toolkit events through `HX-Trigger` when the request completes, after the swap and settle steps when a swap occurs. See [Version compatibility](version-compatibility.md#response-events). @@ -277,6 +277,7 @@ htmx.Reswap = HtmxSwap.OuterHtml; Unlike `Response.Htmx(...)`, direct header access is not conditional: the caller decides whether the headers should be written. The properties can also be read, for example by a filter that sets a default `Retarget` only when the action has not chosen one. +Assigning `null` or an empty string to a string property, such as `Retarget`, removes its header. `HtmxResponseHeaderNames` exposes header-name constants for lower-level integrations. diff --git a/docs/articles/tag-helpers.md b/docs/articles/tag-helpers.md index fa7ff5e..a67f29e 100644 --- a/docs/articles/tag-helpers.md +++ b/docs/articles/tag-helpers.md @@ -130,7 +130,7 @@ The Tag Helper emits an encoded `hx-vals` JSON object. A dictionary can be suppl ``` -HTMX 1.x and 2.x inherit the generated `hx-vals` from parent elements automatically. +HTMX 1.9.x and 2.x inherit the generated `hx-vals` from parent elements automatically. For HTMX 4.x, add `hx-vals-inherited="true"` to the Razor element to generate the explicit inheritance modifier: ```razor @@ -142,7 +142,7 @@ For HTMX 4.x, add `hx-vals-inherited="true"` to the Razor element to generate th This produces `hx-vals:inherited='{"tenant":"..."}'`. -HTMX 1.x and 2.x merge inherited values: a child value overrides a value with the same name +HTMX 1.9.x and 2.x merge inherited values: a child value overrides a value with the same name while other inherited values remain. In HTMX 4.x, a plain child `hx-vals` replaces the inherited object entirely. Add `hx-vals-append="true"` to merge the child object instead: @@ -168,7 +168,7 @@ The button emits `hx-vals:append='{"format":"summary"}'`. ``` `hx-header-*` and `hx-all-headers` generate `hx-headers` JSON. Header names are compared without case. -HTMX 1.x and 2.x inherit the generated attribute automatically. +HTMX 1.9.x and 2.x inherit the generated attribute automatically. For HTMX 4.x, use the Toolkit input `hx-headers-inherited="true"` on a parent element: ```razor @@ -201,7 +201,7 @@ Use `hx-request-*` attributes rather than hand-writing version-specific JSON: The output depends on the configured HTMX target: ```html - + -@* HTMX 1.x and 2.x: hx-request='{"timeout":2000}' - HTMX 4.x: hx-config='{"timeout":2000}' *@ +@* HTMX 1.9.x and 2.x: hx-request='{"timeout":2000}' + HTMX 4.x: hx-config='{"timeout":2000}' *@ diff --git a/src/Ramstack.HtmxToolkit/Collections/SmallDictionary.cs b/src/Ramstack.HtmxToolkit/Collections/SmallDictionary.cs index a112653..ce07de1 100644 --- a/src/Ramstack.HtmxToolkit/Collections/SmallDictionary.cs +++ b/src/Ramstack.HtmxToolkit/Collections/SmallDictionary.cs @@ -11,9 +11,11 @@ namespace Ramstack.HtmxToolkit.Collections; /// The type of keys in the dictionary. /// The type of values in the dictionary. /// -/// This type is intended for scenarios in which a dictionary typically contains only a few entries. -/// It uses array-based storage to reduce memory usage and the overhead of creating, -/// populating, and searching the dictionary. +/// +/// This type is intended for scenarios in which a dictionary typically contains only a few entries. +/// It uses array-based storage to reduce memory usage and the overhead of creating, +/// populating, and searching the dictionary. +/// /// /// For up to entries, keys are located using /// a linear search. Above this threshold, the entries are sorted by key and @@ -191,7 +193,7 @@ public void Clear() /// Returns an enumerator that iterates through the . /// /// - /// A structure for the . + /// An structure for the . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() => @@ -272,7 +274,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => #endregion /// - /// Ensures that the dictionary can hold the specified number of entries without resizing. + /// Grows the storage so that it can hold at least the specified number of entries. /// /// The minimum number of entries /// that the dictionary must be able to hold. @@ -543,7 +545,7 @@ public bool Contains(TKey item) => /// Returns an enumerator that iterates through the . /// /// - /// A for the . + /// An for the . /// public Enumerator GetEnumerator() => new(dictionary); @@ -686,7 +688,7 @@ public bool Contains(TValue item) /// Returns an enumerator that iterates through the . /// /// - /// A for the . + /// An for the . /// public Enumerator GetEnumerator() => new(dictionary); @@ -738,7 +740,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => #region Inner type: Enumerator /// - /// Enumerates the values in a . + /// Represents an enumerator for the values in a . /// public struct Enumerator : IEnumerator { diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxBinaryType.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxBinaryType.cs index f95730a..cdcf229 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxBinaryType.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxBinaryType.cs @@ -4,13 +4,13 @@ namespace Ramstack.HtmxToolkit.Configuration; /// Specifies how binary data received over a WebSocket connection is represented. /// /// -/// For more information, see WebSocket: binaryType property. +/// For more information, see WebSocket: binaryType property. /// public enum HtmxBinaryType { /// /// Represents binary data as Blob objects. - /// This is the default. + /// This is the HTMX default. /// Blob, diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxFetchMode.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxFetchMode.cs index 81f9d6e..4fe6565 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxFetchMode.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxFetchMode.cs @@ -4,9 +4,9 @@ namespace Ramstack.HtmxToolkit.Configuration; /// Specifies the request mode used by HTMX. /// /// -/// In HTMX 4.x this is passed as the mode option of the Fetch API. +/// In HTMX 4.x, this is passed as the mode option of the Fetch API. /// -/// HTMX 1.x and 2.x use XMLHttpRequest instead of fetch. +/// HTMX 1.9.x and 2.x use XMLHttpRequest instead of fetch. /// Use or /// to restrict requests to the current origin. /// @@ -25,7 +25,11 @@ public enum HtmxFetchMode /// /// Allows restricted cross-origin requests that produce opaque responses. - /// Opaque responses cannot normally be swapped by HTMX. /// + /// + /// An opaque response has status 0, and its headers and body are not accessible. + /// HTMX cannot read HX-* response headers, does not treat the response as an error, + /// and swaps the empty body into the target. + /// NoCors } diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxHistoryMode.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxHistoryMode.cs index a1ff0a7..8db9b57 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxHistoryMode.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxHistoryMode.cs @@ -3,14 +3,18 @@ namespace Ramstack.HtmxToolkit.Configuration; /// /// Specifies how HTMX history restoration is handled. /// +/// +/// Used by . HTMX 1.9.x and 2.x use +/// and instead. +/// public enum HtmxHistoryMode { /// - /// Enables HTMX history handling. + /// Enables HTMX history support. /// /// - /// In HTMX 4.x, history navigation requests the URL from the server and swaps - /// the response. Local DOM snapshots require the optional HTMX hx-history-cache extension. + /// History navigation requests the URL from the server and swaps the response. + /// Local DOM snapshots require the optional HTMX hx-history-cache extension. /// Enabled, @@ -22,6 +26,5 @@ public enum HtmxHistoryMode /// /// Reloads the page when restoring history. /// - /// Supported only in HTMX 4.x. Reload } diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxTargetVersion.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxTargetVersion.cs index 511c1eb..82bd477 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxTargetVersion.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxTargetVersion.cs @@ -1,12 +1,12 @@ namespace Ramstack.HtmxToolkit.Configuration; /// -/// Specifies the HTMX major version targeted by generated markup. +/// Specifies the HTMX major version targeted by generated markup and response headers. /// public enum HtmxTargetVersion { /// - /// HTMX 1.x. + /// HTMX 1.9.x. /// V1, diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxToolkitOptions.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxToolkitOptions.cs index fd66d17..ef4909b 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxToolkitOptions.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxToolkitOptions.cs @@ -4,7 +4,7 @@ namespace Ramstack.HtmxToolkit.Configuration; /// -/// Represents configuration options for services provided by HTMX Toolkit. +/// Represents configuration options for services provided by HtmxToolkit. /// public sealed class HtmxToolkitOptions { @@ -14,7 +14,10 @@ public sealed class HtmxToolkitOptions /// Gets the configuration for the selected HTMX major version. /// /// - /// If no version has been explicitly configured, this property defaults to HTMX 2.x. + /// If no version has been selected, reading this property selects HTMX 2.x. + /// A later call to or then throws + /// . Reading + /// or calling has the same effect. /// public HtmxConfig HtmxConfig { @@ -35,20 +38,21 @@ public HtmxConfig HtmxConfig public bool IncludeAntiforgeryToken { get; set; } = true; /// - /// Gets the HTMX major version used for version-sensitive generated markup. + /// Gets the HTMX major version used for version-sensitive generated markup and response headers. /// + /// + /// For example, with HTMX 4.x, + /// emits events with any value through the HX-Trigger header. + /// public HtmxTargetVersion TargetVersion => HtmxConfig.TargetVersion; /// - /// Selects and configures HTMX 1.x. + /// Selects and configures HTMX 1.9.x. /// - /// The optional delegate used to configure HTMX 1.x. + /// The optional delegate used to configure HTMX 1.9.x. /// /// The current options instance. /// - /// - /// A different HTMX major version has already been selected. - /// [MemberNotNull(nameof(_config))] public HtmxToolkitOptions UseHtmxV1(Action? configure = null) { @@ -64,9 +68,6 @@ public HtmxToolkitOptions UseHtmxV1(Action? configure = null) /// /// The current options instance. /// - /// - /// A different HTMX major version has already been selected. - /// [MemberNotNull(nameof(_config))] public HtmxToolkitOptions UseHtmxV2(Action? configure = null) { @@ -82,9 +83,6 @@ public HtmxToolkitOptions UseHtmxV2(Action? configure = null) /// /// The current options instance. /// - /// - /// A different HTMX major version has already been selected. - /// [MemberNotNull(nameof(_config))] public HtmxToolkitOptions UseHtmxV4(Action? configure = null) { @@ -100,9 +98,11 @@ public HtmxToolkitOptions UseHtmxV4(Action? configure = null) /// /// The requested configuration instance. /// - /// - /// does not match the selected HTMX major version. - /// + /// + /// If no version has been selected, HTMX 2.x is selected, as when reading . + /// Throws if the configuration of the selected version + /// is not a . + /// public TConfig GetHtmxConfig() where TConfig : HtmxConfig { if (HtmxConfig is TConfig config) diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs index 1bd2dfc..63e25bd 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs @@ -6,7 +6,7 @@ namespace Ramstack.HtmxToolkit.Configuration; /// -/// Represents the configuration for HTMX 1.x. +/// Represents the configuration for HTMX 1.9.x. /// public sealed class HtmxV1Config() : HtmxConfig(HtmxTargetVersion.V1) { @@ -31,8 +31,8 @@ public int? HistoryCacheSize } /// - /// Gets or sets a value indicating whether a full-page refresh should be issued - /// on history misses rather than using an AJAX request. + /// Gets or sets a value indicating whether a history miss causes a full-page refresh + /// instead of an AJAX request. /// The HTMX default is . /// public bool? RefreshOnHistoryMiss @@ -83,9 +83,13 @@ public bool? IncludeIndicatorStyles } /// - /// Gets or sets the indicator class. + /// Gets or sets the class that marks request indicators. /// The HTMX default is htmx-indicator. /// + /// + /// The indicator styles show these elements only while they or one of their ancestors + /// have the class specified by . + /// public string? IndicatorClass { get; @@ -93,7 +97,8 @@ public string? IndicatorClass } /// - /// Gets or sets the request class. + /// Gets or sets the class added to the elements specified by hx-indicator, + /// or to the requesting element itself, while a request is in flight. /// The HTMX default is htmx-request. /// public string? RequestClass @@ -103,7 +108,8 @@ public string? RequestClass } /// - /// Gets or sets the added class. + /// Gets or sets the class added to new content before it is swapped in + /// and removed after the content settles. /// The HTMX default is htmx-added. /// public string? AddedClass @@ -113,7 +119,8 @@ public string? AddedClass } /// - /// Gets or sets the swapping class. + /// Gets or sets the class added to the target before content is swapped + /// and removed after the swap. /// The HTMX default is htmx-swapping. /// public string? SwappingClass @@ -123,7 +130,8 @@ public string? SwappingClass } /// - /// Gets or sets the settling class. + /// Gets or sets the class added to the target after content is swapped + /// and removed after the content settles. /// The HTMX default is htmx-settling. /// public string? SettlingClass @@ -186,6 +194,7 @@ public bool? UseTemplateFragments /// Gets or sets the type of binary data received over WebSocket connections. /// The HTMX default is . /// + /// Used by the HTMX WebSocket extension and the legacy hx-ws attribute. [JsonConverter(typeof(HtmxBinaryTypeJsonConverter))] public HtmxBinaryType? WsBinaryType { @@ -214,7 +223,7 @@ public bool? WithCredentials } /// - /// Gets or sets the request timeout, in milliseconds. + /// Gets or sets the request timeout in milliseconds. /// The HTMX default is 0. /// public int? Timeout @@ -248,6 +257,7 @@ public HtmxScrollBehavior? ScrollBehavior /// Gets or sets a value indicating whether the focused element should be scrolled into view. /// The HTMX default is . /// + /// The focus-scroll swap modifier overrides this setting. public bool? DefaultFocusScroll { get; @@ -307,7 +317,7 @@ public bool? ScrollIntoViewOnBoost /// /// Gets or sets a value indicating whether parsed trigger specifications use - /// a never-clearing cache. The cache is disabled by default. + /// a never-clearing cache. The HTMX default is . /// [JsonPropertyName("triggerSpecsCache")] [JsonConverter(typeof(HtmxTriggerSpecsCacheJsonConverter))] diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs index ea56299..af8d4bf 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs @@ -83,9 +83,13 @@ public bool? IncludeIndicatorStyles } /// - /// Gets or sets the indicator class. + /// Gets or sets the class that marks request indicators. /// The HTMX default is htmx-indicator. /// + /// + /// The indicator styles show these elements only while they or one of their ancestors + /// have the class specified by . + /// public string? IndicatorClass { get; @@ -93,7 +97,8 @@ public string? IndicatorClass } /// - /// Gets or sets the request class. + /// Gets or sets the class added to the elements specified by hx-indicator, + /// or to the requesting element itself, while a request is in flight. /// The HTMX default is htmx-request. /// public string? RequestClass @@ -103,7 +108,8 @@ public string? RequestClass } /// - /// Gets or sets the added class. + /// Gets or sets the class added to new content before it is swapped in + /// and removed after the content settles. /// The HTMX default is htmx-added. /// public string? AddedClass @@ -113,7 +119,8 @@ public string? AddedClass } /// - /// Gets or sets the swapping class. + /// Gets or sets the class added to the target before content is swapped + /// and removed after the swap. /// The HTMX default is htmx-swapping. /// public string? SwappingClass @@ -123,7 +130,8 @@ public string? SwappingClass } /// - /// Gets or sets the settling class. + /// Gets or sets the class added to the target after content is swapped + /// and removed after the content settles. /// The HTMX default is htmx-settling. /// public string? SettlingClass @@ -225,7 +233,7 @@ public bool? DisableInheritance } /// - /// Gets or sets the request timeout, in milliseconds. + /// Gets or sets the request timeout in milliseconds. /// The HTMX default is 0. /// public int? Timeout @@ -259,6 +267,7 @@ public HtmxScrollBehavior? ScrollBehavior /// Gets or sets a value indicating whether the focused element should be scrolled into view. /// The HTMX default is . /// + /// The focus-scroll swap modifier overrides this setting. public bool? DefaultFocusScroll { get; @@ -276,7 +285,9 @@ public bool? GetCacheBusterParam } /// - /// Gets or sets a value indicating whether the View Transition API should be used for swaps. + /// Gets or sets a value indicating whether the + /// View Transition API + /// should be used for swaps. /// The HTMX default is . /// public bool? GlobalViewTransitions @@ -318,7 +329,7 @@ public bool? ScrollIntoViewOnBoost /// /// Gets or sets a value indicating whether parsed trigger specifications use - /// a never-clearing cache. The cache is disabled by default. + /// a never-clearing cache. The HTMX default is . /// [JsonConverter(typeof(HtmxTriggerSpecsCacheJsonConverter))] [JsonPropertyName("triggerSpecsCache")] @@ -330,6 +341,8 @@ public bool? TriggerSpecsCacheEnabled /// /// Gets or sets the rules that determine how HTTP response status codes are handled. + /// The HTMX default is [{ code: "204", swap: false }, { code: "[23]..", swap: true }, + /// { code: "[45]..", swap: false, error: true }]. /// public IList? ResponseHandling { diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs index 3ffeab4..1bff1f0 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs @@ -59,8 +59,8 @@ public HtmxSwap? DefaultSwap /// /// /// Processed <hx-partial> elements suppress an empty main swap regardless - /// of this setting. The swapEmpty modifier on hx-swap overrides either default. - /// Nonempty main content is still swapped, and the swap style always runs. + /// of this setting. The swapEmpty modifier on hx-swap takes precedence in both cases. + /// Non-empty main content is still swapped, and the swap style always runs. /// [JsonPropertyName("allowEmptySwapAfterOOB")] public bool? AllowEmptySwapAfterOob @@ -91,9 +91,13 @@ public bool? IncludeIndicatorCss } /// - /// Gets or sets the indicator class. + /// Gets or sets the class that marks request indicators. /// The HTMX default is htmx-indicator. /// + /// + /// The indicator styles show these elements only while they or one of their ancestors + /// have the class specified by . + /// public string? IndicatorClass { get; @@ -101,7 +105,8 @@ public string? IndicatorClass } /// - /// Gets or sets the request class. + /// Gets or sets the class added to the elements specified by hx-indicator, + /// or to the requesting element itself, while a request is in flight. /// The HTMX default is htmx-request. /// public string? RequestClass @@ -167,9 +172,9 @@ public HtmxFetchMode? Mode /// /// Gets or sets a value indicating whether the focused element should be scrolled into view. - /// The HTMX default is and can be overridden - /// using the focus-scroll swap modifier. + /// The HTMX default is . /// + /// The focus-scroll swap modifier overrides this setting. public bool? DefaultFocusScroll { get; diff --git a/src/Ramstack.HtmxToolkit/Hosting/ServiceCollectionExtensions.cs b/src/Ramstack.HtmxToolkit/Hosting/ServiceCollectionExtensions.cs index 285367d..cd10b97 100644 --- a/src/Ramstack.HtmxToolkit/Hosting/ServiceCollectionExtensions.cs +++ b/src/Ramstack.HtmxToolkit/Hosting/ServiceCollectionExtensions.cs @@ -5,15 +5,15 @@ namespace Ramstack.HtmxToolkit.Hosting; /// -/// Provides HTMX Toolkit service registration for an . +/// Provides HtmxToolkit service registration for an . /// public static class ServiceCollectionExtensions { /// - /// Registers and configures HTMX Toolkit services. + /// Registers and configures HtmxToolkit services. /// /// The service collection. - /// An optional delegate used to configure HTMX Toolkit. + /// An optional delegate used to configure HtmxToolkit. /// /// The same collection. /// diff --git a/src/Ramstack.HtmxToolkit/HtmxFieldValues.cs b/src/Ramstack.HtmxToolkit/HtmxFieldValues.cs index 0a7a13f..85b87b5 100644 --- a/src/Ramstack.HtmxToolkit/HtmxFieldValues.cs +++ b/src/Ramstack.HtmxToolkit/HtmxFieldValues.cs @@ -7,7 +7,7 @@ namespace Ramstack.HtmxToolkit; /// -/// Represents one or more values for a form field submitted with an HTMX request. +/// Represents zero or more values for a form field submitted with an HTMX request. /// [JsonConverter(typeof(HtmxFieldValuesJsonConverter))] [CollectionBuilder(typeof(HtmxFieldValues), nameof(Create))] @@ -69,9 +69,9 @@ public HtmxFieldValues(string? value) => /// /// Initializes a new instance of the structure with the specified values. /// - /// The specified array is stored directly and is not copied. /// The values to store, or /// to represent no values. + /// The specified array is stored directly and is not copied. public HtmxFieldValues(string[]? values) => _values = values; @@ -148,14 +148,14 @@ private static string OutOfBounds() => /// /// Copies the specified read-only span to a new array. /// - /// - /// This non-inlined wrapper keeps the implementation of - /// out of to limit native code growth at its call sites. - /// /// The values to copy. /// /// A new array containing the specified values. /// + /// + /// This non-inlined wrapper keeps the implementation of + /// out of to limit native code growth at its call sites. + /// [MethodImpl(MethodImplOptions.NoInlining)] private static string[] CreateArray(ReadOnlySpan s) => s.ToArray(); @@ -163,7 +163,7 @@ private static string[] CreateArray(ReadOnlySpan s) => #region Inner type: Enumerator /// - /// Enumerates the string values represented by an . + /// Represents an enumerator for the string values in an . /// public struct Enumerator : IEnumerator { diff --git a/src/Ramstack.HtmxToolkit/HtmxHistoryAction.cs b/src/Ramstack.HtmxToolkit/HtmxHistoryAction.cs index 535a330..4f3b99d 100644 --- a/src/Ramstack.HtmxToolkit/HtmxHistoryAction.cs +++ b/src/Ramstack.HtmxToolkit/HtmxHistoryAction.cs @@ -1,7 +1,7 @@ namespace Ramstack.HtmxToolkit; /// -/// Specifies how an HX-Location request updates the browser history. +/// Specifies how an HX-Location redirect updates the browser history. /// /// Response headers from the loaded page can override the requested history action. public enum HtmxHistoryAction @@ -19,7 +19,7 @@ public enum HtmxHistoryAction Replace, /// - /// Requests that the browser history is not updated. + /// Requests that the browser history not be updated. /// /// /// Supported in HTMX 2.0.8 and later 2.x releases, and in HTMX 4.x. diff --git a/src/Ramstack.HtmxToolkit/HtmxLocationOptions.cs b/src/Ramstack.HtmxToolkit/HtmxLocationOptions.cs index 0a87884..d5a19af 100644 --- a/src/Ramstack.HtmxToolkit/HtmxLocationOptions.cs +++ b/src/Ramstack.HtmxToolkit/HtmxLocationOptions.cs @@ -3,7 +3,7 @@ namespace Ramstack.HtmxToolkit; /// -/// Represents the options for an HX-Location request. +/// Represents the options for the HX-Location response header. /// public sealed class HtmxLocationOptions { @@ -14,7 +14,7 @@ public sealed class HtmxLocationOptions public string? Source { get; set; } /// - /// Gets or sets the selector for the target element into which the response will be swapped. + /// Gets or sets the CSS selector for the target element into which the response will be swapped. /// /// Supported in HTMX 1.9.x, HTMX 2.x, and HTMX 4.x. public string? Target { get; set; } @@ -69,7 +69,7 @@ public HtmxSwap? Swap public IDictionary? Headers { get; set; } /// - /// Gets or sets a selector used to filter the content to swap from the response. + /// Gets or sets a CSS selector used to filter the content to swap from the response. /// /// Supported in HTMX 1.9.x, HTMX 2.x, and HTMX 4.x. public string? Select { get; set; } diff --git a/src/Ramstack.HtmxToolkit/HtmxRequestAttribute.cs b/src/Ramstack.HtmxToolkit/HtmxRequestAttribute.cs index 0bb5adf..9181cac 100644 --- a/src/Ramstack.HtmxToolkit/HtmxRequestAttribute.cs +++ b/src/Ramstack.HtmxToolkit/HtmxRequestAttribute.cs @@ -18,11 +18,12 @@ public sealed class HtmxRequestAttribute : Attribute, IActionConstraint /// /// The default, , does not restrict the request type. /// Other values require a matching . + /// When is also set, both conditions must be satisfied. /// /// - /// HTMX 1.x and 2.x send the HX-Request-Type header only when the page includes - /// the companion script htmx-toolkit.js. - /// When is also set, both conditions must be satisfied. + /// HTMX 4.x sends the HX-Request-Type header itself; for HTMX 1.9.x and 2.x, + /// the Toolkit script (htmx-toolkit.js) adds it. A history restoration request + /// without the header matches . /// /// public HtmxRequestType RequestType { get; set; } diff --git a/src/Ramstack.HtmxToolkit/HtmxRequestHeaderNames.cs b/src/Ramstack.HtmxToolkit/HtmxRequestHeaderNames.cs index 556f811..c334a08 100644 --- a/src/Ramstack.HtmxToolkit/HtmxRequestHeaderNames.cs +++ b/src/Ramstack.HtmxToolkit/HtmxRequestHeaderNames.cs @@ -24,8 +24,9 @@ public static class HtmxRequestHeaderNames /// fetches content for history restoration. /// /// - /// HTMX 1.x and 2.x send this header after a history cache miss. - /// HTMX 4.x refetches history content by default; local caching requires an extension. + /// HTMX 1.9.x and 2.x send this header after a history cache miss. + /// HTMX 4.x has no local history cache unless an extension provides one, + /// so it sends this header with every history restoration request. /// public const string HistoryRestoreRequest = "HX-History-Restore-Request"; @@ -33,9 +34,9 @@ public static class HtmxRequestHeaderNames /// The HX-Prompt header contains the user's response to an hx-prompt. /// /// - /// Supported natively in HTMX 1.x and 2.x. In HTMX 4.x, the + /// Supported natively in HTMX 1.9.x and 2.x. In HTMX 4.x, the /// hx-prompt extension - /// restores this header. + /// restores this header and URI-encodes its value. /// public const string Prompt = "HX-Prompt"; @@ -51,8 +52,8 @@ public static class HtmxRequestHeaderNames /// /// /// The value is "partial" for targeted swaps and "full" for body-level - /// or hx-select requests. HTMX 4.x sends the header itself; for HTMX 1.x and 2.x, - /// the companion script htmx-toolkit.js adds it. + /// or hx-select requests. HTMX 4.x sends the header itself; for HTMX 1.9.x and 2.x, + /// the Toolkit script (htmx-toolkit.js) adds it. /// public const string RequestType = "HX-Request-Type"; @@ -70,7 +71,7 @@ public static class HtmxRequestHeaderNames /// The HX-Target header identifies the target element, if present. /// /// - /// In HTMX 1.x and 2.x, the value is the ID of the target element. + /// In HTMX 1.9.x and 2.x, the value is the ID of the target element. /// /// In HTMX 4.x, the value contains the lowercase tag name and, when the element has an ID, /// # followed by its URI-encoded ID. Examples include div#results and body. @@ -80,18 +81,18 @@ public static class HtmxRequestHeaderNames /// /// The HX-Trigger-Name header contains the name attribute of the element - /// that issued the request, if present. + /// that triggered the request, if present. /// /// - /// Supported only in HTMX 1.x and 2.x. HTMX 4.x identifies the source element with HX-Source instead. + /// Supported only in HTMX 1.9.x and 2.x. HTMX 4.x identifies the source element with HX-Source instead. /// public const string TriggerName = "HX-Trigger-Name"; /// - /// The HX-Trigger header contains the ID of the element that issued the request, if present. + /// The HX-Trigger header contains the ID of the element that triggered the request, if present. /// /// - /// Supported only in HTMX 1.x and 2.x. HTMX 4.x identifies the source element with HX-Source instead. + /// Supported only in HTMX 1.9.x and 2.x. HTMX 4.x identifies the source element with HX-Source instead. /// public const string Trigger = "HX-Trigger"; } diff --git a/src/Ramstack.HtmxToolkit/HtmxRequestHeaders.cs b/src/Ramstack.HtmxToolkit/HtmxRequestHeaders.cs index 520a305..9a6c089 100644 --- a/src/Ramstack.HtmxToolkit/HtmxRequestHeaders.cs +++ b/src/Ramstack.HtmxToolkit/HtmxRequestHeaders.cs @@ -35,6 +35,7 @@ internal HtmxRequestHeaders(HttpRequest request) => /// /// /// The header name is . + /// If the header is not present, the property returns . /// public string? CurrentUrl => GetString(_headers, HtmxRequestHeaderNames.CurrentUrl); @@ -44,8 +45,9 @@ internal HtmxRequestHeaders(HttpRequest request) => /// /// The header name is . /// - /// HTMX 1.x and 2.x send this header after a history cache miss. - /// HTMX 4.x refetches history content by default; local caching requires an extension. + /// HTMX 1.9.x and 2.x send this header after a history cache miss. + /// HTMX 4.x has no local history cache unless an extension provides one, + /// so it sends this header with every history restoration request. /// /// public bool HistoryRestoreRequest => GetBoolean(_headers, HtmxRequestHeaderNames.HistoryRestoreRequest); @@ -54,11 +56,14 @@ internal HtmxRequestHeaders(HttpRequest request) => /// Gets the user's response to an hx-prompt on the client. /// /// - /// The header name is . /// - /// Supported natively in HTMX 1.x and 2.x. In HTMX 4.x, the + /// The header name is . + /// If the header is not present, the property returns . + /// + /// + /// Supported natively in HTMX 1.9.x and 2.x. In HTMX 4.x, the /// hx-prompt extension - /// restores this header. + /// restores this header and URI-encodes its value. /// /// public string? Prompt => GetString(_headers, HtmxRequestHeaderNames.Prompt); @@ -72,16 +77,18 @@ internal HtmxRequestHeaders(HttpRequest request) => public bool Request => _headers.ContainsKey(HtmxRequestHeaderNames.Request); /// - /// Gets the type of the expected response, either a partial or a full page. + /// Gets the type of the expected response. /// /// /// /// The header name is . + /// If the header is not present or its value is not recognized, + /// the property returns . /// /// /// The value is "partial" for targeted swaps and "full" for body-level - /// or hx-select requests. HTMX 4.x sends the header itself; for HTMX 1.x and 2.x, - /// the companion script htmx-toolkit.js adds it. + /// or hx-select requests. HTMX 4.x sends the header itself; for HTMX 1.9.x and 2.x, + /// the Toolkit script (htmx-toolkit.js) adds it. /// /// /// A history restoration request without the header is reported as @@ -108,7 +115,7 @@ static HtmxRequestType GetRequestType(IHeaderDictionary dictionary) return HtmxRequestType.Unspecified; } - // HTMX 1.x and 2.x send history restoration requests without HX-Request-Type. + // HTMX 1.9.x and 2.x send history restoration requests without HX-Request-Type. if (GetBoolean(dictionary, HtmxRequestHeaderNames.HistoryRestoreRequest)) return HtmxRequestType.Full; @@ -123,7 +130,10 @@ static HtmxRequestType GetRequestType(IHeaderDictionary dictionary) /// Gets the identifier of the element that triggered the request. /// /// - /// The header name is . + /// + /// The header name is . + /// If the header is not present, the property returns . + /// /// /// Supported only in HTMX 4.x. The value contains the lowercase tag name and, /// when the element has an ID, # followed by its URI-encoded ID. @@ -133,11 +143,17 @@ static HtmxRequestType GetRequestType(IHeaderDictionary dictionary) public string? Source => GetString(_headers, HtmxRequestHeaderNames.Source); /// - /// Gets the identifier of the target element, if present. + /// Gets the identifier of the target element. /// /// - /// The header name is . - /// In HTMX 1.x and 2.x, the value is the ID of the target element. + /// + /// The header name is . + /// If the header is not present, the property returns . + /// + /// + /// In HTMX 1.9.x and 2.x, the value is the ID of the target element; + /// the header is omitted when the target element has no ID. + /// /// /// In HTMX 4.x, the value contains the lowercase tag name and, /// when the element has an ID, # followed by its URI-encoded ID. @@ -147,25 +163,32 @@ static HtmxRequestType GetRequestType(IHeaderDictionary dictionary) public string? Target => GetString(_headers, HtmxRequestHeaderNames.Target); /// - /// Gets the name attribute of the element that issued the request, if present. + /// Gets the name attribute of the element that triggered the request. /// /// - /// The header name is . /// - /// Supported only in HTMX 1.x and 2.x; HTMX 4.x identifies the source element + /// The header name is . + /// If the header is not present, the property returns . + /// + /// + /// Supported only in HTMX 1.9.x and 2.x, which omit the header when the element + /// has no name attribute; HTMX 4.x identifies the source element /// with HX-Source instead. /// /// public string? TriggerName => GetString(_headers, HtmxRequestHeaderNames.TriggerName); /// - /// Gets the ID of the element that issued the request, if present. + /// Gets the ID of the element that triggered the request. /// /// - /// The header name is . /// - /// Supported only in HTMX 1.x and 2.x; HTMX 4.x identifies the source element - /// with HX-Source instead. + /// The header name is . + /// If the header is not present, the property returns . + /// + /// + /// Supported only in HTMX 1.9.x and 2.x, which omit the header when the element + /// has no ID; HTMX 4.x identifies the source element with HX-Source instead. /// /// public string? Trigger => GetString(_headers, HtmxRequestHeaderNames.Trigger); diff --git a/src/Ramstack.HtmxToolkit/HtmxResponse.cs b/src/Ramstack.HtmxToolkit/HtmxResponse.cs index 1ea962b..ba0adbd 100644 --- a/src/Ramstack.HtmxToolkit/HtmxResponse.cs +++ b/src/Ramstack.HtmxToolkit/HtmxResponse.cs @@ -82,15 +82,15 @@ static HtmxResponse LocationImpl(HtmxResponse response, string path, HtmxLocatio /// /// /// - /// Also removes the HX-Replace-Url header and the legacy HX-Push header, - /// which HTMX 1.9.x and 2.x would otherwise prefer. When , - /// , and are combined, - /// the last call takes effect. + /// Also removes the HX-Replace-Url header and the legacy HX-Push header. + /// HTMX 1.9.x and 2.x check HX-Push before the other history headers. + /// When , , and + /// are combined, the last call takes effect. /// /// - /// HTMX treats "false" and "true" as control values. Use - /// instead of "false". HTMX 4.x replaces "true" with the response URL, while HTMX 1.9.x - /// and 2.x push it as the relative URL true. + /// HTMX treats "false" as a control value; use instead. + /// HTMX 4.x also treats "true" as a control value and replaces it with the response URL, + /// whereas HTMX 1.9.x and 2.x push it as the relative URL true. /// /// public HtmxResponse PushUrl(string url) @@ -104,24 +104,23 @@ public HtmxResponse PushUrl(string url) /// Sets the HX-Replace-Url header to replace the current URL. /// /// - /// A URL to replace the current URL in the location bar. This may be relative or absolute, - /// as supported by history.replaceState(), - /// but must have the same origin as the current URL. + /// A relative or same-origin absolute URL to replace the current URL in the location bar, + /// as supported by history.replaceState(). /// /// /// The current instance. /// /// /// - /// Also removes the HX-Push-Url header and the legacy HX-Push header, - /// which HTMX 1.9.x and 2.x would otherwise prefer. When , - /// , and are combined, - /// the last call takes effect. + /// Also removes the HX-Push-Url header and the legacy HX-Push header. + /// HTMX 1.9.x and 2.x check HX-Push before the other history headers. + /// When , , and + /// are combined, the last call takes effect. /// /// - /// HTMX treats "false" and "true" as control values. Use - /// instead of "false". HTMX 4.x replaces "true" with the response URL, while HTMX 1.9.x - /// and 2.x use it as the relative URL true. + /// HTMX treats "false" as a control value; use instead. + /// HTMX 4.x also treats "true" as a control value and replaces it with the response URL, + /// whereas HTMX 1.9.x and 2.x use it as the relative URL true. /// /// public HtmxResponse ReplaceUrl(string url) @@ -140,10 +139,10 @@ public HtmxResponse ReplaceUrl(string url) /// /// /// - /// Also removes the HX-Replace-Url header and the legacy HX-Push header, - /// which HTMX 1.9.x and 2.x would otherwise prefer. When , - /// , and are combined, - /// the last call takes effect. + /// Also removes the HX-Replace-Url header and the legacy HX-Push header. + /// HTMX 1.9.x and 2.x check HX-Push before the other history headers. + /// When , , and + /// are combined, the last call takes effect. /// /// /// The history is not updated even when the hx-push-url or hx-replace-url attribute @@ -156,7 +155,7 @@ public HtmxResponse PreventHistoryUpdate() => /// /// Sets the HX-Redirect header to perform a client-side redirect with a full-page reload. /// - /// The header value to set. + /// The URL to redirect to. /// /// The current instance. /// @@ -185,7 +184,7 @@ public HtmxResponse Reswap(HtmxSwap value) => /// /// Sets the HX-Reswap header to specify how the response will be swapped. /// - /// The header value to set. + /// The swap style, optionally followed by swap modifiers. /// /// The current instance. /// @@ -193,10 +192,10 @@ public HtmxResponse Reswap(string value) => SetHeader(this, HtmxResponseHeaderNames.Reswap, value); /// - /// Sets the HX-Retarget header to update the target of the content update + /// Sets the HX-Retarget header to change the target of the content update /// to a different element on the page. /// - /// The CSS selector to set. + /// The CSS selector to assign to the header. /// /// The current instance. /// @@ -206,7 +205,7 @@ public HtmxResponse Retarget(string value) => /// /// Sets the HX-Reselect header to select the part of the response to swap in. /// - /// The CSS selector to set. + /// The CSS selector to assign to the header. /// /// The current instance. /// @@ -223,16 +222,17 @@ public HtmxResponse Reselect(string value) => /// /// /// - /// HTMX 4.x has only the HX-Trigger header, so every value - /// is emitted through it. These events fire when the request completes, which is after the swap + /// HTMX 4.x has only the HX-Trigger header, so events with any value + /// are emitted through it. These events fire when the request completes, which is after the swap /// and settle steps whenever a swap is performed. /// See PR #3900. /// /// - /// Events of one header are triggered in the order they are added. If an event name is added - /// more than once for the same header, all events of that header are replayed by the Toolkit script, - /// which must be loaded for any of them to be triggered. In HTMX 4.x, this also applies - /// to a name added with different timings. + /// Events in the same header are triggered in the order they were added. If the same event name + /// is added more than once for a header, all events in that header are sent in a form that only + /// the Toolkit script (htmx-toolkit.js) can replay; without the script, none of them + /// is triggered. In HTMX 4.x, this also applies to a name added with different timings. + /// The event name rs:event is reserved for this purpose and cannot be used. /// /// public HtmxResponse TriggerEvent(string eventName, HtmxTriggerTiming timing = HtmxTriggerTiming.Receive) => @@ -242,26 +242,27 @@ public HtmxResponse TriggerEvent(string eventName, HtmxTriggerTiming timing = Ht /// Adds a client-side event and its detail to the response header selected by /// . /// + /// The event name to trigger. + /// The event detail. + /// The event timing. Defaults to . + /// + /// The current instance. + /// /// /// - /// HTMX 4.x has only the HX-Trigger header, so every value - /// is emitted through it. These events fire when the request completes, which is after the swap + /// HTMX 4.x has only the HX-Trigger header, so events with any value + /// are emitted through it. These events fire when the request completes, which is after the swap /// and settle steps whenever a swap is performed. /// See PR #3900. /// /// - /// Events of one header are triggered in the order they are added. If an event name is added - /// more than once for the same header, all events of that header are replayed by the Toolkit script, - /// which must be loaded for any of them to be triggered. In HTMX 4.x, this also applies - /// to a name added with different timings. + /// Events in the same header are triggered in the order they were added. If the same event name + /// is added more than once for a header, all events in that header are sent in a form that only + /// the Toolkit script (htmx-toolkit.js) can replay; without the script, none of them + /// is triggered. In HTMX 4.x, this also applies to a name added with different timings. + /// The event name rs:event is reserved for this purpose and cannot be used. /// /// - /// The event name to trigger. - /// The event detail. - /// The event timing. Defaults to . - /// - /// The current instance. - /// [RequiresDynamicCode("Event details are serialized using reflection. Use the TriggerEvent overload that accepts JsonTypeInfo for Native AOT applications.")] [RequiresUnreferencedCode("Event details are serialized using reflection. Use the TriggerEvent overload that accepts JsonTypeInfo for trimmed applications.")] public HtmxResponse TriggerEvent(string eventName, object detail, HtmxTriggerTiming timing = HtmxTriggerTiming.Receive) @@ -273,29 +274,32 @@ static HtmxResponse TriggerEventImpl(HtmxResponse response, string eventName, ob } /// - /// Adds a client-side event and serializes its detail using the specified JSON metadata. + /// Adds a client-side event and its detail, serialized with the specified JSON metadata, + /// to the response header selected by . /// /// The event detail type. /// The event name to trigger. /// The event detail. - /// The JSON metadata for the event detail. - /// Use source-generated metadata for trimming and Native AOT. + /// + /// The JSON metadata for the event detail. Use source-generated metadata for trimming and Native AOT. + /// /// The event timing. Defaults to . /// /// The current instance. /// /// /// - /// HTMX 4.x has only the HX-Trigger header, so every value - /// is emitted through it. These events fire when the request completes, which is after the swap + /// HTMX 4.x has only the HX-Trigger header, so events with any value + /// are emitted through it. These events fire when the request completes, which is after the swap /// and settle steps whenever a swap is performed. /// See PR #3900. /// /// - /// Events of one header are triggered in the order they are added. If an event name is added - /// more than once for the same header, all events of that header are replayed by the Toolkit script, - /// which must be loaded for any of them to be triggered. In HTMX 4.x, this also applies - /// to a name added with different timings. + /// Events in the same header are triggered in the order they were added. If the same event name + /// is added more than once for a header, all events in that header are sent in a form that only + /// the Toolkit script (htmx-toolkit.js) can replay; without the script, none of them + /// is triggered. In HTMX 4.x, this also applies to a name added with different timings. + /// The event name rs:event is reserved for this purpose and cannot be used. /// /// public HtmxResponse TriggerEvent(string eventName, T detail, JsonTypeInfo jsonTypeInfo, HtmxTriggerTiming timing = HtmxTriggerTiming.Receive) => @@ -329,7 +333,7 @@ private static HtmxResponse SetHeader(HtmxResponse response, string key, string /// private static HtmxResponse SetHistoryHeader(HtmxResponse response, string key, string value, string conflictingKey) { - // The name of the legacy "HX-Push" header. + // Remove the legacy "HX-Push" header: // - HTMX 1.9.x and 2.x check it before "HX-Push-Url" and "HX-Replace-Url"; // - HTMX 4.x ignores it. response._response.Headers.Remove("HX-Push"); diff --git a/src/Ramstack.HtmxToolkit/HtmxResponseAttribute.cs b/src/Ramstack.HtmxToolkit/HtmxResponseAttribute.cs index f5d4a9b..b54e996 100644 --- a/src/Ramstack.HtmxToolkit/HtmxResponseAttribute.cs +++ b/src/Ramstack.HtmxToolkit/HtmxResponseAttribute.cs @@ -10,6 +10,11 @@ namespace Ramstack.HtmxToolkit; /// /// Specifies HTMX response headers to apply when an action result is executed for an HTMX request. /// +/// +/// A property that is not set, or is set to , , +/// or an empty string, adds no header. The specified headers replace any values that the action +/// has already set for them. +/// [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public sealed class HtmxResponseAttribute : Attribute, IResultFilter { @@ -28,6 +33,11 @@ public bool Refresh /// /// Gets or sets the swap style to specify in the HX-Reswap header. /// + /// + /// Returns if no swap style is specified or the style + /// is not recognized. sets the same header, + /// so specify only one of them. + /// public HtmxSwap Reswap { get @@ -41,6 +51,9 @@ public HtmxSwap Reswap /// /// Gets or sets the complete HX-Reswap header value, including any swap modifiers. /// + /// + /// sets the same header, so specify only one of them. + /// [MaybeNull] public string ReswapExpression { @@ -102,7 +115,7 @@ public void OnResultExecuted(ResultExecutedContext context) } /// - /// Adds a nonempty response header value to the pending headers. + /// Adds a non-empty response header value to the pending headers. /// /// The name of the header. /// The header value. diff --git a/src/Ramstack.HtmxToolkit/HtmxResponseHeaderNames.cs b/src/Ramstack.HtmxToolkit/HtmxResponseHeaderNames.cs index 166ffb6..917f792 100644 --- a/src/Ramstack.HtmxToolkit/HtmxResponseHeaderNames.cs +++ b/src/Ramstack.HtmxToolkit/HtmxResponseHeaderNames.cs @@ -59,12 +59,12 @@ public static class HtmxResponseHeaderNames /// /// The HX-Trigger-After-Settle header triggers client-side events after the settle step. /// - /// Supported only in HTMX 1.x and 2.x. + /// Supported only in HTMX 1.9.x and 2.x. public const string TriggerAfterSettle = "HX-Trigger-After-Settle"; /// /// The HX-Trigger-After-Swap header triggers client-side events after the swap step. /// - /// Supported only in HTMX 1.x and 2.x. + /// Supported only in HTMX 1.9.x and 2.x. public const string TriggerAfterSwap = "HX-Trigger-After-Swap"; } diff --git a/src/Ramstack.HtmxToolkit/HtmxResponseHeaders.cs b/src/Ramstack.HtmxToolkit/HtmxResponseHeaders.cs index 70f0ee5..e0ad00d 100644 --- a/src/Ramstack.HtmxToolkit/HtmxResponseHeaders.cs +++ b/src/Ramstack.HtmxToolkit/HtmxResponseHeaders.cs @@ -25,6 +25,7 @@ internal HtmxResponseHeaders(HttpResponse response) => /// Gets or sets the value of the HX-Location header, which performs /// a client-side redirect without a full-page reload. /// + /// Assigning or an empty string removes the header. [MaybeNull] public string Location { @@ -37,8 +38,11 @@ public string Location /// onto the browser's history stack. /// /// - /// Unlike , setting this property - /// does not remove the HX-Replace-Url header. + /// Assigning or an empty string removes the header. + /// + /// Unlike , setting this property + /// does not remove the HX-Replace-Url header. + /// /// [MaybeNull] public string PushUrl @@ -52,8 +56,11 @@ public string PushUrl /// without pushing a new entry to the browser's history stack. /// /// - /// Unlike , setting this property - /// does not remove the HX-Push-Url header. + /// Assigning or an empty string removes the header. + /// + /// Unlike , setting this property + /// does not remove the HX-Push-Url header. + /// /// [MaybeNull] public string ReplaceUrl @@ -66,6 +73,7 @@ public string ReplaceUrl /// Gets or sets the value of the HX-Redirect header, which performs /// a client-side redirect with a full-page reload. /// + /// Assigning or an empty string removes the header. [MaybeNull] public string Redirect { @@ -95,6 +103,7 @@ public HtmxSwap? Reswap /// /// Gets or sets the complete HX-Reswap header value, including any swap modifiers. /// + /// Assigning or an empty string removes the header. [MaybeNull] public string ReswapExpression { @@ -106,6 +115,7 @@ public string ReswapExpression /// Gets or sets the CSS selector specified by the HX-Retarget header /// to change the target of the content update. /// + /// Assigning or an empty string removes the header. [MaybeNull] public string Retarget { @@ -117,6 +127,7 @@ public string Retarget /// Gets or sets the CSS selector specified by the HX-Reselect header /// to determine which part of the response is swapped in. /// + /// Assigning or an empty string removes the header. [MaybeNull] public string Reselect { @@ -157,9 +168,10 @@ public string Reselect /// the property returns . /// /// - /// HTMX 4.x has no HX-Trigger-After-Swap header, so these events are accumulated - /// in and emitted through HX-Trigger when the request completes - /// (after the swap and settle steps whenever a swap is performed). + /// HTMX 4.x has no HX-Trigger-After-Swap header, so these events are emitted through + /// HX-Trigger when the request completes (after the swap and settle steps whenever + /// a swap is performed). The property therefore returns the same list as , + /// including the events added with other timings. /// /// public IReadOnlyList>? TriggerAfterSwap => PendingEvents.TryGet(_response)?.GetEvents(HtmxTriggerTiming.AfterSwap); @@ -176,9 +188,10 @@ public string Reselect /// the property returns . /// /// - /// HTMX 4.x has no HX-Trigger-After-Settle header, so these events are accumulated - /// in and emitted through HX-Trigger when the request completes - /// (after the swap and settle steps whenever a swap is performed). + /// HTMX 4.x has no HX-Trigger-After-Settle header, so these events are emitted through + /// HX-Trigger when the request completes (after the swap and settle steps whenever + /// a swap is performed). The property therefore returns the same list as , + /// including the events added with other timings. /// /// public IReadOnlyList>? TriggerAfterSettle => PendingEvents.TryGet(_response)?.GetEvents(HtmxTriggerTiming.AfterSettle); @@ -198,7 +211,8 @@ public string Reselect } /// - /// Sets the specified header when is not . + /// Sets the specified header, or removes it when + /// is or empty. /// /// The header collection to update. /// The name of the header. diff --git a/src/Ramstack.HtmxToolkit/HtmxSwap.cs b/src/Ramstack.HtmxToolkit/HtmxSwap.cs index c1c9338..4c5b996 100644 --- a/src/Ramstack.HtmxToolkit/HtmxSwap.cs +++ b/src/Ramstack.HtmxToolkit/HtmxSwap.cs @@ -1,7 +1,7 @@ namespace Ramstack.HtmxToolkit; /// -/// Defines the values for the hx-swap attribute. +/// Specifies HTMX swap styles. /// public enum HtmxSwap { @@ -23,9 +23,8 @@ public enum HtmxSwap /// Supported natively by HTMX 4.x. /// /// - /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension - /// and optionally load Idiomorph. Without Idiomorph, the extension - /// falls back to . + /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension and load Idiomorph. + /// Without Idiomorph, the extension logs a warning and falls back to . /// /// InnerMorph, @@ -38,23 +37,23 @@ public enum HtmxSwap /// Supported natively by HTMX 4.x. /// /// - /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension - /// and optionally load Idiomorph. Without Idiomorph, the extension - /// falls back to . + /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension and load Idiomorph. + /// Without Idiomorph, the extension logs a warning and falls back to . /// /// OuterMorph, /// - /// Synchronizes the target element with the response. + /// Keeps the target element, replaces its attributes with those of the response's root element, + /// and replaces its children with the root element's children. /// /// /// /// Supported natively by HTMX 4.x. /// /// - /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension - /// to fall back to attribute synchronization and . + /// With HTMX 1.9.x or 2.x, activate the ramstack-swap extension, + /// which performs the same steps. /// /// OuterSync, diff --git a/src/Ramstack.HtmxToolkit/HtmxTriggerTiming.cs b/src/Ramstack.HtmxToolkit/HtmxTriggerTiming.cs index e705dca..d816188 100644 --- a/src/Ramstack.HtmxToolkit/HtmxTriggerTiming.cs +++ b/src/Ramstack.HtmxToolkit/HtmxTriggerTiming.cs @@ -1,7 +1,7 @@ namespace Ramstack.HtmxToolkit; /// -/// Specifies the response header used to trigger client-side events. +/// Specifies when client-side events are triggered, by selecting the response header that carries them. /// public enum HtmxTriggerTiming { @@ -9,7 +9,7 @@ public enum HtmxTriggerTiming /// Maps to the HX-Trigger header. /// /// - /// HTMX 1.x and 2.x trigger these events when the response is received, before the swap. + /// HTMX 1.9.x and 2.x trigger these events when the response is received, before the swap. /// /// HTMX 4.x triggers them when the request completes, which is after the swap and settle steps /// whenever a swap is performed. To act before the swap in HTMX 4.x, handle htmx:before:swap @@ -24,7 +24,7 @@ public enum HtmxTriggerTiming /// /// /// - /// HTMX 1.x and 2.x trigger these events only when the response is swapped, + /// HTMX 1.9.x and 2.x trigger these events only when the response is swapped, /// so they do not fire for a status code such as 204. /// /// @@ -42,7 +42,7 @@ public enum HtmxTriggerTiming /// /// /// - /// HTMX 1.x and 2.x trigger these events only when the response is swapped, + /// HTMX 1.9.x and 2.x trigger these events only when the response is swapped, /// so they do not fire for a status code such as 204. /// /// diff --git a/src/Ramstack.HtmxToolkit/HttpRequestExtensions.cs b/src/Ramstack.HtmxToolkit/HttpRequestExtensions.cs index 71c6414..f372eed 100644 --- a/src/Ramstack.HtmxToolkit/HttpRequestExtensions.cs +++ b/src/Ramstack.HtmxToolkit/HttpRequestExtensions.cs @@ -8,7 +8,8 @@ namespace Ramstack.HtmxToolkit; public static class HttpRequestExtensions { /// - /// Determines whether the specified HTTP request is an HTMX request. + /// Determines whether the specified HTTP request is an HTMX request + /// by checking for the HX-Request header. /// /// The HTTP request. /// @@ -19,11 +20,14 @@ public static bool IsHtmxRequest(this HttpRequest request) => request.GetHtmxHeaders().Request; /// - /// Determines whether the specified HTTP request is an HTMX request. + /// Determines whether the specified HTTP request is an HTMX request + /// by checking for the HX-Request header. /// /// The HTTP request. - /// When this method returns, contains the - /// that provides access to well-known HTMX headers. + /// + /// When this method returns, contains a strongly typed view of the HTMX request headers. + /// The value is set even when the method returns . + /// /// /// if the specified HTTP request is an HTMX request; /// otherwise, . @@ -55,8 +59,8 @@ public static bool IsHtmxBoosted(this HttpRequest request) => /// otherwise, . /// /// - /// HTMX 4.x sends the header itself; for HTMX 1.x and 2.x, the companion - /// script htmx-toolkit.js adds it. See . + /// HTMX 4.x sends the header itself; for HTMX 1.9.x and 2.x, the Toolkit script + /// adds it. See . /// public static bool IsHtmxFullRequest(this HttpRequest request) { @@ -79,8 +83,8 @@ public static bool IsHtmxFullRequest(this HttpRequest request) /// otherwise, . /// /// - /// HTMX 4.x sends the header itself; for HTMX 1.x and 2.x, the companion - /// script htmx-toolkit.js adds it. Without the header, the method returns . + /// HTMX 4.x sends the header itself; for HTMX 1.9.x and 2.x, the Toolkit script + /// adds it. Without the header, the method returns . /// public static bool IsHtmxPartialRequest(this HttpRequest request) => // PERF: See IsHtmxFullRequest. diff --git a/src/Ramstack.HtmxToolkit/HttpResponseExtensions.cs b/src/Ramstack.HtmxToolkit/HttpResponseExtensions.cs index a4cde7d..f978119 100644 --- a/src/Ramstack.HtmxToolkit/HttpResponseExtensions.cs +++ b/src/Ramstack.HtmxToolkit/HttpResponseExtensions.cs @@ -31,6 +31,7 @@ public static void Htmx(this HttpResponse response, Action configu /// /// Configures the HTMX response headers when the current request is an HTMX request. /// + /// The type of the state passed to . /// The HTTP response to configure. /// The delegate that configures the HTMX response headers /// using . diff --git a/src/Ramstack.HtmxToolkit/Internal/DebugHelpers.cs b/src/Ramstack.HtmxToolkit/Internal/DebugHelpers.cs index b1382e7..c9f4e11 100644 --- a/src/Ramstack.HtmxToolkit/Internal/DebugHelpers.cs +++ b/src/Ramstack.HtmxToolkit/Internal/DebugHelpers.cs @@ -8,7 +8,7 @@ namespace Ramstack.HtmxToolkit.Internal; internal static class DebugHelpers { /// - /// Returns all headers whose names start with "HX-", + /// Returns all headers whose names start with HX-, /// using a case-insensitive comparison. /// /// The header dictionary to inspect. diff --git a/src/Ramstack.HtmxToolkit/Internal/EnumHelper.cs b/src/Ramstack.HtmxToolkit/Internal/EnumHelper.cs index 79f872d..00ca9f2 100644 --- a/src/Ramstack.HtmxToolkit/Internal/EnumHelper.cs +++ b/src/Ramstack.HtmxToolkit/Internal/EnumHelper.cs @@ -5,7 +5,7 @@ namespace Ramstack.HtmxToolkit.Internal; /// -/// Provides extension methods for converting and parsing HTMX-related enum values. +/// Provides methods for converting and parsing HTMX-related enum values. /// internal static class EnumHelper { diff --git a/src/Ramstack.HtmxToolkit/Internal/PendingEvents.cs b/src/Ramstack.HtmxToolkit/Internal/PendingEvents.cs index 5fa0ac1..e82522b 100644 --- a/src/Ramstack.HtmxToolkit/Internal/PendingEvents.cs +++ b/src/Ramstack.HtmxToolkit/Internal/PendingEvents.cs @@ -23,7 +23,8 @@ internal sealed class PendingEvents private const string ProxyEventName = "rs:event"; /// - /// The maximum number of events for which repeated names are found using a linear scan. + /// The maximum number of events for which repeated names are found by comparing + /// each name with the preceding ones instead of using a set. /// private const int LinearScanThreshold = 8; @@ -44,7 +45,7 @@ private PendingEvents(HttpResponse response) => /// /// Adds the specified event to the end of the pending events for . /// - /// The time at which to trigger the events. + /// The time at which to trigger the event. /// The event name. /// The event detail as JSON. public void AddEvent(HtmxTriggerTiming timing, string eventName, string detailJson) @@ -146,14 +147,15 @@ public static PendingEvents GetOrCreate(HttpResponse response) /// Serializes the specified events into a response header /// when the collection is not . /// - /// - /// A JSON object cannot hold repeated names. If an event name repeats, all events - /// are written in order under the key, and the Toolkit script replays them. - /// Writing only the repeats there would change the order in which the events are triggered. - /// /// The response header name. /// The events to serialize. /// The buffer that receives the serialized header value. + /// + /// HTMX parses the header with JSON.parse, which keeps only the last of repeated names. + /// If an event name repeats, all events are written in order under the key, + /// and the Toolkit script replays them. + /// Writing only the repeats there would change the order in which the events are triggered. + /// private void SetHeader(string name, EventList? events, ArrayBufferWriter buffer) { if (events is null) @@ -236,7 +238,7 @@ private static bool HasRepeatedNames(EventList events) /// The event detail as JSON. private static void WriteDetail(Utf8JsonWriter writer, string detailJson) { - // HTMX 1.x and 2.x pass a null detail to listeners as { value: null }, + // HTMX 1.9.x and 2.x pass a null detail to listeners as { value: null }, // while HTMX 4.x fails on it. Write the wrapped form for all versions. if (detailJson == "null") { @@ -251,7 +253,8 @@ private static void WriteDetail(Utf8JsonWriter writer, string detailJson) } /// - /// Normalizes unsupported HTMX 4.x trigger timings to the primary trigger header. + /// Maps timings that HTMX 4.x does not support to , + /// whose events go to HX-Trigger. /// /// The requested event timing. /// @@ -263,7 +266,8 @@ private HtmxTriggerTiming NormalizeTiming(HtmxTriggerTiming timing) => : timing; /// - /// Returns the configured HTMX target version, defaulting to HTMX 2.x when toolkit services are unavailable. + /// Returns the configured HTMX target version, or HTMX 2.x if + /// of is not registered. /// /// The response whose request services are inspected. /// diff --git a/src/Ramstack.HtmxToolkit/Serialization/HtmxConfigJsonSerializerContext.cs b/src/Ramstack.HtmxToolkit/Serialization/HtmxConfigJsonSerializerContext.cs index 202578d..76148f4 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HtmxConfigJsonSerializerContext.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HtmxConfigJsonSerializerContext.cs @@ -5,7 +5,7 @@ namespace Ramstack.HtmxToolkit.Serialization; /// -/// Represents source-generated JSON serialization metadata for HTMX configuration data. +/// Provides source-generated JSON serialization metadata for HTMX configuration data. /// [JsonSourceGenerationOptions( WriteIndented = false, diff --git a/src/Ramstack.HtmxToolkit/Serialization/HtmxFieldValuesJsonConverter.cs b/src/Ramstack.HtmxToolkit/Serialization/HtmxFieldValuesJsonConverter.cs index c11ad57..fe0b912 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HtmxFieldValuesJsonConverter.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HtmxFieldValuesJsonConverter.cs @@ -5,7 +5,7 @@ namespace Ramstack.HtmxToolkit.Serialization; /// -/// Represents a JSON converter for . +/// Represents a for values. /// internal sealed class HtmxFieldValuesJsonConverter : JsonConverter { diff --git a/src/Ramstack.HtmxToolkit/Serialization/HtmxHistoryModeJsonConverter.cs b/src/Ramstack.HtmxToolkit/Serialization/HtmxHistoryModeJsonConverter.cs index 85463ae..c18db24 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HtmxHistoryModeJsonConverter.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HtmxHistoryModeJsonConverter.cs @@ -7,9 +7,12 @@ namespace Ramstack.HtmxToolkit.Serialization; /// -/// Converts values to the JSON representation expected -/// by the history configuration option. +/// Represents a for nullable values. /// +/// +/// Writes the history value expected by HTMX: , +/// , or "reload". +/// internal sealed class HtmxHistoryModeJsonConverter : JsonConverter { /// diff --git a/src/Ramstack.HtmxToolkit/Serialization/HtmxLocationOptionsJsonView.cs b/src/Ramstack.HtmxToolkit/Serialization/HtmxLocationOptionsJsonView.cs index 4fae8cd..e29a0ec 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HtmxLocationOptionsJsonView.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HtmxLocationOptionsJsonView.cs @@ -51,14 +51,16 @@ internal readonly struct HtmxLocationOptionsJsonView(string path, HtmxLocationOp public string? SelectOob => options.SelectOob; /// - /// Gets the URL to push, or the string that disables pushing. + /// Gets the URL to push, "true" to push the URL of the loaded page, + /// or "false" to disable pushing. /// public string Push => options.HistoryAction == HtmxHistoryAction.Push ? options.HistoryUrl ?? "true" : "false"; /// - /// Gets the URL to replace, or the string that disables replacement. + /// Gets the replacement URL, "true" to use the URL of the loaded page, + /// or "false" to disable replacement. /// public string Replace => options.HistoryAction == HtmxHistoryAction.Replace ? options.HistoryUrl ?? "true" diff --git a/src/Ramstack.HtmxToolkit/Serialization/HtmxTriggerSpecsCacheJsonConverter.cs b/src/Ramstack.HtmxToolkit/Serialization/HtmxTriggerSpecsCacheJsonConverter.cs index 3252d4c..befa3b4 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HtmxTriggerSpecsCacheJsonConverter.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HtmxTriggerSpecsCacheJsonConverter.cs @@ -4,8 +4,12 @@ namespace Ramstack.HtmxToolkit.Serialization; /// -/// Converts the boolean triggerSpecsCache abstraction to its HTMX JSON representation. +/// Represents a for the TriggerSpecsCacheEnabled configuration option. /// +/// +/// Writes the triggerSpecsCache value expected by HTMX: +/// an empty object when enabled, otherwise . +/// internal sealed class HtmxTriggerSpecsCacheJsonConverter : JsonConverter { /// diff --git a/src/Ramstack.HtmxToolkit/Serialization/JsonOptions.cs b/src/Ramstack.HtmxToolkit/Serialization/JsonOptions.cs index 1471e2e..b740c2f 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/JsonOptions.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/JsonOptions.cs @@ -11,8 +11,7 @@ namespace Ramstack.HtmxToolkit.Serialization; internal static class JsonOptions { /// - /// Preserves characters from all Unicode ranges while escaping JavaScript- - /// and HTML-sensitive characters. + /// The shared encoder instance exposed by . /// private static readonly JavaScriptEncoder s_encoder = JavaScriptEncoder.Create(new TextEncoderSettings(UnicodeRanges.All)); diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxConfigTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxConfigTagHelper.cs index e43e63a..8221bb8 100644 --- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxConfigTagHelper.cs +++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxConfigTagHelper.cs @@ -12,7 +12,18 @@ namespace Ramstack.HtmxToolkit.TagHelpers; /// Renders the application-wide HTMX configuration as a <meta> element. /// /// The service used to generate antiforgery tokens. -/// The configured HTMX Toolkit options. +/// The configured HtmxToolkit options. +/// +/// +/// The tag helper applies to both <htmx-config /> and +/// <meta htmx-config />. +/// +/// +/// When is , +/// the element also includes the antiforgery metadata that the Toolkit script +/// (htmx-toolkit.js) uses to send the token with HTMX requests. +/// +/// [HtmlTargetElement("meta", Attributes = "htmx-config", TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("htmx-config", TagStructure = TagStructure.NormalOrSelfClosing)] public sealed class HtmxConfigTagHelper(IAntiforgery antiforgery, IOptions options) : TagHelper diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxHeaderTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxHeaderTagHelper.cs index b5d42ad..a80088c 100644 --- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxHeaderTagHelper.cs +++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxHeaderTagHelper.cs @@ -11,16 +11,17 @@ namespace Ramstack.HtmxToolkit.TagHelpers; /// -/// Applies an hx-headers attribute assembled from strongly typed tag helper attributes. +/// Applies an hx-headers attribute assembled from hx-header-* attributes +/// and the hx-all-headers dictionary. /// /// /// -/// HTMX 1.x and 2.x merge-inherit hx-headers from parent elements. +/// HTMX 1.9.x and 2.x merge-inherit hx-headers from parent elements. /// /// HTMX 4.x requires either the explicit inheritance modifier or the global /// option for inheritance. /// -/// The append modifier merges a child declaration into inherited headers. +/// In HTMX 4.x, the append modifier merges a child declaration into inherited headers. /// /// [HtmlTargetElement(Attributes = InheritedAttributeName)] @@ -38,8 +39,14 @@ public sealed class HtmxHeaderTagHelper(IOptions options) : /// Gets or sets a value indicating whether hx-headers is explicitly inherited. /// /// - /// HTMX 1.x and 2.x merge-inherit headers without a modifier. - /// HTMX 4.x emits the inherited modifier when this property is . + /// + /// HTMX 1.9.x and 2.x merge-inherit headers without a modifier, + /// so the property does not change the generated attribute name for them. + /// + /// + /// For HTMX 4.x, the tag helper emits the inherited modifier + /// when this property is . + /// /// [HtmlAttributeName(InheritedAttributeName)] public bool Inherited { get; set; } @@ -48,15 +55,22 @@ public sealed class HtmxHeaderTagHelper(IOptions options) : /// Gets or sets a value indicating whether hx-headers is appended to inherited headers. /// /// - /// HTMX 4.x emits the append modifier when this property is . - /// The property does not change the generated attribute name for HTMX 1.x and 2.x. + /// The property does not change the generated attribute name for HTMX 1.9.x and 2.x. + /// + /// For HTMX 4.x, the tag helper emits the append modifier + /// when this property is . + /// /// [HtmlAttributeName(AppendAttributeName)] public bool Append { get; set; } /// - /// Gets or sets the hx-headers attribute values. + /// Gets or sets the hx-headers attribute values, which can also be set + /// with hx-header-* attributes. /// + /// + /// The default dictionary compares header names case-insensitively. + /// [HtmlAttributeName(HeadersDictionaryName, DictionaryAttributePrefix = HeadersPrefix)] public IDictionary Headers { diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs index a5c791d..2d7c175 100644 --- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs +++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs @@ -10,14 +10,18 @@ namespace Ramstack.HtmxToolkit.TagHelpers; /// -/// Applies version-specific HTMX request configuration to matching elements. +/// Renders request options as hx-request (HTMX 1.9.x and 2.x) or hx-config (HTMX 4.x). /// /// -/// HTMX 1.x and 2.x use the merge-inherited hx-request attribute. +/// HTMX 1.9.x and 2.x merge-inherit hx-request from parent elements. /// -/// HTMX 4.x uses hx-config, supports the inherited and append modifiers, +/// HTMX 4.x supports the inherited and append modifiers on hx-config /// and can enable inheritance globally with . /// +/// +/// Options that the selected HTMX version does not support are not rendered. +/// If no supported option is set, the tag helper renders no attribute. +/// /// [HtmlTargetElement(Attributes = RequestInheritedAttributeName)] [HtmlTargetElement(Attributes = RequestAppendAttributeName)] @@ -48,8 +52,14 @@ public sealed class HtmxRequestTagHelper(IOptions options) : /// Gets or sets a value indicating whether the generated attribute is explicitly inherited. /// /// - /// HTMX 1.x and 2.x merge-inherit request configuration without a modifier. - /// HTMX 4.x emits the inherited modifier when this property is . + /// + /// HTMX 1.9.x and 2.x merge-inherit request configuration without a modifier, + /// so the property does not change the generated attribute name for them. + /// + /// + /// For HTMX 4.x, the tag helper emits the inherited modifier + /// when this property is . + /// /// [HtmlAttributeName(RequestInheritedAttributeName)] public bool Inherited { get; set; } @@ -58,8 +68,11 @@ public sealed class HtmxRequestTagHelper(IOptions options) : /// Gets or sets a value indicating whether the generated attribute is appended to inherited configuration. /// /// - /// HTMX 4.x emits the append modifier when this property is . - /// The property does not change the generated attribute name for HTMX 1.x and 2.x. + /// The property does not change the generated attribute name for HTMX 1.9.x and 2.x. + /// + /// For HTMX 4.x, the tag helper emits the append modifier + /// when this property is . + /// /// [HtmlAttributeName(RequestAppendAttributeName)] public bool Append { get; set; } @@ -67,7 +80,7 @@ public sealed class HtmxRequestTagHelper(IOptions options) : /// /// Gets or sets the timeout for the request in milliseconds. /// - /// Supported in HTMX 1.x, 2.x, and 4.x. + /// Supported in HTMX 1.9.x, 2.x, and 4.x. [HtmlAttributeName(RequestTimeoutAttributeName)] public int? Timeout { @@ -80,19 +93,17 @@ public int? Timeout /// /// /// - /// In HTMX 1.x and 2.x, this property maps to the boolean + /// In HTMX 1.9.x and 2.x, this property maps to the boolean /// credentials option of hx-request. /// yields . /// yields . + /// is unsupported, so the option is omitted + /// and HTMX uses its default value. /// /// /// In HTMX 4.x, this property maps to the string credentials option /// of hx-config. /// - /// - /// is unsupported in HTMX 1.x and 2.x, - /// so the option is omitted and HTMX uses its default value. - /// /// [HtmlAttributeName(RequestCredentialsAttributeName)] public HtmxRequestCredentials? Credentials @@ -106,8 +117,9 @@ public HtmxRequestCredentials? Credentials /// including custom headers supplied through hx-headers. /// /// - /// Supported in HTMX 1.x and 2.x. Removed in HTMX 4.x. + /// Supported only in HTMX 1.9.x and 2.x. /// Headers added by the browser are unaffected. + /// The Toolkit script also omits the HX-Request-Type header. /// [HtmlAttributeName(RequestNoHeadersAttributeName)] public bool? NoHeaders @@ -211,7 +223,7 @@ public override Task ProcessAsync(TagHelperContext context, TagHelperOutput outp #region Inner type: HtmxRequestData /// - /// Stores the request configuration shared by all supported HTMX versions. + /// Stores the request options before they are projected for the target HTMX version. /// internal sealed class HtmxRequestData { @@ -245,22 +257,22 @@ internal sealed class HtmxRequestData #region Inner type: HtmxRequestDataPrior /// - /// Projects request configuration into the hx-request contract used by HTMX 1.x and 2.x. + /// Projects request configuration into the hx-request contract used by HTMX 1.9.x and 2.x. /// - /// The shared request configuration. + /// The request options to project. internal readonly struct HtmxRequestDataPrior(HtmxRequestData data) { /// public int? Timeout => data.Timeout; /// - /// Gets the boolean credentials value supported by HTMX 1.x and 2.x. + /// Gets the boolean credentials value supported by HTMX 1.9.x and 2.x. /// public bool? Credentials => data.Credentials switch { HtmxRequestCredentials.SameOrigin => false, HtmxRequestCredentials.Include => true, - // TODO: Consider throwing an exception when an HTMX 4-only credentials mode is configured for HTMX 1.x or 2.x. + // TODO: Consider throwing an exception when an HTMX 4-only credentials mode is configured for HTMX 1.9.x or 2.x. // HtmxRequestCredentials.Omit => throw new InvalidOperationException(), _ => null }; @@ -276,7 +288,7 @@ internal readonly struct HtmxRequestDataPrior(HtmxRequestData data) /// /// Projects request configuration into the hx-config contract used by HTMX 4.x. /// - /// The shared request configuration. + /// The request options to project. internal readonly struct HtmxRequestDataV4(HtmxRequestData data) { /// diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxUrlTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxUrlTagHelper.cs index f48a7cb..5054225 100644 --- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxUrlTagHelper.cs +++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxUrlTagHelper.cs @@ -17,6 +17,19 @@ namespace Ramstack.HtmxToolkit.TagHelpers; /// Generates URLs for HTMX request attributes on matching elements. /// /// The factory used to create URL helpers. +/// +/// +/// The URL is written to the existing hx-get, hx-post, hx-put, +/// hx-patch, or hx-delete attribute. If none of them is present, +/// the tag helper adds hx-get. +/// +/// +/// An is thrown when more than one of these +/// attributes is present, or when attributes from more than one of the following groups +/// are specified: hx-route; hx-controller and hx-action; +/// hx-page and hx-page-handler. +/// +/// [HtmlTargetElement(Attributes = ActionAttributeName)] [HtmlTargetElement(Attributes = ControllerAttributeName)] [HtmlTargetElement(Attributes = AreaAttributeName)] @@ -48,6 +61,9 @@ public sealed class HtmxUrlTagHelper(IUrlHelperFactory factory) : TagHelper private IDictionary? _routeValues; /// + /// + /// Runs before tag helpers with the default order, so that they see the generated URL attribute. + /// public override int Order => -1000; /// @@ -246,7 +262,7 @@ private static void Error_CannotDetermineUrl() [DoesNotReturn] private static void Error_AmbiguousMethods() { - const string Message = "Ambiguous htmx method. Only one of the following methods is allowed: hx-get, hx-post, hx-delete, hx-put, hx-patch"; + const string Message = "Ambiguous HTMX method. Only one of the following methods is allowed: hx-get, hx-post, hx-delete, hx-put, hx-patch"; throw new InvalidOperationException(Message); } } diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxValsTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxValsTagHelper.cs index 65b06e4..94587a6 100644 --- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxValsTagHelper.cs +++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxValsTagHelper.cs @@ -11,16 +11,17 @@ namespace Ramstack.HtmxToolkit.TagHelpers; /// -/// Applies an hx-vals attribute assembled from strongly typed tag helper attributes. +/// Applies an hx-vals attribute assembled from hx-val-* attributes +/// and the hx-all-vals dictionary. /// /// /// -/// HTMX 1.x and 2.x merge-inherit hx-vals from parent elements. +/// HTMX 1.9.x and 2.x merge-inherit hx-vals from parent elements. /// /// HTMX 4.x requires either the explicit inheritance modifier or the global /// option for inheritance. /// -/// The append modifier merges a child declaration into inherited values. +/// In HTMX 4.x, the append modifier merges a child declaration into inherited values. /// /// [HtmlTargetElement(Attributes = InheritedAttributeName)] @@ -38,8 +39,14 @@ public sealed class HtmxValsTagHelper(IOptions options) : Ta /// Gets or sets a value indicating whether hx-vals is explicitly inherited. /// /// - /// HTMX 1.x and 2.x merge-inherit values without a modifier. - /// HTMX 4.x emits the inherited modifier when this property is . + /// + /// HTMX 1.9.x and 2.x merge-inherit values without a modifier, + /// so the property does not change the generated attribute name for them. + /// + /// + /// For HTMX 4.x, the tag helper emits the inherited modifier + /// when this property is . + /// /// [HtmlAttributeName(InheritedAttributeName)] public bool Inherited { get; set; } @@ -48,15 +55,22 @@ public sealed class HtmxValsTagHelper(IOptions options) : Ta /// Gets or sets a value indicating whether hx-vals is appended to inherited values. /// /// - /// HTMX 4.x emits the append modifier when this property is . - /// The property does not change the generated attribute name for HTMX 1.x and 2.x. + /// The property does not change the generated attribute name for HTMX 1.9.x and 2.x. + /// + /// For HTMX 4.x, the tag helper emits the append modifier + /// when this property is . + /// /// [HtmlAttributeName(AppendAttributeName)] public bool Append { get; set; } /// - /// Gets or sets the hx-vals attribute values. + /// Gets or sets the hx-vals attribute values, which can also be set + /// with hx-val-* attributes. /// + /// + /// The default dictionary compares value names case-sensitively. + /// [HtmlAttributeName(ValuesDictionaryName, DictionaryAttributePrefix = ValuesPrefix)] public IDictionary Values { diff --git a/src/Ramstack.HtmxToolkit/wwwroot/htmx-toolkit.js b/src/Ramstack.HtmxToolkit/wwwroot/htmx-toolkit.js index 30fe5a6..20c7f58 100644 --- a/src/Ramstack.HtmxToolkit/wwwroot/htmx-toolkit.js +++ b/src/Ramstack.HtmxToolkit/wwwroot/htmx-toolkit.js @@ -2,7 +2,7 @@ document._r_htmx ||= ((document, htmx) => { const warn = message => console.warn(`ramstack.htmxtoolkit: ${message}`); const htmx_major = parseInt(htmx.version); - // The internal API of HTMX 1.x and 2.x. HTMX passes it to init when the extension is defined, + // The internal API of HTMX 1.9.x and 2.x. HTMX passes it to init when the extension is defined, // whether or not any element enables the extension with hx-ext. let api; @@ -153,7 +153,7 @@ document._r_htmx ||= ((document, htmx) => { return result; }; - // HTMX 4.x sends HX-Request-Type; HTMX 1.x and 2.x do not, so it is computed here + // HTMX 4.x sends HX-Request-Type; HTMX 1.9.x and 2.x do not, so it is computed here // the same way: "full" when the response replaces the body or is filtered with a selector. // htmx:configRequest does not see the select of htmx.ajax and HX-Location requests; // htmx:beforeRequest does, and its request is open but not yet sent. @@ -170,13 +170,13 @@ document._r_htmx ||= ((document, htmx) => { } }); - // A JSON object cannot hold repeated names, so when an event name repeats, the server + // JSON.parse keeps only the last of repeated names, so when an event name repeats, the server // sends all events of the header under "rs:event". Each event is prepared the way HTMX // prepares an HX-Trigger entry before passing it to htmx.trigger. listen("rs:event", e => { let elt = e.target; - // HTMX 1.x and 2.x wrap the array in { value }. + // HTMX 1.9.x and 2.x wrap the array in { value }. // HTMX 4.x passes it as is. for (let { key, value: detail } of e.detail.value || e.detail) { let target = elt; @@ -194,7 +194,7 @@ document._r_htmx ||= ((document, htmx) => { } else if (Object.prototype.toString.call(detail) === "[object Object]") { // HTMX 2.x applies "target" to this and all following events. - // HTMX 1.x ignores it. + // HTMX 1.9.x ignores it. if (htmx_major === 2 && detail.target !== undefined) { elt = target = detail.target; }