From 48bd7793f53b738a342d135a8130f4083f5b6f5f Mon Sep 17 00:00:00 2001 From: rameel Date: Mon, 10 Aug 2026 00:16:43 +0500 Subject: [PATCH] Support multiple trigger events with the same name --- .../Assets/htmx-toolkit.js | 5 +++++ .../Assets/htmx-toolkit.min.js | 2 +- src/Ramstack.HtmxToolkit/PendingEvents.cs | 22 +++++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.js b/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.js index 6099862..f954ee8 100644 --- a/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.js +++ b/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.js @@ -24,5 +24,10 @@ document._r_htmx ||= ((document, htmx) => { } }); + document.addEventListener("_r_proxy", e => { + for (let kvp of e.detail) + htmx.trigger(e.target, kvp.key, kvp.value); + }); + return true; })(document, htmx); diff --git a/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.min.js b/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.min.js index 811157f..8703d23 100644 --- a/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.min.js +++ b/src/Ramstack.HtmxToolkit/Assets/htmx-toolkit.min.js @@ -1 +1 @@ -document._r_htmx||=((e,t)=>(e.addEventListener("htmx:afterOnLoad",e=>{if(e.detail.boosted){const r=(new DOMParser).parseFromString(e.detail.xhr.responseText,"text/html").querySelector("meta[name='htmx-config']");r&&(t.config.antiForgery=JSON.parse(r.content).antiForgery)}}),e.addEventListener("htmx:configRequest",e=>{if(!/^get$/i.test(e.detail.verb)){const{headerName:r,formFieldName:a,requestToken:n}=t.config.antiForgery??{};n&&!e.detail.parameters[a]&&(r?e.detail.headers[r]=n:e.detail.parameters[a]=n)}}),!0))(document,htmx); \ No newline at end of file +document._r_htmx||=((e,t)=>(e.addEventListener("htmx:afterOnLoad",e=>{if(e.detail.boosted){const r=(new DOMParser).parseFromString(e.detail.xhr.responseText,"text/html").querySelector("meta[name='htmx-config']");r&&(t.config.antiForgery=JSON.parse(r.content).antiForgery)}}),e.addEventListener("htmx:configRequest",e=>{if(!/^get$/i.test(e.detail.verb)){const{headerName:r,formFieldName:a,requestToken:n}=t.config.antiForgery??{};n&&!e.detail.parameters[a]&&(r?e.detail.headers[r]=n:e.detail.parameters[a]=n)}}),e.addEventListener("_r_proxy",e=>{for(let r of e.detail)t.trigger(e.target,r.key,r.value)}),!0))(document,htmx); \ No newline at end of file diff --git a/src/Ramstack.HtmxToolkit/PendingEvents.cs b/src/Ramstack.HtmxToolkit/PendingEvents.cs index ec56acb..38f9b9e 100644 --- a/src/Ramstack.HtmxToolkit/PendingEvents.cs +++ b/src/Ramstack.HtmxToolkit/PendingEvents.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using System.Text.Json; using Microsoft.AspNetCore.Http; @@ -12,13 +13,15 @@ namespace Ramstack.HtmxToolkit; /// internal sealed class PendingEvents(HttpResponse response) { + private const string ProxyEventName = "_r_proxy"; + private Dictionary? _receive; private Dictionary? _afterSwap; private Dictionary? _afterSettle; /// /// Adds the specified events to the pending set for the given . - /// Keys already present are preserved. + /// When a key already exists, the duplicate event is accumulated under a _r_proxy key and replayed client-side. /// /// The time at which the events will be triggered. /// A dictionary containing event names as keys and event details as values. @@ -31,15 +34,16 @@ public void AddEvents(HtmxTriggerTiming timing, IReadOnlyDictionary _afterSettle ??= new Dictionary(), }; - if (events is Dictionary dictionary) - { - foreach (var (k, v) in dictionary) - current.TryAdd(k, v); - } - else + foreach (var (k, v) in events) { - foreach (var (k, v) in events) - current.TryAdd(k, v); + if (current.TryAdd(k, v)) + continue; + + ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(current, ProxyEventName, out _); + if (value is not List> collection) + value = collection = []; + + collection.Add(new KeyValuePair(k, v)); } }