diff --git a/src/Htmxor/HtmxConfig.cs b/src/Htmxor/HtmxConfig.cs index c677138..9c4763d 100644 --- a/src/Htmxor/HtmxConfig.cs +++ b/src/Htmxor/HtmxConfig.cs @@ -8,6 +8,8 @@ namespace Htmxor; /// public partial record class HtmxConfig { + private SwapStyle? defaultSwapStyle; + /// /// Defaults to if this property is null. really only useful for testing /// @@ -31,7 +33,11 @@ public partial record class HtmxConfig /// Defaults to if this property is null. /// [JsonPropertyName("defaultSwapStyle")] - public SwapStyle? DefaultSwapStyle { get; set; } + public SwapStyle? DefaultSwapStyle + { + get => defaultSwapStyle; + set => defaultSwapStyle = value is SwapStyle.Default ? null : value; + } /// /// Defaults to if this property is null. diff --git a/src/Htmxor/Http/HtmxResponse.cs b/src/Htmxor/Http/HtmxResponse.cs index 74d06ee..e47c4c4 100644 --- a/src/Htmxor/Http/HtmxResponse.cs +++ b/src/Htmxor/Http/HtmxResponse.cs @@ -144,8 +144,8 @@ public HtmxResponse ReplaceUrl(string url) /// This object instance. public HtmxResponse Reswap(string modifier) { + ArgumentNullException.ThrowIfNullOrWhiteSpace(modifier); headers[HtmxResponseHeaderNames.Reswap] = modifier; - return this; } @@ -159,20 +159,16 @@ public HtmxResponse Reswap(SwapStyle swapStyle, string? modifier = null) { AssertIsHtmxRequest(); - var style = swapStyle switch + if (swapStyle is SwapStyle.Default) { - SwapStyle.InnerHTML => "innerHTML", - SwapStyle.OuterHTML => "outerHTML", - SwapStyle.BeforeBegin => "beforebegin", - SwapStyle.AfterBegin => "afterbegin", - SwapStyle.BeforeEnd => "beforeend", - SwapStyle.AfterEnd => "afterend", - SwapStyle.Delete => "delete", - SwapStyle.None => "none", - _ => throw new SwitchExpressionException(swapStyle), - }; + Reswap(modifier); + return this; + } - var value = modifier != null ? $"{style} {modifier}" : style; + var style = swapStyle.ToHtmxString(); + var value = !string.IsNullOrWhiteSpace(modifier) + ? $"{style} {modifier}" + : style; headers[HtmxResponseHeaderNames.Reswap] = value; @@ -180,16 +176,18 @@ public HtmxResponse Reswap(SwapStyle swapStyle, string? modifier = null) } /// - /// Allows you to specify how the response will be swapped. + /// Allows you to specify how the response will be swapped. /// /// /// - /// + /// This object instance. public HtmxResponse Reswap(SwapStyleBuilder swapStyle) { - var (style, modifier) = swapStyle.Build(); + var (style, modifier) = swapStyle.Build(); - return style is null ? Reswap(modifier) : Reswap((SwapStyle)style, modifier); + return style is SwapStyle.Default + ? Reswap(modifier) + : Reswap(style, modifier); } /// @@ -224,11 +222,12 @@ public HtmxResponse Reselect(string selector) /// Sets response code to stop polling /// /// + /// This object instance. public HtmxResponse StopPolling() { - context.Response.StatusCode = HtmxStatusCodes.StopPolling; + context.Response.StatusCode = HtmxStatusCodes.StopPolling; - return this; + return this; } /// @@ -314,7 +313,7 @@ public override string ToString() private void AssertIsHtmxRequest() { - if(!isHtmxRequest) + if (!isHtmxRequest) { throw new InvalidOperationException( "The active request is not an htmx request. " + diff --git a/src/Htmxor/Http/SwapStyleBuilder.cs b/src/Htmxor/Http/SwapStyleBuilder.cs index 433eb40..0b4afc3 100644 --- a/src/Htmxor/Http/SwapStyleBuilder.cs +++ b/src/Htmxor/Http/SwapStyleBuilder.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Specialized; +using System.Diagnostics; using System.Numerics; namespace Htmxor.Http; @@ -7,16 +8,17 @@ namespace Htmxor.Http; /// /// A builder class for constructing a swap style command string for HTMX responses. /// -public sealed class SwapStyleBuilder +[DebuggerDisplay("{ToString(),nq}")] +public sealed class SwapStyleBuilder { - private readonly SwapStyle? style; - private readonly OrderedDictionary modifiers = new(); + private readonly SwapStyle style; + private readonly OrderedDictionary modifiers = new(StringComparer.Ordinal); /// /// Initializes a new instance of the SwapStyleBuilder with a specified swap style. /// /// The initial swap style to be applied. - public SwapStyleBuilder(SwapStyle? style = null) + public SwapStyleBuilder(SwapStyle style = SwapStyle.Default) { this.style = style; } @@ -32,6 +34,7 @@ public SwapStyleBuilder(SwapStyle? style = null) /// /// The amount of time htmx should wait after receiving a /// response to swap the content. + /// This object instance. public SwapStyleBuilder AfterSwapDelay(TimeSpan time) { AddModifier("swap", time.TotalMilliseconds < 1000 ? $"{time.TotalMilliseconds}ms" : $"{time.TotalSeconds}s"); @@ -50,11 +53,12 @@ public SwapStyleBuilder AfterSwapDelay(TimeSpan time) /// /// The amount of time htmx should wait after receiving a /// response to swap the content. + /// This object instance. public SwapStyleBuilder AfterSettleDelay(TimeSpan time) { - AddModifier("settle", time.TotalMilliseconds < 1000 ? $"{time.TotalMilliseconds}ms" : $"{time.TotalSeconds}s"); + AddModifier("settle", time.TotalMilliseconds < 1000 ? $"{time.TotalMilliseconds}ms" : $"{time.TotalSeconds}s"); - return this; + return this; } /// @@ -67,7 +71,7 @@ public SwapStyleBuilder AfterSettleDelay(TimeSpan time) /// /// The scroll direction after the swap. /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder Scroll(ScrollDirection direction, string? selector = null) { switch (direction) @@ -92,7 +96,7 @@ public SwapStyleBuilder Scroll(ScrollDirection direction, string? selector = nul /// is present then the page is scrolled to the top of the content identified by the css selector. /// /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ScrollTop(string? selector = null) => Scroll(ScrollDirection.Top, selector); /// @@ -104,9 +108,9 @@ public SwapStyleBuilder Scroll(ScrollDirection direction, string? selector = nul /// is present then the page is scrolled to the bottom of the content identified by the css selector. /// /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ScrollBottom(string? selector = null) => Scroll(ScrollDirection.Bottom, selector); - + /// /// Determines whether to ignore the document title in the swap response by appending the modifier /// ignoreTitle:. @@ -117,7 +121,7 @@ public SwapStyleBuilder Scroll(ScrollDirection direction, string? selector = nul /// This keeps the current title unchanged regardless of the incoming swap content's title tag. /// /// Whether to ignore the title. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder IgnoreTitle(bool ignore = true) { AddModifier("ignoreTitle", ignore); @@ -132,7 +136,7 @@ public SwapStyleBuilder IgnoreTitle(bool ignore = true) /// This method ensures the title of the document is updated according to the swap response by removing any /// ignoreTitle modifiers, effectively setting ignoreTitle:false. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder IncludeTitle() => IgnoreTitle(false); /// @@ -143,7 +147,7 @@ public SwapStyleBuilder IgnoreTitle(bool ignore = true) /// will add the modifier transition:true to enable smooth transitions. /// /// Whether to show transition effects. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder Transition(bool show) { AddModifier("transition", show); @@ -157,7 +161,7 @@ public SwapStyleBuilder Transition(bool show) /// /// By calling this method, transition effects are enabled for the swap, adding the modifier transition:true. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder IncludeTransition() => Transition(true); /// @@ -166,7 +170,7 @@ public SwapStyleBuilder Transition(bool show) /// /// This method disables transition effects by adding the modifier transition:false to the swap commands. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder IgnoreTransition() => Transition(false); /// @@ -180,7 +184,7 @@ public SwapStyleBuilder Transition(bool show) /// will be focus-scroll:false /// /// Whether to scroll to the focus element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ScrollFocus(bool scroll = true) { AddModifier("focus-scroll", scroll); @@ -195,7 +199,7 @@ public SwapStyleBuilder ScrollFocus(bool scroll = true) /// /// Adds a modifier of focus-scroll:false /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder PreserveFocus() => ScrollFocus(false); /// @@ -209,7 +213,7 @@ public SwapStyleBuilder ScrollFocus(bool scroll = true) /// /// The scroll direction after swap. /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowOn(ScrollDirection direction, string? selector = null) { switch (direction) @@ -233,7 +237,7 @@ public SwapStyleBuilder ShowOn(ScrollDirection direction, string? selector = nul /// . /// /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowOnTop(string? selector = null) => ShowOn(ScrollDirection.Top, selector); /// @@ -244,18 +248,18 @@ public SwapStyleBuilder ShowOn(ScrollDirection direction, string? selector = nul /// . /// /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowOnBottom(string? selector = null) => ShowOn(ScrollDirection.Bottom, selector); /// /// Specifies that the swap should show in the window by smoothly scrolling to either the top or bottom of the window. /// - /// The direction to scroll the window after the swap. /// /// This method adds the modifier show:window:, directing the swap to display the specified /// element at the bottom of the window. /// - /// The SwapStyleBuilder instance for chaining. + /// The direction to scroll the window after the swap. + /// This object instance. public SwapStyleBuilder ShowWindow(ScrollDirection direction) { switch (direction) @@ -280,7 +284,7 @@ public SwapStyleBuilder ShowWindow(ScrollDirection direction) /// for ensuring that important content or notifications at the top of the page are immediately visible to /// the user after a swap operation. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowWindowTop() => ShowWindow(ScrollDirection.Top); /// @@ -291,7 +295,7 @@ public SwapStyleBuilder ShowWindow(ScrollDirection direction) /// at the bottom of the window following a swap by smoothly animating the scrollbar position. This positioning /// can be used for infinite scrolling, footers, or information appended at the end of the page. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowWindowBottom() => ShowWindow(ScrollDirection.Bottom); /// @@ -301,7 +305,7 @@ public SwapStyleBuilder ShowWindow(ScrollDirection direction) /// This method disables automatic scrolling by setting the modifier show:none, ensuring the page /// position remains unchanged after the swap. /// - /// The SwapStyleBuilder instance for chaining. + /// This object instance. public SwapStyleBuilder ShowNone() { AddModifier("show", "none"); @@ -309,17 +313,29 @@ public SwapStyleBuilder ShowNone() return this; } + /// + public override string ToString() + { + var (swapStyle, modifier) = Build(); + var style = swapStyle.ToHtmxString(); + var value = !string.IsNullOrWhiteSpace(modifier) + ? $"{style} {modifier}" + : style; + return value; + } + /// /// Builds the swap style command string with all specified modifiers. /// - /// A tuple containing the SwapStyle and the constructed command string. - internal (SwapStyle?, string) Build() + /// A tuple containing the and the constructed command string. + internal (SwapStyle, string) Build() { var value = string.Empty; if (modifiers.Count > 0) { - value = modifiers.Cast() + value = modifiers + .Cast() .Select(entry => $"{entry.Key}:{entry.Value}") .Aggregate((current, next) => $"{current} {next}"); } @@ -336,7 +352,7 @@ private void AddModifier(string modifier, string options) { if (modifiers.Contains(modifier)) modifiers.Remove(modifier); - + modifiers.Add(modifier, options); } @@ -347,245 +363,3 @@ private void AddModifier(string modifier, string options) /// private void AddModifier(string modifier, bool option) => AddModifier(modifier, option ? "true" : "false"); } - -/// -/// Extension methods for the SwapStyle enum to facilitate building swap style commands. -/// -public static class SwapStyleBuilderExtension -{ - /// - /// Modifies the amount of time that htmx will wait after receiving a - /// response to swap the content by including the modifier swap:. - /// - /// - /// will be converted to milliseconds if less than 1000, otherwise seconds, - /// meaning the resulting modifier will be swap:500ms for a of 500 milliseconds - /// or swap:2s for a of 2 seconds.. - /// - /// The swap style. - /// The amount of time htmx should wait after receiving a - /// response to swap the content. - public static SwapStyleBuilder AfterSwapDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSwapDelay(time); - - /// - /// Modifies the amount of time that htmx will wait between the swap - /// and the settle logic by including the modifier settle:. - /// - /// - /// will be converted to milliseconds if less than 1000, otherwise seconds, - /// meaning the resulting modifier will be settle:500ms for a of 500 milliseconds - /// or settle:2s for a of 2 seconds.. - /// - /// The swap style. - /// The amount of time htmx should wait after receiving a - /// response to swap the content. - public static SwapStyleBuilder AfterSettleDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSettleDelay(time); - - /// - /// Specifies how to set the content scrollbar position after the swap and appends the modifier scroll:. - /// - /// - /// Sets the swapped content scrollbar position after swapping immediately (without animation). For instance, using - /// will add the modifier scroll:top which sets the scrollbar position to the top of swap content after the swap. - /// If css is present then the page is scrolled to the of the content identified by the css selector. - /// - /// The swap style. - /// The scroll direction after the swap. - /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder Scroll(this SwapStyle style, ScrollDirection direction, string? selector) => new SwapStyleBuilder(style).Scroll(direction, selector); - - /// - /// Sets the content scrollbar position to the top of the swapped content after a swap. - /// - /// - /// This method adds the modifier scroll:top to the swap commands, instructing the page to scroll to - /// the top of the content after content is swapped immediately and without animation. If css - /// is present then the page is scrolled to the top of the content identified by the css selector. - /// - /// Optional CSS selector of the target element. - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ScrollTop(this SwapStyle style, string? selector) => new SwapStyleBuilder(style).ScrollTop(selector); - - /// - /// Sets the content scrollbar position to the bottom of the swapped content after a swap. - /// - /// - /// This method adds the modifier scroll:bottom to the swap commands, instructing the page to scroll to - /// the bottom of the content after content is swapped immediately and without animation. If css - /// is present then the page is scrolled to the bottom of the content identified by the css selector. - /// - /// Optional CSS selector of the target element. - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ScrollBottom(this SwapStyle style, string? selector) => new SwapStyleBuilder(style).ScrollBottom(selector); - - /// - /// Determines whether to ignore the document title in the swap response by appending the modifier - /// ignoreTitle:. - /// - /// - /// When set to true, the document title in the swap response will be ignored by adding the modifier - /// ignoreTitle:true. - /// This keeps the current title unchanged regardless of the incoming swap content's title tag. - /// - /// The swap style. - /// Whether to ignore the title. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder IgnoreTitle(this SwapStyle style, bool ignore = true) => new SwapStyleBuilder(style).IgnoreTitle(ignore); - - /// - /// Includes the document title from the swap response in the current page. - /// - /// - /// This method ensures the title of the document is updated according to the swap response by removing any - /// ignoreTitle modifiers, effectively setting ignoreTitle:false. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder IncludeTitle(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTitle(); - - /// - /// Enables or disables transition effects for the swap by appending the modifier transition:{show}. - /// - /// - /// Controls the display of transition effects during the swap. For example, setting to true - /// will add the modifier transition:true to enable smooth transitions. - /// - /// The swap style. - /// Whether to show transition effects. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder Transition(this SwapStyle style, bool show) => new SwapStyleBuilder(style).Transition(show); - - /// - /// Explicitly includes transition effects for the swap. - /// - /// - /// By calling this method, transition effects are enabled for the swap, adding the modifier transition:true. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder IncludeTransition(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTransition(); - - /// - /// Explicitly ignores transition effects for the swap. - /// - /// - /// This method disables transition effects by adding the modifier transition:false to the swap commands. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder IgnoreTransition(this SwapStyle style) => new SwapStyleBuilder(style).IgnoreTransition(); - - /// - /// Allows you to specify that htmx should scroll to the focused element when a request completes. - /// htmx preserves focus between requests for inputs that have a defined id attribute. By - /// default htmx prevents auto-scrolling to focused inputs between requests which can be - /// unwanted behavior on longer requests when the user has already scrolled away. - /// - /// - /// when true will be focus-scroll:true, otherwise when false - /// will be focus-scroll:false - /// - /// The swap style. - /// Whether to scroll to the focus element. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ScrollFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).ScrollFocus(scroll); - - /// - /// Explicitly preserves focus between requests for inputs that have a defined id attribute without - /// scrolling. - /// - /// - /// Adds a modifier of focus-scroll:false - /// - /// The swap style. - /// Whether to scroll to current focus or preserve focus - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder PreserveFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).PreserveFocus(); - - /// - /// Specifies a CSS selector to dynamically target for the swap operation, with a scroll direction after the swap. - /// - /// - /// Adds a show modifier with the specified CSS selector and scroll direction. For example, if - /// is ".item" and is , the modifier show:.item:top - /// is added. - /// - /// The swap style. - /// Optional CSS selector of the target element. - /// The scroll direction after swap. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowOn(this SwapStyle style, ScrollDirection direction, string? selector = null) => new SwapStyleBuilder(style).ShowOn(direction, selector); - - /// - /// Specifies that the swap should show the element matching the CSS selector at the top of the window. - /// - /// - /// This method adds the modifier show:{selector}:top, directing the swap to display the specified element at the top of the window. - /// - /// The swap style. - /// Optional CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowOnTop(this SwapStyle style, string? selector = null) => new SwapStyleBuilder(style).ShowOnTop(selector); - - /// - /// Specifies that the swap should show the element matching the CSS selector at the bottom of the window. - /// - /// - /// This method adds the modifier show:{selector}:bottom, directing the swap to display the specified element at the bottom of the window. - /// - /// The swap style. - /// The CSS selector of the target element. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowOnBottom(this SwapStyle style, string? selector = null) => new SwapStyleBuilder(style).ShowOnBottom(selector); - - /// - /// Specifies that the swap should show in the window by smoothly scrolling to either the top or bottom of the window. - /// - /// The direction to scroll the window after the swap. - /// - /// This method adds the modifier show:window:, directing the swap to display the specified - /// element at the bottom of the window. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowWindow(this SwapStyle style, ScrollDirection direction) => new SwapStyleBuilder(style).ShowWindow(direction); - - /// - /// Specifies that the swap should smoothly scroll to the top of the window. - /// - /// - /// This method adds the modifier show:window:top, instructing the content to be displayed - /// at the top of the window following a swap by smoothly animating the scrollbar position. This can be useful - /// for ensuring that important content or notifications at the top of the page are immediately visible to - /// the user after a swap operation. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowWindowTop(this SwapStyle style) => new SwapStyleBuilder(style).ShowWindowTop(); - - /// - /// Specifies that the swap should smoothly scroll to the bottom of the window. - /// - /// - /// This method adds the modifier show:window:bottom, instructing the content to be displayed - /// at the bottom of the window following a swap by smoothly animating the scrollbar position. This positioning - /// can be used for infinite scrolling, footers, or information appended at the end of the page. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowWindowBottom(this SwapStyle style) => new SwapStyleBuilder(style).ShowWindowBottom(); - - /// - /// Turns off scrolling after swap. - /// - /// - /// This method disables automatic scrolling by setting the modifier show:none, ensuring the page - /// position remains unchanged after the swap. - /// - /// The swap style. - /// The SwapStyleBuilder instance for chaining. - public static SwapStyleBuilder ShowNone(this SwapStyle style) => new SwapStyleBuilder(style).ShowNone(); -} \ No newline at end of file diff --git a/src/Htmxor/Http/SwapStyleBuilderExtension.cs b/src/Htmxor/Http/SwapStyleBuilderExtension.cs new file mode 100644 index 0000000..79ffa37 --- /dev/null +++ b/src/Htmxor/Http/SwapStyleBuilderExtension.cs @@ -0,0 +1,245 @@ +namespace Htmxor.Http; + +/// +/// Extension methods for the SwapStyle enum to facilitate building swap style commands. +/// +public static class SwapStyleBuilderExtension +{ + /// + /// Modifies the amount of time that htmx will wait after receiving a + /// response to swap the content by including the modifier swap:. + /// + /// + /// will be converted to milliseconds if less than 1000, otherwise seconds, + /// meaning the resulting modifier will be swap:500ms for a of 500 milliseconds + /// or swap:2s for a of 2 seconds.. + /// + /// The swap style. + /// The amount of time htmx should wait after receiving a + /// response to swap the content. + /// A object instance. + public static SwapStyleBuilder AfterSwapDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSwapDelay(time); + + /// + /// Modifies the amount of time that htmx will wait between the swap + /// and the settle logic by including the modifier settle:. + /// + /// + /// will be converted to milliseconds if less than 1000, otherwise seconds, + /// meaning the resulting modifier will be settle:500ms for a of 500 milliseconds + /// or settle:2s for a of 2 seconds.. + /// + /// The swap style. + /// The amount of time htmx should wait after receiving a + /// response to swap the content. + /// A object instance. + public static SwapStyleBuilder AfterSettleDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSettleDelay(time); + + /// + /// Specifies how to set the content scrollbar position after the swap and appends the modifier scroll:. + /// + /// + /// Sets the swapped content scrollbar position after swapping immediately (without animation). For instance, using + /// will add the modifier scroll:top which sets the scrollbar position to the top of swap content after the swap. + /// If css is present then the page is scrolled to the of the content identified by the css selector. + /// + /// The swap style. + /// The scroll direction after the swap. + /// Optional CSS selector of the target element. + /// A object instance. + public static SwapStyleBuilder Scroll(this SwapStyle style, ScrollDirection direction, string? selector) => new SwapStyleBuilder(style).Scroll(direction, selector); + + /// + /// Sets the content scrollbar position to the top of the swapped content after a swap. + /// + /// + /// This method adds the modifier scroll:top to the swap commands, instructing the page to scroll to + /// the top of the content after content is swapped immediately and without animation. If css + /// is present then the page is scrolled to the top of the content identified by the css selector. + /// + /// Optional CSS selector of the target element. + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ScrollTop(this SwapStyle style, string? selector) => new SwapStyleBuilder(style).ScrollTop(selector); + + /// + /// Sets the content scrollbar position to the bottom of the swapped content after a swap. + /// + /// + /// This method adds the modifier scroll:bottom to the swap commands, instructing the page to scroll to + /// the bottom of the content after content is swapped immediately and without animation. If css + /// is present then the page is scrolled to the bottom of the content identified by the css selector. + /// + /// Optional CSS selector of the target element. + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ScrollBottom(this SwapStyle style, string? selector) => new SwapStyleBuilder(style).ScrollBottom(selector); + + /// + /// Determines whether to ignore the document title in the swap response by appending the modifier + /// ignoreTitle:. + /// + /// + /// When set to true, the document title in the swap response will be ignored by adding the modifier + /// ignoreTitle:true. + /// This keeps the current title unchanged regardless of the incoming swap content's title tag. + /// + /// The swap style. + /// Whether to ignore the title. + /// A object instance. + public static SwapStyleBuilder IgnoreTitle(this SwapStyle style, bool ignore = true) => new SwapStyleBuilder(style).IgnoreTitle(ignore); + + /// + /// Includes the document title from the swap response in the current page. + /// + /// + /// This method ensures the title of the document is updated according to the swap response by removing any + /// ignoreTitle modifiers, effectively setting ignoreTitle:false. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder IncludeTitle(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTitle(); + + /// + /// Enables or disables transition effects for the swap by appending the modifier transition:{show}. + /// + /// + /// Controls the display of transition effects during the swap. For example, setting to true + /// will add the modifier transition:true to enable smooth transitions. + /// + /// The swap style. + /// Whether to show transition effects. + /// A object instance. + public static SwapStyleBuilder Transition(this SwapStyle style, bool show) => new SwapStyleBuilder(style).Transition(show); + + /// + /// Explicitly includes transition effects for the swap. + /// + /// + /// By calling this method, transition effects are enabled for the swap, adding the modifier transition:true. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder IncludeTransition(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTransition(); + + /// + /// Explicitly ignores transition effects for the swap. + /// + /// + /// This method disables transition effects by adding the modifier transition:false to the swap commands. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder IgnoreTransition(this SwapStyle style) => new SwapStyleBuilder(style).IgnoreTransition(); + + /// + /// Allows you to specify that htmx should scroll to the focused element when a request completes. + /// htmx preserves focus between requests for inputs that have a defined id attribute. By + /// default htmx prevents auto-scrolling to focused inputs between requests which can be + /// unwanted behavior on longer requests when the user has already scrolled away. + /// + /// + /// when true will be focus-scroll:true, otherwise when false + /// will be focus-scroll:false + /// + /// The swap style. + /// Whether to scroll to the focus element. + /// A object instance. + public static SwapStyleBuilder ScrollFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).ScrollFocus(scroll); + + /// + /// Explicitly preserves focus between requests for inputs that have a defined id attribute without + /// scrolling. + /// + /// + /// Adds a modifier of focus-scroll:false + /// + /// The swap style. + /// Whether to scroll to current focus or preserve focus + /// A object instance. + public static SwapStyleBuilder PreserveFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).PreserveFocus(); + + /// + /// Specifies a CSS selector to dynamically target for the swap operation, with a scroll direction after the swap. + /// + /// + /// Adds a show modifier with the specified CSS selector and scroll direction. For example, if + /// is ".item" and is , the modifier show:.item:top + /// is added. + /// + /// The swap style. + /// Optional CSS selector of the target element. + /// The scroll direction after swap. + /// A object instance. + public static SwapStyleBuilder ShowOn(this SwapStyle style, ScrollDirection direction, string? selector = null) => new SwapStyleBuilder(style).ShowOn(direction, selector); + + /// + /// Specifies that the swap should show the element matching the CSS selector at the top of the window. + /// + /// + /// This method adds the modifier show:{selector}:top, directing the swap to display the specified element at the top of the window. + /// + /// The swap style. + /// Optional CSS selector of the target element. + /// A object instance. + public static SwapStyleBuilder ShowOnTop(this SwapStyle style, string? selector = null) => new SwapStyleBuilder(style).ShowOnTop(selector); + + /// + /// Specifies that the swap should show the element matching the CSS selector at the bottom of the window. + /// + /// + /// This method adds the modifier show:{selector}:bottom, directing the swap to display the specified element at the bottom of the window. + /// + /// The swap style. + /// The CSS selector of the target element. + /// A object instance. + public static SwapStyleBuilder ShowOnBottom(this SwapStyle style, string? selector = null) => new SwapStyleBuilder(style).ShowOnBottom(selector); + + /// + /// Specifies that the swap should show in the window by smoothly scrolling to either the top or bottom of the window. + /// + /// The direction to scroll the window after the swap. + /// + /// This method adds the modifier show:window:, directing the swap to display the specified + /// element at the bottom of the window. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ShowWindow(this SwapStyle style, ScrollDirection direction) => new SwapStyleBuilder(style).ShowWindow(direction); + + /// + /// Specifies that the swap should smoothly scroll to the top of the window. + /// + /// + /// This method adds the modifier show:window:top, instructing the content to be displayed + /// at the top of the window following a swap by smoothly animating the scrollbar position. This can be useful + /// for ensuring that important content or notifications at the top of the page are immediately visible to + /// the user after a swap operation. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ShowWindowTop(this SwapStyle style) => new SwapStyleBuilder(style).ShowWindowTop(); + + /// + /// Specifies that the swap should smoothly scroll to the bottom of the window. + /// + /// + /// This method adds the modifier show:window:bottom, instructing the content to be displayed + /// at the bottom of the window following a swap by smoothly animating the scrollbar position. This positioning + /// can be used for infinite scrolling, footers, or information appended at the end of the page. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ShowWindowBottom(this SwapStyle style) => new SwapStyleBuilder(style).ShowWindowBottom(); + + /// + /// Turns off scrolling after swap. + /// + /// + /// This method disables automatic scrolling by setting the modifier show:none, ensuring the page + /// position remains unchanged after the swap. + /// + /// The swap style. + /// A object instance. + public static SwapStyleBuilder ShowNone(this SwapStyle style) => new SwapStyleBuilder(style).ShowNone(); +} diff --git a/src/Htmxor/Http/SwapStyleExtensions.cs b/src/Htmxor/Http/SwapStyleExtensions.cs new file mode 100644 index 0000000..21f9e68 --- /dev/null +++ b/src/Htmxor/Http/SwapStyleExtensions.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; + +namespace Htmxor.Http; + +public static class SwapStyleExtensions +{ + /// + /// Converts the to their string version expected by htmx. + /// + /// The style to convert. + public static string ToHtmxString(this SwapStyle swapStyle) + { + var style = swapStyle switch + { + SwapStyle.InnerHTML => "innerHTML", + SwapStyle.OuterHTML => "outerHTML", + SwapStyle.BeforeBegin => "beforebegin", + SwapStyle.AfterBegin => "afterbegin", + SwapStyle.BeforeEnd => "beforeend", + SwapStyle.AfterEnd => "afterend", + SwapStyle.Delete => "delete", + SwapStyle.None => "none", + SwapStyle.Default => "", + _ => throw new SwitchExpressionException(swapStyle), + }; + + return style; + } +} \ No newline at end of file diff --git a/src/Htmxor/SwapStyle.cs b/src/Htmxor/SwapStyle.cs index 3ea8f5a..109dc2f 100644 --- a/src/Htmxor/SwapStyle.cs +++ b/src/Htmxor/SwapStyle.cs @@ -5,6 +5,12 @@ /// public enum SwapStyle { + /// + /// Default style is what is specified in for the application + /// or htmx's default, which is . + /// + Default, + /// /// Replace the inner html of the target element. /// diff --git a/test/Htmxor.Tests/Configuration/HtmxHeadOutletTest.cs b/test/Htmxor.Tests/Configuration/HtmxHeadOutletTest.cs index c33846d..1a71d8e 100644 --- a/test/Htmxor.Tests/Configuration/HtmxHeadOutletTest.cs +++ b/test/Htmxor.Tests/Configuration/HtmxHeadOutletTest.cs @@ -90,4 +90,15 @@ public void HtmxConfig_serializer() } """); } + + [Fact] + public void HtmxConfig_default_swap_style_serializer() + { + var config = new HtmxConfig + { + DefaultSwapStyle = SwapStyle.Default, + }; + + config.DefaultSwapStyle.Should().BeNull(); + } } diff --git a/test/Htmxor.Tests/Http/SwapStyleBuilderTests.cs b/test/Htmxor.Tests/Http/SwapStyleBuilderTests.cs index 6f38856..96d6aea 100644 --- a/test/Htmxor.Tests/Http/SwapStyleBuilderTests.cs +++ b/test/Htmxor.Tests/Http/SwapStyleBuilderTests.cs @@ -191,11 +191,11 @@ public void SwapStyleBuilder_ShowWindow_BottomDirection_AddsCorrectModifier() public void SwapStyleBuilder_NullSwapStyle_ReturnsNullStyle() { // Arrange & Act - var builder = new SwapStyleBuilder(null); + var builder = new SwapStyleBuilder(); var (style, _) = builder.Build(); // Assert - style.Should().BeNull(); + style.Should().Be(SwapStyle.Default); } [Fact]