phlex-reactive 0.15.2 → 0.15.4

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -2
  3. data/README.md +92 -13
  4. data/app/javascript/phlex/reactive/core.js +1 -0
  5. data/app/javascript/phlex/reactive/core.min.js +6 -6
  6. data/app/javascript/phlex/reactive/core.min.js.map +4 -4
  7. data/app/javascript/phlex/reactive/features/bindings.js +153 -21
  8. data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
  9. data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
  10. data/app/javascript/phlex/reactive/features/countdown.js +140 -0
  11. data/app/javascript/phlex/reactive/features/countdown.min.js +4 -0
  12. data/app/javascript/phlex/reactive/features/countdown.min.js.map +10 -0
  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 +41 -8
  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.js +2 -0
  20. data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -4
  21. data/app/javascript/phlex/reactive/reactive_controller.min.js.map +9 -8
  22. data/app/javascript/phlex/reactive/runtime.js +313 -32
  23. data/lib/phlex/reactive/component/countdown_binding.rb +80 -0
  24. data/lib/phlex/reactive/component/field_references.rb +130 -0
  25. data/lib/phlex/reactive/component/helpers.rb +81 -19
  26. data/lib/phlex/reactive/component/identity.rb +34 -1
  27. data/lib/phlex/reactive/component/image_fallback_binding.rb +67 -0
  28. data/lib/phlex/reactive/component/media_binding.rb +30 -4
  29. data/lib/phlex/reactive/component/media_pattern.rb +79 -0
  30. data/lib/phlex/reactive/component/selection.rb +24 -2
  31. data/lib/phlex/reactive/component/show_options.rb +91 -0
  32. data/lib/phlex/reactive/doctor.rb +20 -8
  33. data/lib/phlex/reactive/engine.rb +2 -2
  34. data/lib/phlex/reactive/js.rb +58 -5
  35. data/lib/phlex/reactive/show_conditions/compare_terms.rb +142 -0
  36. data/lib/phlex/reactive/show_conditions.rb +39 -6
  37. data/lib/phlex/reactive/streamable.rb +6 -3
  38. data/lib/phlex/reactive/version.rb +1 -1
  39. metadata +10 -1
@@ -103,6 +103,15 @@ export function connect(controller, core) {
103
103
  syncShow(c)
104
104
  }
105
105
  listen(c, "input change", c.show)
106
+ // The after: gate (issue #396): an owned field's first blur marks its
107
+ // name touched for this controller (a morph keeps it, a new one starts
108
+ // without), and re-syncs once if a binding waits on a blur.
109
+ listen(c, "focusout", (event) => {
110
+ const name = event.target?.getAttribute?.("name")
111
+ if (!name || c.touched?.has(name) || !c.core.owns(event.target)) return
112
+ ;(c.touched ??= new Set()).add(name)
113
+ if (c.root.querySelector?.("[data-reactive-show-after]")) syncShow(c)
114
+ })
106
115
  seed(c, c.show)
107
116
  }
108
117
 
@@ -441,6 +450,10 @@ function syncShow(c) {
441
450
  // several bindings (and several DNF terms) reads exactly once. Scope-aware:
442
451
  // a bare field `director` resolves as `[name="scope[director]"]` (issue #180).
443
452
  const fieldValue = fieldResolver(c, owns)
453
+ // Bindings whose visibility flipped this pass and that declare transition
454
+ // work (issue #394) — run after the pass, so the `input` a clear dispatches
455
+ // re-syncs from fresh values instead of this pass's memo.
456
+ const moved = []
444
457
  for (const el of c.root.querySelectorAll(SHOW_BINDING_SELECTOR)) {
445
458
  if (!owns(el)) continue // a nested root's binding is its own controller's job
446
459
 
@@ -450,7 +463,19 @@ function syncShow(c) {
450
463
  const payloadRaw = el.getAttribute("data-reactive-show")
451
464
  if (payloadRaw !== null) {
452
465
  const match = showPayloadMatches(parseShowCompound(payloadRaw), fieldValue)
453
- if (match !== null) applyShowVisibility(el, match, owns, scope)
466
+ if (match === null) continue
467
+ // after: (issue #396) keeps it hidden until its field has blurred once.
468
+ const after = el.getAttribute("data-reactive-show-after")
469
+ const visible = match && (after == null || !!c.touched?.has(wireName(after, scope)))
470
+ applyShowVisibility(el, visible, owns, scope)
471
+ // The previous visibility lives on this controller (absent on connect,
472
+ // kept by a morph), so first paint never counts as a change.
473
+ // Tracked for every binding, so a morph that drops the transition attrs
474
+ // and later restores them never compares against a stale value.
475
+ const was = (c.shown ??= new WeakMap()).get(el)
476
+ c.shown.set(el, visible)
477
+ const moves = SHOW_MOVES.some((name) => el.hasAttribute(`data-reactive-show-${name}`))
478
+ if (moves && was !== undefined && was !== visible) moved.push(el)
454
479
  continue
455
480
  }
456
481
 
@@ -470,6 +495,44 @@ function syncShow(c) {
470
495
  syncMedia(c, fieldValue, owns)
471
496
  syncShowTargets(c, fieldValue)
472
497
  syncGroupTargets(c, fieldValue)
498
+ for (const el of moved) showMoved(c, el, owns, scope)
499
+ }
500
+
501
+ // The transition attrs a show binding may declare (issue #394).
502
+ const SHOW_MOVES = ["clear", "focus", "scroll"]
503
+
504
+ // A binding just changed visibility (issue #394). Hidden: empty each clear:
505
+ // field with set_value's write (which announces only a real change), uncheck a
506
+ // checkbox or radio, skip a file input. Visible: scroll the section into view,
507
+ // then focus the first match of focus: inside the root.
508
+ function showMoved(c, el, owns, scope) {
509
+ if (el.hidden) {
510
+ let names = []
511
+ try {
512
+ names = JSON.parse(el.getAttribute("data-reactive-show-clear") ?? "[]")
513
+ } catch {}
514
+ // Compared by attribute, not a [name="…"] selector, so a name holding a
515
+ // `"` or `\` clears its field instead of throwing.
516
+ const wires = new Set((Array.isArray(names) ? names : []).map((name) => wireName(String(name), scope)))
517
+ for (const field of wires.size ? c.root.querySelectorAll("[name]") : []) {
518
+ if (!wires.has(field.getAttribute("name")) || !owns(field) || field.type === "file") continue
519
+ if (field.type === "checkbox" || field.type === "radio") {
520
+ if (field.checked) {
521
+ field.checked = false
522
+ fireInput(field)
523
+ }
524
+ } else c.core.setValue(field, "")
525
+ }
526
+ return
527
+ }
528
+ const scroll = el.getAttribute("data-reactive-show-scroll") === "true"
529
+ if (scroll) el.scrollIntoView?.({ block: "nearest" })
530
+ const focus = el.getAttribute("data-reactive-show-focus")
531
+ let target = null
532
+ try {
533
+ target = focus ? c.root.querySelector(focus) : null
534
+ } catch {}
535
+ target?.focus?.(scroll ? { preventScroll: true } : undefined)
473
536
  }
474
537
 
475
538
  // Toggle `hidden` (and, when the binding declares data-reactive-show-disable,
@@ -618,7 +681,11 @@ function showFieldValue(c, name, owns, scope) {
618
681
  // "form"` resolves as `[name="form[director]"]`. A name already carrying a
619
682
  // bracket (a raw wire name the author passed, like "ids[]") is used verbatim.
620
683
  function namedSelector(name, scope) {
621
- return `[name="${scope && !name.includes("[") ? `${scope}[${name}]` : name}"]`
684
+ return `[name="${wireName(name, scope)}"]`
685
+ }
686
+
687
+ function wireName(name, scope) {
688
+ return scope && !name.includes("[") ? `${scope}[${name}]` : name
622
689
  }
623
690
 
624
691
  // The per-pass field resolver every DNF fold reads (show, enable, on-complete).
@@ -638,12 +705,18 @@ function fieldResolver(c, owns) {
638
705
  }
639
706
 
640
707
  // --- Media bindings (issue #373) ---------------------------------------------
641
- // data-reactive-media='{"field","take"?,"pool","label"?,"blank"?}' on a preview
642
- // <img>: the owned field's value (its first `take` characters, or all of it)
643
- // is a key into `pool`, whose value is a SELECTOR for an <img> inside the root.
644
- // The core's copyMedia copies that element's src, srcset and alt — a pool
645
- // value is never assigned to anything. A key, selector or element that misses
646
- // clears the preview; `label` gets the alt (or `blank`) via textContent. An
708
+ // data-reactive-media='{"field","take"?,"match"?,"when"?,"pending"?,"pool","label"?,"blank"?}'
709
+ // on a preview <img>: the owned field's value (its first `take` characters, or
710
+ // all of it) is a key into `pool`, whose value is a SELECTOR for an <img>
711
+ // inside the root. The core's copyMedia copies that element's src, srcset and
712
+ // alt — a pool value is never assigned to anything. A key, selector or element
713
+ // that misses clears the preview; `label` gets the alt (or `blank`) via
714
+ // textContent. A key shorter than `take`, a key the anchored `match` source
715
+ // rejects, or a WHOLE field value the anchored `when` source rejects (issue
716
+ // #390 — tested before the pool lookup; the key stays the prefix) is PENDING
717
+ // (issue #380) and never gets `blank`: "keep" leaves the preview and label
718
+ // alone, the default clears both. The preview's
719
+ // data-reactive-media-state names the outcome (pending | hit | miss). An
647
720
  // unowned field leaves the preview alone (the show default-deny); a malformed
648
721
  // attr warns and is skipped.
649
722
  const MEDIA_BINDING_SELECTOR = "[data-reactive-media]"
@@ -651,26 +724,48 @@ const MEDIA_BINDING_SELECTOR = "[data-reactive-media]"
651
724
  function syncMedia(c, fieldValue, owns) {
652
725
  for (const el of c.root.querySelectorAll(MEDIA_BINDING_SELECTOR)) {
653
726
  if (!owns(el)) continue
654
- let wire
727
+ let wire, pattern, whole
655
728
  try {
656
729
  wire = JSON.parse(el.getAttribute("data-reactive-media"))
730
+ if (wire?.match != null) pattern = new RegExp(`^(?:${wire.match})$`)
731
+ if (wire?.when != null) whole = new RegExp(`^(?:${wire.when})$`)
657
732
  } catch {}
658
- if (typeof wire?.field !== "string" || !wire.pool || typeof wire.pool !== "object") {
733
+ if (
734
+ typeof wire?.field !== "string" ||
735
+ !wire.pool ||
736
+ typeof wire.pool !== "object" ||
737
+ (wire.match != null && !pattern) ||
738
+ (wire.when != null && !whole)
739
+ ) {
659
740
  console.warn("[phlex-reactive] malformed data-reactive-media — skipped")
660
741
  continue
661
742
  }
662
743
  const value = fieldValue(wire.field)
663
744
  if (value === null) continue
664
745
  // Characters (code points), as Ruby counts them — not UTF-16 code units.
665
- const key = wire.take > 0 ? [...value].slice(0, wire.take).join("") : value
666
- const source = Object.hasOwn(wire.pool, key) ? mediaMatches(c, wire.pool[key])[0] : null
746
+ const chars = wire.take > 0 ? [...value] : null
747
+ const key = chars ? chars.slice(0, wire.take).join("") : value
748
+ const pending = chars?.length < wire.take || pattern?.test(key) === false || whole?.test(value) === false
749
+ if (pending && wire.pending === "keep") {
750
+ mediaState(el, "pending")
751
+ continue
752
+ }
753
+ const source = !pending && Object.hasOwn(wire.pool, key) ? mediaMatches(c, wire.pool[key])[0] : null
667
754
  const alt = c.core.copyMedia(el, source ?? null)
668
- if (alt === null || typeof wire.label !== "string") continue
669
- const text = alt === false ? String(wire.blank ?? "") : alt
755
+ if (alt === null) continue
756
+ mediaState(el, pending ? "pending" : alt === false ? "miss" : "hit")
757
+ if (typeof wire.label !== "string") continue
758
+ const text = pending ? "" : alt === false ? String(wire.blank ?? "") : alt
670
759
  for (const node of mediaMatches(c, wire.label)) if (node.textContent !== text) node.textContent = text
671
760
  }
672
761
  }
673
762
 
763
+ // The outcome a stylesheet (or :has()) can hide a row on, written only when
764
+ // it changes.
765
+ function mediaState(el, state) {
766
+ if (el.getAttribute("data-reactive-media-state") !== state) el.setAttribute("data-reactive-media-state", state)
767
+ }
768
+
674
769
  // The owned elements a pool or label selector names — none when it does not
675
770
  // parse (a URL string is not a selector), and none for the "@root" sentinel:
676
771
  // a label naming the root would rewrite the whole root's text.
@@ -688,7 +783,9 @@ function mediaMatches(c, selector) {
688
783
  // (data-reactive-count), and the checked_* terms any condition can use —
689
784
  // reactive_enable (data-reactive-enable) flips the element's own `disabled`
690
785
  // from one. They re-sync with the show pass: on every input/change, on a
691
- // morph, and when boxes are added or removed (observeGroups).
786
+ // morph, and when boxes are added or removed (observeGroups). A header with
787
+ // data-reactive-select-all-under (issue #398) is one branch of a tree: it owns
788
+ // the boxes of the name whose data-reactive-group is in its path.
692
789
  const GROUP_BINDING_SELECTOR = "[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]"
693
790
 
694
791
  // Whether the root holds any group binding (owned or not — a stray nested
@@ -698,15 +795,25 @@ function hasGroups(c) {
698
795
  }
699
796
 
700
797
  // The owned checkboxes of a group (a nested root's boxes are its own, #15).
701
- function groupBoxes(c, name, owns, scope) {
702
- return [...c.root.querySelectorAll(namedSelector(name, scope))].filter((el) => el.type === "checkbox" && owns(el))
798
+ // With `under` (a nested header, issue #398) only the boxes whose
799
+ // data-reactive-group is that path or below it, on whole segments.
800
+ function groupBoxes(c, name, owns, scope, under) {
801
+ return [...c.root.querySelectorAll(namedSelector(name, scope))].filter(
802
+ (el) => el.type === "checkbox" && owns(el) && (!under || inGroupPath(el.getAttribute("data-reactive-group"), under)),
803
+ )
804
+ }
805
+
806
+ function inGroupPath(path, under) {
807
+ return path === under || !!path?.startsWith(`${under}.`)
703
808
  }
704
809
 
705
810
  // A header's edit ticks or unticks every owned box of its group (the header
706
811
  // itself is never a member). The show listener syncs once afterwards.
707
812
  function flipGroup(c, header) {
708
813
  const group = header?.getAttribute?.("data-reactive-select-all")
709
- if (group && c.core.owns(header)) setGroup(c, group, header.checked, header)
814
+ if (group && c.core.owns(header)) {
815
+ setGroup(c, group, header.checked, header, header.getAttribute("data-reactive-select-all-under"))
816
+ }
710
817
  }
711
818
 
712
819
  // The check_group op (issue #342): set a root's group the way its header
@@ -726,9 +833,9 @@ export function checkGroup(root, group, checked) {
726
833
  // on each one it flips so computes, shows and on-complete bindings see a real
727
834
  // edit. The re-entrant events are skipped by the show listener while this
728
835
  // runs (c.flipping); the caller syncs once afterwards.
729
- function setGroup(c, group, checked, except) {
836
+ function setGroup(c, group, checked, except, under) {
730
837
  const scope = c.root.getAttribute?.("data-reactive-scope") || null
731
- const flipped = groupBoxes(c, group, c.core.ownership(), scope).filter((box) => box !== except && box.checked !== checked)
838
+ const flipped = groupBoxes(c, group, c.core.ownership(), scope, under).filter((box) => box !== except && box.checked !== checked)
732
839
  // Every box first, then the events: a listener (a checked-count
733
840
  // on_complete) must see the group's final count, never a half-flipped one.
734
841
  for (const box of flipped) box.checked = checked
@@ -761,7 +868,8 @@ function syncGroups(c, fieldValue, owns, scope) {
761
868
  }
762
869
  const group = el.getAttribute("data-reactive-select-all")
763
870
  if (group) {
764
- const boxes = groupBoxes(c, group, owns, scope).filter((box) => box !== el)
871
+ const under = el.getAttribute("data-reactive-select-all-under")
872
+ const boxes = groupBoxes(c, group, owns, scope, under).filter((box) => box !== el)
765
873
  const ticked = boxes.filter((box) => box.checked).length
766
874
  el.checked = ticked > 0 && ticked === boxes.length
767
875
  el.indeterminate = ticked > 0 && ticked < boxes.length
@@ -1468,6 +1576,9 @@ const SHOW_LENGTH_KEYS = ["len_eq", "len_gte", "len_gt", "len_lte", "len_lt"]
1468
1576
  // ShowConditions::CHECKED_KEYS: how many owned boxes of a group are ticked.
1469
1577
  const SHOW_CHECKED_KEYS = ["checked_eq", "checked_gte", "checked_gt", "checked_lte", "checked_lt"]
1470
1578
 
1579
+ // The digit-count keys (issue #396) — Ruby's CompareTerms::DIGITS_KEYS.
1580
+ const SHOW_DIGITS_KEYS = ["digits_eq", "digits_gte", "digits_lt", "digits_lte"]
1581
+
1471
1582
  // Evaluate one count predicate (len_* or checked_*). A count is a TOTAL
1472
1583
  // function (blank/absent → 0), so every field value is decidable — no
1473
1584
  // fail-closed special case like the numeric thresholds ({ length: 0 }
@@ -1567,6 +1678,26 @@ function dnfTermMatches(term, fieldValue) {
1567
1678
  const n = fieldValue(term.field, true)
1568
1679
  return countPredicateMatches(checked, term[checked], Array.isArray(n) ? n.length : Number(n) || 0) === true
1569
1680
  }
1681
+ // A same: term (issue #395) compares this field with ANOTHER owned field,
1682
+ // both as strings (an absent one is ""); fold lowercases ASCII A-Z only, the
1683
+ // exact replace Ruby's tr("A-Z", "a-z") does. negate is unless:'s flip.
1684
+ if (typeof term.same === "string") {
1685
+ const read = (name) => {
1686
+ const v = String(fieldValue(name) ?? "")
1687
+ return term.fold === true ? v.replace(/[A-Z]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : v
1688
+ }
1689
+ return (read(term.field) === read(term.same)) !== (term.negate === true)
1690
+ }
1691
+ // A digits: term (issue #396): ASCII digits only, the count within every
1692
+ // digits_* bound, and the optional prefix; negate flips it. A malformed
1693
+ // literal is false either way (fail-closed, like Ruby).
1694
+ const digits = SHOW_DIGITS_KEYS.filter((key) => key in term)
1695
+ if (digits.length) {
1696
+ const v = String(fieldValue(term.field) ?? "")
1697
+ if (!digits.every((key) => Number.isInteger(term[key])) || (term.prefix != null && typeof term.prefix !== "string")) return false
1698
+ const ok = /^[0-9]*$/.test(v) && digits.every((key) => countPredicateMatches(key, term[key], v.length)) && v.startsWith(term.prefix ?? "")
1699
+ return ok !== (term.negate === true)
1700
+ }
1570
1701
  // An absent owned field reads as "" — identical to the server evaluator
1571
1702
  // (ShowConditions.match? treats a missing field as blank). This keeps the
1572
1703
  // Ruby first-paint and the client live-toggle in exact agreement (the shared
@@ -1600,6 +1731,7 @@ function dnfGroupFields(groups) {
1600
1731
  if (!Array.isArray(group)) continue
1601
1732
  for (const term of group) {
1602
1733
  if (term && typeof term === "object" && typeof term.field === "string") fields.add(term.field)
1734
+ if (typeof term?.same === "string") fields.add(term.same)
1603
1735
  }
1604
1736
  }
1605
1737
  return fields.size > 0 ? [...fields] : null
@@ -1,4 +1,4 @@
1
- import{confirmPredicate as ae}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 o of r.split(" "))e.root.addEventListener?.(o,t),e.offs.push(()=>e.root.removeEventListener?.(o,t))}function y(e,r){d(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Be(e,r){let t=p(e,r);t.offs=[],t.seeds=[];let o,n,i,a;(t.wire=()=>{if(!t.show&&fe(t))t.show=(s)=>{if(t.flipping)return;if(s?.type==="input"||s?.type==="change")Ae(t,s.target);else t.groups=be(t),M(t);ge(t)},d(t,"input change",t.show),y(t,t.show);if(!t.arm&&de(t))d(t,"input change",(s)=>R(t,s)),y(t,t.arm=()=>{R(t,null),M(t)});if(!o&&(o=t.show||t.arm))d(t,"reset",()=>setTimeout(()=>{if(g.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!n&&(n=A(t)))d(t,"input",(s)=>xe(t,s)&&m(t)),y(t,()=>m(t));if(!i&&(i=S(t)))y(t,()=>Y(t));if(!a&&(a=Ce(t)))d(t,"input change",(s)=>V(t,s)),y(t,()=>O(t))})(),d(t,"keydown",(s)=>Ie(t,s)),d(t,"paste drop",(s)=>Pe(t,s)),d(t,"input",(s)=>We(t,s))}function Fe(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 Ke(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 He(e,r,t){return V(p(e,r),t)}function ze(e,r,t){return oe(p(e,r),t)}var se=/^#[A-Za-z_][\w-]*$/,D="[data-reactive-show-field], [data-reactive-show]";function ce(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 X){let a=e.getAttribute(`data-reactive-show-${i}`);if(a!==null)return re(i,a,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function I(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 le(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 P(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 ue(e,r)}function ue(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)=>E(i,r));return t==="all"?n.every(Boolean):n.some(Boolean)}function b(e,r="show"){if(typeof e==="string"&&se.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function fe(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?.(D)??[];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;for(let t of e.root.querySelectorAll?.(F)??[])if(e.core.owns(t))return!0;return!1}function de(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function pe(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:le(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function R(e,r){let t=pe(e);if(!t.length)return;let o=B(e,e.core.ownership());t.forEach((n,i)=>{let a=v(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 ge(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,o=B(e,r);for(let n of e.root.querySelectorAll(D)){if(!r(n))continue;let i=n.getAttribute("data-reactive-show");if(i!==null){let l=P(I(i),o);if(l!==null)J(n,l,r,t);continue}let a=n.getAttribute("data-reactive-show-field");if(!a)continue;let s=o(a);if(s===null)continue;let c=ce(n,s);if(c===null)continue;J(n,c,r,t)}if(e.groups)we(e,o,r,t);ve(e,o,r),he(e,o),Se(e,o)}function J(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 he(e,r){let t=W(e);for(let[o,n]of Object.entries(t)){if(o.startsWith("#")){ye(o,n,r);continue}if(!n||typeof n!=="object"||Array.isArray(n))continue;let i=r(o);if(i===null)continue;let a=(s,c)=>c?r(o,!0):i;for(let[s,c]of Object.entries(n)){if(!b(s))continue;let l;if(Array.isArray(c)){if(c.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${s} — skipped`);continue}l=c.every((u)=>E(u,a))}else{let u=ne(c,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${s} — skipped`);continue}l=u}for(let u of document.querySelectorAll(s))u.hidden=!l}}}function ye(e,r,t){if(!b(e))return;let o=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,n=Le(o);if(n===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(n.every((a)=>t(a)===null)&&!De(o))return;let i=v(o,t);if(i===null)return;for(let a of document.querySelectorAll(e))a.hidden=!i}function W(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 me(e,r,t,o){let n=!1,i=null;for(let a of e.root.querySelectorAll(G(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 G(e,r){return`[name="${r&&!e.includes("[")?`${r}[${e}]`:e}"]`}function B(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:me(e,n,r,t));return o.get(a)}}var F="[data-reactive-media]";function ve(e,r,t){for(let o of e.root.querySelectorAll(F)){if(!t(o))continue;let n;try{n=JSON.parse(o.getAttribute("data-reactive-media"))}catch{}if(typeof n?.field!=="string"||!n.pool||typeof n.pool!=="object"){console.warn("[phlex-reactive] malformed data-reactive-media — skipped");continue}let i=r(n.field);if(i===null)continue;let a=n.take>0?[...i].slice(0,n.take).join(""):i,s=Object.hasOwn(n.pool,a)?T(e,n.pool[a])[0]:null,c=e.core.copyMedia(o,s??null);if(c===null||typeof n.label!=="string")continue;let l=c===!1?String(n.blank??""):c;for(let u of T(e,n.label))if(u.textContent!==l)u.textContent=l}}function T(e,r){try{return r==="@root"?[]:e.core.opTargets({to:r})}catch{return[]}}var w="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function be(e){return!!e.root.querySelector?.(w)}function _(e,r,t,o){return[...e.root.querySelectorAll(G(r,o))].filter((n)=>n.type==="checkbox"&&t(n))}function Ae(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))K(e,t,r.checked,r)}function Ye(e,r,t){if(!r)return;let o=q.get(e)??{root:e,core:{ownership:()=>(n)=>n.closest('[data-controller~="reactive"]')===e}};K(o,r,t!==!1),o.show?.()}function K(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 we(e,r,t,o){for(let n of e.root.querySelectorAll(w)){if(!t(n))continue;let i=n.getAttribute("data-reactive-enable");if(i!==null){let c=P(I(i),r);if(c!==null)n.disabled=!c}let a=n.getAttribute("data-reactive-count");if(a){let c=String(r(a,!0));if(n.textContent!==c)n.textContent=c}let s=n.getAttribute("data-reactive-select-all");if(s){let c=_(e,s,t,o).filter((u)=>u!==n),l=c.filter((u)=>u.checked).length;n.checked=l>0&&l===c.length,n.indeterminate=l>0&&l<c.length}}}function Se(e,r){for(let[t,o]of Object.entries(W(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(!b(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(!b(a,"group"))continue;let c=v(s?.any,r);if(c===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${a} — skipped`);continue}for(let l of document.querySelectorAll(a))l.disabled=!c}}}function M(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?.(`${w}, [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 A(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function xe(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 o=e.core.ownership(),n=[...e.root.querySelectorAll(r)].find(o);if(!n)return;let i=(n.value??"").trim().toLowerCase(),a=0;for(let l of e.root.querySelectorAll(t)){if(!o(l))continue;let u=(l.getAttribute("data-reactive-filter-text")??l.textContent??"").toLowerCase(),f=l.hasAttribute?.("data-reactive-tags-selected")||i!==""&&!u.includes(i);if(l.hidden=f,f)l.removeAttribute("data-reactive-highlighted");else a++}let s=e.root.getAttribute("data-reactive-filter-group");if(s)for(let l of e.root.querySelectorAll(s)){if(!o(l))continue;let u=[...l.querySelectorAll(t)].filter(o);if(u.length===0)continue;l.hidden=u.every((f)=>f.hidden)}let c=e.root.getAttribute("data-reactive-filter-empty");if(c){for(let l of e.root.querySelectorAll(c))if(o(l))l.hidden=a>0}}function S(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function Ce(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function k(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 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 H(e,r){let t=k(e);if(!t)return!1;let o=N(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)z(e,t,o);return i}function z(e,r,t){r.value=t.join(","),h(r),Y(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 Y(e){let r=k(e);if(!r)return;let t=N(r),o=e.core.ownership();_e(e,t,o),ke(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 c=s.matches?.("[data-reactive-tag-text]")?s:(s.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(c)c.textContent=a;let l=[...s.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(s.matches?.('[data-action*="reactive#tagsRemove"]'))l.push(s);for(let u of l)u.setAttribute?.("data-reactive-tag-param",a);o.appendChild(s)}}function ke(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"),!A(e))n.hidden=!1}}if(A(e))m(e)}function Ne(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Oe(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 Ee(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 $e(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[c,l]of Object.entries(n)){let u=[...e.root.querySelectorAll?.(l)??[]].find(i);if(!u)continue;let f=a.find((x)=>Q(x.getAttribute?.("name"))===c);if(!f)continue;je(f,u),s.push(u)}if(o)for(let c of s)Re(c);return s[0]??null}function je(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:C(r)!=="";else e.value=C(r);h(e)}function Re(e){if(e.type==="checkbox")e.checked=!1;else e.value="";h(e)}function Je(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,o)=>Object.prototype.hasOwnProperty.call(r,o)?r[o]: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))Z(e,t.getAttribute("data-reactive-nested-json"))}function Te(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 U(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=Q(n.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=C(n)}return t}function Q(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 Z(e,r){let t=e.core.ownership(),o=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!o)return;let n=Te(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(U(e,s))}let a=JSON.stringify(i);if(n.value===a)return;n.value=a,h(n)}function Ue(e,r,t){let o=p(e,r);if(!S(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(!H(o,String(n.value??"").split(",")))return;if(n.value="",A(o))m(o)}function Qe(e,r,t){let o=p(e,r);if(!S(o))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!H(o,[i]))return;let a=qe(o);if(!a)return;a.value="",m(o),a.focus?.()}function Ze(e,r,t){let o=p(e,r);if(!S(o))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let a=k(o);if(!a)return;let s=N(a),c=s.filter((l)=>l.toLowerCase()!==i.toLowerCase());if(c.length===s.length)return;z(o,a,c)}function Ve(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),l=[...o.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(a)?.content?.firstElementChild;if(!s||!l){Ee(o,i);return}let u=l.cloneNode(!0);Oe(u,Ne(o)),s.appendChild(u);let f=n?.getAttribute?.("data-reactive-nested-from-param"),x=n?.getAttribute?.("data-reactive-nested-clear-param")==="true",ie=$e(o,u,f,x);if(f)ie?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(s.getAttribute?.("data-reactive-nested-json")===i)Z(o,i)}function Xe(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"),c=a||(s?oe(o,s):null);if(!c)return L(o,i);let l=U(o,i),u=Je(c,l);return o.core.confirm(u,{el:n,row:i,fields:l}).then((f)=>{if(f)L(o,i)})}function V(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;O(e)}var X=["gte","gt","lte","lt"],Me=["len_eq","len_gte","len_gt","len_lte","len_lt"],ee=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function te(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 re(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 ne(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 X)if(t in e)return re(t,e[t],r);for(let t of Me)if(t in e)return te(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=ee.find((n)=>(n in e));if(t){let n=r(e.field,!0);return te(t,e[t],Array.isArray(n)?n.length:Number(n)||0)===!0}let o=r(e.field)??"";return ne(e,o)===!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((o)=>E(o,r)))}function Le(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 De(e){return e.flat().every((r)=>ee.some((t)=>(t in Object(r))))}function oe(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=ae(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=v(t.groups?.any,n)===!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 Ie(e,r){let t=j(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),h(t),t.blur()}function Pe(e,r){let t=j(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 We(e,r){let t=j(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{Ye as checkGroup,ze as confirmMessage,Be as connect,Fe as disconnect,Ve as nestedAdd,Xe as nestedRemove,Ke as reseed,He as syncNestedJson,Ue as tagsAdd,Qe as tagsPick,Ze as tagsRemove};
1
+ import{confirmPredicate as fe}from"phlex/reactive/confirm_predicate";var v=new WeakMap,N=new WeakMap;function g(e,r){let t=v.get(e);if(!t){if(v.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)N.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 m(e,r){p(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Qe(e,r){let t=g(e,r);t.offs=[],t.seeds=[];let n,o,i,s;(t.wire=()=>{if(!t.show&&ye(t))t.show=(a)=>{if(t.flipping)return;if(a?.type==="input"||a?.type==="change")qe(t,a.target);else t.groups=_e(t),G(t);D(t)},p(t,"input change",t.show),p(t,"focusout",(a)=>{let l=a.target?.getAttribute?.("name");if(!l||t.touched?.has(l)||!t.core.owns(a.target))return;if((t.touched??=new Set).add(l),t.root.querySelector?.("[data-reactive-show-after]"))D(t)}),m(t,t.show);if(!t.arm&&ve(t))p(t,"input change",(a)=>I(t,a)),m(t,t.arm=()=>{I(t,null),G(t)});if(!n&&(n=t.show||t.arm))p(t,"reset",()=>setTimeout(()=>{if(v.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!o&&(o=S(t)))p(t,"input",(a)=>Ee(t,a)&&b(t)),m(t,()=>b(t));if(!i&&(i=k(t)))m(t,()=>ee(t));if(!s&&(s=$e(t)))p(t,"input change",(a)=>oe(t,a)),m(t,()=>M(t))})(),p(t,"keydown",(a)=>ze(t,a)),p(t,"paste drop",(a)=>Ye(t,a)),p(t,"input",(a)=>Ue(t,a))}function Ve(e){let r=v.get(e);if(!r)return;v.delete(e),N.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function Xe(e){let r=v.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 et(e,r,t){return oe(g(e,r),t)}function tt(e,r,t){return le(g(e,r),t)}var de=/^#[A-Za-z_][\w-]*$/,F="[data-reactive-show-field], [data-reactive-show]";function pe(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 ie){let s=e.getAttribute(`data-reactive-show-${i}`);if(s!==null)return se(i,s,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function H(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 ge(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 K(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 he(e,r)}function he(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)=>J(i,r));return t==="all"?o.every(Boolean):o.some(Boolean)}function w(e,r="show"){if(typeof e==="string"&&de.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function ye(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?.(F)??[];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?.(Z)??[])if(e.core.owns(t))return!0;return!1}function ve(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function me(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:ge(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function I(e,r){let t=me(e);if(!t.length)return;let n=U(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 D(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,n=U(e,r),o=[];for(let i of e.root.querySelectorAll(F)){if(!r(i))continue;let s=i.getAttribute("data-reactive-show");if(s!==null){let u=K(H(s),n);if(u===null)continue;let f=i.getAttribute("data-reactive-show-after"),d=u&&(f==null||!!e.touched?.has(O(f,t)));L(i,d,r,t);let h=(e.shown??=new WeakMap).get(i);if(e.shown.set(i,d),be.some((ue)=>i.hasAttribute(`data-reactive-show-${ue}`))&&h!==void 0&&h!==d)o.push(i);continue}let a=i.getAttribute("data-reactive-show-field");if(!a)continue;let l=n(a);if(l===null)continue;let c=pe(i,l);if(c===null)continue;L(i,c,r,t)}if(e.groups)Ne(e,n,r,t);ke(e,n,r),we(e,n),Oe(e,n);for(let i of o)Ae(e,i,r,t)}var be=["clear","focus","scroll"];function Ae(e,r,t,n){if(r.hidden){let a=[];try{a=JSON.parse(r.getAttribute("data-reactive-show-clear")??"[]")}catch{}let l=new Set((Array.isArray(a)?a:[]).map((c)=>O(String(c),n)));for(let c of l.size?e.root.querySelectorAll("[name]"):[]){if(!l.has(c.getAttribute("name"))||!t(c)||c.type==="file")continue;if(c.type==="checkbox"||c.type==="radio"){if(c.checked)c.checked=!1,y(c)}else e.core.setValue(c,"")}return}let o=r.getAttribute("data-reactive-show-scroll")==="true";if(o)r.scrollIntoView?.({block:"nearest"});let i=r.getAttribute("data-reactive-show-focus"),s=null;try{s=i?e.root.querySelector(i):null}catch{}s?.focus?.(o?{preventScroll:!0}:void 0)}function L(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 we(e,r){let t=z(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){Se(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(!w(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)=>J(u,s))}else{let u=ce(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 Se(e,r,t){if(!w(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=He(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((s)=>t(s)===null)&&!Ke(n))return;let i=A(n,t);if(i===null)return;for(let s of document.querySelectorAll(e))s.hidden=!i}function z(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 xe(e,r,t,n){let o=!1,i=null;for(let s of e.root.querySelectorAll(Y(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 Y(e,r){return`[name="${O(e,r)}"]`}function O(e,r){return r&&!e.includes("[")?`${r}[${e}]`:e}function U(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(e,o,r,t).filter((a)=>a.checked).length:xe(e,o,r,t));return n.get(s)}}var Z="[data-reactive-media]";function ke(e,r,t){for(let n of e.root.querySelectorAll(Z)){if(!t(n))continue;let o,i,s;try{if(o=JSON.parse(n.getAttribute("data-reactive-media")),o?.match!=null)i=new RegExp(`^(?:${o.match})$`);if(o?.when!=null)s=new RegExp(`^(?:${o.when})$`)}catch{}if(typeof o?.field!=="string"||!o.pool||typeof o.pool!=="object"||o.match!=null&&!i||o.when!=null&&!s){console.warn("[phlex-reactive] malformed data-reactive-media — skipped");continue}let a=r(o.field);if(a===null)continue;let l=o.take>0?[...a]:null,c=l?l.slice(0,o.take).join(""):a,u=l?.length<o.take||i?.test(c)===!1||s?.test(a)===!1;if(u&&o.pending==="keep"){W(n,"pending");continue}let f=!u&&Object.hasOwn(o.pool,c)?P(e,o.pool[c])[0]:null,d=e.core.copyMedia(n,f??null);if(d===null)continue;if(W(n,u?"pending":d===!1?"miss":"hit"),typeof o.label!=="string")continue;let h=u?"":d===!1?String(o.blank??""):d;for(let _ of P(e,o.label))if(_.textContent!==h)_.textContent=h}}function W(e,r){if(e.getAttribute("data-reactive-media-state")!==r)e.setAttribute("data-reactive-media-state",r)}function P(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 _e(e){return!!e.root.querySelector?.(x)}function E(e,r,t,n,o){return[...e.root.querySelectorAll(Y(r,n))].filter((i)=>i.type==="checkbox"&&t(i)&&(!o||Ce(i.getAttribute("data-reactive-group"),o)))}function Ce(e,r){return e===r||!!e?.startsWith(`${r}.`)}function qe(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))Q(e,t,r.checked,r,r.getAttribute("data-reactive-select-all-under"))}function rt(e,r,t){if(!r)return;let n=N.get(e)??{root:e,core:{ownership:()=>(o)=>o.closest('[data-controller~="reactive"]')===e}};Q(n,r,t!==!1),n.show?.()}function Q(e,r,t,n,o){let i=e.root.getAttribute?.("data-reactive-scope")||null,s=E(e,r,e.core.ownership(),i,o).filter((a)=>a!==n&&a.checked!==t);for(let a of s)a.checked=t;e.flipping=!0;try{for(let a of s)for(let l of["input","change"])a.dispatchEvent?.(new Event(l,{bubbles:!0}))}finally{e.flipping=!1}}function Ne(e,r,t,n){for(let o of e.root.querySelectorAll(x)){if(!t(o))continue;let i=o.getAttribute("data-reactive-enable");if(i!==null){let l=K(H(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=o.getAttribute("data-reactive-select-all-under"),c=E(e,a,t,n,l).filter((f)=>f!==o),u=c.filter((f)=>f.checked).length;o.checked=u>0&&u===c.length,o.indeterminate=u>0&&u<c.length}}}function Oe(e,r){for(let[t,n]of Object.entries(z(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(!w(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(!w(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 G(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?.(`${x}, [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 S(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function Ee(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function b(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 k(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function $e(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function j(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 R(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 V(e,r){let t=j(e);if(!t)return!1;let n=R(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)X(e,t,n);return i}function X(e,r,t){r.value=t.join(","),y(r),ee(e)}function je(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 ee(e){let r=j(e);if(!r)return;let t=R(r),n=e.core.ownership();Re(e,t,n),Me(e,t,n)}function Re(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"),!S(e))o.hidden=!1}}if(S(e))b(e)}function Je(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Te(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 Ie(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 De(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((d)=>re(d.getAttribute?.("name"))===l);if(!f)continue;Le(f,u),a.push(u)}if(n)for(let l of a)We(l);return a[0]??null}function Le(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:C(r)!=="";else e.value=C(r);y(e)}function We(e){if(e.type==="checkbox")e.checked=!1;else e.value="";y(e)}function Pe(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,n)=>Object.prototype.hasOwnProperty.call(r,n)?r[n]:t)}function B(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);M(e)}function M(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))ne(e,t.getAttribute("data-reactive-nested-json"))}function Ge(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 te(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=re(o.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=C(o)}return t}function re(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 ne(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=Ge(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(te(e,a))}let s=JSON.stringify(i);if(o.value===s)return;o.value=s,y(o)}function nt(e,r,t){let n=g(e,r);if(!k(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(!V(n,String(o.value??"").split(",")))return;if(o.value="",S(n))b(n)}function ot(e,r,t){let n=g(e,r);if(!k(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!V(n,[i]))return;let s=je(n);if(!s)return;s.value="",b(n),s.focus?.()}function it(e,r,t){let n=g(e,r);if(!k(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let s=j(n);if(!s)return;let a=R(s),l=a.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===a.length)return;X(n,s,l)}function at(e,r,t){let n=g(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){Ie(n,i);return}let u=c.cloneNode(!0);Te(u,Je(n)),a.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),d=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",h=De(n,u,f,d);if(f)h?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(a.getAttribute?.("data-reactive-nested-json")===i)ne(n,i)}function st(e,r,t){let n=g(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?le(n,a):null);if(!l)return B(n,i);let c=te(n,i),u=Pe(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)B(n,i)})}function oe(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;M(e)}var ie=["gte","gt","lte","lt"],Be=["len_eq","len_gte","len_gt","len_lte","len_lt"],ae=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"],Fe=["digits_eq","digits_gte","digits_lt","digits_lte"];function q(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 se(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 ce(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 ie)if(t in e)return se(t,e[t],r);for(let t of Be)if(t in e)return q(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=ae.find((i)=>(i in e));if(t){let i=r(e.field,!0);return q(t,e[t],Array.isArray(i)?i.length:Number(i)||0)===!0}if(typeof e.same==="string"){let i=(s)=>{let a=String(r(s)??"");return e.fold===!0?a.replace(/[A-Z]/g,(l)=>String.fromCharCode(l.charCodeAt(0)+32)):a};return i(e.field)===i(e.same)!==(e.negate===!0)}let n=Fe.filter((i)=>(i in e));if(n.length){let i=String(r(e.field)??"");if(!n.every((a)=>Number.isInteger(e[a]))||e.prefix!=null&&typeof e.prefix!=="string")return!1;return(/^[0-9]*$/.test(i)&&n.every((a)=>q(a,e[a],i.length))&&i.startsWith(e.prefix??""))!==(e.negate===!0)}let o=r(e.field)??"";return ce(e,o)===!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)=>J(n,r)))}function He(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);if(typeof n?.same==="string")r.add(n.same)}}return r.size>0?[...r]:null}function Ke(e){return e.flat().every((r)=>ae.some((t)=>(t in Object(r))))}function le(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=fe(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}function T(e,r){let t=r.target?.closest?.("[data-reactive-editable]");return t&&e.core.owns(t)?t:null}function ze(e,r){let t=T(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),y(t),t.blur()}function Ye(e,r){let t=T(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 Ue(e,r){let t=T(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{rt as checkGroup,tt as confirmMessage,Qe as connect,Ve as disconnect,at as nestedAdd,st as nestedRemove,Xe as reseed,et as syncNestedJson,nt as tagsAdd,ot as tagsPick,it as tagsRemove};
2
2
 
3
- //# debugId=099864F98B8480EC64756E2164756E21
3
+ //# debugId=43EEE1B3CF9BBE3564756E2164756E21
4
4
  //# sourceMappingURL=bindings.min.js.map