Skip to content

Commit 76509fb

Browse files
committed
refactor: wip enhance SwapStyleBuilder functionality
Significant enhancements have been made to the SwapStyleBuilder class. The class has been sealed and several new methods have been added to provide more granular control over swap style commands. These include methods for controlling scroll direction, transition effects, title inclusion, and delay times for both swap and settle operations. Existing methods have also been updated with more detailed comments and improved parameter naming for better clarity.
1 parent aaa5807 commit 76509fb

File tree

1 file changed

+180
-22
lines changed

1 file changed

+180
-22
lines changed

src/Htmxor/Http/SwapStyleBuilder.cs

+180-22
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Htmxor.Http;
66
/// <summary>
77
/// A builder class for constructing a swap style command string for HTMX responses.
88
/// </summary>
9-
public class SwapStyleBuilder
9+
public sealed class SwapStyleBuilder
1010
{
1111
private readonly SwapStyle? style;
1212
private readonly OrderedDictionary modifiers = new();
@@ -21,21 +21,50 @@ public SwapStyleBuilder(SwapStyle? style = null)
2121
}
2222

2323
/// <summary>
24-
/// Adds a delay to the swap operation.
24+
/// Modifies the amount of time that htmx will wait after receiving a
25+
/// response to swap the content by including the modifier <c>swap:<paramref name="time"/></c>.
2526
/// </summary>
26-
/// <param name="span">The time span to delay the swap.</param>
27-
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
28-
public SwapStyleBuilder After(TimeSpan span)
27+
/// <remarks>
28+
/// <paramref name="time"/> will be converted to milliseconds if less than 1000, otherwise seconds,
29+
/// meaning the resulting modifier will be <c>swap:500ms</c> for a <see cref="TimeSpan"/> of 500 milliseconds
30+
/// or <c>swap:2s</c> for a <see cref="TimeSpan"/> of 2 seconds..
31+
/// </remarks>
32+
/// <param name="style">The swap style.</param>
33+
/// <param name="time">The amount of time htmx should wait after receiving a
34+
/// response to swap the content.</param>
35+
public SwapStyleBuilder AfterSwapDelay(TimeSpan time)
2936
{
30-
AddModifier("swap", span.TotalMilliseconds < 1000 ? $"{span.TotalMilliseconds}ms" : $"{span.TotalSeconds}s");
37+
AddModifier("swap", time.TotalMilliseconds < 1000 ? $"{time.TotalMilliseconds}ms" : $"{time.TotalSeconds}s");
3138

3239
return this;
3340
}
3441

3542
/// <summary>
36-
/// Specifies the direction to scroll the page after the swap.
43+
/// Modifies the amount of time that htmx will wait between the swap
44+
/// and the settle logic by including the modifier <c>settle:<paramref name="time"/></c>.
45+
/// </summary>
46+
/// <remarks>
47+
/// <paramref name="time"/> will be converted to milliseconds if less than 1000, otherwise seconds,
48+
/// meaning the resulting modifier will be <c>swap:500ms</c> for a <see cref="TimeSpan"/> of 500 milliseconds
49+
/// or <c>swap:2s</c> for a <see cref="TimeSpan"/> of 2 seconds..
50+
/// </remarks>
51+
/// <param name="time">The amount of time htmx should wait after receiving a
52+
/// response to swap the content.</param>
53+
public SwapStyleBuilder AfterSettleDelay(TimeSpan time)
54+
{
55+
AddModifier("settle", time.TotalMilliseconds < 1000 ? $"{time.TotalMilliseconds}ms" : $"{time.TotalSeconds}s");
56+
57+
return this;
58+
}
59+
60+
/// <summary>
61+
/// Specifies the direction to scroll the page after the swap and appends the modifier <c>scroll:{direction}</c>.
3762
/// </summary>
38-
/// <param name="direction">The scroll direction.</param>
63+
/// <remarks>
64+
/// Sets the scroll direction on the page after swapping. For instance, using <see cref="ScrollDirection.Top"/>
65+
/// will add the modifier <c>scroll:top</c> which instructs the page to scroll to the top after the swap.
66+
/// </remarks>
67+
/// <param name="direction">The scroll direction after the swap.</param>
3968
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
4069
public SwapStyleBuilder Scroll(ScrollDirection direction)
4170
{
@@ -53,20 +82,60 @@ public SwapStyleBuilder Scroll(ScrollDirection direction)
5382
}
5483

5584
/// <summary>
56-
/// Determines whether to ignore the document title in the swap response.
85+
/// Automatically scrolls to the top of the page after a swap.
86+
/// </summary>
87+
/// <remarks>
88+
/// This method adds the modifier <c>scroll:top</c> to the swap commands, instructing the page to scroll to
89+
/// the top after content is swapped.
90+
/// </remarks>
91+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
92+
public SwapStyleBuilder ScrollTop() => Scroll(ScrollDirection.Top);
93+
94+
/// <summary>
95+
/// Automatically scrolls to the bottom of the page after a swap.
96+
/// </summary>
97+
/// <remarks>
98+
/// This method adds the modifier <c>scroll:bottom</c> to the swap commands, instructing the page to scroll to
99+
/// the bottom after content is swapped.
100+
/// </remarks>
101+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
102+
public SwapStyleBuilder ScrollBottom() => Scroll(ScrollDirection.Bottom);
103+
104+
/// <summary>
105+
/// Determines whether to ignore the document title in the swap response by appending the modifier
106+
/// <c>ignoreTitle:{ignore}</c>.
57107
/// </summary>
108+
/// <remarks>
109+
/// When set to true, the document title in the swap response will be ignored by adding the modifier
110+
/// <c>ignoreTitle:true</c>.
111+
/// This keeps the current title unchanged regardless of the incoming swap content's title tag.
112+
/// </remarks>
58113
/// <param name="ignore">Whether to ignore the title.</param>
59114
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
60-
public SwapStyleBuilder IgnoreTitle(bool ignore)
115+
public SwapStyleBuilder IgnoreTitle(bool ignore = true)
61116
{
62117
AddModifier("ignoreTitle", ignore);
63118

64119
return this;
65120
}
66121

67122
/// <summary>
68-
/// Enables or disables transition effects for the swap.
123+
/// Includes the document title from the swap response in the current page.
124+
/// </summary>
125+
/// <remarks>
126+
/// This method ensures the title of the document is updated according to the swap response by removing any
127+
/// ignoreTitle modifiers, effectively setting <c>ignoreTitle:false</c>.
128+
/// </remarks>
129+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
130+
public SwapStyleBuilder IncludeTitle() => IgnoreTitle(false);
131+
132+
/// <summary>
133+
/// Enables or disables transition effects for the swap by appending the modifier <c>transition:{show}</c>.
69134
/// </summary>
135+
/// <remarks>
136+
/// Controls the display of transition effects during the swap. For example, setting <paramref name="show"/> to true
137+
/// will add the modifier <c>transition:true</c> to enable smooth transitions.
138+
/// </remarks>
70139
/// <param name="show">Whether to show transition effects.</param>
71140
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
72141
public SwapStyleBuilder Transition(bool show)
@@ -77,20 +146,49 @@ public SwapStyleBuilder Transition(bool show)
77146
}
78147

79148
/// <summary>
80-
/// Sets whether to focus and scroll to the swapped content.
149+
/// Explicitly includes transition effects for the swap.
150+
/// </summary>
151+
/// <remarks>
152+
/// By calling this method, transition effects are enabled for the swap, adding the modifier <c>transition:true</c>.
153+
/// </remarks>
154+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
155+
public SwapStyleBuilder IncludeTransition() => Transition(true);
156+
157+
/// <summary>
158+
/// Explicitly ignores transition effects for the swap.
81159
/// </summary>
160+
/// <remarks>
161+
/// This method disables transition effects by adding the modifier <c>transition:false</c> to the swap commands.
162+
/// </remarks>
163+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
164+
public SwapStyleBuilder IgnoreTransition() => Transition(false);
165+
166+
/// <summary>
167+
/// htmx preserves focus between requests for inputs that have a defined id attribute. By
168+
/// default htmx prevents auto-scrolling to focused inputs between requests which can be
169+
/// unwanted behavior on longer requests when the user has already scrolled away.
170+
/// </summary>
171+
/// <remarks>
172+
/// <paramref name="scroll"/> when true will be <c>focus-scroll:true</c>, otherwise when false
173+
/// will be <c>focus-scroll:false</c>
174+
/// </remarks>
82175
/// <param name="scroll">Whether to scroll to the focus element.</param>
83176
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
84-
public SwapStyleBuilder FocusScroll(bool scroll)
177+
public SwapStyleBuilder ScrollFocus(bool scroll = true)
85178
{
86179
AddModifier("focus-scroll", scroll);
87180

88181
return this;
89182
}
90183

184+
public SwapStyleBuilder PreserveFocus() => ScrollFocus(false);
185+
91186
/// <summary>
92-
/// Specifies a CSS selector to dynamically target for the swap operation.
187+
/// Specifies a CSS selector to dynamically target for the swap operation, with an optional scroll direction after the swap.
93188
/// </summary>
189+
/// <remarks>
190+
/// Adds a show modifier with the specified CSS selector and scroll direction. For example, if <paramref name="selector"/> is ".item" and <paramref name="direction"/> is <see cref="ScrollDirection.Top"/>, the modifier <c>show:.item:top</c> is added.
191+
/// </remarks>
94192
/// <param name="selector">The CSS selector of the target element.</param>
95193
/// <param name="direction">The scroll direction after swap.</param>
96194
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
@@ -109,12 +207,35 @@ public SwapStyleBuilder ShowOn(string selector, ScrollDirection direction)
109207
return this;
110208
}
111209

210+
/// <summary>
211+
/// Specifies that the swap should show the element matching the CSS selector at the top of the window.
212+
/// </summary>
213+
/// <remarks>
214+
/// This method adds the modifier <c>show:{selector}:top</c>, directing the swap to display the specified element at the top of the window.
215+
/// </remarks>
216+
/// <param name="selector">The CSS selector of the target element.</param>
217+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
218+
public SwapStyleBuilder ShowOnTop(string selector) => ShowOn(selector, ScrollDirection.Top);
219+
220+
/// <summary>
221+
/// Specifies that the swap should show the element matching the CSS selector at the bottom of the window.
222+
/// </summary>
223+
/// <remarks>
224+
/// This method adds the modifier <c>show:{selector}:bottom</c>, directing the swap to display the specified element at the bottom of the window.
225+
/// </remarks>
226+
/// <param name="selector">The CSS selector of the target element.</param>
227+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
228+
public SwapStyleBuilder ShowOnBottom(string selector) => ShowOn(selector, ScrollDirection.Bottom);
229+
112230
/// <summary>
113231
/// Specifies that the swap should show in the window, with an optional scroll direction.
114232
/// </summary>
115233
/// <param name="direction">The direction to scroll the window after the swap.</param>
234+
/// <remarks>
235+
/// This method adds the modifier <c>show:window:{direction}</c>, directing the swap to display the specified element at the bottom of the window.
236+
/// </remarks>
116237
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
117-
public SwapStyleBuilder ShowWindow(ScrollDirection direction)
238+
public SwapStyleBuilder ShowOnWindow(ScrollDirection direction)
118239
{
119240
switch (direction)
120241
{
@@ -130,8 +251,34 @@ public SwapStyleBuilder ShowWindow(ScrollDirection direction)
130251
}
131252

132253
/// <summary>
133-
/// Turns off scrolling after swap
254+
/// Specifies that the swap should show content at the top of the window.
255+
/// </summary>
256+
/// <remarks>
257+
/// This method adds the modifier <c>show:window:top</c>, instructing the content to be displayed
258+
/// at the top of the window following a swap. This can be useful for ensuring that important content or
259+
/// notifications are immediately visible to the user after a swap operation.
260+
/// </remarks>
261+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
262+
public SwapStyleBuilder ShowOnWindowTop() => ShowOnWindow(ScrollDirection.Top);
263+
264+
/// <summary>
265+
/// Specifies that the swap should show content at the bottom of the window.
134266
/// </summary>
267+
/// <remarks>
268+
/// This method adds the modifier <c>show:window:bottom</c>, instructing the content to be displayed
269+
/// at the bottom of the window following a swap. This positioning can be used for infinite scrolling, footers or
270+
/// lower-priority information that should not immediately distract from other content.
271+
/// </remarks>
272+
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
273+
public SwapStyleBuilder ShowOnWindowBottom() => ShowOnWindow(ScrollDirection.Bottom);
274+
275+
/// <summary>
276+
/// Turns off scrolling after swap.
277+
/// </summary>
278+
/// <remarks>
279+
/// This method disables automatic scrolling by setting the modifier <c>show:none</c>, ensuring the page
280+
/// position remains unchanged after the swap.
281+
/// </remarks>
135282
/// <returns>The SwapStyleBuilder instance for chaining.</returns>
136283
public SwapStyleBuilder ShowNone()
137284
{
@@ -167,11 +314,11 @@ private void AddModifier(string modifier, string options)
167314
{
168315
if (modifiers.Contains(modifier))
169316
modifiers.Remove(modifier);
170-
317+
171318
modifiers.Add(modifier, options);
172319
}
173320

174-
private void AddModifier(string modifier, bool option) => AddModifier(modifier, option.ToString().ToLowerInvariant());
321+
private void AddModifier(string modifier, bool option) => AddModifier(modifier, option ? "true" : "false");
175322
}
176323

177324
/// <summary>
@@ -182,12 +329,23 @@ public static class SwapStyleBuilderExtension
182329
// Each method below returns a SwapStyleBuilder instance initialized with the respective SwapStyle
183330
// and applies the specified modifier to it. This allows for fluent configuration of swap style commands.
184331

185-
public static SwapStyleBuilder After(this SwapStyle style, TimeSpan span) => new SwapStyleBuilder(style).After(span);
332+
public static SwapStyleBuilder AfterSwapDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSwapDelay(time);
333+
public static SwapStyleBuilder AfterSettleDelay(this SwapStyle style, TimeSpan time) => new SwapStyleBuilder(style).AfterSettleDelay(time);
186334
public static SwapStyleBuilder Scroll(this SwapStyle style, ScrollDirection direction) => new SwapStyleBuilder(style).Scroll(direction);
187-
public static SwapStyleBuilder IgnoreTitle(this SwapStyle style, bool ignore) => new SwapStyleBuilder(style).IgnoreTitle(ignore);
335+
public static SwapStyleBuilder ScrollTop(this SwapStyle style) => new SwapStyleBuilder(style).ScrollTop();
336+
public static SwapStyleBuilder ScrollBottom(this SwapStyle style) => new SwapStyleBuilder(style).ScrollBottom();
337+
public static SwapStyleBuilder IgnoreTitle(this SwapStyle style, bool ignore = true) => new SwapStyleBuilder(style).IgnoreTitle(ignore);
338+
public static SwapStyleBuilder IncludeTitle(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTitle();
188339
public static SwapStyleBuilder Transition(this SwapStyle style, bool show) => new SwapStyleBuilder(style).Transition(show);
189-
public static SwapStyleBuilder FocusScroll(this SwapStyle style, bool scroll) => new SwapStyleBuilder(style).FocusScroll(scroll);
340+
public static SwapStyleBuilder IncludeTransition(this SwapStyle style) => new SwapStyleBuilder(style).IncludeTransition();
341+
public static SwapStyleBuilder IgnoreTransition(this SwapStyle style) => new SwapStyleBuilder(style).IgnoreTransition();
342+
public static SwapStyleBuilder ScrollFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).ScrollFocus(scroll);
343+
public static SwapStyleBuilder PreserveFocus(this SwapStyle style, bool scroll = true) => new SwapStyleBuilder(style).PreserveFocus();
190344
public static SwapStyleBuilder ShowOn(this SwapStyle style, string selector, ScrollDirection direction) => new SwapStyleBuilder(style).ShowOn(selector, direction);
191-
public static SwapStyleBuilder ShowWindow(this SwapStyle style, ScrollDirection direction) => new SwapStyleBuilder(style).ShowWindow(direction);
345+
public static SwapStyleBuilder ShowOnTop(this SwapStyle style, string selector) => new SwapStyleBuilder(style).ShowOnTop(selector);
346+
public static SwapStyleBuilder ShowOnBottom(this SwapStyle style, string selector) => new SwapStyleBuilder(style).ShowOnBottom(selector);
347+
public static SwapStyleBuilder ShowOnWindow(this SwapStyle style, ScrollDirection direction) => new SwapStyleBuilder(style).ShowOnWindow(direction);
348+
public static SwapStyleBuilder ShowOnWindowTop(this SwapStyle style) => new SwapStyleBuilder(style).ShowOnWindowTop();
349+
public static SwapStyleBuilder ShowOnWindowBottom(this SwapStyle style) => new SwapStyleBuilder(style).ShowOnWindowBottom();
192350
public static SwapStyleBuilder ShowNone(this SwapStyle style) => new SwapStyleBuilder(style).ShowNone();
193351
}

0 commit comments

Comments
 (0)