phlex-reactive 0.9.5 → 0.10.0
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 +4 -4
- data/CHANGELOG.md +69 -0
- data/README.md +49 -44
- data/app/controllers/phlex/reactive/actions_controller.rb +14 -1
- data/app/javascript/phlex/reactive/reactive_controller.js +105 -39
- data/app/javascript/phlex/reactive/reactive_controller.min.js +2 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +3 -3
- data/lib/phlex/reactive/client_bindings.rb +57 -0
- data/lib/phlex/reactive/component/dsl.rb +37 -0
- data/lib/phlex/reactive/component/helpers.rb +174 -219
- data/lib/phlex/reactive/component/registry.rb +2 -0
- data/lib/phlex/reactive/component.rb +23 -9
- data/lib/phlex/reactive/show_conditions.rb +249 -0
- data/lib/phlex/reactive/streamable.rb +62 -50
- data/lib/phlex/reactive/test_helpers.rb +9 -1
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Controller as d}from"@hotwired/stimulus";import{confirmResolver as l}from"phlex/reactive/confirm";import{computeReducer as s}from"phlex/reactive/compute";function o(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:visit"])return;j["reactive:visit"]=function(){let Q=this.getAttribute("data-url");if(Q)window.Turbo.visit(Q,{action:"advance"})}}function n(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:token"])return;j["reactive:token"]=function(){let Q=this.getAttribute("data-reactive-token-value"),X=this.getAttribute("target");if(!Q||!X)return;let Z=document.getElementById(X);if(Z)Z.setAttribute("data-reactive-token-value",Q)}}function i(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:js"])return;j["reactive:js"]=function(){let Q=c(this.getAttribute("data-reactive-ops"));if(!Q.length)return;let X=this.getAttribute("target"),Z=X?document.getElementById(X):null;if(X&&!Z)return;g(Q,($)=>Oj($,Z))}}var H=new Map;function Ej(){H.clear(),E=!1}function Fj(j){return H.get(j)?.via}var E=!1;function a(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:defer"])return;if(j["reactive:defer"]=function(){let Q=this.getAttribute("target");if(!Q)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){t(Q,this);return}let X=this.getAttribute("data-reactive-defer-token");if(!X)return;L(Q,X)},!E&&typeof document<"u"&&document.addEventListener)E=!0,document.addEventListener("turbo:before-stream-render",r)}function r(j){let X=j.target?.getAttribute?.("target");if(!X)return;let Z=X.startsWith("reactive-defer-src-")?X.slice(19):X;if(H.get(Z)?.via==="stream")H.delete(Z)}function L(j,Q){let X=document.getElementById(j);if(!X){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}h(j),y(X);let Z={via:"fetch",abort:new AbortController,timedOut:!1};H.set(j,Z),e(j,Z,Q)}function t(j,Q){let X=document.getElementById(j);if(!X){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}let Z=Q.getAttribute("data-reactive-defer-src");if(!Z)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let J=Q.getAttribute("data-reactive-defer-token");if(J){L(j,J);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}h(j),y(X);let $=document.createElement("pgbus-stream-source");$.id=w(j),$.setAttribute("src",Z),$.setAttribute("since-id",Q.getAttribute("data-reactive-defer-since-id")??"0"),$.setAttribute("hidden",""),document.body.appendChild($),H.set(j,{via:"stream"})}function w(j){return`reactive-defer-src-${j}`}async function e(j,Q,X){let Z=setTimeout(()=>{Q.timedOut=!0,Q.abort.abort()},Xj()),$;try{$=await fetch(jj(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":Qj()},body:JSON.stringify({token:X}),credentials:"same-origin",signal:Q.abort.signal})}catch(G){if(clearTimeout(Z),H.get(j)!==Q)return;console.error("[phlex-reactive] deferred render failed",G),O(j,X);return}if(H.get(j)!==Q){clearTimeout(Z);return}if($.status===204){clearTimeout(Z),I(j);return}if(!$.ok){clearTimeout(Z),console.error(`[phlex-reactive] deferred render failed: HTTP ${$.status}`),O(j,X,$.status);return}let J;try{J=await $.text()}catch(G){if(clearTimeout(Z),H.get(j)!==Q)return;console.error("[phlex-reactive] deferred render failed reading the body",G),O(j,X);return}if(clearTimeout(Z),H.get(j)!==Q)return;I(j),window.Turbo.renderStreamMessage(J)}function h(j){let Q=H.get(j);if(!Q)return;if(H.delete(j),Q.via==="fetch")Q.abort.abort();else document.getElementById(w(j))?.remove?.()}function y(j){j.setAttribute("data-reactive-defer-pending","true"),j.setAttribute("aria-busy","true")}function v(j){j.removeAttribute("data-reactive-defer-pending"),j.removeAttribute("aria-busy")}function I(j){H.delete(j);let Q=document.getElementById(j);if(!Q)return;v(Q),Q.removeAttribute("data-reactive-error")}function O(j,Q,X){H.delete(j);let Z=document.getElementById(j);if(!Z)return;v(Z),Z.setAttribute("data-reactive-error","defer");let $=()=>{let J=document.getElementById(j);if(!J){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}J.removeAttribute("data-reactive-error"),L(j,Q)};Z.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:j,status:X,retry:$}}))}function jj(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function Qj(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function Xj(){let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Q=Number(j);return Number.isFinite(Q)&&Q>0?Q:30000}var F=!1;function Zj(){if(F)return;if(typeof document>"u"||!document.addEventListener)return;F=!0,document.addEventListener("turbo:before-stream-render",$j)}function $j(j){let Q=j.detail,X=Q?.render;if(typeof X!=="function"||X.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(B);else setTimeout(B,0);return}let Z=async($)=>{await X($),B()};Z.__reactiveDismissWrapped=!0,Q.render=Z}function B(){let j=document.querySelectorAll("[data-reactive-dismiss-after]");for(let Q of j){if(Q.hasAttribute("data-reactive-dismiss-scheduled"))continue;let X=Number(Q.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite(X)||X<=0)continue;Q.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>Q.remove(),X)}}function Rj(){F=!1}var R=!1;function Jj(){if(R)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;R=!0;let j=()=>{let Q=document.documentElement;if(typeof Q?.toggleAttribute!=="function")return;Q.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};j(),window.addEventListener("online",j),window.addEventListener("offline",j)}function Dj(){R=!1}var D="phlex-reactive:latency",N=!1;function Gj(j){if(typeof sessionStorage>"u")return;sessionStorage.setItem(D,String(j))}function Wj(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(D),N=!1}function zj(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:Gj,disableLatencySim:Wj}}function Tj(){N=!1}function S(){o(),n(),i(),a(),Zj(),Jj(),zj()}function Kj(j){return j.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)S();else document.addEventListener("turbo:load",S,{once:!0});var x=!1;function qj(){if(x)return;if(typeof document>"u")return;let j=document.querySelectorAll('[data-controller~="reactive"]');if(!j||j.length===0)return;console.warn("[phlex-reactive] found "+j.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}function Ij(){x=!1}function Sj(){x=!0}if(typeof window<"u"&&typeof document<"u"){let j=()=>setTimeout(qj,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",j,{once:!0});else j()}var Yj=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function Uj(j){let Q=String(j).toLowerCase();return Q.startsWith("on")||Yj.has(Q)}function Hj(j,Q,X){let[Z,$,J]=Q;j.classList.add(Z,$),X(),requestAnimationFrame(()=>{j.classList.remove($),j.classList.add(J)});let G=!1,W=()=>{if(G)return;G=!0,j.classList.remove(Z,J)};j.addEventListener("animationend",W,{once:!0}),setTimeout(W,350)}var k=Object.freeze({show:(j,Q)=>C(j,!1,Q),hide:(j,Q)=>C(j,!0,Q),toggle:(j,Q)=>C(j,!j.hidden,Q),add_class:(j,Q)=>j.classList.add(...Q.classes??[]),remove_class:(j,Q)=>j.classList.remove(...Q.classes??[]),toggle_class:(j,Q)=>(Q.classes??[]).forEach((X)=>j.classList.toggle(X)),set_attr:(j,Q)=>{if(P(Q.name))j.setAttribute(Q.name,Q.value??"")},remove_attr:(j,Q)=>{if(P(Q.name))j.removeAttribute(Q.name)},toggle_attr:(j,Q)=>{if(!P(Q.name))return;if(j.hasAttribute(Q.name))j.removeAttribute(Q.name);else j.setAttribute(Q.name,"")},focus:(j)=>j.focus?.(),focus_first:(j)=>xj(j)?.focus?.(),text:(j,Q)=>{let X=String(Q.value??"");if(j.textContent!==X)j.textContent=X},dispatch:(j,Q)=>{j.dispatchEvent(new CustomEvent(Q.name,{bubbles:!0,composed:!0,detail:Q.detail??{}}))}});function C(j,Q,X){if(X?.transition)Hj(j,X.transition,()=>j.hidden=Q);else j.hidden=Q}function P(j){if(!Uj(j))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(j)} — skipped`),!1}var f=/^#[A-Za-z_][\w-]*$/;function Vj(j){if(typeof j==="string"&&f.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(j)} — skipped`),!1}function Aj(j,Q){let X=j.getAttribute("data-reactive-show-equals");if(X!==null)return Q===X;let Z=j.getAttribute("data-reactive-show-not");if(Z!==null)return Q!==Z;let $=j.getAttribute("data-reactive-show-in");if($!==null){try{let J=JSON.parse($);if(Array.isArray(J))return J.includes(Q)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify($)} — skipped`),null}for(let J of u){let G=j.getAttribute(`data-reactive-show-${J}`);if(G!==null)return p(J,G,Q)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var u=["gte","gt","lte","lt"];function p(j,Q,X){let Z=Number(Q);if(Number.isNaN(Z))return console.warn(`[phlex-reactive] reactive_show ${j}: needs a numeric literal, got ${JSON.stringify(Q)} — skipped`),null;let $=X==null?"":String(X).trim(),J=$===""?NaN:Number($);if(Number.isNaN(J))return!1;switch(j){case"gte":return J>=Z;case"gt":return J>Z;case"lte":return J<=Z;case"lt":return J<Z;default:return null}}function m(j,Q){if(!j||typeof j!=="object")return null;if(typeof j.equals==="string")return Q===j.equals;if(typeof j.not==="string")return Q!==j.not;if(Array.isArray(j.in))return j.in.includes(Q);for(let X of u)if(X in j)return p(X,j[X],Q);return null}var b="[data-reactive-show-field], [data-reactive-show]";function _j(j){try{let Q=JSON.parse(j);if(Q&&typeof Q==="object"&&!Array.isArray(Q))return Q}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(j)} — skipped`),null}function Mj(j,Q){if(!j||typeof j!=="object")return null;let X=Array.isArray(j.all)?"all":Array.isArray(j.any)?"any":null;if(!X)return null;let Z=j[X];if(Z.length===0)return null;let $=Z.map((J)=>{if(!J||typeof J!=="object"||typeof J.field!=="string")return!1;let G=Q(J.field);if(G===null)return!1;return m(J,G)===!0});return X==="all"?$.every(Boolean):$.some(Boolean)}function Nj(j){if(typeof j==="string"&&f.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(j)} — skipped`),!1}var Lj='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function xj(j){return j.querySelectorAll?.(Lj)?.[0]??null}function c(j){if(Array.isArray(j))return j;if(typeof j!=="string")return[];try{let Q=JSON.parse(j);return Array.isArray(Q)?Q:[]}catch{return[]}}function g(j,Q){for(let X of j){if(!Array.isArray(X))continue;let[Z,$={}]=X;if(!Object.hasOwn(k,Z)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(Z)} — skipped`);continue}for(let J of Q($))k[Z](J,$)}}function Oj(j,Q){let X=j.to;if(Q){if(X==="@root")return[Q];if(typeof X!=="string"||X==="")return[];if(j.global)return[...document.querySelectorAll(X)];return[...Q.querySelectorAll(X)]}if(typeof X!=="string"||X===""||X==="@root")return[];return[...document.querySelectorAll(X)]}class kj extends d{static values={token:String};#R;#V=new Map;#z=new Map;#g;#L;#D;#x=0;#K=new Map;#O=new WeakMap;#A=new Map;#q;#Y;#U;#j;#X;#_;connect(){if(x=!0,this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-defer-token"))this.#T(),this.#_=()=>this.#T(),this.element.addEventListener?.("turbo:morph-element",this.#_);if(this.#d()){if(this.#q=()=>this.#P(),this.element.addEventListener?.("turbo:morph-element",this.#q),this.#P(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#qj()}if(this.#Uj())this.#j=()=>this.#f(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#j),this.#f();if(this.#_j())this.#X=(j)=>{if(j?.type==="input"&&!this.#Mj(j))return;this.#p()},this.element.addEventListener?.("input",this.#X),this.element.addEventListener?.("turbo:morph-element",this.#X),this.#p()}#d(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let j=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let Q of j)if(this.#Q(Q))return!0;return!1}disconnect(){if(this.#r(),this.#e(),this.#Yj(),this.#Aj(),this.#Nj(),this.#_)this.element.removeEventListener?.("turbo:morph-element",this.#_)}#T(){let j=this.element;if(!j?.id)return;let Q=j.getAttribute?.("data-reactive-defer-token");if(!Q)return;if(j.getAttribute?.("data-reactive-defer-pending")!=="true")return;L(j.id,Q)}dispatch(j){let{action:Q,params:X,debounce:Z,throttle:$,confirm:J,outside:G,window:W,optimistic:z,loading:Y}=j.params;if(!Q)return;if(G&&this.element.contains(j.target))return;let U=j.currentTarget??j.target;if(!W&&!this.#Cj(z,U))j.preventDefault();if(!J)return this.#b(U,Q,X,Z,$,z,Y);Promise.resolve().then(()=>l(J)).catch(()=>!1).then((K)=>{if(K)this.#b(U,Q,X,Z,$,z,Y)})}runOps(j){let{ops:Q,outside:X,window:Z}=j.params;if(X&&this.element.contains(j.target))return;if(!Z)j.preventDefault();this.#Bj(this.#Oj(Q))}trackDirty(){this.#P()}recompute(j){let Q=this.#s(),X=Q.map(([K])=>K),Z=this.#H(),$=new Map,J=(K)=>{if($.has(K))return $.get(K);let q=null;for(let A of this.element.querySelectorAll(`[name="${K}"]`))if(Z(A)){q=A;break}return $.set(K,q),q};for(let K of X)this.#S(K,J(K)?.value??"");let G=this.element.getAttribute("data-reactive-compute-reducer-param"),W=G?s(G):null;if(!W){this.#k({},J);return}let z=this.#l("data-reactive-compute-outputs-param"),Y={};for(let[K,q]of Q){let A=J(K);if(q==="string")Y[K]=A?.value??"";else{let _=Number(A?.value);Y[K]=Number.isFinite(_)?_:0}}let U=W(Y,{changed:this.#o(j,X)})||{};for(let K of z){if(!(K in U))continue;let q=J(K);if(q){if(String(U[K])===q.value)continue;q.value=U[K],q.dispatchEvent(new Event("input",{bubbles:!0}))}else this.#S(K,U[K])}this.#k(U,J)}listnavNext(j){this.#I(j,1)}listnavPrev(j){this.#I(j,-1)}listnavPick(j){let Q=this.#B(j),X=Q.findIndex((Z)=>Z.hasAttribute("data-reactive-highlighted"));if(X<0)return;j.preventDefault(),Q[X].click()}listnavClose(j){for(let Q of this.#B(j))Q.removeAttribute("data-reactive-highlighted")}#I(j,Q){let X=this.#B(j);if(!X.length)return;j.preventDefault();let Z=X.findIndex((G)=>G.hasAttribute("data-reactive-highlighted")),$=Z<0?Q>0?0:X.length-1:(Z+Q+X.length)%X.length;for(let G of X)G.removeAttribute("data-reactive-highlighted");let J=X[$];J.setAttribute("data-reactive-highlighted","true"),J.scrollIntoView?.({block:"nearest"})}#B(j){let X=(j?.currentTarget??j?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!X)return[];let Z=this.#H();return Array.from(this.element.querySelectorAll(X)).filter(($)=>!$.hidden&&Z($))}#l(j){let Q=this.element.getAttribute(j);if(!Q)return[];try{let X=JSON.parse(Q);return Array.isArray(X)?X:[]}catch{return[]}}#s(){let j=this.element.getAttribute("data-reactive-compute-inputs-param");if(!j)return[];try{let Q=JSON.parse(j);if(Array.isArray(Q))return Q.map((X)=>[X,"number"]);if(Q&&typeof Q==="object")return Object.entries(Q);return[]}catch{return[]}}#o(j,Q){let X=j?.target;if(!X?.name||typeof X.closest!=="function")return null;if(!Q.includes(X.name))return null;return this.#Q(X)?X.name:null}#S(j,Q){let X=String(Q);for(let Z of this.#n(j)){if(Z.textContent===X)continue;Z.textContent=X}}#n(j){let Q=this.element.querySelectorAll(`[data-reactive-text="${j}"]`);return Array.from(Q).filter((X)=>this.#Q(X))}#k(j,Q){let X=this.#i();for(let[Z,$]of Object.entries(X)){let J=Z in j?j[Z]:Q(Z)?.value;if(J===void 0||J===null)continue;let G=String(J);for(let W of Array.isArray($)?$:[$]){if(!Vj(W))continue;for(let z of document.querySelectorAll(W)){if(z.textContent===G)continue;z.textContent=G}}}}#i(){let j=this.element.getAttribute("data-reactive-compute-mirror-param");if(!j)return{};try{let Q=JSON.parse(j);return Q&&typeof Q==="object"&&!Array.isArray(Q)?Q:{}}catch{return{}}}#b(j,Q,X,Z,$,J,G){if(this.#N("reactive:before-dispatch",{action:Q,params:this.#m(X),element:this.element},{cancelable:!0}).defaultPrevented)return;let z=Number(Z)||0;if(z>0)return this.#a(j,z,Q,X,J,G);let Y=Number($)||0;if(Y>0)return this.#t(j,Y,Q,X,J,G);return this.#M(Q,X,J,j,G)}#M(j,Q,X,Z,$){let J=this.#Pj(X,Z),G=this.#Fj(j,Z,$);return this.queue=(this.queue??Promise.resolve()).then(()=>this.#Gj(j,Q,J,G)),this.queue}#a(j,Q,X,Z,$,J){this.#C(j);let G=()=>{this.#C(j),this.#M(X,Z,$,j,J)},W=setTimeout(G,Q);j?.addEventListener?.("blur",G,{once:!0}),this.#V.set(j,{timer:W,flush:G})}#C(j){let Q=this.#V.get(j);if(!Q)return;clearTimeout(Q.timer),j?.removeEventListener?.("blur",Q.flush),this.#V.delete(j)}#r(){for(let j of[...this.#V.keys()])this.#C(j)}#t(j,Q,X,Z,$,J){let G=this.#z.get(j)??new Map;if(G.has(X))return;let W=setTimeout(()=>{if(G.delete(X),G.size===0)this.#z.delete(j)},Q);return G.set(X,W),this.#z.set(j,G),this.#M(X,Z,$,j,J)}#e(){for(let j of this.#z.values())for(let Q of j.values())clearTimeout(Q);this.#z.clear()}#N(j,Q,{cancelable:X=!1}={}){let Z=new CustomEvent(j,{bubbles:!0,composed:!0,cancelable:X,detail:Q});return(this.element.isConnected?this.element:document).dispatchEvent(Z),Z}#Z(j,Q,X,Z){let $=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#M(j,Q)};this.#N("reactive:error",{action:j,params:X,...Z,retry:$})}#$(j){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",j)}#jj(){this.element?.removeAttribute?.("data-reactive-error")}#Qj(){let j=document.querySelector("[data-reactive-error-flash]");if(!j?.content)return;let Q=j.getAttribute("data-reactive-error-flash")||"flash",X=document.getElementById(Q);if(!X)return;X.appendChild(j.content.cloneNode(!0))}#Xj(){if(typeof sessionStorage>"u")return Promise.resolve();let j=Number(sessionStorage.getItem(D));if(!Number.isFinite(j)||j<=0)return Promise.resolve();if(!N)N=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${j}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((Q)=>setTimeout(Q,j))}#Zj(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#w(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#$j(j){if(!j)return[];let Q=[],X=/<turbo-stream\b([^>]*)>/g,Z;while((Z=X.exec(j))!==null){let $=Z[1],J=$.match(/\baction="([^"]*)"/)?.[1]??"?",G=$.match(/\btarget="([^"]*)"/)?.[1];Q.push(G?`${J} → #${G}`:J)}return Q}#Jj(j){let{action:Q,status:X,ms:Z}=j,J=`reactive ${this.element?.id?`#${this.element.id} `:""}${Q} → ${X??"—"} (${Math.round(Z)}ms)`;if(console.groupCollapsed(J),console.log(`params: [${j.paramNames.join(", ")}] + collected: [${j.fieldNames.join(", ")}]`),console.log(`encoding: ${j.encoding}`),j.streams.length)console.log(`streams: ${j.streams.join(" ")}`);console.log(`token: ${j.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#Gj(j,Q,X,Z){let{fields:$,files:J}=this.#zj(),G=this.#m(Q),W={...$,...G},z=this.#J,Y=J.length>0,U=Y?this.#Lj(z,j,W,J):JSON.stringify({token:z,act:j,params:W}),K=this.#Zj()?{action:j,paramNames:Object.keys(G),fieldNames:Object.keys($),encoding:Y?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#w()}:null;await this.#Xj();try{if(navigator.onLine===!1){this.#G(X),this.#$("offline"),this.#Z(j,Q,W,{kind:"offline"});return}let q;try{let V={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#wj()};if(!Y)V["Content-Type"]="application/json";let M=this.#hj();if(M)V["X-Pgbus-Connection"]=M;q=await fetch(this.#kj(),{method:"POST",headers:V,body:U,credentials:"same-origin",signal:AbortSignal.timeout(this.#bj())})}catch(V){if(console.error("[phlex-reactive] action error",V),this.#G(X),V?.name==="TimeoutError"||V?.name==="AbortError"){this.#$("timeout"),this.#Z(j,Q,W,{kind:"timeout"});return}this.#Qj(),this.#$("network"),this.#Z(j,Q,W,{kind:"network"});return}if(K)K.status=q.status;if(q.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#G(X),this.#$("redirected"),this.#Z(j,Q,W,{kind:"redirected",status:q.status});return}if(!q.ok){let V=await q.text();if(console.error(`[phlex-reactive] action failed: HTTP ${q.status}`,V),this.#G(X),(q.headers.get("Content-Type")||"").includes("turbo-stream")){let M=this.#y(V);if(this.#J=M??this.#J,K)this.#h(K,V,M);window.Turbo.renderStreamMessage(V)}this.#$("http"),this.#Z(j,Q,W,{kind:"http",status:q.status,body:V});return}let A=q.headers.get("Content-Type")||"";if(!A.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${A}" — no update applied`),this.#G(X),this.#$("content-type"),this.#Z(j,Q,W,{kind:"content-type",status:q.status});return}let _=await q.text(),T=this.#y(_);if(this.#J=T??this.#J,K)this.#h(K,_,T);window.Turbo.renderStreamMessage(_),this.#jj(),this.#N("reactive:applied",{action:j,params:W,html:_})}catch(q){console.error("[phlex-reactive] action error",q),this.#G(X),this.#N("reactive:error",{action:j,params:W,kind:"apply"})}finally{if(Z?.(),K)this.#Jj({...K,ms:this.#w()-K.started})}}#h(j,Q,X){j.streams=this.#$j(Q),j.tokenRefreshed=X!=null}get#J(){return this.#R??this.tokenValue}set#J(j){this.#R=j}#y(j){let Q=this.element.id;if(!Q)return j.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:X,self:Z}=this.#Wj(Q),$=j.match(X);if($)return $[1];let J=j.match(Z);if(J)return J[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#Wj(j){let Q=this.#D;if(Q&&Q.id===j)return Q;let X=Kj(j);return this.#D={id:j,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${X}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${X}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#Q(j){return j.closest('[data-controller~="reactive"]')===this.element}#H(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(Q)=>this.#Q(Q)}#zj(){let j={},Q=[],X=this.#H();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Z)=>{if(!X(Z))return;if(Z.type==="file")for(let $ of Z.files??[])Q.push({name:Z.name,file:$,multiple:Z.multiple});else if(Z.type==="checkbox")j[Z.name]=Z.checked;else if(Z.type==="radio"){if(Z.checked)j[Z.name]=Z.value}else j[Z.name]=Z.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach((Z)=>{if(!X(Z))return;let $=Z.getAttribute("name");if(!$)return;let J=j[$];if(J==null||J==="")j[$]=Z.value??Z.textContent??Z.innerHTML??""}),{fields:j,files:Q}}#P(){if(typeof this.element?.querySelectorAll!=="function")return;let j=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Q)=>{if(!this.#Q(Q))return;if(Q.type==="file")return;if(this.#Kj(Q))Q.setAttribute("data-reactive-dirty","true"),j++;else Q.removeAttribute("data-reactive-dirty")}),j>0)this.element.setAttribute("data-reactive-dirty",String(j));else this.element.removeAttribute("data-reactive-dirty")}#Kj(j){if(j.type==="checkbox"||j.type==="radio")return j.checked!==j.defaultChecked;if(j.tag==="select"||j.options)return Array.from(j.options??[]).some((Q)=>Q.selected!==Q.defaultSelected);return j.value!==j.defaultValue}#v(){let j=this.element.getAttribute?.("data-reactive-dirty"),Q=Number(j);return Number.isFinite(Q)&&Q>0?Q:0}#qj(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#Y=(j)=>{if(this.#v()===0)return;return j.preventDefault(),j.returnValue="You have unsaved changes.",j.returnValue},this.#U=(j)=>{if(this.#v()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))j.preventDefault?.()},window.addEventListener("beforeunload",this.#Y),window.addEventListener("turbo:before-visit",this.#U)}#Yj(){if(this.#q)this.element.removeEventListener?.("turbo:morph-element",this.#q),this.#q=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#Y)window.removeEventListener("beforeunload",this.#Y);if(this.#U)window.removeEventListener("turbo:before-visit",this.#U)}this.#Y=void 0,this.#U=void 0}#Uj(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let j=this.element.querySelectorAll?.(b)??[];for(let Q of j)if(this.#Q(Q))return!0;return!1}#f(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.#H(),Q=new Map,X=(Z)=>{if(!Q.has(Z))Q.set(Z,this.#u(Z,j));return Q.get(Z)};for(let Z of this.element.querySelectorAll(b)){if(!j(Z))continue;let $=Z.getAttribute("data-reactive-show");if($!==null){let z=Mj(_j($),X);if(z!==null)Z.hidden=!z;continue}let J=Z.getAttribute("data-reactive-show-field");if(!J)continue;let G=X(J);if(G===null)continue;let W=Aj(Z,G);if(W===null)continue;Z.hidden=!W}this.#Hj(j,Q)}#Hj(j,Q){let X=this.#Vj();for(let[Z,$]of Object.entries(X)){if(!$||typeof $!=="object"||Array.isArray($))continue;if(!Q.has(Z))Q.set(Z,this.#u(Z,j));let J=Q.get(Z);if(J===null)continue;for(let[G,W]of Object.entries($)){if(!Nj(G))continue;let z=m(W,J);if(z===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${G} — skipped`);continue}for(let Y of document.querySelectorAll(G))Y.hidden=!z}}}#Vj(){let j=this.element.getAttribute?.("data-reactive-show-targets");if(!j)return{};try{let Q=JSON.parse(j);if(Q&&typeof Q==="object"&&!Array.isArray(Q))return Q}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#u(j,Q){let X=!1,Z=null;for(let $ of this.element.querySelectorAll(`[name="${j}"]`)){if(!Q($))continue;if($.type==="checkbox")return $.checked?"true":"false";if($.type==="radio"){if($.checked)return $.value??"";X=!0;continue}Z??=$}if(Z)return Z.value??"";return X?"":null}#Aj(){if(!this.#j)return;this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.element.removeEventListener?.("turbo:morph-element",this.#j),this.#j=void 0}#_j(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#Mj(j){let Q=this.element.getAttribute("data-reactive-filter-input");return!!Q&&typeof j.target?.matches==="function"&&j.target.matches(Q)}#p(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.element.getAttribute("data-reactive-filter-input"),Q=this.element.getAttribute("data-reactive-filter-option");if(!j||!Q)return;let X=this.#H(),Z=[...this.element.querySelectorAll(j)].find(X);if(!Z)return;let $=(Z.value??"").trim().toLowerCase(),J=0;for(let z of this.element.querySelectorAll(Q)){if(!X(z))continue;let Y=(z.getAttribute("data-reactive-filter-text")??z.textContent??"").toLowerCase(),U=$!==""&&!Y.includes($);if(z.hidden=U,U)z.removeAttribute("data-reactive-highlighted");else J++}let G=this.element.getAttribute("data-reactive-filter-group");if(G)for(let z of this.element.querySelectorAll(G)){if(!X(z))continue;let Y=[...z.querySelectorAll(Q)].filter(X);if(Y.length===0)continue;z.hidden=Y.every((U)=>U.hidden)}let W=this.element.getAttribute("data-reactive-filter-empty");if(W){for(let z of this.element.querySelectorAll(W))if(X(z))z.hidden=J>0}}#Nj(){if(!this.#X)return;this.element.removeEventListener?.("input",this.#X),this.element.removeEventListener?.("turbo:morph-element",this.#X),this.#X=void 0}#Lj(j,Q,X,Z){let $=new FormData;$.append("token",j),$.append("act",Q);for(let[G,W]of Object.entries(X))this.#E($,`params[${G}]`,W);let J=this.#xj(Z);for(let{name:G,file:W,multiple:z}of Z){let U=z||J.has(G)?`params[${G}][]`:`params[${G}]`;$.append(U,W,W.name)}return $}#E(j,Q,X){if(X==null)j.append(Q,"");else if(Array.isArray(X))X.forEach((Z,$)=>this.#E(j,`${Q}[${$}]`,Z));else if(typeof X==="object")for(let[Z,$]of Object.entries(X))this.#E(j,`${Q}[${Z}]`,$);else j.append(Q,String(X))}#xj(j){let Q=new Map;for(let{name:X}of j)Q.set(X,(Q.get(X)??0)+1);return new Set([...Q].filter(([,X])=>X>1).map(([X])=>X))}#m(j){if(!j)return{};try{return typeof j==="string"?JSON.parse(j):j}catch{return{}}}#Oj(j){return c(j)}#Bj(j){g(j,(Q)=>this.#F(Q))}#F(j){let Q=j.to;if(Q==="@root")return[this.element];if(typeof Q!=="string"||Q==="")return[];if(j.global)return[...document.querySelectorAll(Q)];return[...this.element.querySelectorAll(Q)].filter((X)=>this.#Q(X))}#Cj(j,Q){if(j?.checked!=="keep")return!1;let X=Q?.type;return X==="checkbox"||X==="radio"}#Pj(j,Q){if(!j)return null;let X=this.#Ej(j,Q),Z=[];for(let $ of X){if(j.add_class){let J=j.add_class.filter((G)=>!$.classList.contains(G));if($.classList.add(...J),J.length)Z.push(()=>$.classList.remove(...J))}if(j.remove_class){let J=j.remove_class.filter((G)=>$.classList.contains(G));if($.classList.remove(...J),J.length)Z.push(()=>$.classList.add(...J))}if(j.toggle_class)j.toggle_class.forEach((J)=>$.classList.toggle(J)),Z.push(()=>j.toggle_class.forEach((J)=>$.classList.toggle(J)));if(j.hide)$.hidden=!0,Z.push(()=>$.hidden=!1)}if(j.checked==="keep"&&Q&&"checked"in Q){let $=Q.checked;Z.push(()=>Q.checked=!$)}return Z.length?Z:null}#G(j){if(!j)return;if(!this.element.isConnected)return;for(let Q of j)Q()}#Ej(j,Q){if(j.to==null)return Q?[Q]:[];return this.#F({to:j.to})}#Fj(j,Q,X){this.#Rj(j,Q);let Z=this.#Tj(j,Q,X),$=!1;return()=>{if($)return;$=!0,this.#Dj(j,Q),Z()}}#Rj(j,Q){if(this.#W(Q,j,1),this.#W(this.element,j,1),this.#K.set(j,(this.#K.get(j)??0)+1),this.#x++===0)this.element.setAttribute("aria-busy","true");for(let X of this.#c(j))this.#W(X,j,1)}#Dj(j,Q){this.#W(Q,j,-1),this.#W(this.element,j,-1);let X=(this.#K.get(j)??1)-1;if(X<=0)this.#K.delete(j);else this.#K.set(j,X);if(--this.#x<=0)this.#x=0,this.element.removeAttribute("aria-busy");for(let Z of this.#c(j))this.#W(Z,j,-1)}#W(j,Q,X){if(!j||typeof j.getAttribute!=="function")return;let Z=this.#O.get(j)??new Map,$=(Z.get(Q)??0)+X;if($<=0)Z.delete(Q);else Z.set(Q,$);if(Z.size===0){this.#O.delete(j),j.removeAttribute("data-reactive-busy");return}this.#O.set(j,Z),j.setAttribute("data-reactive-busy",[...Z.keys()].join(" "))}#c(j){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter((X)=>X.getAttribute("data-reactive-busy-on")===j&&this.#Q(X))}#Tj(j,Q,X){if(!X||!Q)return()=>{};let Z=this.#Sj(X,Q),$=Array.isArray(X.class)?X.class:[],J=[];for(let W of Z){let z=$.filter((Y)=>!W.classList.contains(Y));if(W.classList.add(...z),z.length)J.push([W,z])}let G=this.#A.get(Q);if(G)G.count++;else if(X.disable||X.text!=null)this.#A.set(Q,{count:1,disabled:Q.disabled,text:Q.textContent,hadText:X.text!=null});if(X.disable)Q.disabled=!0;if(X.text!=null)Q.textContent=X.text;return()=>{for(let[W,z]of J)if(W.isConnected)W.classList.remove(...z);this.#Ij(Q,X)}}#Ij(j,Q){let X=this.#A.get(j);if(!X)return;if(--X.count>0)return;if(this.#A.delete(j),!j.isConnected)return;if(Q.disable)j.disabled=X.disabled;if(X.hadText&&j.textContent===Q.text)j.textContent=X.text}#Sj(j,Q){if(j.to==null)return Q?[Q]:[];return this.#F({to:j.to})}#kj(){return this.#g??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#bj(){if(this.#L!=null)return this.#L;let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Q=Number(j);return this.#L=Number.isFinite(Q)&&Q>0?Q:30000}#wj(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#hj(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{Ej as resetReactiveDefers,o as registerReactiveVisit,n as registerReactiveToken,Jj as registerReactiveOffline,i as registerReactiveJs,Zj as registerReactiveDismiss,a as registerReactiveDefer,S as registerReactiveActions,Fj as pendingDeferVia,Kj as escapeRegExp,Gj as enableLatencySim,Wj as disableLatencySim,kj as default,qj as checkReactiveRegistration,Ij as __resetReactiveRegistrationForTest,Dj as __resetReactiveOfflineForTest,Tj as __resetReactiveLatencyForTest,Rj as __resetReactiveDismissForTest,Sj as __markReactiveConnectedForTest,D as LATENCY_KEY};
|
|
1
|
+
import{Controller as l}from"@hotwired/stimulus";import{confirmResolver as s}from"phlex/reactive/confirm";import{computeReducer as o}from"phlex/reactive/compute";function n(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:visit"])return;j["reactive:visit"]=function(){let Q=this.getAttribute("data-url");if(Q)window.Turbo.visit(Q,{action:"advance"})}}function i(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:token"])return;j["reactive:token"]=function(){let Q=this.getAttribute("data-reactive-token-value"),X=this.getAttribute("target");if(!Q||!X)return;let Z=document.getElementById(X);if(Z)Z.setAttribute("data-reactive-token-value",Q)}}function a(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:js"])return;j["reactive:js"]=function(){let Q=g(this.getAttribute("data-reactive-ops"));if(!Q.length)return;let X=this.getAttribute("target"),Z=X?document.getElementById(X):null;if(X&&!Z)return;d(Q,($)=>Pj($,Z))}}var H=new Map;function Dj(){H.clear(),E=!1}function Tj(j){return H.get(j)?.via}var E=!1;function r(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:defer"])return;if(j["reactive:defer"]=function(){let Q=this.getAttribute("target");if(!Q)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){e(Q,this);return}let X=this.getAttribute("data-reactive-defer-token");if(!X)return;M(Q,X)},!E&&typeof document<"u"&&document.addEventListener)E=!0,document.addEventListener("turbo:before-stream-render",t)}function t(j){let X=j.target?.getAttribute?.("target");if(!X)return;let Z=X.startsWith("reactive-defer-src-")?X.slice(19):X;if(H.get(Z)?.via==="stream")H.delete(Z)}function M(j,Q){let X=document.getElementById(j);if(!X){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}y(j),v(X);let Z={via:"fetch",abort:new AbortController,timedOut:!1};H.set(j,Z),jj(j,Z,Q)}function e(j,Q){let X=document.getElementById(j);if(!X){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}let Z=Q.getAttribute("data-reactive-defer-src");if(!Z)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let J=Q.getAttribute("data-reactive-defer-token");if(J){M(j,J);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}y(j),v(X);let $=document.createElement("pgbus-stream-source");$.id=h(j),$.setAttribute("src",Z),$.setAttribute("since-id",Q.getAttribute("data-reactive-defer-since-id")??"0"),$.setAttribute("hidden",""),document.body.appendChild($),H.set(j,{via:"stream"})}function h(j){return`reactive-defer-src-${j}`}async function jj(j,Q,X){let Z=setTimeout(()=>{Q.timedOut=!0,Q.abort.abort()},Zj()),$;try{$=await fetch(Qj(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":Xj()},body:JSON.stringify({token:X}),credentials:"same-origin",signal:Q.abort.signal})}catch(G){if(clearTimeout(Z),H.get(j)!==Q)return;console.error("[phlex-reactive] deferred render failed",G),O(j,X);return}if(H.get(j)!==Q){clearTimeout(Z);return}if($.status===204){clearTimeout(Z),S(j);return}if(!$.ok){clearTimeout(Z),console.error(`[phlex-reactive] deferred render failed: HTTP ${$.status}`),O(j,X,$.status);return}let J;try{J=await $.text()}catch(G){if(clearTimeout(Z),H.get(j)!==Q)return;console.error("[phlex-reactive] deferred render failed reading the body",G),O(j,X);return}if(clearTimeout(Z),H.get(j)!==Q)return;S(j),window.Turbo.renderStreamMessage(J)}function y(j){let Q=H.get(j);if(!Q)return;if(H.delete(j),Q.via==="fetch")Q.abort.abort();else document.getElementById(h(j))?.remove?.()}function v(j){j.setAttribute("data-reactive-defer-pending","true"),j.setAttribute("aria-busy","true")}function f(j){j.removeAttribute("data-reactive-defer-pending"),j.removeAttribute("aria-busy")}function S(j){H.delete(j);let Q=document.getElementById(j);if(!Q)return;f(Q),Q.removeAttribute("data-reactive-error")}function O(j,Q,X){H.delete(j);let Z=document.getElementById(j);if(!Z)return;f(Z),Z.setAttribute("data-reactive-error","defer");let $=()=>{let J=document.getElementById(j);if(!J){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}J.removeAttribute("data-reactive-error"),M(j,Q)};Z.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:j,status:X,retry:$}}))}function Qj(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function Xj(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function Zj(){let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Q=Number(j);return Number.isFinite(Q)&&Q>0?Q:30000}var F=!1;function $j(){if(F)return;if(typeof document>"u"||!document.addEventListener)return;F=!0,document.addEventListener("turbo:before-stream-render",Jj)}function Jj(j){let Q=j.detail,X=Q?.render;if(typeof X!=="function"||X.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(B);else setTimeout(B,0);return}let Z=async($)=>{await X($),B()};Z.__reactiveDismissWrapped=!0,Q.render=Z}function B(){let j=document.querySelectorAll("[data-reactive-dismiss-after]");for(let Q of j){if(Q.hasAttribute("data-reactive-dismiss-scheduled"))continue;let X=Number(Q.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite(X)||X<=0)continue;Q.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>Q.remove(),X)}}function Ij(){F=!1}var R=!1;function Gj(){if(R)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;R=!0;let j=()=>{let Q=document.documentElement;if(typeof Q?.toggleAttribute!=="function")return;Q.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};j(),window.addEventListener("online",j),window.addEventListener("offline",j)}function Sj(){R=!1}var D="phlex-reactive:latency",L=!1;function Wj(j){if(typeof sessionStorage>"u")return;sessionStorage.setItem(D,String(j))}function zj(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(D),L=!1}function Kj(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:Wj,disableLatencySim:zj}}function kj(){L=!1}function k(){n(),i(),a(),r(),$j(),Gj(),Kj()}function qj(j){return j.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)k();else document.addEventListener("turbo:load",k,{once:!0});var x=!1;function Yj(){if(x)return;if(typeof document>"u")return;let j=document.querySelectorAll('[data-controller~="reactive"]');if(!j||j.length===0)return;console.warn("[phlex-reactive] found "+j.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}function bj(){x=!1}function wj(){x=!0}if(typeof window<"u"&&typeof document<"u"){let j=()=>setTimeout(Yj,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",j,{once:!0});else j()}var Uj=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function Hj(j){let Q=String(j).toLowerCase();return Q.startsWith("on")||Uj.has(Q)}function Aj(j,Q,X){let[Z,$,J]=Q;j.classList.add(Z,$),X(),requestAnimationFrame(()=>{j.classList.remove($),j.classList.add(J)});let G=!1,W=()=>{if(G)return;G=!0,j.classList.remove(Z,J)};j.addEventListener("animationend",W,{once:!0}),setTimeout(W,350)}var b=Object.freeze({show:(j,Q)=>C(j,!1,Q),hide:(j,Q)=>C(j,!0,Q),toggle:(j,Q)=>C(j,!j.hidden,Q),add_class:(j,Q)=>j.classList.add(...Q.classes??[]),remove_class:(j,Q)=>j.classList.remove(...Q.classes??[]),toggle_class:(j,Q)=>(Q.classes??[]).forEach((X)=>j.classList.toggle(X)),set_attr:(j,Q)=>{if(P(Q.name))j.setAttribute(Q.name,Q.value??"")},remove_attr:(j,Q)=>{if(P(Q.name))j.removeAttribute(Q.name)},toggle_attr:(j,Q)=>{if(!P(Q.name))return;if(j.hasAttribute(Q.name))j.removeAttribute(Q.name);else j.setAttribute(Q.name,"")},focus:(j)=>j.focus?.(),focus_first:(j)=>Cj(j)?.focus?.(),text:(j,Q)=>{let X=String(Q.value??"");if(j.textContent!==X)j.textContent=X},dispatch:(j,Q)=>{j.dispatchEvent(new CustomEvent(Q.name,{bubbles:!0,composed:!0,detail:Q.detail??{}}))}});function C(j,Q,X){if(X?.transition)Aj(j,X.transition,()=>j.hidden=Q);else j.hidden=Q}function P(j){if(!Hj(j))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(j)} — skipped`),!1}var u=/^#[A-Za-z_][\w-]*$/;function _j(j){if(typeof j==="string"&&u.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(j)} — skipped`),!1}function Nj(j,Q){let X=j.getAttribute("data-reactive-show-equals");if(X!==null)return Q===X;let Z=j.getAttribute("data-reactive-show-not");if(Z!==null)return Q!==Z;let $=j.getAttribute("data-reactive-show-in");if($!==null){try{let J=JSON.parse($);if(Array.isArray(J))return J.includes(Q)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify($)} — skipped`),null}for(let J of p){let G=j.getAttribute(`data-reactive-show-${J}`);if(G!==null)return m(J,G,Q)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var p=["gte","gt","lte","lt"];function m(j,Q,X){let Z=Number(Q);if(Number.isNaN(Z))return console.warn(`[phlex-reactive] reactive_show ${j}: needs a numeric literal, got ${JSON.stringify(Q)} — skipped`),null;let $=X==null?"":String(X).trim(),J=$===""?NaN:Number($);if(Number.isNaN(J))return!1;switch(j){case"gte":return J>=Z;case"gt":return J>Z;case"lte":return J<=Z;case"lt":return J<Z;default:return null}}function c(j,Q){if(!j||typeof j!=="object")return null;if(typeof j.equals==="string")return Q===j.equals;if(typeof j.not==="string")return Q!==j.not;if(Array.isArray(j.in))return j.in.includes(Q);for(let X of p)if(X in j)return m(X,j[X],Q);return null}var w="[data-reactive-show-field], [data-reactive-show]";function Vj(j){try{let Q=JSON.parse(j);if(Q&&typeof Q==="object"&&!Array.isArray(Q))return Q}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(j)} — skipped`),null}function T(j,Q){if(!j||typeof j!=="object"||typeof j.field!=="string")return!1;let X=Q(j.field)??"";return c(j,X)===!0}function Lj(j,Q){if(!Array.isArray(j)||j.length===0)return null;return j.some((X)=>Array.isArray(X)&&X.length>0&&X.every((Z)=>T(Z,Q)))}function Mj(j,Q){if(!j||typeof j!=="object")return null;let X=j.any;if(Array.isArray(X)&&(X.length===0||Array.isArray(X[0])))return Lj(X,Q);return xj(j,Q)}function xj(j,Q){let X=Array.isArray(j.all)?"all":Array.isArray(j.any)?"any":null;if(!X)return null;let Z=j[X];if(Z.length===0)return null;let $=Z.map((J)=>T(J,Q));return X==="all"?$.every(Boolean):$.some(Boolean)}function Oj(j){if(typeof j==="string"&&u.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(j)} — skipped`),!1}var Bj='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Cj(j){return j.querySelectorAll?.(Bj)?.[0]??null}function g(j){if(Array.isArray(j))return j;if(typeof j!=="string")return[];try{let Q=JSON.parse(j);return Array.isArray(Q)?Q:[]}catch{return[]}}function d(j,Q){for(let X of j){if(!Array.isArray(X))continue;let[Z,$={}]=X;if(!Object.hasOwn(b,Z)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(Z)} — skipped`);continue}for(let J of Q($))b[Z](J,$)}}function Pj(j,Q){let X=j.to;if(Q){if(X==="@root")return[Q];if(typeof X!=="string"||X==="")return[];if(j.global)return[...document.querySelectorAll(X)];return[...Q.querySelectorAll(X)]}if(typeof X!=="string"||X===""||X==="@root")return[];return[...document.querySelectorAll(X)]}class hj extends l{static values={token:String};#R;#A=new Map;#z=new Map;#d;#M;#D;#x=0;#K=new Map;#O=new WeakMap;#_=new Map;#q;#Y;#U;#j;#X;#N;connect(){if(x=!0,this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-defer-token"))this.#T(),this.#N=()=>this.#T(),this.element.addEventListener?.("turbo:morph-element",this.#N);if(this.#l()){if(this.#q=()=>this.#P(),this.element.addEventListener?.("turbo:morph-element",this.#q),this.#P(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#Yj()}if(this.#Hj())this.#j=()=>this.#f(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#j),this.#f();if(this.#Vj())this.#X=(j)=>{if(j?.type==="input"&&!this.#Lj(j))return;this.#m()},this.element.addEventListener?.("input",this.#X),this.element.addEventListener?.("turbo:morph-element",this.#X),this.#m()}#l(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let j=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let Q of j)if(this.#Q(Q))return!0;return!1}disconnect(){if(this.#t(),this.#jj(),this.#Uj(),this.#Nj(),this.#Mj(),this.#N)this.element.removeEventListener?.("turbo:morph-element",this.#N)}#T(){let j=this.element;if(!j?.id)return;let Q=j.getAttribute?.("data-reactive-defer-token");if(!Q)return;if(j.getAttribute?.("data-reactive-defer-pending")!=="true")return;M(j.id,Q)}dispatch(j){let{action:Q,params:X,debounce:Z,throttle:$,confirm:J,outside:G,window:W,optimistic:z,loading:q}=j.params;if(!Q)return;if(G&&this.element.contains(j.target))return;let U=j.currentTarget??j.target;if(!W&&!this.#Pj(z,U))j.preventDefault();if(!J)return this.#b(U,Q,X,Z,$,z,q);Promise.resolve().then(()=>s(J)).catch(()=>!1).then((K)=>{if(K)this.#b(U,Q,X,Z,$,z,q)})}runOps(j){let{ops:Q,outside:X,window:Z}=j.params;if(X&&this.element.contains(j.target))return;if(!Z)j.preventDefault();this.#Cj(this.#Bj(Q))}trackDirty(){this.#P()}recompute(j){let Q=this.#o(),X=Q.map(([K])=>K),Z=this.#H(),$=new Map,J=(K)=>{if($.has(K))return $.get(K);let Y=null;for(let _ of this.element.querySelectorAll(`[name="${K}"]`))if(Z(_)){Y=_;break}return $.set(K,Y),Y};for(let K of X)this.#S(K,J(K)?.value??"");let G=this.element.getAttribute("data-reactive-compute-reducer-param"),W=G?o(G):null;if(!W){this.#k({},J);return}let z=this.#s("data-reactive-compute-outputs-param"),q={};for(let[K,Y]of Q){let _=J(K);if(Y==="string")q[K]=_?.value??"";else{let N=Number(_?.value);q[K]=Number.isFinite(N)?N:0}}let U=W(q,{changed:this.#n(j,X)})||{};for(let K of z){if(!(K in U))continue;let Y=J(K);if(Y){if(String(U[K])===Y.value)continue;Y.value=U[K],Y.dispatchEvent(new Event("input",{bubbles:!0}))}else this.#S(K,U[K])}this.#k(U,J)}listnavNext(j){this.#I(j,1)}listnavPrev(j){this.#I(j,-1)}listnavPick(j){let Q=this.#B(j),X=Q.findIndex((Z)=>Z.hasAttribute("data-reactive-highlighted"));if(X<0)return;j.preventDefault(),Q[X].click()}listnavClose(j){for(let Q of this.#B(j))Q.removeAttribute("data-reactive-highlighted")}#I(j,Q){let X=this.#B(j);if(!X.length)return;j.preventDefault();let Z=X.findIndex((G)=>G.hasAttribute("data-reactive-highlighted")),$=Z<0?Q>0?0:X.length-1:(Z+Q+X.length)%X.length;for(let G of X)G.removeAttribute("data-reactive-highlighted");let J=X[$];J.setAttribute("data-reactive-highlighted","true"),J.scrollIntoView?.({block:"nearest"})}#B(j){let X=(j?.currentTarget??j?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!X)return[];let Z=this.#H();return Array.from(this.element.querySelectorAll(X)).filter(($)=>!$.hidden&&Z($))}#s(j){let Q=this.element.getAttribute(j);if(!Q)return[];try{let X=JSON.parse(Q);return Array.isArray(X)?X:[]}catch{return[]}}#o(){let j=this.element.getAttribute("data-reactive-compute-inputs-param");if(!j)return[];try{let Q=JSON.parse(j);if(Array.isArray(Q))return Q.map((X)=>[X,"number"]);if(Q&&typeof Q==="object")return Object.entries(Q);return[]}catch{return[]}}#n(j,Q){let X=j?.target;if(!X?.name||typeof X.closest!=="function")return null;if(!Q.includes(X.name))return null;return this.#Q(X)?X.name:null}#S(j,Q){let X=String(Q);for(let Z of this.#i(j)){if(Z.textContent===X)continue;Z.textContent=X}}#i(j){let Q=this.element.querySelectorAll(`[data-reactive-text="${j}"]`);return Array.from(Q).filter((X)=>this.#Q(X))}#k(j,Q){let X=this.#a();for(let[Z,$]of Object.entries(X)){let J=Z in j?j[Z]:Q(Z)?.value;if(J===void 0||J===null)continue;let G=String(J);for(let W of Array.isArray($)?$:[$]){if(!_j(W))continue;for(let z of document.querySelectorAll(W)){if(z.textContent===G)continue;z.textContent=G}}}}#a(){let j=this.element.getAttribute("data-reactive-compute-mirror-param");if(!j)return{};try{let Q=JSON.parse(j);return Q&&typeof Q==="object"&&!Array.isArray(Q)?Q:{}}catch{return{}}}#b(j,Q,X,Z,$,J,G){if(this.#L("reactive:before-dispatch",{action:Q,params:this.#c(X),element:this.element},{cancelable:!0}).defaultPrevented)return;let z=Number(Z)||0;if(z>0)return this.#r(j,z,Q,X,J,G);let q=Number($)||0;if(q>0)return this.#e(j,q,Q,X,J,G);return this.#V(Q,X,J,j,G)}#V(j,Q,X,Z,$){let J=this.#Ej(X,Z),G=this.#Rj(j,Z,$);return this.queue=(this.queue??Promise.resolve()).then(()=>this.#Wj(j,Q,J,G)),this.queue}#r(j,Q,X,Z,$,J){this.#C(j);let G=()=>{this.#C(j),this.#V(X,Z,$,j,J)},W=setTimeout(G,Q);j?.addEventListener?.("blur",G,{once:!0}),this.#A.set(j,{timer:W,flush:G})}#C(j){let Q=this.#A.get(j);if(!Q)return;clearTimeout(Q.timer),j?.removeEventListener?.("blur",Q.flush),this.#A.delete(j)}#t(){for(let j of[...this.#A.keys()])this.#C(j)}#e(j,Q,X,Z,$,J){let G=this.#z.get(j)??new Map;if(G.has(X))return;let W=setTimeout(()=>{if(G.delete(X),G.size===0)this.#z.delete(j)},Q);return G.set(X,W),this.#z.set(j,G),this.#V(X,Z,$,j,J)}#jj(){for(let j of this.#z.values())for(let Q of j.values())clearTimeout(Q);this.#z.clear()}#L(j,Q,{cancelable:X=!1}={}){let Z=new CustomEvent(j,{bubbles:!0,composed:!0,cancelable:X,detail:Q});return(this.element.isConnected?this.element:document).dispatchEvent(Z),Z}#Z(j,Q,X,Z){let $=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#V(j,Q)};this.#L("reactive:error",{action:j,params:X,...Z,retry:$})}#$(j){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",j)}#Qj(){this.element?.removeAttribute?.("data-reactive-error")}#Xj(){let j=document.querySelector("[data-reactive-error-flash]");if(!j?.content)return;let Q=j.getAttribute("data-reactive-error-flash")||"flash",X=document.getElementById(Q);if(!X)return;X.appendChild(j.content.cloneNode(!0))}#Zj(){if(typeof sessionStorage>"u")return Promise.resolve();let j=Number(sessionStorage.getItem(D));if(!Number.isFinite(j)||j<=0)return Promise.resolve();if(!L)L=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${j}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((Q)=>setTimeout(Q,j))}#$j(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#w(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#Jj(j){if(!j)return[];let Q=[],X=/<turbo-stream\b([^>]*)>/g,Z;while((Z=X.exec(j))!==null){let $=Z[1],J=$.match(/\baction="([^"]*)"/)?.[1]??"?",G=$.match(/\btarget="([^"]*)"/)?.[1];Q.push(G?`${J} → #${G}`:J)}return Q}#Gj(j){let{action:Q,status:X,ms:Z}=j,J=`reactive ${this.element?.id?`#${this.element.id} `:""}${Q} → ${X??"—"} (${Math.round(Z)}ms)`;if(console.groupCollapsed(J),console.log(`params: [${j.paramNames.join(", ")}] + collected: [${j.fieldNames.join(", ")}]`),console.log(`encoding: ${j.encoding}`),j.streams.length)console.log(`streams: ${j.streams.join(" ")}`);console.log(`token: ${j.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#Wj(j,Q,X,Z){let{fields:$,files:J}=this.#Kj(),G=this.#c(Q),W={...$,...G},z=this.#J,q=J.length>0,U=q?this.#xj(z,j,W,J):JSON.stringify({token:z,act:j,params:W}),K=this.#$j()?{action:j,paramNames:Object.keys(G),fieldNames:Object.keys($),encoding:q?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#w()}:null;await this.#Zj();try{if(navigator.onLine===!1){this.#G(X),this.#$("offline"),this.#Z(j,Q,W,{kind:"offline"});return}let Y;try{let A={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#hj()};if(!q)A["Content-Type"]="application/json";let V=this.#yj();if(V)A["X-Pgbus-Connection"]=V;Y=await fetch(this.#bj(),{method:"POST",headers:A,body:U,credentials:"same-origin",signal:AbortSignal.timeout(this.#wj())})}catch(A){if(console.error("[phlex-reactive] action error",A),this.#G(X),A?.name==="TimeoutError"||A?.name==="AbortError"){this.#$("timeout"),this.#Z(j,Q,W,{kind:"timeout"});return}this.#Xj(),this.#$("network"),this.#Z(j,Q,W,{kind:"network"});return}if(K)K.status=Y.status;if(Y.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#G(X),this.#$("redirected"),this.#Z(j,Q,W,{kind:"redirected",status:Y.status});return}if(!Y.ok){let A=await Y.text();if(console.error(`[phlex-reactive] action failed: HTTP ${Y.status}`,A),this.#G(X),(Y.headers.get("Content-Type")||"").includes("turbo-stream")){let V=this.#y(A);if(this.#J=V??this.#J,K)this.#h(K,A,V);window.Turbo.renderStreamMessage(A)}this.#$("http"),this.#Z(j,Q,W,{kind:"http",status:Y.status,body:A});return}let _=Y.headers.get("Content-Type")||"";if(!_.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${_}" — no update applied`),this.#G(X),this.#$("content-type"),this.#Z(j,Q,W,{kind:"content-type",status:Y.status});return}let N=await Y.text(),I=this.#y(N);if(this.#J=I??this.#J,K)this.#h(K,N,I);window.Turbo.renderStreamMessage(N),this.#Qj(),this.#L("reactive:applied",{action:j,params:W,html:N})}catch(Y){console.error("[phlex-reactive] action error",Y),this.#G(X),this.#L("reactive:error",{action:j,params:W,kind:"apply"})}finally{if(Z?.(),K)this.#Gj({...K,ms:this.#w()-K.started})}}#h(j,Q,X){j.streams=this.#Jj(Q),j.tokenRefreshed=X!=null}get#J(){return this.#R??this.tokenValue}set#J(j){this.#R=j}#y(j){let Q=this.element.id;if(!Q)return j.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:X,self:Z}=this.#zj(Q),$=j.match(X);if($)return $[1];let J=j.match(Z);if(J)return J[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#zj(j){let Q=this.#D;if(Q&&Q.id===j)return Q;let X=qj(j);return this.#D={id:j,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${X}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${X}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#Q(j){return j.closest('[data-controller~="reactive"]')===this.element}#H(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(Q)=>this.#Q(Q)}#Kj(){let j={},Q=[],X=this.#H();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Z)=>{if(!X(Z))return;if(Z.type==="file")for(let $ of Z.files??[])Q.push({name:Z.name,file:$,multiple:Z.multiple});else if(Z.type==="checkbox")j[Z.name]=Z.checked;else if(Z.type==="radio"){if(Z.checked)j[Z.name]=Z.value}else j[Z.name]=Z.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach((Z)=>{if(!X(Z))return;let $=Z.getAttribute("name");if(!$)return;let J=j[$];if(J==null||J==="")j[$]=Z.value??Z.textContent??Z.innerHTML??""}),{fields:j,files:Q}}#P(){if(typeof this.element?.querySelectorAll!=="function")return;let j=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Q)=>{if(!this.#Q(Q))return;if(Q.type==="file")return;if(this.#qj(Q))Q.setAttribute("data-reactive-dirty","true"),j++;else Q.removeAttribute("data-reactive-dirty")}),j>0)this.element.setAttribute("data-reactive-dirty",String(j));else this.element.removeAttribute("data-reactive-dirty")}#qj(j){if(j.type==="checkbox"||j.type==="radio")return j.checked!==j.defaultChecked;if(j.tag==="select"||j.options)return Array.from(j.options??[]).some((Q)=>Q.selected!==Q.defaultSelected);return j.value!==j.defaultValue}#v(){let j=this.element.getAttribute?.("data-reactive-dirty"),Q=Number(j);return Number.isFinite(Q)&&Q>0?Q:0}#Yj(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#Y=(j)=>{if(this.#v()===0)return;return j.preventDefault(),j.returnValue="You have unsaved changes.",j.returnValue},this.#U=(j)=>{if(this.#v()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))j.preventDefault?.()},window.addEventListener("beforeunload",this.#Y),window.addEventListener("turbo:before-visit",this.#U)}#Uj(){if(this.#q)this.element.removeEventListener?.("turbo:morph-element",this.#q),this.#q=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#Y)window.removeEventListener("beforeunload",this.#Y);if(this.#U)window.removeEventListener("turbo:before-visit",this.#U)}this.#Y=void 0,this.#U=void 0}#Hj(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let j=this.element.querySelectorAll?.(w)??[];for(let Q of j)if(this.#Q(Q))return!0;return!1}#f(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.#H(),Q=this.element.getAttribute?.("data-reactive-scope")||null,X=new Map,Z=($)=>{if(!X.has($))X.set($,this.#p($,j,Q));return X.get($)};for(let $ of this.element.querySelectorAll(w)){if(!j($))continue;let J=$.getAttribute("data-reactive-show");if(J!==null){let q=Mj(Vj(J),Z);if(q!==null)this.#u($,q,j,Q);continue}let G=$.getAttribute("data-reactive-show-field");if(!G)continue;let W=Z(G);if(W===null)continue;let z=Nj($,W);if(z===null)continue;this.#u($,z,j,Q)}this.#Aj(j,X,Q)}#u(j,Q,X,Z){if(j.hidden=!Q,j.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof j.querySelectorAll!=="function")return;for(let $ of j.querySelectorAll("input[name], select[name], textarea[name]"))if(X($))$.disabled=!Q;if(j.name&&X(j))j.disabled=!Q}#Aj(j,Q,X){let Z=this.#_j();for(let[$,J]of Object.entries(Z)){if(!J||typeof J!=="object"||Array.isArray(J))continue;if(!Q.has($))Q.set($,this.#p($,j,X));let G=Q.get($);if(G===null)continue;let W=()=>G;for(let[z,q]of Object.entries(J)){if(!Oj(z))continue;let U;if(Array.isArray(q)){if(q.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${z} — skipped`);continue}U=q.every((K)=>T(K,W))}else{let K=c(q,G);if(K===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${z} — skipped`);continue}U=K}for(let K of document.querySelectorAll(z))K.hidden=!U}}}#_j(){let j=this.element.getAttribute?.("data-reactive-show-targets");if(!j)return{};try{let Q=JSON.parse(j);if(Q&&typeof Q==="object"&&!Array.isArray(Q))return Q}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#p(j,Q,X){let Z=X&&!j.includes("[")?`${X}[${j}]`:j,$=!1,J=null;for(let G of this.element.querySelectorAll(`[name="${Z}"]`)){if(!Q(G))continue;if(G.type==="checkbox")return G.checked?"true":"false";if(G.type==="radio"){if(G.checked)return G.value??"";$=!0;continue}J??=G}if(J)return J.value??"";return $?"":null}#Nj(){if(!this.#j)return;this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.element.removeEventListener?.("turbo:morph-element",this.#j),this.#j=void 0}#Vj(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#Lj(j){let Q=this.element.getAttribute("data-reactive-filter-input");return!!Q&&typeof j.target?.matches==="function"&&j.target.matches(Q)}#m(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.element.getAttribute("data-reactive-filter-input"),Q=this.element.getAttribute("data-reactive-filter-option");if(!j||!Q)return;let X=this.#H(),Z=[...this.element.querySelectorAll(j)].find(X);if(!Z)return;let $=(Z.value??"").trim().toLowerCase(),J=0;for(let z of this.element.querySelectorAll(Q)){if(!X(z))continue;let q=(z.getAttribute("data-reactive-filter-text")??z.textContent??"").toLowerCase(),U=$!==""&&!q.includes($);if(z.hidden=U,U)z.removeAttribute("data-reactive-highlighted");else J++}let G=this.element.getAttribute("data-reactive-filter-group");if(G)for(let z of this.element.querySelectorAll(G)){if(!X(z))continue;let q=[...z.querySelectorAll(Q)].filter(X);if(q.length===0)continue;z.hidden=q.every((U)=>U.hidden)}let W=this.element.getAttribute("data-reactive-filter-empty");if(W){for(let z of this.element.querySelectorAll(W))if(X(z))z.hidden=J>0}}#Mj(){if(!this.#X)return;this.element.removeEventListener?.("input",this.#X),this.element.removeEventListener?.("turbo:morph-element",this.#X),this.#X=void 0}#xj(j,Q,X,Z){let $=new FormData;$.append("token",j),$.append("act",Q);for(let[G,W]of Object.entries(X))this.#E($,`params[${G}]`,W);let J=this.#Oj(Z);for(let{name:G,file:W,multiple:z}of Z){let U=z||J.has(G)?`params[${G}][]`:`params[${G}]`;$.append(U,W,W.name)}return $}#E(j,Q,X){if(X==null)j.append(Q,"");else if(Array.isArray(X))X.forEach((Z,$)=>this.#E(j,`${Q}[${$}]`,Z));else if(typeof X==="object")for(let[Z,$]of Object.entries(X))this.#E(j,`${Q}[${Z}]`,$);else j.append(Q,String(X))}#Oj(j){let Q=new Map;for(let{name:X}of j)Q.set(X,(Q.get(X)??0)+1);return new Set([...Q].filter(([,X])=>X>1).map(([X])=>X))}#c(j){if(!j)return{};try{return typeof j==="string"?JSON.parse(j):j}catch{return{}}}#Bj(j){return g(j)}#Cj(j){d(j,(Q)=>this.#F(Q))}#F(j){let Q=j.to;if(Q==="@root")return[this.element];if(typeof Q!=="string"||Q==="")return[];if(j.global)return[...document.querySelectorAll(Q)];return[...this.element.querySelectorAll(Q)].filter((X)=>this.#Q(X))}#Pj(j,Q){if(j?.checked!=="keep")return!1;let X=Q?.type;return X==="checkbox"||X==="radio"}#Ej(j,Q){if(!j)return null;let X=this.#Fj(j,Q),Z=[];for(let $ of X){if(j.add_class){let J=j.add_class.filter((G)=>!$.classList.contains(G));if($.classList.add(...J),J.length)Z.push(()=>$.classList.remove(...J))}if(j.remove_class){let J=j.remove_class.filter((G)=>$.classList.contains(G));if($.classList.remove(...J),J.length)Z.push(()=>$.classList.add(...J))}if(j.toggle_class)j.toggle_class.forEach((J)=>$.classList.toggle(J)),Z.push(()=>j.toggle_class.forEach((J)=>$.classList.toggle(J)));if(j.hide)$.hidden=!0,Z.push(()=>$.hidden=!1)}if(j.checked==="keep"&&Q&&"checked"in Q){let $=Q.checked;Z.push(()=>Q.checked=!$)}return Z.length?Z:null}#G(j){if(!j)return;if(!this.element.isConnected)return;for(let Q of j)Q()}#Fj(j,Q){if(j.to==null)return Q?[Q]:[];return this.#F({to:j.to})}#Rj(j,Q,X){this.#Dj(j,Q);let Z=this.#Ij(j,Q,X),$=!1;return()=>{if($)return;$=!0,this.#Tj(j,Q),Z()}}#Dj(j,Q){if(this.#W(Q,j,1),this.#W(this.element,j,1),this.#K.set(j,(this.#K.get(j)??0)+1),this.#x++===0)this.element.setAttribute("aria-busy","true");for(let X of this.#g(j))this.#W(X,j,1)}#Tj(j,Q){this.#W(Q,j,-1),this.#W(this.element,j,-1);let X=(this.#K.get(j)??1)-1;if(X<=0)this.#K.delete(j);else this.#K.set(j,X);if(--this.#x<=0)this.#x=0,this.element.removeAttribute("aria-busy");for(let Z of this.#g(j))this.#W(Z,j,-1)}#W(j,Q,X){if(!j||typeof j.getAttribute!=="function")return;let Z=this.#O.get(j)??new Map,$=(Z.get(Q)??0)+X;if($<=0)Z.delete(Q);else Z.set(Q,$);if(Z.size===0){this.#O.delete(j),j.removeAttribute("data-reactive-busy");return}this.#O.set(j,Z),j.setAttribute("data-reactive-busy",[...Z.keys()].join(" "))}#g(j){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter((X)=>X.getAttribute("data-reactive-busy-on")===j&&this.#Q(X))}#Ij(j,Q,X){if(!X||!Q)return()=>{};let Z=this.#kj(X,Q),$=Array.isArray(X.class)?X.class:[],J=[];for(let W of Z){let z=$.filter((q)=>!W.classList.contains(q));if(W.classList.add(...z),z.length)J.push([W,z])}let G=this.#_.get(Q);if(G)G.count++;else if(X.disable||X.text!=null)this.#_.set(Q,{count:1,disabled:Q.disabled,text:Q.textContent,hadText:X.text!=null});if(X.disable)Q.disabled=!0;if(X.text!=null)Q.textContent=X.text;return()=>{for(let[W,z]of J)if(W.isConnected)W.classList.remove(...z);this.#Sj(Q,X)}}#Sj(j,Q){let X=this.#_.get(j);if(!X)return;if(--X.count>0)return;if(this.#_.delete(j),!j.isConnected)return;if(Q.disable)j.disabled=X.disabled;if(X.hadText&&j.textContent===Q.text)j.textContent=X.text}#kj(j,Q){if(j.to==null)return Q?[Q]:[];return this.#F({to:j.to})}#bj(){return this.#d??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#wj(){if(this.#M!=null)return this.#M;let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Q=Number(j);return this.#M=Number.isFinite(Q)&&Q>0?Q:30000}#hj(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#yj(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{Dj as resetReactiveDefers,n as registerReactiveVisit,i as registerReactiveToken,Gj as registerReactiveOffline,a as registerReactiveJs,$j as registerReactiveDismiss,r as registerReactiveDefer,k as registerReactiveActions,Tj as pendingDeferVia,qj as escapeRegExp,Wj as enableLatencySim,zj as disableLatencySim,hj as default,Yj as checkReactiveRegistration,bj as __resetReactiveRegistrationForTest,Sj as __resetReactiveOfflineForTest,kj as __resetReactiveLatencyForTest,Ij as __resetReactiveDismissForTest,wj as __markReactiveConnectedForTest,D as LATENCY_KEY};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=B4ECABAC7819655164756E2164756E21
|
|
4
4
|
//# sourceMappingURL=reactive_controller.min.js.map
|