phlex-reactive 0.14.0 → 0.14.2

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.
@@ -30,10 +30,16 @@ import { confirmPredicate } from "phlex/reactive/confirm_predicate"
30
30
  // minifier never renames, so the record carries no more of them than it
31
31
  // must (issue #310).
32
32
  const contexts = new WeakMap()
33
+ // root element -> the same context, for the check_group op (issue #342),
34
+ // which names a root, not a controller.
35
+ const rootContexts = new WeakMap()
33
36
 
34
37
  function ctx(controller, core) {
35
38
  let c = contexts.get(controller)
36
- if (!c) contexts.set(controller, (c = { root: controller.element, core, nestedIndex: 0 }))
39
+ if (!c) {
40
+ contexts.set(controller, (c = { root: controller.element, core, nestedIndex: 0 }))
41
+ if (c.root) rootContexts.set(c.root, c)
42
+ }
37
43
  return c
38
44
  }
39
45
 
@@ -115,6 +121,20 @@ export function connect(controller, core) {
115
121
  )
116
122
  }
117
123
 
124
+ // A form reset (issue #342) restores its fields AFTER the `reset` event
125
+ // dispatches (and only if nothing cancelled it), so this waits a task, then
126
+ // re-syncs the bindings and re-arms the completion latches without firing
127
+ // (a reset fires no input/change, so a checked-count latch would stay high).
128
+ if (c.show || c.arm) {
129
+ listen(c, "reset", () =>
130
+ setTimeout(() => {
131
+ if (contexts.get(controller) !== c) return
132
+ c.show?.()
133
+ c.arm?.()
134
+ }, 0),
135
+ )
136
+ }
137
+
118
138
  // Option filtering (issue #163) — ONLY when the root declares the binding
119
139
  // (reactive_filter emits both attrs together), so a component without one
120
140
  // pays two attribute reads. ONE delegated input listener on the root — the
@@ -162,6 +182,7 @@ export function disconnect(controller) {
162
182
  const c = contexts.get(controller)
163
183
  if (!c) return
164
184
  contexts.delete(controller)
185
+ rootContexts.delete(c.root)
165
186
  for (const off of c.offs ?? []) off()
166
187
  c.groupObserver?.disconnect()
167
188
  }
@@ -311,10 +332,11 @@ function legacyCompoundShowMatches(payload, fieldValue) {
311
332
  // a refused show target is distinguishable in the console. The client half of
312
333
  // the two-sided default-deny: reactive_show_targets raises at declare time; a
313
334
  // hand-built wire attr must not widen the escape to class/compound selectors.
314
- // A refused selector warns + skips — its siblings still apply.
315
- function guardShowTargetSelector(selector) {
335
+ // A refused selector warns + skips — its siblings still apply. The group
336
+ // targets (issue #343) share it, naming their own kind in the warn.
337
+ function guardShowTargetSelector(selector, kind = "show") {
316
338
  if (typeof selector === "string" && ID_SELECTOR.test(selector)) return true
317
- console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(selector)} — skipped`)
339
+ console.warn(`[phlex-reactive] refused cross-root ${kind} target ${JSON.stringify(selector)} — skipped`)
318
340
  return false
319
341
  }
320
342
 
@@ -327,6 +349,7 @@ function guardShowTargetSelector(selector) {
327
349
  // getAttribute, cheaper than the binding walk.
328
350
  function showSyncEnabled(c) {
329
351
  if (c.root.getAttribute?.("data-reactive-show-targets")) return true
352
+ if (c.root.getAttribute?.("data-reactive-group-targets")) return true
330
353
  // Single-field bindings carry -field; compound all:/any: bindings (issue
331
354
  // #176) carry data-reactive-show and have NO single controlling field, so
332
355
  // both selectors gate the sync.
@@ -423,6 +446,7 @@ function syncShow(c) {
423
446
  // share the same owned-field memo, so a field driving several reads once.
424
447
  if (c.groups) syncGroups(c, fieldValue, owns, scope)
425
448
  syncShowTargets(c, fieldValue)
449
+ syncGroupTargets(c, fieldValue)
426
450
  }
427
451
 
428
452
  // Toggle `hidden` (and, when the binding declares data-reactive-show-disable,
@@ -524,8 +548,8 @@ function applyConditionsTarget(selector, payload, fieldValue) {
524
548
  // contract), but never silent either: the likeliest cause is TWO
525
549
  // reactive_show_targets calls on one root, whose JSON strings Phlex `mix`
526
550
  // space-joined into an unparseable attr. The warn names the fix.
527
- function parseShowTargets(c) {
528
- const raw = c.root.getAttribute?.("data-reactive-show-targets")
551
+ function parseShowTargets(c, kind = "show", fix = "mode: { ... }, kind: { ... }") {
552
+ const raw = c.root.getAttribute?.(`data-reactive-${kind}-targets`)
529
553
  if (!raw) return {}
530
554
  try {
531
555
  const parsed = JSON.parse(raw)
@@ -534,9 +558,9 @@ function parseShowTargets(c) {
534
558
  // fall through to the shared warn below
535
559
  }
536
560
  console.warn(
537
- "[phlex-reactive] malformed data-reactive-show-targets — ignored. " +
538
- "Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: " +
539
- "reactive_show_targets(mode: { ... }, kind: { ... })"
561
+ `[phlex-reactive] malformed data-reactive-${kind}-targets — ignored. ` +
562
+ `Did two reactive_${kind}_targets calls collide on one root? Declare every entry in ONE call: ` +
563
+ `reactive_${kind}_targets(${fix})`
540
564
  )
541
565
  return {}
542
566
  }
@@ -608,18 +632,36 @@ function groupBoxes(c, name, owns, scope) {
608
632
  return [...c.root.querySelectorAll(namedSelector(name, scope))].filter((el) => el.type === "checkbox" && owns(el))
609
633
  }
610
634
 
611
- // A header's edit ticks or unticks every owned box of its group, and
612
- // dispatches `input` + `change` on each one it flips so computes, shows and on-complete
613
- // bindings see a real edit. The re-entrant change events are skipped by the
614
- // show listener while this runs (c.flipping); it syncs once afterwards.
635
+ // A header's edit ticks or unticks every owned box of its group (the header
636
+ // itself is never a member). The show listener syncs once afterwards.
615
637
  function flipGroup(c, header) {
616
638
  const group = header?.getAttribute?.("data-reactive-select-all")
617
- if (!group || !c.core.owns(header)) return
639
+ if (group && c.core.owns(header)) setGroup(c, group, header.checked, header)
640
+ }
641
+
642
+ // The check_group op (issue #342): set a root's group the way its header
643
+ // would, then sync its bindings once. A root this module never connected (no
644
+ // group binding, only the boxes) still flips, with the #15 ownership check.
645
+ export function checkGroup(root, group, checked) {
646
+ if (!group) return
647
+ const c = rootContexts.get(root) ?? {
648
+ root,
649
+ core: { ownership: () => (el) => el.closest('[data-controller~="reactive"]') === root },
650
+ }
651
+ setGroup(c, group, checked !== false)
652
+ c.show?.()
653
+ }
654
+
655
+ // Tick or untick every owned box of a group, and dispatch `input` + `change`
656
+ // on each one it flips so computes, shows and on-complete bindings see a real
657
+ // edit. The re-entrant events are skipped by the show listener while this
658
+ // runs (c.flipping); the caller syncs once afterwards.
659
+ function setGroup(c, group, checked, except) {
618
660
  const scope = c.root.getAttribute?.("data-reactive-scope") || null
619
- const flipped = groupBoxes(c, group, c.core.ownership(), scope).filter((box) => box !== header && box.checked !== header.checked)
661
+ const flipped = groupBoxes(c, group, c.core.ownership(), scope).filter((box) => box !== except && box.checked !== checked)
620
662
  // Every box first, then the events: a listener (a checked-count
621
663
  // on_complete) must see the group's final count, never a half-flipped one.
622
- for (const box of flipped) box.checked = header.checked
664
+ for (const box of flipped) box.checked = checked
623
665
  c.flipping = true
624
666
  try {
625
667
  for (const box of flipped) {
@@ -657,6 +699,34 @@ function syncGroups(c, fieldValue, owns, scope) {
657
699
  }
658
700
  }
659
701
 
702
+ // The declared cross-root group targets (issue #343) — the count and enable
703
+ // parallel of syncShowTargets, declared on the root that OWNS the group:
704
+ // { group: { count: ["#id", …], enable: { "#id": { any: … } } } }. The count
705
+ // is this root's owned ticked boxes (a nested root's are its own, #15), the
706
+ // enable folds its DNF payload with the same owned-field memo as every other
707
+ // binding. Targets are id-only (warn-skip — the Ruby helper raised at
708
+ // declare time) and resolved document-wide; a missing one is skipped.
709
+ function syncGroupTargets(c, fieldValue) {
710
+ for (const [group, targets] of Object.entries(parseShowTargets(c, "group", '"ids[]" => { ... }, "tags[]" => { ... }'))) {
711
+ if (!targets || typeof targets !== "object" || Array.isArray(targets)) continue
712
+ const text = String(fieldValue(group, true))
713
+ for (const selector of Array.isArray(targets.count) ? targets.count : []) {
714
+ if (!guardShowTargetSelector(selector, "group")) continue
715
+ for (const node of document.querySelectorAll(selector)) if (node.textContent !== text) node.textContent = text
716
+ }
717
+ const enable = targets.enable && typeof targets.enable === "object" ? targets.enable : {}
718
+ for (const [selector, payload] of Object.entries(enable)) {
719
+ if (!guardShowTargetSelector(selector, "group")) continue
720
+ const match = anyOfAllsMatches(payload?.any, fieldValue)
721
+ if (match === null) {
722
+ console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${selector} — skipped`)
723
+ continue
724
+ }
725
+ for (const node of document.querySelectorAll(selector)) node.disabled = !match
726
+ }
727
+ }
728
+ }
729
+
660
730
  // Boxes added or removed later — a stream append, a removal — fire no event,
661
731
  // so a root with a group binding (a header, a count, an enable, or a show,
662
732
  // show target or on_complete with a checked_* term) watches its subtree and
@@ -667,7 +737,8 @@ function syncGroups(c, fieldValue, owns, scope) {
667
737
  function observeGroups(c) {
668
738
  if (c.groupObserver || typeof MutationObserver !== "function") return
669
739
  const onRoot = (name) => c.root.getAttribute?.(`data-reactive-${name}`)?.includes("checked_")
670
- if (!c.root.querySelector?.(`${GROUP_BINDING_SELECTOR}, [data-reactive-show*=checked_]`) && !onRoot("show-targets") && !onRoot("on-complete")) return
740
+ const targeted = !!c.root.getAttribute?.("data-reactive-group-targets")
741
+ if (!targeted && !c.root.querySelector?.(`${GROUP_BINDING_SELECTOR}, [data-reactive-show*=checked_]`) && !onRoot("show-targets") && !onRoot("on-complete")) return
671
742
  const boxIn = (node) => node.nodeType === 1 && (node.matches('input[type="checkbox"]') || !!node.querySelector('input[type="checkbox"]'))
672
743
  c.groupObserver = new MutationObserver((records) => {
673
744
  if (!records.some((r) => [...r.addedNodes, ...r.removedNodes].some(boxIn))) return
@@ -1,4 +1,4 @@
1
- import{confirmPredicate as X}from"phlex/reactive/confirm_predicate";var h=new WeakMap;function d(e,r){let t=h.get(e);if(!t)h.set(e,t={root:e.element,core:r,nestedIndex:0});return t}function g(e,r,t){for(let n of r.split(" "))e.root.addEventListener?.(n,t),e.offs.push(()=>e.root.removeEventListener?.(n,t))}function p(e,r){g(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Re(e,r){let t=d(e,r);if(t.offs=[],t.seeds=[],oe(t))t.show=(n)=>{if(t.flipping)return;if(n?.type==="input"||n?.type==="change")pe(t,n.target);else t.groups=de(t),R(t);ae(t)},g(t,"input change",t.show),p(t,t.show);if(ie(t))g(t,"input change",(n)=>O(t,n)),p(t,t.arm=()=>{O(t,null),R(t)});if(A(t))g(t,"input",(n)=>he(t,n)&&y(t)),p(t,()=>y(t));if(w(t))p(t,()=>F(t));if(ye(t))g(t,"input change",(n)=>K(t,n)),p(t,()=>k(t))}function je(e){let r=h.get(e);if(!r)return;h.delete(e);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function Je(e){for(let r of h.get(e)?.seeds??[])r()}function v(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function Le(e,r,t){return K(d(e,r),t)}function Me(e,r,t){return V(d(e,r),t)}var ee=/^#[A-Za-z_][\w-]*$/,J="[data-reactive-show-field], [data-reactive-show]";function te(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 Y){let s=e.getAttribute(`data-reactive-show-${i}`);if(s!==null)return U(i,s,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function L(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 re(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 M(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 ne(e,r)}function ne(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)=>C(i,r));return t==="all"?o.every(Boolean):o.some(Boolean)}function T(e){if(typeof e==="string"&&ee.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(e)} — skipped`),!1}function oe(e){if(e.root.getAttribute?.("data-reactive-show-targets"))return!0;let r=e.root.querySelectorAll?.(J)??[];for(let t of r)if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(m)??[])if(e.core.owns(t))return!0;return!1}function ie(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function se(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:re(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function O(e,r){let t=se(e);if(!t.length)return;let n=P(e,e.core.ownership());t.forEach((o,i)=>{let s=b(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 ae(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,n=P(e,r);for(let o of e.root.querySelectorAll(J)){if(!r(o))continue;let i=o.getAttribute("data-reactive-show");if(i!==null){let c=M(L(i),n);if(c!==null)E(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=te(o,a);if(l===null)continue;E(o,l,r,t)}if(e.groups)ge(e,n,r,t);ce(e,n)}function E(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 ce(e,r){let t=ue(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){le(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(!T(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)=>C(u,s))}else{let u=Q(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 le(e,r,t){if(!T(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Ee(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((s)=>t(s)===null))return;let i=b(n,t);if(i===null)return;for(let s of document.querySelectorAll(e))s.hidden=!i}function ue(e){let r=e.root.getAttribute?.("data-reactive-show-targets");if(!r)return{};try{let t=JSON.parse(r);if(t&&typeof t==="object"&&!Array.isArray(t))return t}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: { ... })"),{}}function fe(e,r,t,n){let o=!1,i=null;for(let s of e.root.querySelectorAll(I(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 I(e,r){return`[name="${r&&!e.includes("[")?`${r}[${e}]`:e}"]`}function P(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:fe(e,o,r,t));return n.get(s)}}var m="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function de(e){return!!e.root.querySelector?.(m)}function _(e,r,t,n){return[...e.root.querySelectorAll(I(r,n))].filter((o)=>o.type==="checkbox"&&t(o))}function pe(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(!t||!e.core.owns(r))return;let n=e.root.getAttribute?.("data-reactive-scope")||null,o=_(e,t,e.core.ownership(),n).filter((i)=>i!==r&&i.checked!==r.checked);for(let i of o)i.checked=r.checked;e.flipping=!0;try{for(let i of o)for(let s of["input","change"])i.dispatchEvent?.(new Event(s,{bubbles:!0}))}finally{e.flipping=!1}}function ge(e,r,t,n){for(let o of e.root.querySelectorAll(m)){if(!t(o))continue;let i=o.getAttribute("data-reactive-enable");if(i!==null){let l=M(L(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 R(e){if(e.groupObserver||typeof MutationObserver!=="function")return;let r=(n)=>e.root.getAttribute?.(`data-reactive-${n}`)?.includes("checked_");if(!e.root.querySelector?.(`${m}, [data-reactive-show*=checked_]`)&&!r("show-targets")&&!r("on-complete"))return;let t=(n)=>n.nodeType===1&&(n.matches('input[type="checkbox"]')||!!n.querySelector('input[type="checkbox"]'));e.groupObserver=new MutationObserver((n)=>{if(!n.some((o)=>[...o.addedNodes,...o.removedNodes].some(t)))return;e.show?.(),e.arm?.()}),e.groupObserver.observe(e.root,{childList:!0,subtree:!0})}function A(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function he(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function y(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 w(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function ye(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function q(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 N(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 W(e,r){let t=q(e);if(!t)return!1;let n=N(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)D(e,t,n);return i}function D(e,r,t){r.value=t.join(","),v(r),F(e)}function ve(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 F(e){let r=q(e);if(!r)return;let t=N(r),n=e.core.ownership();Ae(e,t,n),me(e,t,n)}function Ae(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 me(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"),!A(e))o.hidden=!1}}if(A(e))y(e)}function we(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function be(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 Se(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 xe(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((S)=>B(S.getAttribute?.("name"))===l);if(!f)continue;_e(f,u),a.push(u)}if(n)for(let l of a)qe(l);return a[0]??null}function _e(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:x(r)!=="";else e.value=x(r);v(e)}function qe(e){if(e.type==="checkbox")e.checked=!1;else e.value="";v(e)}function Ne(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,n)=>Object.prototype.hasOwnProperty.call(r,n)?r[n]:t)}function j(e,r){let t=[...r.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(t)t.value="1",v(t),r.hidden=!0;else r.parentNode?.removeChild?.(r);k(e)}function k(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))H(e,t.getAttribute("data-reactive-nested-json"))}function ke(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 G(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=B(o.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=x(o)}return t}function B(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function x(e){if(e.type==="checkbox")return e.checked?e.value||"on":"";return e.value??""}function H(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=ke(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(G(e,a))}let s=JSON.stringify(i);if(o.value===s)return;o.value=s,v(o)}function Te(e,r,t){let n=d(e,r);if(!w(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(!W(n,String(o.value??"").split(",")))return;if(o.value="",A(n))y(n)}function Ie(e,r,t){let n=d(e,r);if(!w(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!W(n,[i]))return;let s=ve(n);if(!s)return;s.value="",y(n),s.focus?.()}function Pe(e,r,t){let n=d(e,r);if(!w(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let s=q(n);if(!s)return;let a=N(s),l=a.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===a.length)return;D(n,s,l)}function We(e,r,t){let n=d(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){Se(n,i);return}let u=c.cloneNode(!0);be(u,we(n)),a.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),S=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",Z=xe(n,u,f,S);if(f)Z?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(a.getAttribute?.("data-reactive-nested-json")===i)H(n,i)}function De(e,r,t){let n=d(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?V(n,a):null);if(!l)return j(n,i);let c=G(n,i),u=Ne(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)j(n,i)})}function K(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;k(e)}var Y=["gte","gt","lte","lt"],Ce=["len_eq","len_gte","len_gt","len_lte","len_lt"],Oe=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function z(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 U(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 Q(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 Y)if(t in e)return U(t,e[t],r);for(let t of Ce)if(t in e)return z(t,e[t],[...String(r??"")].length);return null}function C(e,r){if(!e||typeof e!=="object"||typeof e.field!=="string")return!1;let t=Oe.find((o)=>(o in e));if(t){let o=r(e.field,!0);return z(t,e[t],Array.isArray(o)?o.length:Number(o)||0)===!0}let n=r(e.field)??"";return Q(e,n)===!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((n)=>C(n,r)))}function Ee(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 V(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=X(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=b(t.groups?.any,o)===!0;return i?t.message:null}export{Me as confirmMessage,Re as connect,je as disconnect,We as nestedAdd,De as nestedRemove,Je as reseed,Le as syncNestedJson,Te as tagsAdd,Ie as tagsPick,Pe as tagsRemove};
1
+ import{confirmPredicate as re}from"phlex/reactive/confirm_predicate";var g=new WeakMap,q=new WeakMap;function d(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 p(e,r,t){for(let n of r.split(" "))e.root.addEventListener?.(n,t),e.offs.push(()=>e.root.removeEventListener?.(n,t))}function h(e,r){p(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Te(e,r){let t=d(e,r);if(t.offs=[],t.seeds=[],ae(t))t.show=(n)=>{if(t.flipping)return;if(n?.type==="input"||n?.type==="change")he(t,n.target);else t.groups=ge(t),J(t);ue(t)},p(t,"input change",t.show),h(t,t.show);if(ce(t))p(t,"input change",(n)=>j(t,n)),h(t,t.arm=()=>{j(t,null),J(t)});if(t.show||t.arm)p(t,"reset",()=>setTimeout(()=>{if(g.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(b(t))p(t,"input",(n)=>Ae(t,n)&&y(t)),h(t,()=>y(t));if(S(t))h(t,()=>H(t));if(me(t))p(t,"input change",(n)=>U(t,n)),h(t,()=>O(t))}function Le(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 Me(e){for(let r of g.get(e)?.seeds??[])r()}function v(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function Ie(e,r,t){return U(d(e,r),t)}function Pe(e,r,t){return ee(d(e,r),t)}var ne=/^#[A-Za-z_][\w-]*$/,L="[data-reactive-show-field], [data-reactive-show]";function oe(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 Q){let s=e.getAttribute(`data-reactive-show-${i}`);if(s!==null)return V(i,s,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function M(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 ie(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 A(t,r);return se(e,r)}function se(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 m(e,r="show"){if(typeof e==="string"&&ne.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function ae(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?.(L)??[];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 ce(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function le(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:ie(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function j(e,r){let t=le(e);if(!t.length)return;let n=D(e,e.core.ownership());t.forEach((o,i)=>{let s=A(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 ue(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,n=D(e,r);for(let o of e.root.querySelectorAll(L)){if(!r(o))continue;let i=o.getAttribute("data-reactive-show");if(i!==null){let c=I(M(i),n);if(c!==null)R(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=oe(o,a);if(l===null)continue;R(o,l,r,t)}if(e.groups)ye(e,n,r,t);fe(e,n),ve(e,n)}function R(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 fe(e,r){let t=P(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){de(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(!m(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=X(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 de(e,r,t){if(!m(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Re(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((s)=>t(s)===null))return;let i=A(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 pe(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 D(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?N(e,o,r,t).filter((a)=>a.checked).length:pe(e,o,r,t));return n.get(s)}}var w="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function ge(e){return!!e.root.querySelector?.(w)}function N(e,r,t,n){return[...e.root.querySelectorAll(W(r,n))].filter((o)=>o.type==="checkbox"&&t(o))}function he(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))G(e,t,r.checked,r)}function We(e,r,t){if(!r)return;let n=q.get(e)??{root:e,core:{ownership:()=>(o)=>o.closest('[data-controller~="reactive"]')===e}};G(n,r,t!==!1),n.show?.()}function G(e,r,t,n){let o=e.root.getAttribute?.("data-reactive-scope")||null,i=N(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 ye(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(M(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=N(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 ve(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(!m(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(!m(s,"group"))continue;let l=A(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 J(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 Ae(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function y(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 me(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function C(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 F(e,r){let t=C(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)B(e,t,n);return i}function B(e,r,t){r.value=t.join(","),v(r),H(e)}function be(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=C(e);if(!r)return;let t=k(r),n=e.core.ownership();we(e,t,n),Se(e,t,n)}function we(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 Se(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))y(e)}function xe(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 qe(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 Ne(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;Ce(f,u),a.push(u)}if(n)for(let l of a)ke(l);return a[0]??null}function Ce(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:_(r)!=="";else e.value=_(r);v(e)}function ke(e){if(e.type==="checkbox")e.checked=!1;else e.value="";v(e)}function Oe(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,n)=>Object.prototype.hasOwnProperty.call(r,n)?r[n]:t)}function T(e,r){let t=[...r.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(t)t.value="1",v(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))z(e,t.getAttribute("data-reactive-nested-json"))}function $e(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 K(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]=_(o)}return t}function Y(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function _(e){if(e.type==="checkbox")return e.checked?e.value||"on":"";return e.value??""}function z(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=$e(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(K(e,a))}let s=JSON.stringify(i);if(o.value===s)return;o.value=s,v(o)}function De(e,r,t){let n=d(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(!F(n,String(o.value??"").split(",")))return;if(o.value="",b(n))y(n)}function Ge(e,r,t){let n=d(e,r);if(!S(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!F(n,[i]))return;let s=be(n);if(!s)return;s.value="",y(n),s.focus?.()}function Fe(e,r,t){let n=d(e,r);if(!S(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let s=C(n);if(!s)return;let a=k(s),l=a.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===a.length)return;B(n,s,l)}function Be(e,r,t){let n=d(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){qe(n,i);return}let u=c.cloneNode(!0);_e(u,xe(n)),a.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),x=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",te=Ne(n,u,f,x);if(f)te?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(a.getAttribute?.("data-reactive-nested-json")===i)z(n,i)}function He(e,r,t){let n=d(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?ee(n,a):null);if(!l)return T(n,i);let c=K(n,i),u=Oe(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)T(n,i)})}function U(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;O(e)}var Q=["gte","gt","lte","lt"],Ee=["len_eq","len_gte","len_gt","len_lte","len_lt"],je=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function Z(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 V(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 X(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 Q)if(t in e)return V(t,e[t],r);for(let t of Ee)if(t in e)return Z(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=je.find((o)=>(o in e));if(t){let o=r(e.field,!0);return Z(t,e[t],Array.isArray(o)?o.length:Number(o)||0)===!0}let n=r(e.field)??"";return X(e,n)===!0}function A(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 Re(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 ee(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=re(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=A(t.groups?.any,o)===!0;return i?t.message:null}export{We as checkGroup,Pe as confirmMessage,Te as connect,Le as disconnect,Be as nestedAdd,He as nestedRemove,Me as reseed,Ie as syncNestedJson,De as tagsAdd,Ge as tagsPick,Fe as tagsRemove};
2
2
 
3
- //# debugId=14DCF0291369D53764756E2164756E21
3
+ //# debugId=5A6A0FBFE381B39A64756E2164756E21
4
4
  //# sourceMappingURL=bindings.min.js.map