phlex-reactive 0.15.1 → 0.15.3

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -0
  3. data/README.md +82 -9
  4. data/app/controllers/phlex/reactive/actions_controller.rb +4 -1
  5. data/app/javascript/phlex/reactive/compute.js +14 -1
  6. data/app/javascript/phlex/reactive/compute.min.js +2 -2
  7. data/app/javascript/phlex/reactive/compute.min.js.map +3 -3
  8. data/app/javascript/phlex/reactive/core.min.js +4 -3
  9. data/app/javascript/phlex/reactive/core.min.js.map +3 -3
  10. data/app/javascript/phlex/reactive/features/bindings.js +66 -0
  11. data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
  12. data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
  13. data/app/javascript/phlex/reactive/features/form.js +54 -22
  14. data/app/javascript/phlex/reactive/features/form.min.js +2 -2
  15. data/app/javascript/phlex/reactive/features/form.min.js.map +3 -3
  16. data/app/javascript/phlex/reactive/features/persist.js +31 -2
  17. data/app/javascript/phlex/reactive/features/persist.min.js +2 -2
  18. data/app/javascript/phlex/reactive/features/persist.min.js.map +3 -3
  19. data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -3
  20. data/app/javascript/phlex/reactive/reactive_controller.min.js.map +6 -6
  21. data/app/javascript/phlex/reactive/runtime.js +476 -13
  22. data/lib/phlex/reactive/component/helpers.rb +24 -5
  23. data/lib/phlex/reactive/component/identity.rb +34 -1
  24. data/lib/phlex/reactive/component/image_fallback_binding.rb +54 -0
  25. data/lib/phlex/reactive/component/media_binding.rb +103 -0
  26. data/lib/phlex/reactive/component/media_pattern.rb +79 -0
  27. data/lib/phlex/reactive/doctor.rb +20 -8
  28. data/lib/phlex/reactive/held_fields.rb +87 -0
  29. data/lib/phlex/reactive/js.rb +153 -29
  30. data/lib/phlex/reactive/streamable.rb +26 -2
  31. data/lib/phlex/reactive/test_helpers.rb +11 -2
  32. data/lib/phlex/reactive/version.rb +1 -1
  33. metadata +5 -1
@@ -377,6 +377,7 @@ function showSyncEnabled(c) {
377
377
  const nodes = c.root.querySelectorAll?.(SHOW_BINDING_SELECTOR) ?? []
378
378
  for (const el of nodes) if (c.core.owns(el)) return true
379
379
  for (const el of c.root.querySelectorAll?.(GROUP_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true
380
+ for (const el of c.root.querySelectorAll?.(MEDIA_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true
380
381
  return false
381
382
  }
382
383
 
@@ -466,6 +467,7 @@ function syncShow(c) {
466
467
  // The group bindings (issue #319) and the cross-root pass (issue #164)
467
468
  // share the same owned-field memo, so a field driving several reads once.
468
469
  if (c.groups) syncGroups(c, fieldValue, owns, scope)
470
+ syncMedia(c, fieldValue, owns)
469
471
  syncShowTargets(c, fieldValue)
470
472
  syncGroupTargets(c, fieldValue)
471
473
  }
@@ -635,6 +637,70 @@ function fieldResolver(c, owns) {
635
637
  }
636
638
  }
637
639
 
640
+ // --- Media bindings (issue #373) ---------------------------------------------
641
+ // data-reactive-media='{"field","take"?,"match"?,"pending"?,"pool","label"?,"blank"?}'
642
+ // on a preview <img>: the owned field's value (its first `take` characters, or
643
+ // all of it) is a key into `pool`, whose value is a SELECTOR for an <img>
644
+ // inside the root. The core's copyMedia copies that element's src, srcset and
645
+ // alt — a pool value is never assigned to anything. A key, selector or element
646
+ // that misses clears the preview; `label` gets the alt (or `blank`) via
647
+ // textContent. A key shorter than `take`, or one the anchored `match` source
648
+ // rejects, is PENDING (issue #380) and never gets `blank`: "keep" leaves the
649
+ // preview and label alone, the default clears both. The preview's
650
+ // data-reactive-media-state names the outcome (pending | hit | miss). An
651
+ // unowned field leaves the preview alone (the show default-deny); a malformed
652
+ // attr warns and is skipped.
653
+ const MEDIA_BINDING_SELECTOR = "[data-reactive-media]"
654
+
655
+ function syncMedia(c, fieldValue, owns) {
656
+ for (const el of c.root.querySelectorAll(MEDIA_BINDING_SELECTOR)) {
657
+ if (!owns(el)) continue
658
+ let wire, pattern
659
+ try {
660
+ wire = JSON.parse(el.getAttribute("data-reactive-media"))
661
+ if (wire?.match != null) pattern = new RegExp(`^(?:${wire.match})$`)
662
+ } catch {}
663
+ if (typeof wire?.field !== "string" || !wire.pool || typeof wire.pool !== "object" || (wire.match != null && !pattern)) {
664
+ console.warn("[phlex-reactive] malformed data-reactive-media — skipped")
665
+ continue
666
+ }
667
+ const value = fieldValue(wire.field)
668
+ if (value === null) continue
669
+ // Characters (code points), as Ruby counts them — not UTF-16 code units.
670
+ const chars = wire.take > 0 ? [...value] : null
671
+ const key = chars ? chars.slice(0, wire.take).join("") : value
672
+ const pending = chars?.length < wire.take || pattern?.test(key) === false
673
+ if (pending && wire.pending === "keep") {
674
+ mediaState(el, "pending")
675
+ continue
676
+ }
677
+ const source = !pending && Object.hasOwn(wire.pool, key) ? mediaMatches(c, wire.pool[key])[0] : null
678
+ const alt = c.core.copyMedia(el, source ?? null)
679
+ if (alt === null) continue
680
+ mediaState(el, pending ? "pending" : alt === false ? "miss" : "hit")
681
+ if (typeof wire.label !== "string") continue
682
+ const text = pending ? "" : alt === false ? String(wire.blank ?? "") : alt
683
+ for (const node of mediaMatches(c, wire.label)) if (node.textContent !== text) node.textContent = text
684
+ }
685
+ }
686
+
687
+ // The outcome a stylesheet (or :has()) can hide a row on, written only when
688
+ // it changes.
689
+ function mediaState(el, state) {
690
+ if (el.getAttribute("data-reactive-media-state") !== state) el.setAttribute("data-reactive-media-state", state)
691
+ }
692
+
693
+ // The owned elements a pool or label selector names — none when it does not
694
+ // parse (a URL string is not a selector), and none for the "@root" sentinel:
695
+ // a label naming the root would rewrite the whole root's text.
696
+ function mediaMatches(c, selector) {
697
+ try {
698
+ return selector === "@root" ? [] : c.core.opTargets({ to: selector })
699
+ } catch {
700
+ return []
701
+ }
702
+ }
703
+
638
704
  // --- Bulk selection (issue #319) ---------------------------------------------
639
705
  // A checkbox GROUP is the owned boxes sharing one name. Three bindings read it:
640
706
  // the select-all header (data-reactive-select-all), the ticked count
@@ -1,4 +1,4 @@
1
- import{confirmPredicate as oe}from"phlex/reactive/confirm_predicate";var g=new WeakMap,q=new WeakMap;function p(e,r){let t=g.get(e);if(!t){if(g.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)q.set(t.root,t)}return t}function d(e,r,t){for(let n of r.split(" "))e.root.addEventListener?.(n,t),e.offs.push(()=>e.root.removeEventListener?.(n,t))}function y(e,r){d(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Pe(e,r){let t=p(e,r);t.offs=[],t.seeds=[];let n,o,i,s;(t.wire=()=>{if(!t.show&&le(t))t.show=(a)=>{if(t.flipping)return;if(a?.type==="input"||a?.type==="change")me(t,a.target);else t.groups=ye(t),T(t);de(t)},d(t,"input change",t.show),y(t,t.show);if(!t.arm&&ue(t))d(t,"input change",(a)=>R(t,a)),y(t,t.arm=()=>{R(t,null),T(t)});if(!n&&(n=t.show||t.arm))d(t,"reset",()=>setTimeout(()=>{if(g.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!o&&(o=b(t)))d(t,"input",(a)=>be(t,a)&&m(t)),y(t,()=>m(t));if(!i&&(i=S(t)))y(t,()=>H(t));if(!s&&(s=we(t)))d(t,"input change",(a)=>Q(t,a)),y(t,()=>O(t))})(),d(t,"keydown",(a)=>Le(t,a)),d(t,"paste drop",(a)=>Me(t,a)),d(t,"input",(a)=>De(t,a))}function We(e){let r=g.get(e);if(!r)return;g.delete(e),q.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function Ge(e){let r=g.get(e);if(!r)return;for(let t of r.seeds)t();r.wire()}function h(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function Fe(e,r,t){return Q(p(e,r),t)}function Be(e,r,t){return re(p(e,r),t)}var ie=/^#[A-Za-z_][\w-]*$/,M="[data-reactive-show-field], [data-reactive-show]";function se(e,r){let t=e.getAttribute("data-reactive-show-equals");if(t!==null)return r===t;let n=e.getAttribute("data-reactive-show-not");if(n!==null)return r!==n;let o=e.getAttribute("data-reactive-show-in");if(o!==null){try{let i=JSON.parse(o);if(Array.isArray(i))return i.includes(r)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(o)} — skipped`),null}for(let i of Z){let s=e.getAttribute(`data-reactive-show-${i}`);if(s!==null)return ee(i,s,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function D(e){try{let r=JSON.parse(e);if(r&&typeof r==="object"&&!Array.isArray(r))return r}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(e)} — skipped`),null}function ae(e){try{let r=JSON.parse(e);if(Array.isArray(r)&&r.every((t)=>t&&typeof t==="object"&&Array.isArray(t.any)&&Array.isArray(t.ops)))return r}catch{}return console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(e)} — skipped`),[]}function I(e,r){if(!e||typeof e!=="object")return null;let t=e.any;if(Array.isArray(t)&&(t.length===0||Array.isArray(t[0])))return v(t,r);return ce(e,r)}function ce(e,r){let t=Array.isArray(e.all)?"all":Array.isArray(e.any)?"any":null;if(!t)return null;let n=e[t];if(n.length===0)return null;let o=n.map((i)=>E(i,r));return t==="all"?o.every(Boolean):o.some(Boolean)}function A(e,r="show"){if(typeof e==="string"&&ie.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function le(e){if(e.root.getAttribute?.("data-reactive-show-targets"))return!0;if(e.root.getAttribute?.("data-reactive-group-targets"))return!0;let r=e.root.querySelectorAll?.(M)??[];for(let t of r)if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(w)??[])if(e.core.owns(t))return!0;return!1}function ue(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function fe(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:ae(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function R(e,r){let t=fe(e);if(!t.length)return;let n=G(e,e.core.ownership());t.forEach((o,i)=>{let s=v(o.any,n);if(s===null)return;let a=s&&!e.onCompleteStates[i]&&Boolean(r);if(e.onCompleteStates[i]=s,a)e.core.applyOps(o.ops,"@root")})}function de(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,n=G(e,r);for(let o of e.root.querySelectorAll(M)){if(!r(o))continue;let i=o.getAttribute("data-reactive-show");if(i!==null){let c=I(D(i),n);if(c!==null)J(o,c,r,t);continue}let s=o.getAttribute("data-reactive-show-field");if(!s)continue;let a=n(s);if(a===null)continue;let l=se(o,a);if(l===null)continue;J(o,l,r,t)}if(e.groups)ve(e,n,r,t);pe(e,n),Ae(e,n)}function J(e,r,t,n){if(e.hidden=!r,e.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof e.querySelectorAll!=="function")return;for(let o of e.querySelectorAll("input[name], select[name], textarea[name]"))if(t(o))o.disabled=!r;if(e.name&&t(e))e.disabled=!r}function pe(e,r){let t=P(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){ge(n,o,r);continue}if(!o||typeof o!=="object"||Array.isArray(o))continue;let i=r(n);if(i===null)continue;let s=(a,l)=>l?r(n,!0):i;for(let[a,l]of Object.entries(o)){if(!A(a))continue;let c;if(Array.isArray(l)){if(l.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${a} — skipped`);continue}c=l.every((u)=>E(u,s))}else{let u=te(l,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${a} — skipped`);continue}c=u}for(let u of document.querySelectorAll(a))u.hidden=!c}}}function ge(e,r,t){if(!A(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Je(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((s)=>t(s)===null)&&!Te(n))return;let i=v(n,t);if(i===null)return;for(let s of document.querySelectorAll(e))s.hidden=!i}function P(e,r="show",t="mode: { ... }, kind: { ... }"){let n=e.root.getAttribute?.(`data-reactive-${r}-targets`);if(!n)return{};try{let o=JSON.parse(n);if(o&&typeof o==="object"&&!Array.isArray(o))return o}catch{}return console.warn(`[phlex-reactive] malformed data-reactive-${r}-targets — ignored. `+`Did two reactive_${r}_targets calls collide on one root? Declare every entry in ONE call: reactive_${r}_targets(${t})`),{}}function he(e,r,t,n){let o=!1,i=null;for(let s of e.root.querySelectorAll(W(r,n))){if(!t(s))continue;if(s.type==="checkbox")return s.checked?"true":"false";if(s.type==="radio"){if(s.checked)return s.value??"";o=!0;continue}i??=s}if(i)return i.value??"";return o?"":null}function W(e,r){return`[name="${r&&!e.includes("[")?`${r}[${e}]`:e}"]`}function G(e,r){let t=e.root.getAttribute?.("data-reactive-scope")||null,n=new Map;return(o,i)=>{let s=i?`#${o}`:o;if(!n.has(s))n.set(s,i?_(e,o,r,t).filter((a)=>a.checked).length:he(e,o,r,t));return n.get(s)}}var w="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function ye(e){return!!e.root.querySelector?.(w)}function _(e,r,t,n){return[...e.root.querySelectorAll(W(r,n))].filter((o)=>o.type==="checkbox"&&t(o))}function me(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))F(e,t,r.checked,r)}function Ke(e,r,t){if(!r)return;let n=q.get(e)??{root:e,core:{ownership:()=>(o)=>o.closest('[data-controller~="reactive"]')===e}};F(n,r,t!==!1),n.show?.()}function F(e,r,t,n){let o=e.root.getAttribute?.("data-reactive-scope")||null,i=_(e,r,e.core.ownership(),o).filter((s)=>s!==n&&s.checked!==t);for(let s of i)s.checked=t;e.flipping=!0;try{for(let s of i)for(let a of["input","change"])s.dispatchEvent?.(new Event(a,{bubbles:!0}))}finally{e.flipping=!1}}function ve(e,r,t,n){for(let o of e.root.querySelectorAll(w)){if(!t(o))continue;let i=o.getAttribute("data-reactive-enable");if(i!==null){let l=I(D(i),r);if(l!==null)o.disabled=!l}let s=o.getAttribute("data-reactive-count");if(s){let l=String(r(s,!0));if(o.textContent!==l)o.textContent=l}let a=o.getAttribute("data-reactive-select-all");if(a){let l=_(e,a,t,n).filter((u)=>u!==o),c=l.filter((u)=>u.checked).length;o.checked=c>0&&c===l.length,o.indeterminate=c>0&&c<l.length}}}function Ae(e,r){for(let[t,n]of Object.entries(P(e,"group",'"ids[]" => { ... }, "tags[]" => { ... }'))){if(!n||typeof n!=="object"||Array.isArray(n))continue;let o=String(r(t,!0));for(let s of Array.isArray(n.count)?n.count:[]){if(!A(s,"group"))continue;for(let a of document.querySelectorAll(s))if(a.textContent!==o)a.textContent=o}let i=n.enable&&typeof n.enable==="object"?n.enable:{};for(let[s,a]of Object.entries(i)){if(!A(s,"group"))continue;let l=v(a?.any,r);if(l===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${s} — skipped`);continue}for(let c of document.querySelectorAll(s))c.disabled=!l}}}function T(e){if(e.groupObserver||typeof MutationObserver!=="function")return;let r=(o)=>e.root.getAttribute?.(`data-reactive-${o}`)?.includes("checked_");if(!e.root.getAttribute?.("data-reactive-group-targets")&&!e.root.querySelector?.(`${w}, [data-reactive-show*=checked_]`)&&!r("show-targets")&&!r("on-complete"))return;let n=(o)=>o.nodeType===1&&(o.matches('input[type="checkbox"]')||!!o.querySelector('input[type="checkbox"]'));e.groupObserver=new MutationObserver((o)=>{if(!o.some((i)=>[...i.addedNodes,...i.removedNodes].some(n)))return;e.show?.(),e.arm?.()}),e.groupObserver.observe(e.root,{childList:!0,subtree:!0})}function b(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function be(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function m(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.root.getAttribute("data-reactive-filter-input"),t=e.root.getAttribute("data-reactive-filter-option");if(!r||!t)return;let n=e.core.ownership(),o=[...e.root.querySelectorAll(r)].find(n);if(!o)return;let i=(o.value??"").trim().toLowerCase(),s=0;for(let c of e.root.querySelectorAll(t)){if(!n(c))continue;let u=(c.getAttribute("data-reactive-filter-text")??c.textContent??"").toLowerCase(),f=c.hasAttribute?.("data-reactive-tags-selected")||i!==""&&!u.includes(i);if(c.hidden=f,f)c.removeAttribute("data-reactive-highlighted");else s++}let a=e.root.getAttribute("data-reactive-filter-group");if(a)for(let c of e.root.querySelectorAll(a)){if(!n(c))continue;let u=[...c.querySelectorAll(t)].filter(n);if(u.length===0)continue;c.hidden=u.every((f)=>f.hidden)}let l=e.root.getAttribute("data-reactive-filter-empty");if(l){for(let c of e.root.querySelectorAll(l))if(n(c))c.hidden=s>0}}function S(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function we(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function N(e){if(typeof e.root?.querySelectorAll!=="function")return null;let r=e.root.getAttribute("data-reactive-tags-field");if(!r)return null;let t=e.core.ownership();return[...e.root.querySelectorAll(r)].find(t)??null}function k(e){let r=new Set,t=[];for(let n of String(e.value??"").split(",")){let o=n.trim();if(o===""||r.has(o.toLowerCase()))continue;r.add(o.toLowerCase()),t.push(o)}return t}function B(e,r){let t=N(e);if(!t)return!1;let n=k(t),o=new Set(n.map((s)=>s.toLowerCase())),i=!1;for(let s of r){let a=String(s??"").trim();if(a===""||o.has(a.toLowerCase()))continue;o.add(a.toLowerCase()),n.push(a),i=!0}if(i)K(e,t,n);return i}function K(e,r,t){r.value=t.join(","),h(r),H(e)}function Se(e){if(typeof e.root?.querySelectorAll!=="function")return null;let r=e.root.getAttribute("data-reactive-filter-input");if(!r)return null;let t=e.core.ownership();return[...e.root.querySelectorAll(r)].find(t)??null}function H(e){let r=N(e);if(!r)return;let t=k(r),n=e.core.ownership();xe(e,t,n),Ce(e,t,n)}function xe(e,r,t){let n=[...e.root.querySelectorAll("[data-reactive-tags-list]")].find(t);if(!n)return;let i=[...e.root.querySelectorAll("[data-reactive-tags-template]")].find(t)?.content?.firstElementChild;if(!i){if(!e.tagsWarnedTemplate)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),e.tagsWarnedTemplate=!0;return}while(n.firstChild)n.removeChild(n.firstChild);for(let s of r){let a=i.cloneNode(!0);a.setAttribute?.("data-reactive-tag",s);let l=a.matches?.("[data-reactive-tag-text]")?a:(a.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(l)l.textContent=s;let c=[...a.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(a.matches?.('[data-action*="reactive#tagsRemove"]'))c.push(a);for(let u of c)u.setAttribute?.("data-reactive-tag-param",s);n.appendChild(a)}}function Ce(e,r,t){let n=new Set(r.map((o)=>o.toLowerCase()));for(let o of e.root.querySelectorAll("[role=option]")){if(!t(o))continue;let i=o.getAttribute?.("data-reactive-tag-param");if(!i)continue;if(n.has(i.toLowerCase()))o.setAttribute("data-reactive-tags-selected","true"),o.hidden=!0,o.removeAttribute?.("data-reactive-highlighted");else if(o.hasAttribute?.("data-reactive-tags-selected")){if(o.removeAttribute("data-reactive-tags-selected"),!b(e))o.hidden=!1}}if(b(e))m(e)}function qe(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function _e(e,r){let t=[e,...e.querySelectorAll?.("*")??[]];for(let n of t)for(let o of["name","id","for"]){let i=n.getAttribute?.(o);if(i&&i.includes("NEW_ROW"))n.setAttribute?.(o,i.replaceAll("NEW_ROW",String(r)))}}function Ne(e,r){if(e.nestedWarned)return;console.warn(`[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${r}"] container + <template data-reactive-nested-template="${r}"> pair found in this root — the add `+"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / reactive_nested_template)."),e.nestedWarned=!0}function ke(e,r,t,n){if(!t)return null;let o;try{o=JSON.parse(t)}catch{return null}if(!o||typeof o!=="object")return null;let i=e.core.ownership(),s=[...r.querySelectorAll?.("input, select, textarea")??[]],a=[];for(let[l,c]of Object.entries(o)){let u=[...e.root.querySelectorAll?.(c)??[]].find(i);if(!u)continue;let f=s.find((x)=>Y(x.getAttribute?.("name"))===l);if(!f)continue;Oe(f,u),a.push(u)}if(n)for(let l of a)Ee(l);return a[0]??null}function Oe(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:C(r)!=="";else e.value=C(r);h(e)}function Ee(e){if(e.type==="checkbox")e.checked=!1;else e.value="";h(e)}function $e(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,n)=>Object.prototype.hasOwnProperty.call(r,n)?r[n]:t)}function L(e,r){let t=[...r.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(t)t.value="1",h(t),r.hidden=!0;else r.parentNode?.removeChild?.(r);O(e)}function O(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership();for(let t of[...e.root.querySelectorAll("[data-reactive-nested-json]")].filter(r))U(e,t.getAttribute("data-reactive-nested-json"))}function je(e,r){let t=r.getAttribute?.("data-reactive-nested-json-field");if(!t)return null;let n=e.core.ownership();return[...e.root.querySelectorAll(t)].find(n)??null}function z(e,r){let t={},n=e.core.ownership();for(let o of[...r.querySelectorAll?.("input, select, textarea")??[]]){if(o.matches(":disabled")||o.type==="radio"&&!o.checked||!n(o))continue;let i=Y(o.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=C(o)}return t}function Y(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function C(e){if(e.type==="checkbox")return e.checked?e.value||"on":"";return e.value??""}function U(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=je(e,n);if(!o)return;let i=[];for(let a of[...n.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!t(a)||a.hidden)continue;i.push(z(e,a))}let s=JSON.stringify(i);if(o.value===s)return;o.value=s,h(o)}function He(e,r,t){let n=p(e,r);if(!S(n))return;if(t?.defaultPrevented)return;if(n.core.listnavOptions(t).some((s)=>s.hasAttribute?.("data-reactive-highlighted")))return;t?.preventDefault?.();let o=t?.currentTarget??t?.target;if(!o)return;if(!B(n,String(o.value??"").split(",")))return;if(o.value="",b(n))m(n)}function ze(e,r,t){let n=p(e,r);if(!S(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!B(n,[i]))return;let s=Se(n);if(!s)return;s.value="",m(n),s.focus?.()}function Ye(e,r,t){let n=p(e,r);if(!S(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let s=N(n);if(!s)return;let a=k(s),l=a.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===a.length)return;K(n,s,l)}function Ue(e,r,t){let n=p(e,r);t?.preventDefault?.();let o=t?.currentTarget??t?.target,i=o?.getAttribute?.("data-reactive-association-param");if(!i)return;if(typeof n.root?.querySelectorAll!=="function")return;let s=n.core.ownership(),a=[...n.root.querySelectorAll(`[data-reactive-nested-list="${i}"]`)].find(s),c=[...n.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(s)?.content?.firstElementChild;if(!a||!c){Ne(n,i);return}let u=c.cloneNode(!0);_e(u,qe(n)),a.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),x=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",ne=ke(n,u,f,x);if(f)ne?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(a.getAttribute?.("data-reactive-nested-json")===i)U(n,i)}function Qe(e,r,t){let n=p(e,r);t?.preventDefault?.();let o=t?.currentTarget??t?.target,i=o?.closest?.("[data-reactive-nested-row]");if(!i)return;if(i.closest?.('[data-controller~="reactive"]')!==n.root)return;let s=o?.getAttribute?.("data-reactive-confirm-param"),a=o?.getAttribute?.("data-reactive-confirm-when-param"),l=s||(a?re(n,a):null);if(!l)return L(n,i);let c=z(n,i),u=$e(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)L(n,i)})}function Q(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;O(e)}var Z=["gte","gt","lte","lt"],Re=["len_eq","len_gte","len_gt","len_lte","len_lt"],V=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function X(e,r,t){if(!Number.isInteger(r))return console.warn(`[phlex-reactive] reactive_show ${e}: needs an integer literal, got ${JSON.stringify(r)} — skipped`),null;switch(e.slice(e.indexOf("_")+1)){case"eq":return t===r;case"gte":return t>=r;case"gt":return t>r;case"lte":return t<=r;case"lt":return t<r;default:return null}}function ee(e,r,t){let n=Number(r);if(Number.isNaN(n))return console.warn(`[phlex-reactive] reactive_show ${e}: needs a numeric literal, got ${JSON.stringify(r)} — skipped`),null;let o=t==null?"":String(t).trim(),i=o===""?NaN:Number(o);if(Number.isNaN(i))return!1;switch(e){case"gte":return i>=n;case"gt":return i>n;case"lte":return i<=n;case"lt":return i<n;default:return null}}function te(e,r){if(!e||typeof e!=="object")return null;if(typeof e.equals==="string")return r===e.equals;if(typeof e.not==="string")return r!==e.not;if(Array.isArray(e.in))return e.in.includes(r);for(let t of Z)if(t in e)return ee(t,e[t],r);for(let t of Re)if(t in e)return X(t,e[t],[...String(r??"")].length);return null}function E(e,r){if(!e||typeof e!=="object"||typeof e.field!=="string")return!1;let t=V.find((o)=>(o in e));if(t){let o=r(e.field,!0);return X(t,e[t],Array.isArray(o)?o.length:Number(o)||0)===!0}let n=r(e.field)??"";return te(e,n)===!0}function v(e,r){if(!Array.isArray(e)||e.length===0)return null;return e.some((t)=>Array.isArray(t)&&t.length>0&&t.every((n)=>E(n,r)))}function Je(e){if(!Array.isArray(e)||e.length===0)return null;let r=new Set;for(let t of e){if(!Array.isArray(t))continue;for(let n of t)if(n&&typeof n==="object"&&typeof n.field==="string")r.add(n.field)}return r.size>0?[...r]:null}function Te(e){return e.flat().every((r)=>V.some((t)=>(t in Object(r))))}function re(e,r){let t=r;if(typeof r==="string")try{t=JSON.parse(r)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(r)} — skipped`),null}if(!t||typeof t!=="object")return null;let{fields:n}=e.core.collectFields(),o=(s)=>n[s],i;if(typeof t.predicate==="string"){let s=oe(t.predicate);if(!s)return console.warn(`[phlex-reactive] confirm predicate "${t.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;i=!!s(n)}else i=v(t.groups?.any,o)===!0;return i?t.message:null}function j(e,r){let t=r.target?.closest?.("[data-reactive-editable]");return t&&e.core.owns(t)?t:null}function Le(e,r){let t=j(e,r);if(!t||r.isComposing||r.keyCode===229)return;if(r.key==="Enter"){let n=t.getAttribute("data-reactive-editable-enter");if(t.getAttribute("data-reactive-editable")==="multi"&&(n==null||r.shiftKey))return;if(r.preventDefault(),n==="submit")t.closest("form")?.requestSubmit();else if(n)e.core.applyOps(JSON.parse(n),"@root")}else if(r.key==="Escape"&&t.hasAttribute("data-reactive-editable-escape"))r.preventDefault(),t.textContent=e.core.served(t),h(t),t.blur()}function Me(e,r){let t=j(e,r),n=t?.getAttribute("data-reactive-editable")!=="multi",o=(r.clipboardData??r.dataTransfer)?.getData("text/plain");if(!t||o==null||e.core.plain()&&!(n&&/[\r\n]/.test(o)))return;if(r.preventDefault(),n)o=o.replace(/\r?\n|\r/g," ");t.focus(),document.execCommand("insertText",!1,o)}function De(e,r){let t=j(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{Ke as checkGroup,Be as confirmMessage,Pe as connect,We as disconnect,Ue as nestedAdd,Qe as nestedRemove,Ge as reseed,Fe as syncNestedJson,He as tagsAdd,ze as tagsPick,Ye as tagsRemove};
1
+ import{confirmPredicate as se}from"phlex/reactive/confirm_predicate";var h=new WeakMap,q=new WeakMap;function p(e,r){let t=h.get(e);if(!t){if(h.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)q.set(t.root,t)}return t}function d(e,r,t){for(let o of r.split(" "))e.root.addEventListener?.(o,t),e.offs.push(()=>e.root.removeEventListener?.(o,t))}function m(e,r){d(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Fe(e,r){let t=p(e,r);t.offs=[],t.seeds=[];let o,n,i,a;(t.wire=()=>{if(!t.show&&de(t))t.show=(s)=>{if(t.flipping)return;if(s?.type==="input"||s?.type==="change")we(t,s.target);else t.groups=Ae(t),D(t);he(t)},d(t,"input change",t.show),m(t,t.show);if(!t.arm&&pe(t))d(t,"input change",(s)=>J(t,s)),m(t,t.arm=()=>{J(t,null),D(t)});if(!o&&(o=t.show||t.arm))d(t,"reset",()=>setTimeout(()=>{if(h.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!n&&(n=S(t)))d(t,"input",(s)=>Ce(t,s)&&v(t)),m(t,()=>v(t));if(!i&&(i=C(t)))m(t,()=>Q(t));if(!a&&(a=ke(t)))d(t,"input change",(s)=>ee(t,s)),m(t,()=>E(t))})(),d(t,"keydown",(s)=>Pe(t,s)),d(t,"paste drop",(s)=>We(t,s)),d(t,"input",(s)=>Ge(t,s))}function Ke(e){let r=h.get(e);if(!r)return;h.delete(e),q.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function He(e){let r=h.get(e);if(!r)return;for(let t of r.seeds)t();r.wire()}function y(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function ze(e,r,t){return ee(p(e,r),t)}function Ye(e,r,t){return ae(p(e,r),t)}var ce=/^#[A-Za-z_][\w-]*$/,P="[data-reactive-show-field], [data-reactive-show]";function le(e,r){let t=e.getAttribute("data-reactive-show-equals");if(t!==null)return r===t;let o=e.getAttribute("data-reactive-show-not");if(o!==null)return r!==o;let n=e.getAttribute("data-reactive-show-in");if(n!==null){try{let i=JSON.parse(n);if(Array.isArray(i))return i.includes(r)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(n)} — skipped`),null}for(let i of te){let a=e.getAttribute(`data-reactive-show-${i}`);if(a!==null)return oe(i,a,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function W(e){try{let r=JSON.parse(e);if(r&&typeof r==="object"&&!Array.isArray(r))return r}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(e)} — skipped`),null}function ue(e){try{let r=JSON.parse(e);if(Array.isArray(r)&&r.every((t)=>t&&typeof t==="object"&&Array.isArray(t.any)&&Array.isArray(t.ops)))return r}catch{}return console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(e)} — skipped`),[]}function G(e,r){if(!e||typeof e!=="object")return null;let t=e.any;if(Array.isArray(t)&&(t.length===0||Array.isArray(t[0])))return b(t,r);return fe(e,r)}function fe(e,r){let t=Array.isArray(e.all)?"all":Array.isArray(e.any)?"any":null;if(!t)return null;let o=e[t];if(o.length===0)return null;let n=o.map((i)=>j(i,r));return t==="all"?n.every(Boolean):n.some(Boolean)}function w(e,r="show"){if(typeof e==="string"&&ce.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function de(e){if(e.root.getAttribute?.("data-reactive-show-targets"))return!0;if(e.root.getAttribute?.("data-reactive-group-targets"))return!0;let r=e.root.querySelectorAll?.(P)??[];for(let t of r)if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(x)??[])if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(H)??[])if(e.core.owns(t))return!0;return!1}function pe(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function ge(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:ue(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function J(e,r){let t=ge(e);if(!t.length)return;let o=K(e,e.core.ownership());t.forEach((n,i)=>{let a=b(n.any,o);if(a===null)return;let s=a&&!e.onCompleteStates[i]&&Boolean(r);if(e.onCompleteStates[i]=a,s)e.core.applyOps(n.ops,"@root")})}function he(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,o=K(e,r);for(let n of e.root.querySelectorAll(P)){if(!r(n))continue;let i=n.getAttribute("data-reactive-show");if(i!==null){let c=G(W(i),o);if(c!==null)T(n,c,r,t);continue}let a=n.getAttribute("data-reactive-show-field");if(!a)continue;let s=o(a);if(s===null)continue;let l=le(n,s);if(l===null)continue;T(n,l,r,t)}if(e.groups)Se(e,o,r,t);be(e,o,r),ye(e,o),xe(e,o)}function T(e,r,t,o){if(e.hidden=!r,e.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof e.querySelectorAll!=="function")return;for(let n of e.querySelectorAll("input[name], select[name], textarea[name]"))if(t(n))n.disabled=!r;if(e.name&&t(e))e.disabled=!r}function ye(e,r){let t=B(e);for(let[o,n]of Object.entries(t)){if(o.startsWith("#")){me(o,n,r);continue}if(!n||typeof n!=="object"||Array.isArray(n))continue;let i=r(o);if(i===null)continue;let a=(s,l)=>l?r(o,!0):i;for(let[s,l]of Object.entries(n)){if(!w(s))continue;let c;if(Array.isArray(l)){if(l.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${s} — skipped`);continue}c=l.every((u)=>j(u,a))}else{let u=ie(l,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${s} — skipped`);continue}c=u}for(let u of document.querySelectorAll(s))u.hidden=!c}}}function me(e,r,t){if(!w(e))return;let o=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,n=De(o);if(n===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(n.every((a)=>t(a)===null)&&!Ie(o))return;let i=b(o,t);if(i===null)return;for(let a of document.querySelectorAll(e))a.hidden=!i}function B(e,r="show",t="mode: { ... }, kind: { ... }"){let o=e.root.getAttribute?.(`data-reactive-${r}-targets`);if(!o)return{};try{let n=JSON.parse(o);if(n&&typeof n==="object"&&!Array.isArray(n))return n}catch{}return console.warn(`[phlex-reactive] malformed data-reactive-${r}-targets — ignored. `+`Did two reactive_${r}_targets calls collide on one root? Declare every entry in ONE call: reactive_${r}_targets(${t})`),{}}function ve(e,r,t,o){let n=!1,i=null;for(let a of e.root.querySelectorAll(F(r,o))){if(!t(a))continue;if(a.type==="checkbox")return a.checked?"true":"false";if(a.type==="radio"){if(a.checked)return a.value??"";n=!0;continue}i??=a}if(i)return i.value??"";return n?"":null}function F(e,r){return`[name="${r&&!e.includes("[")?`${r}[${e}]`:e}"]`}function K(e,r){let t=e.root.getAttribute?.("data-reactive-scope")||null,o=new Map;return(n,i)=>{let a=i?`#${n}`:n;if(!o.has(a))o.set(a,i?_(e,n,r,t).filter((s)=>s.checked).length:ve(e,n,r,t));return o.get(a)}}var H="[data-reactive-media]";function be(e,r,t){for(let o of e.root.querySelectorAll(H)){if(!t(o))continue;let n,i;try{if(n=JSON.parse(o.getAttribute("data-reactive-media")),n?.match!=null)i=new RegExp(`^(?:${n.match})$`)}catch{}if(typeof n?.field!=="string"||!n.pool||typeof n.pool!=="object"||n.match!=null&&!i){console.warn("[phlex-reactive] malformed data-reactive-media — skipped");continue}let a=r(n.field);if(a===null)continue;let s=n.take>0?[...a]:null,l=s?s.slice(0,n.take).join(""):a,c=s?.length<n.take||i?.test(l)===!1;if(c&&n.pending==="keep"){M(o,"pending");continue}let u=!c&&Object.hasOwn(n.pool,l)?L(e,n.pool[l])[0]:null,f=e.core.copyMedia(o,u??null);if(f===null)continue;if(M(o,c?"pending":f===!1?"miss":"hit"),typeof n.label!=="string")continue;let g=c?"":f===!1?String(n.blank??""):f;for(let A of L(e,n.label))if(A.textContent!==g)A.textContent=g}}function M(e,r){if(e.getAttribute("data-reactive-media-state")!==r)e.setAttribute("data-reactive-media-state",r)}function L(e,r){try{return r==="@root"?[]:e.core.opTargets({to:r})}catch{return[]}}var x="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function Ae(e){return!!e.root.querySelector?.(x)}function _(e,r,t,o){return[...e.root.querySelectorAll(F(r,o))].filter((n)=>n.type==="checkbox"&&t(n))}function we(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))z(e,t,r.checked,r)}function Ue(e,r,t){if(!r)return;let o=q.get(e)??{root:e,core:{ownership:()=>(n)=>n.closest('[data-controller~="reactive"]')===e}};z(o,r,t!==!1),o.show?.()}function z(e,r,t,o){let n=e.root.getAttribute?.("data-reactive-scope")||null,i=_(e,r,e.core.ownership(),n).filter((a)=>a!==o&&a.checked!==t);for(let a of i)a.checked=t;e.flipping=!0;try{for(let a of i)for(let s of["input","change"])a.dispatchEvent?.(new Event(s,{bubbles:!0}))}finally{e.flipping=!1}}function Se(e,r,t,o){for(let n of e.root.querySelectorAll(x)){if(!t(n))continue;let i=n.getAttribute("data-reactive-enable");if(i!==null){let l=G(W(i),r);if(l!==null)n.disabled=!l}let a=n.getAttribute("data-reactive-count");if(a){let l=String(r(a,!0));if(n.textContent!==l)n.textContent=l}let s=n.getAttribute("data-reactive-select-all");if(s){let l=_(e,s,t,o).filter((u)=>u!==n),c=l.filter((u)=>u.checked).length;n.checked=c>0&&c===l.length,n.indeterminate=c>0&&c<l.length}}}function xe(e,r){for(let[t,o]of Object.entries(B(e,"group",'"ids[]" => { ... }, "tags[]" => { ... }'))){if(!o||typeof o!=="object"||Array.isArray(o))continue;let n=String(r(t,!0));for(let a of Array.isArray(o.count)?o.count:[]){if(!w(a,"group"))continue;for(let s of document.querySelectorAll(a))if(s.textContent!==n)s.textContent=n}let i=o.enable&&typeof o.enable==="object"?o.enable:{};for(let[a,s]of Object.entries(i)){if(!w(a,"group"))continue;let l=b(s?.any,r);if(l===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${a} — skipped`);continue}for(let c of document.querySelectorAll(a))c.disabled=!l}}}function D(e){if(e.groupObserver||typeof MutationObserver!=="function")return;let r=(n)=>e.root.getAttribute?.(`data-reactive-${n}`)?.includes("checked_");if(!e.root.getAttribute?.("data-reactive-group-targets")&&!e.root.querySelector?.(`${x}, [data-reactive-show*=checked_]`)&&!r("show-targets")&&!r("on-complete"))return;let o=(n)=>n.nodeType===1&&(n.matches('input[type="checkbox"]')||!!n.querySelector('input[type="checkbox"]'));e.groupObserver=new MutationObserver((n)=>{if(!n.some((i)=>[...i.addedNodes,...i.removedNodes].some(o)))return;e.show?.(),e.arm?.()}),e.groupObserver.observe(e.root,{childList:!0,subtree:!0})}function S(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function Ce(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function v(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.root.getAttribute("data-reactive-filter-input"),t=e.root.getAttribute("data-reactive-filter-option");if(!r||!t)return;let o=e.core.ownership(),n=[...e.root.querySelectorAll(r)].find(o);if(!n)return;let i=(n.value??"").trim().toLowerCase(),a=0;for(let c of e.root.querySelectorAll(t)){if(!o(c))continue;let u=(c.getAttribute("data-reactive-filter-text")??c.textContent??"").toLowerCase(),f=c.hasAttribute?.("data-reactive-tags-selected")||i!==""&&!u.includes(i);if(c.hidden=f,f)c.removeAttribute("data-reactive-highlighted");else a++}let s=e.root.getAttribute("data-reactive-filter-group");if(s)for(let c of e.root.querySelectorAll(s)){if(!o(c))continue;let u=[...c.querySelectorAll(t)].filter(o);if(u.length===0)continue;c.hidden=u.every((f)=>f.hidden)}let l=e.root.getAttribute("data-reactive-filter-empty");if(l){for(let c of e.root.querySelectorAll(l))if(o(c))c.hidden=a>0}}function C(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function ke(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function N(e){if(typeof e.root?.querySelectorAll!=="function")return null;let r=e.root.getAttribute("data-reactive-tags-field");if(!r)return null;let t=e.core.ownership();return[...e.root.querySelectorAll(r)].find(t)??null}function O(e){let r=new Set,t=[];for(let o of String(e.value??"").split(",")){let n=o.trim();if(n===""||r.has(n.toLowerCase()))continue;r.add(n.toLowerCase()),t.push(n)}return t}function Y(e,r){let t=N(e);if(!t)return!1;let o=O(t),n=new Set(o.map((a)=>a.toLowerCase())),i=!1;for(let a of r){let s=String(a??"").trim();if(s===""||n.has(s.toLowerCase()))continue;n.add(s.toLowerCase()),o.push(s),i=!0}if(i)U(e,t,o);return i}function U(e,r,t){r.value=t.join(","),y(r),Q(e)}function qe(e){if(typeof e.root?.querySelectorAll!=="function")return null;let r=e.root.getAttribute("data-reactive-filter-input");if(!r)return null;let t=e.core.ownership();return[...e.root.querySelectorAll(r)].find(t)??null}function Q(e){let r=N(e);if(!r)return;let t=O(r),o=e.core.ownership();_e(e,t,o),Ne(e,t,o)}function _e(e,r,t){let o=[...e.root.querySelectorAll("[data-reactive-tags-list]")].find(t);if(!o)return;let i=[...e.root.querySelectorAll("[data-reactive-tags-template]")].find(t)?.content?.firstElementChild;if(!i){if(!e.tagsWarnedTemplate)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),e.tagsWarnedTemplate=!0;return}while(o.firstChild)o.removeChild(o.firstChild);for(let a of r){let s=i.cloneNode(!0);s.setAttribute?.("data-reactive-tag",a);let l=s.matches?.("[data-reactive-tag-text]")?s:(s.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(l)l.textContent=a;let c=[...s.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(s.matches?.('[data-action*="reactive#tagsRemove"]'))c.push(s);for(let u of c)u.setAttribute?.("data-reactive-tag-param",a);o.appendChild(s)}}function Ne(e,r,t){let o=new Set(r.map((n)=>n.toLowerCase()));for(let n of e.root.querySelectorAll("[role=option]")){if(!t(n))continue;let i=n.getAttribute?.("data-reactive-tag-param");if(!i)continue;if(o.has(i.toLowerCase()))n.setAttribute("data-reactive-tags-selected","true"),n.hidden=!0,n.removeAttribute?.("data-reactive-highlighted");else if(n.hasAttribute?.("data-reactive-tags-selected")){if(n.removeAttribute("data-reactive-tags-selected"),!S(e))n.hidden=!1}}if(S(e))v(e)}function Oe(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Ee(e,r){let t=[e,...e.querySelectorAll?.("*")??[]];for(let o of t)for(let n of["name","id","for"]){let i=o.getAttribute?.(n);if(i&&i.includes("NEW_ROW"))o.setAttribute?.(n,i.replaceAll("NEW_ROW",String(r)))}}function $e(e,r){if(e.nestedWarned)return;console.warn(`[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${r}"] container + <template data-reactive-nested-template="${r}"> pair found in this root — the add `+"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / reactive_nested_template)."),e.nestedWarned=!0}function je(e,r,t,o){if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}if(!n||typeof n!=="object")return null;let i=e.core.ownership(),a=[...r.querySelectorAll?.("input, select, textarea")??[]],s=[];for(let[l,c]of Object.entries(n)){let u=[...e.root.querySelectorAll?.(c)??[]].find(i);if(!u)continue;let f=a.find((g)=>V(g.getAttribute?.("name"))===l);if(!f)continue;Re(f,u),s.push(u)}if(o)for(let l of s)Je(l);return s[0]??null}function Re(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:k(r)!=="";else e.value=k(r);y(e)}function Je(e){if(e.type==="checkbox")e.checked=!1;else e.value="";y(e)}function Te(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,o)=>Object.prototype.hasOwnProperty.call(r,o)?r[o]:t)}function I(e,r){let t=[...r.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(t)t.value="1",y(t),r.hidden=!0;else r.parentNode?.removeChild?.(r);E(e)}function E(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership();for(let t of[...e.root.querySelectorAll("[data-reactive-nested-json]")].filter(r))X(e,t.getAttribute("data-reactive-nested-json"))}function Me(e,r){let t=r.getAttribute?.("data-reactive-nested-json-field");if(!t)return null;let o=e.core.ownership();return[...e.root.querySelectorAll(t)].find(o)??null}function Z(e,r){let t={},o=e.core.ownership();for(let n of[...r.querySelectorAll?.("input, select, textarea")??[]]){if(n.matches(":disabled")||n.type==="radio"&&!n.checked||!o(n))continue;let i=V(n.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=k(n)}return t}function V(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function k(e){if(e.type==="checkbox")return e.checked?e.value||"on":"";return e.value??""}function X(e,r){let t=e.core.ownership(),o=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!o)return;let n=Me(e,o);if(!n)return;let i=[];for(let s of[...o.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!t(s)||s.hidden)continue;i.push(Z(e,s))}let a=JSON.stringify(i);if(n.value===a)return;n.value=a,y(n)}function Qe(e,r,t){let o=p(e,r);if(!C(o))return;if(t?.defaultPrevented)return;if(o.core.listnavOptions(t).some((a)=>a.hasAttribute?.("data-reactive-highlighted")))return;t?.preventDefault?.();let n=t?.currentTarget??t?.target;if(!n)return;if(!Y(o,String(n.value??"").split(",")))return;if(n.value="",S(o))v(o)}function Ze(e,r,t){let o=p(e,r);if(!C(o))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!Y(o,[i]))return;let a=qe(o);if(!a)return;a.value="",v(o),a.focus?.()}function Ve(e,r,t){let o=p(e,r);if(!C(o))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let a=N(o);if(!a)return;let s=O(a),l=s.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===s.length)return;U(o,a,l)}function Xe(e,r,t){let o=p(e,r);t?.preventDefault?.();let n=t?.currentTarget??t?.target,i=n?.getAttribute?.("data-reactive-association-param");if(!i)return;if(typeof o.root?.querySelectorAll!=="function")return;let a=o.core.ownership(),s=[...o.root.querySelectorAll(`[data-reactive-nested-list="${i}"]`)].find(a),c=[...o.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(a)?.content?.firstElementChild;if(!s||!c){$e(o,i);return}let u=c.cloneNode(!0);Ee(u,Oe(o)),s.appendChild(u);let f=n?.getAttribute?.("data-reactive-nested-from-param"),g=n?.getAttribute?.("data-reactive-nested-clear-param")==="true",A=je(o,u,f,g);if(f)A?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(s.getAttribute?.("data-reactive-nested-json")===i)X(o,i)}function et(e,r,t){let o=p(e,r);t?.preventDefault?.();let n=t?.currentTarget??t?.target,i=n?.closest?.("[data-reactive-nested-row]");if(!i)return;if(i.closest?.('[data-controller~="reactive"]')!==o.root)return;let a=n?.getAttribute?.("data-reactive-confirm-param"),s=n?.getAttribute?.("data-reactive-confirm-when-param"),l=a||(s?ae(o,s):null);if(!l)return I(o,i);let c=Z(o,i),u=Te(l,c);return o.core.confirm(u,{el:n,row:i,fields:c}).then((f)=>{if(f)I(o,i)})}function ee(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;E(e)}var te=["gte","gt","lte","lt"],Le=["len_eq","len_gte","len_gt","len_lte","len_lt"],re=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function ne(e,r,t){if(!Number.isInteger(r))return console.warn(`[phlex-reactive] reactive_show ${e}: needs an integer literal, got ${JSON.stringify(r)} — skipped`),null;switch(e.slice(e.indexOf("_")+1)){case"eq":return t===r;case"gte":return t>=r;case"gt":return t>r;case"lte":return t<=r;case"lt":return t<r;default:return null}}function oe(e,r,t){let o=Number(r);if(Number.isNaN(o))return console.warn(`[phlex-reactive] reactive_show ${e}: needs a numeric literal, got ${JSON.stringify(r)} — skipped`),null;let n=t==null?"":String(t).trim(),i=n===""?NaN:Number(n);if(Number.isNaN(i))return!1;switch(e){case"gte":return i>=o;case"gt":return i>o;case"lte":return i<=o;case"lt":return i<o;default:return null}}function ie(e,r){if(!e||typeof e!=="object")return null;if(typeof e.equals==="string")return r===e.equals;if(typeof e.not==="string")return r!==e.not;if(Array.isArray(e.in))return e.in.includes(r);for(let t of te)if(t in e)return oe(t,e[t],r);for(let t of Le)if(t in e)return ne(t,e[t],[...String(r??"")].length);return null}function j(e,r){if(!e||typeof e!=="object"||typeof e.field!=="string")return!1;let t=re.find((n)=>(n in e));if(t){let n=r(e.field,!0);return ne(t,e[t],Array.isArray(n)?n.length:Number(n)||0)===!0}let o=r(e.field)??"";return ie(e,o)===!0}function b(e,r){if(!Array.isArray(e)||e.length===0)return null;return e.some((t)=>Array.isArray(t)&&t.length>0&&t.every((o)=>j(o,r)))}function De(e){if(!Array.isArray(e)||e.length===0)return null;let r=new Set;for(let t of e){if(!Array.isArray(t))continue;for(let o of t)if(o&&typeof o==="object"&&typeof o.field==="string")r.add(o.field)}return r.size>0?[...r]:null}function Ie(e){return e.flat().every((r)=>re.some((t)=>(t in Object(r))))}function ae(e,r){let t=r;if(typeof r==="string")try{t=JSON.parse(r)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(r)} — skipped`),null}if(!t||typeof t!=="object")return null;let{fields:o}=e.core.collectFields(),n=(a)=>o[a],i;if(typeof t.predicate==="string"){let a=se(t.predicate);if(!a)return console.warn(`[phlex-reactive] confirm predicate "${t.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;i=!!a(o)}else i=b(t.groups?.any,n)===!0;return i?t.message:null}function R(e,r){let t=r.target?.closest?.("[data-reactive-editable]");return t&&e.core.owns(t)?t:null}function Pe(e,r){let t=R(e,r);if(!t||r.isComposing||r.keyCode===229)return;if(r.key==="Enter"){let o=t.getAttribute("data-reactive-editable-enter");if(t.getAttribute("data-reactive-editable")==="multi"&&(o==null||r.shiftKey))return;if(r.preventDefault(),o==="submit")t.closest("form")?.requestSubmit();else if(o)e.core.applyOps(JSON.parse(o),"@root")}else if(r.key==="Escape"&&t.hasAttribute("data-reactive-editable-escape"))r.preventDefault(),t.textContent=e.core.served(t),y(t),t.blur()}function We(e,r){let t=R(e,r),o=t?.getAttribute("data-reactive-editable")!=="multi",n=(r.clipboardData??r.dataTransfer)?.getData("text/plain");if(!t||n==null||e.core.plain()&&!(o&&/[\r\n]/.test(n)))return;if(r.preventDefault(),o)n=n.replace(/\r?\n|\r/g," ");t.focus(),document.execCommand("insertText",!1,n)}function Ge(e,r){let t=R(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{Ue as checkGroup,Ye as confirmMessage,Fe as connect,Ke as disconnect,Xe as nestedAdd,et as nestedRemove,He as reseed,ze as syncNestedJson,Qe as tagsAdd,Ze as tagsPick,Ve as tagsRemove};
2
2
 
3
- //# debugId=4463679976B636C764756E2164756E21
3
+ //# debugId=37913BD89CC3850E64756E2164756E21
4
4
  //# sourceMappingURL=bindings.min.js.map