flash_unified 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ff9e758b822e2e06c38f1543fc5e2be3b4aa101879bdd1137ab70f01b0cc350
4
- data.tar.gz: 1df992f44cd7c2db0992b4130d9047af5c7017a26e3367aa395597a884adbddb
3
+ metadata.gz: 0717ebeb4edd16afaa95ed6bbb292021c21cf1a2fef5c4e563ccb3beb54d8665
4
+ data.tar.gz: 2a43740750aedf357dbf589f0c2066b89b9a93d3cbef791adfdf2eb0e7a5f4fa
5
5
  SHA512:
6
- metadata.gz: 1f327a2f3b87d9974b81b1031f5d787cc6b3f03927398030e0e89fb39e8c0e94232cc6ec86195dbe6e56d53a1cb140d07bb5c52d803a34d1f7930c60584aeacc
7
- data.tar.gz: fcd91ea10bdeccefc552f5c66e6c6e8e4ddc053acb4f3e638b1c3b8e04db51efd6463478464cc7e308e38bbc7db54fb266ccf21ee7df3782569ffe6664abc575
6
+ metadata.gz: ccdfc4a4061e885e80cd9a25aaf6e68a4f0afe82b7fbbafec1cf9525bdda8b870a2ab3d8368e75da545bfb408b0109100f2b83378d220864ef5e63cb5cf54be4
7
+ data.tar.gz: a2e898fa7af7a45a71715d8c2a9864c33d371d5575d357150e127c9bc19f82cd4a6050a8f80283f01b5d2549854278a69a9372ab3fd374849615b0b1c57e17c5
@@ -1 +1 @@
1
- var d=null;function x(e){if(e!==null&&typeof e!="function")throw new TypeError("Renderer must be a function or null");d=e}function S(e){if(!e||!e.isConnected)return!1;let r=window.getComputedStyle(e);return r&&r.display!=="none"&&r.visibility!=="hidden"&&parseFloat(r.opacity)>0}function N(e={}){let{primaryOnly:r=!1,visibleOnly:n=!1,sortByPriority:t=!1,firstOnly:s=!1,filter:o}=e,i=Array.from(document.querySelectorAll("[data-flash-message-container]"));return r&&(i=i.filter(l=>l.hasAttribute("data-flash-primary")&&l.getAttribute("data-flash-primary")!=="false")),n&&(i=i.filter(S)),typeof o=="function"&&(i=i.filter(o)),t&&i.sort((l,v)=>{let c=Number(l.getAttribute("data-flash-message-container-priority")),m=Number(v.getAttribute("data-flash-message-container-priority")),A=Number.isFinite(c)?c:Number.POSITIVE_INFINITY,C=Number.isFinite(m)?m:Number.POSITIVE_INFINITY;return A-C}),s?i.length>0?[i[0]]:[]:i}function L(){let e=document.documentElement,r=n=>{let t=e.getAttribute(n);if(t!==null)return t===""||t.toLowerCase()==="true"||t==="1"?!0:!(t.toLowerCase()==="false"||t==="0")};return{primaryOnly:r("data-flash-unified-container-primary-only"),visibleOnly:r("data-flash-unified-container-visible-only"),sortByPriority:r("data-flash-unified-container-sort-by-priority"),firstOnly:r("data-flash-unified-container-first-only")}}function F(e){N(L()).forEach(n=>{e.forEach(({type:t,message:s})=>{s&&n.appendChild(I(t,s))})})}function h(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){a()},{once:!0}):a()}function a(){let e=g(!1);typeof d=="function"?d(e):F(e)}function g(e=!1){let r=document.querySelectorAll("[data-flash-storage]"),n=[];return r.forEach(t=>{let s=t.querySelector("ul");s&&s.children.length>0&&s.querySelectorAll("li").forEach(o=>{n.push({type:o.dataset.type||"notice",message:o.textContent.trim()})}),e||t.remove()}),n}function O(){return g(!0)}function f(e,r="notice"){let n=document.getElementById("flash-storage");if(!n){console.error('[FlashUnified] #flash-storage not found. Define <div id="flash-storage" style="display:none"></div> in layout.');return}let t=n.querySelector("[data-flash-storage]");t||(t=document.createElement("div"),t.setAttribute("data-flash-storage","true"),t.style.display="none",n.appendChild(t));let s=t.querySelector("ul");s||(s=document.createElement("ul"),t.appendChild(s));let o=document.createElement("li");o.dataset.type=r,o.textContent=e,s.appendChild(o)}function y(){let e=document.documentElement;e.hasAttribute("data-flash-unified-custom-listener")||(e.setAttribute("data-flash-unified-custom-listener","true"),document.addEventListener("flash-unified:messages",function(r){try{M(r.detail)}catch(n){console.error("[FlashUnified] Failed to handle custom payload",n)}}))}function T(e){document.querySelectorAll("[data-flash-message-container]").forEach(r=>{if(typeof e>"u"){r.querySelectorAll("[data-flash-message]")?.forEach(n=>n.remove());return}r.querySelectorAll("[data-flash-message]")?.forEach(n=>{let t=n.querySelector(".flash-message-text");t&&t.textContent.trim()===e&&n.remove()})})}function I(e,r){let n=`flash-message-template-${e}`,t=document.getElementById(n);if(t&&t.content){let s=t.content.firstElementChild;if(!s){console.error(`[FlashUnified] Template #${n} has no root element`);let l=document.createElement("div");return l.setAttribute("role","alert"),l.setAttribute("data-flash-message","true"),l.textContent=r,l}let o=s.cloneNode(!0);o.setAttribute("data-flash-message","true");let i=o.querySelector(".flash-message-text");return i&&(i.textContent=r),o}else{console.error(`[FlashUnified] No template found for type: ${e}`);let s=document.createElement("div");s.setAttribute("role","alert"),s.setAttribute("data-flash-message","true");let o=document.createElement("span");return o.className="flash-message-text",o.textContent=r,s.appendChild(o),s}}function p(){let e=document.querySelectorAll("[data-flash-storage]");for(let r of e){let n=r.querySelector("ul");if(n&&n.children.length>0)return!0}return!1}function M(e){if(!e)return;let r=Array.isArray(e)?e:Array.isArray(e.messages)?e.messages:[];r.length!==0&&(r.forEach(({type:n,message:t})=>{t&&f(String(t),n)}),a())}function R(){let e=document.documentElement;if(e.hasAttribute("data-flash-unified-observer-enabled"))return;e.setAttribute("data-flash-unified-observer-enabled","true"),new MutationObserver(n=>{let t=!1;for(let s of n)s.type==="childList"&&s.addedNodes.forEach(o=>{o instanceof Element&&(o.matches('[data-flash-storage], [data-flash-message-container], template[id^="flash-message-template-"]')&&(t=!0),o.querySelector&&o.querySelector("[data-flash-storage]")&&(t=!0))});t&&a()}).observe(document.body,{childList:!0,subtree:!0})}function u(e){if(p())return;let r=Number(e);if(isNaN(r)||r<0||r>0&&r<400)return;let n;r===0?n="network":n=String(r);let t=document.querySelector("[data-flash-message-container]");if(t&&t.querySelector("[data-flash-message]"))return;let s=document.getElementById("general-error-messages");if(!s){console.error("[FlashUnified] No general error messages element found");return}let o=s.querySelector(`li[data-status="${n}"]`);o?f(o.textContent.trim(),"alert"):console.error(`[FlashUnified] No error message defined for status: ${e}`)}function P(){u(0),a()}function B(e){u(e),a()}function q(){let e=document.documentElement;e.hasAttribute("data-flash-unified-turbo-listeners")||(e.setAttribute("data-flash-unified-turbo-listeners","true"),document.addEventListener("turbo:load",function(){a()}),document.addEventListener("turbo:frame-load",function(){a()}),document.addEventListener("turbo:render",function(){a()}),w())}function w(){let e=new Event("turbo:after-stream-render");document.addEventListener("turbo:before-stream-render",r=>{let n=r.detail.render;r.detail.render=async function(t){await n(t),document.dispatchEvent(e)}}),document.addEventListener("turbo:after-stream-render",function(){a()})}function b(){let e=document.documentElement;e.hasAttribute("data-flash-unified-initialized")||(e.setAttribute("data-flash-unified-initialized","true"),q(),y())}function E(){let e=document.documentElement;e.hasAttribute("data-flash-unified-network-listeners")||(e.setAttribute("data-flash-unified-network-listeners","true"),document.addEventListener("turbo:submit-end",function(r){let n=r.detail.fetchResponse,t;n===void 0?(t=0,console.warn("[FlashUnified] No response received from server. Possible network or proxy error.")):t=n.statusCode,u(t),a()}),document.addEventListener("turbo:fetch-request-error",function(r){u(0),a()}))}if(typeof document<"u"){let e=document.documentElement;if(e.getAttribute("data-flash-unified-auto-init")!=="false"){let n=e.getAttribute("data-flash-unified-enable-network-errors")==="true",t=async()=>{b(),h(),n&&E()};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}}export{O as aggregateFlashMessages,f as appendMessageToStorage,T as clearFlashMessages,g as consumeFlashMessages,N as getFlashMessageContainers,L as getHtmlContainerOptions,y as installCustomEventListener,h as installInitialRenderListener,E as installNetworkErrorListeners,b as installTurboIntegration,q as installTurboRenderListeners,B as notifyHttpError,P as notifyNetworkError,M as processMessagePayload,a as renderFlashMessages,u as resolveAndAppendErrorMessage,x as setFlashMessageRenderer,R as startMutationObserver,p as storageHasMessages};
1
+ var d=null;function x(e){if(e!==null&&typeof e!="function")throw new TypeError("Renderer must be a function or null");d=e}function C(e){if(!e||!e.isConnected)return!1;let r=window.getComputedStyle(e);return r&&r.display!=="none"&&r.visibility!=="hidden"&&parseFloat(r.opacity)>0}function N(e={}){let{primaryOnly:r=!1,visibleOnly:n=!1,sortByPriority:t=!1,firstOnly:s=!1,filter:o}=e,a=Array.from(document.querySelectorAll("[data-flash-message-container]"));return r&&(a=a.filter(l=>l.hasAttribute("data-flash-primary")&&l.getAttribute("data-flash-primary")!=="false")),n&&(a=a.filter(C)),typeof o=="function"&&(a=a.filter(o)),t&&a.sort((l,v)=>{let c=Number(l.getAttribute("data-flash-message-container-priority")),m=Number(v.getAttribute("data-flash-message-container-priority")),A=Number.isFinite(c)?c:Number.POSITIVE_INFINITY,S=Number.isFinite(m)?m:Number.POSITIVE_INFINITY;return A-S}),s?a.length>0?[a[0]]:[]:a}function L(){let e=document.documentElement,r=n=>{let t=e.getAttribute(n);if(t!==null)return t===""||t.toLowerCase()==="true"||t==="1"?!0:!(t.toLowerCase()==="false"||t==="0")};return{primaryOnly:r("data-flash-unified-container-primary-only"),visibleOnly:r("data-flash-unified-container-visible-only"),sortByPriority:r("data-flash-unified-container-sort-by-priority"),firstOnly:r("data-flash-unified-container-first-only")}}function F(e){N(L()).forEach(n=>{e.forEach(({type:t,message:s})=>{s&&n.appendChild(I(t,s))})})}function h(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){i()},{once:!0}):i()}function i(){let e=g(!1);typeof d=="function"?d(e):F(e)}function g(e=!1){let r=document.querySelectorAll("[data-flash-storage]"),n=new Set,t=[];return r.forEach(s=>{let o=s.getAttribute("data-object-id");if(o&&n.has(o)){e||s.remove();return}o&&n.add(o);let a=s.querySelector("ul");a&&a.children.length>0&&a.querySelectorAll("li").forEach(l=>{t.push({type:l.dataset.type||"notice",message:l.textContent.trim()})}),e||s.remove()}),t}function O(){return g(!0)}function f(e,r="notice"){let n=document.getElementById("flash-storage");if(!n){console.error('[FlashUnified] #flash-storage not found. Define <div id="flash-storage" style="display:none"></div> in layout.');return}let t=n.querySelector("[data-flash-storage]");t||(t=document.createElement("div"),t.setAttribute("data-flash-storage","true"),t.style.display="none",n.appendChild(t));let s=t.querySelector("ul");s||(s=document.createElement("ul"),t.appendChild(s));let o=document.createElement("li");o.dataset.type=r,o.textContent=e,s.appendChild(o)}function y(){let e=document.documentElement;e.hasAttribute("data-flash-unified-custom-listener")||(e.setAttribute("data-flash-unified-custom-listener","true"),document.addEventListener("flash-unified:messages",function(r){try{w(r.detail)}catch(n){console.error("[FlashUnified] Failed to handle custom payload",n)}}))}function T(e){document.querySelectorAll("[data-flash-message-container]").forEach(r=>{if(typeof e>"u"){r.querySelectorAll("[data-flash-message]")?.forEach(n=>n.remove());return}r.querySelectorAll("[data-flash-message]")?.forEach(n=>{let t=n.querySelector(".flash-message-text");t&&t.textContent.trim()===e&&n.remove()})})}function I(e,r){let n=`flash-message-template-${e}`,t=document.getElementById(n);if(t&&t.content){let s=t.content.firstElementChild;if(!s){console.error(`[FlashUnified] Template #${n} has no root element`);let l=document.createElement("div");return l.setAttribute("role","alert"),l.setAttribute("data-flash-message","true"),l.textContent=r,l}let o=s.cloneNode(!0);o.setAttribute("data-flash-message","true");let a=o.querySelector(".flash-message-text");return a&&(a.textContent=r),o}else{console.error(`[FlashUnified] No template found for type: ${e}`);let s=document.createElement("div");s.setAttribute("role","alert"),s.setAttribute("data-flash-message","true");let o=document.createElement("span");return o.className="flash-message-text",o.textContent=r,s.appendChild(o),s}}function b(){let e=document.querySelectorAll("[data-flash-storage]");for(let r of e){let n=r.querySelector("ul");if(n&&n.children.length>0)return!0}return!1}function w(e){if(!e)return;let r=Array.isArray(e)?e:Array.isArray(e.messages)?e.messages:[];r.length!==0&&(r.forEach(({type:n,message:t})=>{t&&f(String(t),n)}),i())}function R(){let e=document.documentElement;if(e.hasAttribute("data-flash-unified-observer-enabled"))return;e.setAttribute("data-flash-unified-observer-enabled","true"),new MutationObserver(n=>{let t=!1;for(let s of n)s.type==="childList"&&s.addedNodes.forEach(o=>{o instanceof Element&&(o.matches('[data-flash-storage], [data-flash-message-container], template[id^="flash-message-template-"]')&&(t=!0),o.querySelector&&o.querySelector("[data-flash-storage]")&&(t=!0))});t&&i()}).observe(document.body,{childList:!0,subtree:!0})}function u(e){if(b())return;let r=Number(e);if(isNaN(r)||r<0||r>0&&r<400)return;let n;r===0?n="network":n=String(r);let t=document.querySelector("[data-flash-message-container]");if(t&&t.querySelector("[data-flash-message]"))return;let s=document.getElementById("general-error-messages");if(!s){console.error("[FlashUnified] No general error messages element found");return}let o=s.querySelector(`li[data-status="${n}"]`);o?f(o.textContent.trim(),"alert"):console.error(`[FlashUnified] No error message defined for status: ${e}`)}function P(){u(0),i()}function B(e){u(e),i()}function M(){let e=document.documentElement;e.hasAttribute("data-flash-unified-turbo-listeners")||(e.setAttribute("data-flash-unified-turbo-listeners","true"),document.addEventListener("turbo:load",function(){i()}),document.addEventListener("turbo:frame-load",function(){i()}),document.addEventListener("turbo:render",function(){i()}),q())}function q(){let e=new Event("turbo:after-stream-render");document.addEventListener("turbo:before-stream-render",r=>{let n=r.detail.render;r.detail.render=async function(t){await n(t),document.dispatchEvent(e)}}),document.addEventListener("turbo:after-stream-render",function(){i()})}function p(){let e=document.documentElement;e.hasAttribute("data-flash-unified-initialized")||(e.setAttribute("data-flash-unified-initialized","true"),M(),y())}function E(){let e=document.documentElement;e.hasAttribute("data-flash-unified-network-listeners")||(e.setAttribute("data-flash-unified-network-listeners","true"),document.addEventListener("turbo:submit-end",function(r){let n=r.detail.fetchResponse,t;n===void 0?(t=0,console.warn("[FlashUnified] No response received from server. Possible network or proxy error.")):t=n.statusCode,u(t),i()}),document.addEventListener("turbo:fetch-request-error",function(r){u(0),i()}))}if(typeof document<"u"){let e=document.documentElement;if(e.getAttribute("data-flash-unified-auto-init")!=="false"){let n=e.getAttribute("data-flash-unified-enable-network-errors")==="true",t=async()=>{p(),h(),n&&E()};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}}export{O as aggregateFlashMessages,f as appendMessageToStorage,T as clearFlashMessages,g as consumeFlashMessages,N as getFlashMessageContainers,L as getHtmlContainerOptions,y as installCustomEventListener,h as installInitialRenderListener,E as installNetworkErrorListeners,p as installTurboIntegration,M as installTurboRenderListeners,B as notifyHttpError,P as notifyNetworkError,w as processMessagePayload,i as renderFlashMessages,u as resolveAndAppendErrorMessage,x as setFlashMessageRenderer,R as startMutationObserver,b as storageHasMessages};
@@ -192,6 +192,21 @@ function renderFlashMessages() {
192
192
  * Collect messages from all `[data-flash-storage]` elements.
193
193
  * By default, removes each storage after reading; pass `keep = true` to preserve them.
194
194
  *
195
+ * Notes about deduplication using object_id:
196
+ * - Each storage may include a `data-object-id` attribute populated server-side
197
+ * (the Rails `flash.object_id` in `_storage.html.erb`). This value is used to
198
+ * deduplicate storages that originate from the same FlashHash instance.
199
+ * - This is useful for the common case where the same `flash` object is rendered
200
+ * both in the layout and inside a Turbo Frame during a single full-page render:
201
+ * those storages will share the same `data-object-id` and only one will be processed.
202
+ * - `flash.object_id` is scoped to the Ruby object instance for the current request.
203
+ * Storages coming from separate requests will have different object ids and
204
+ * therefore will not be deduplicated (this is intentional — separate requests
205
+ * should be allowed to show their messages).
206
+ * - If a storage does not provide `data-object-id`, it is treated as independent.
207
+ * Consumers may want to ensure the server partial emits `data-object-id` when
208
+ * appropriate to enable robust deduplication.
209
+ *
195
210
  * @param {boolean} [keep=false] - When true, do not remove storage elements after reading.
196
211
  * @returns {{type: string, message: string}[]} Array of message objects.
197
212
  *
@@ -200,8 +215,16 @@ function renderFlashMessages() {
200
215
  */
201
216
  function consumeFlashMessages(keep = false) {
202
217
  const storages = document.querySelectorAll('[data-flash-storage]');
218
+ const seen = new Set();
203
219
  const messages = [];
204
220
  storages.forEach(storage => {
221
+ const objectId = storage.getAttribute('data-object-id');
222
+ if (objectId && seen.has(objectId)) {
223
+ if (!keep) storage.remove();
224
+ return; // skip duplicate
225
+ }
226
+ if (objectId) seen.add(objectId);
227
+
205
228
  const ul = storage.querySelector('ul');
206
229
  if (ul && ul.children.length > 0) {
207
230
  ul.querySelectorAll('li').forEach(li => {
@@ -1,4 +1,4 @@
1
- <div data-flash-storage style="display: none;">
1
+ <div data-flash-storage data-object-id="<%= flash.object_id %>" style="display: none;">
2
2
  <ul>
3
3
  <% flash.each do |type, message| %>
4
4
  <li data-type="<%= type %>"><%= message %></li>
@@ -1,3 +1,3 @@
1
1
  module FlashUnified
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flash_unified
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - hiroaki