Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{yml,props,targets,csproj}]
[*.{cshtml,css,yml,props,targets,csproj}]
indent_size = 2

[*.min.js]
insert_final_newline = false

[*.{tt,ttinclude}]
end_of_line = crlf

[*.{cs,cshtml}]
resharper_csharp_max_line_length = 160

Expand Down
1 change: 1 addition & 0 deletions Ramstack.HtmxToolkit.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
<File Path="rollup.config.js" />
</Folder>
<Project Path="src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj" />
<Project Path="samples/Ramstack.HtmxToolkit.Demo/Ramstack.HtmxToolkit.Demo.csproj" />
</Solution>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@page
@model AntiforgeryModel
@{
ViewData["Title"] = "Antiforgery";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 10</p>
<h1>Antiforgery forms</h1>
<p>Submit HTMX forms while the toolkit automatically supplies the verification token</p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2><code>hx-post</code> with antiforgery</h2>
<p>The layout configures token inclusion and the toolkit script attaches it to every HTMX request.</p>
</div>

<form class="demo-form"
hx-post
hx-page="/Examples/Antiforgery"
hx-page-handler="FormSubmit"
hx-target="#form-result">
<label>
Name
<input type="text" name="name" placeholder="John Doe"/>
</label>

<label>
Email
<input type="email" name="email" placeholder="john@example.com"/>
</label>

<label>
Message
<textarea name="message" rows="4" placeholder="Your message"></textarea>
</label>

<button type="submit">Submit form</button>
</form>

<div id="form-result" class="result">
Submit the form to see the posted values.
</div>
</section>
</article>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

[ValidateAntiForgeryToken]
public class AntiforgeryModel : PageModel
{
public IActionResult OnPostFormSubmit(ContactForm form) =>
Content($"""
<b>Form submitted successfully!</b><br/>
<b>Name:</b> {form.Name}<br/>
<b>Email:</b> {form.Email}<br/>
<b>Message:</b> {form.Message}
""");

public class ContactForm
{
public string? Name { get; set; }
public string? Email { get; set; }
public string? Message { get; set; }
}
}
34 changes: 34 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Boosted.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@page
@model BoostedModel
@{
ViewData["Title"] = "Boosted requests";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 08</p>
<h1>Detect boosted navigation</h1>
<p>Identify a request initiated by an <code>hx-boost</code> link</p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2><code>IsHtmxBoosted()</code></h2>
<p>The link is progressively enhanced and its response is placed into the result panel.</p>
</div>

<div class="demo-actions">
<a class="button"
asp-page="/Examples/Boosted"
asp-page-handler="BoostedCheck"
hx-boost="true"
hx-target="#boosted-result">
Run boosted request
</a>
</div>

<div id="boosted-result" class="result">
The handler has not been called.
</div>
</section>
</article>
13 changes: 13 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Boosted.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

public class BoostedModel : PageModel
{
public IActionResult OnGetBoostedCheck() =>
Content(
Request.IsHtmxBoosted()
? "Boosted HTMX request detected!"
: "Non-boosted HTMX request.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@page
@model FluentResponseModel
@{
ViewData["Title"] = "Fluent response API";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 04</p>
<h1>Fluent response API</h1>
<p>Set HTMX response headers from the page handler with <code>Response.Htmx()</code></p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2>Retarget and reswap</h2>
<p>The response changes the target or swap mode chosen by the triggering element.</p>
</div>

<div class="demo-actions">
<button
hx-get
hx-page="/Examples/FluentResponse"
hx-page-handler="Reswap"
hx-target="#fluent-result"
hx-swap="outerHTML">
Reswap after begin
</button>
<button
class="button-secondary"
hx-get hx-page="/Examples/FluentResponse"
hx-page-handler="Retarget"
hx-target="#ignored-target">
Retarget response
</button>

<span id="ignored-target" hidden></span>
</div>

<div id="fluent-result" class="result">
Try either response directive.
</div>
</section>
</article>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

public class FluentResponseModel : PageModel
{
public IActionResult OnGetReswap()
{
Response.Htmx(h => h.Reswap(HtmxSwap.AfterBegin));
return Content("<p>Prepended to top with AfterBegin swap!</p>");
}

public IActionResult OnGetRetarget()
{
Response.Htmx(h => h.Retarget("#fluent-result"));
return Content("<b>Retargeted to #fluent-result!</b>");
}
}
33 changes: 33 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Headers.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@page
@model HeadersModel
@{
ViewData["Title"] = "Request headers";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 03</p>
<h1>Request headers</h1>
<p>Define custom HTMX headers declaratively in Razor</p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2><code>hx-header-*</code></h2>
<p>The handler reads the header and triggers two client events.</p>
</div>
<div class="demo-actions">
<button
hx-get
hx-page="/Examples/Headers"
hx-page-handler="CustomHeader"
hx-header-Custom-Header="MyValue"
hx-target="#header-result">
Send custom header
</button>
</div>

<div id="header-result" class="result">No request has been sent.</div>
<div id="event-log" class="event-log"></div>
</section>
</article>
19 changes: 19 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Headers.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

public class HeadersModel : PageModel
{
public IActionResult OnGetCustomHeader()
{
Response.Htmx(h => h
.TriggerEvent("customEvent", new { message = "#1 Fired from server!" })
.TriggerEvent("logEvent", new { message = $"Custom-Header = {Request.Headers["Custom-Header"]}" }));

return Content("<b>Custom headers sent!</b>")
.Htmx(h => h
.TriggerEvent("customEvent", new { message = "#2 Fired from server!" })
.TriggerEvent("customEvent", new { message = "#3 Fired from server!" }));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@page
@model HtmxRequestModel
@{
ViewData["Title"] = "HTMX requests";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 06</p>
<h1>Detect HTMX requests</h1>
<p>Return an appropriate representation when the handler is called asynchronously or directly</p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2><code>Request.IsHtmxRequest()</code></h2>
<p>Compare a normal browser navigation with an HTMX request to the same handler.</p>
</div>

<div class="demo-actions">
<button
hx-get
hx-page="/Examples/HtmxRequest"
hx-page-handler="PartialOrFull"
hx-target="#partial-result">
Fetch via HTMX
</button>
<a
class="button button-secondary"
asp-page="/Examples/HtmxRequest"
asp-page-handler="PartialOrFull"
target="_blank">
Open full request
</a>
</div>

<div id="partial-result" class="result">
Choose a request type.
</div>
</section>
</article>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

public class HtmxRequestModel : PageModel
{
public IActionResult OnGetPartialOrFull() =>
Content(
Request.IsHtmxRequest()
? "Partial response (HTMX request detected via <code>IsHtmxRequest()</code>)"
: "Full page response. This wouldn't normally be a Content result, but demonstrates the check.");
}
32 changes: 32 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Polling.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@page
@model PollingModel
@{
ViewData["Title"] = "Polling";
}

<article class="example-page">
<header class="page-heading">
<p class="eyebrow">Example 05</p>
<h1>Polling</h1>
<p>Stop a repeating HTMX request from the server when a condition is reached</p>
</header>

<section class="demo-card">
<div class="demo-card__header">
<h2><code>HtmxResponse.StopPolling()</code></h2>
<p>The request is evaluated every second. The server randomly asks HTMX to stop polling.</p>
</div>
<div class="poll-status"
hx-get hx-page="/Examples/Polling"
hx-page-handler="StopPolling"
hx-target="#poll-result"
hx-trigger="every 1s"
hx-swap="innerHTML">
<span id="poll-status">Polling is active</span>
</div>

<div id="poll-result" class="result">
Waiting for the first response.
</div>
</section>
</article>
19 changes: 19 additions & 0 deletions samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Polling.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Ramstack.HtmxToolkit.Demo.Pages.Examples;

public class PollingModel : PageModel
{
public IActionResult OnGetStopPolling()
{
var stop = Random.Shared.Next(0, 20) == 10;
Response.Htmx((h, f) => h.StopPolling(f), stop);

var content = $"Polling... {DateTime.Now:HH:mm:ss}";
if (stop)
content += "<span id='poll-status' hx-swap-oob='true' style='color: orange'>Polling stopped!</span>";

return Content(content);
}
}
Loading
Loading