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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +269 -2
- data/README.md +92 -13
- data/app/javascript/phlex/reactive/core.js +1 -0
- data/app/javascript/phlex/reactive/core.min.js +6 -6
- data/app/javascript/phlex/reactive/core.min.js.map +4 -4
- data/app/javascript/phlex/reactive/features/bindings.js +153 -21
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/countdown.js +140 -0
- data/app/javascript/phlex/reactive/features/countdown.min.js +4 -0
- data/app/javascript/phlex/reactive/features/countdown.min.js.map +10 -0
- data/app/javascript/phlex/reactive/features/form.js +54 -22
- data/app/javascript/phlex/reactive/features/form.min.js +2 -2
- data/app/javascript/phlex/reactive/features/form.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/persist.js +41 -8
- data/app/javascript/phlex/reactive/features/persist.min.js +2 -2
- data/app/javascript/phlex/reactive/features/persist.min.js.map +3 -3
- data/app/javascript/phlex/reactive/reactive_controller.js +2 -0
- data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -4
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +9 -8
- data/app/javascript/phlex/reactive/runtime.js +313 -32
- data/lib/phlex/reactive/component/countdown_binding.rb +80 -0
- data/lib/phlex/reactive/component/field_references.rb +130 -0
- data/lib/phlex/reactive/component/helpers.rb +81 -19
- data/lib/phlex/reactive/component/identity.rb +34 -1
- data/lib/phlex/reactive/component/image_fallback_binding.rb +67 -0
- data/lib/phlex/reactive/component/media_binding.rb +30 -4
- data/lib/phlex/reactive/component/media_pattern.rb +79 -0
- data/lib/phlex/reactive/component/selection.rb +24 -2
- data/lib/phlex/reactive/component/show_options.rb +91 -0
- data/lib/phlex/reactive/doctor.rb +20 -8
- data/lib/phlex/reactive/engine.rb +2 -2
- data/lib/phlex/reactive/js.rb +58 -5
- data/lib/phlex/reactive/show_conditions/compare_terms.rb +142 -0
- data/lib/phlex/reactive/show_conditions.rb +39 -6
- data/lib/phlex/reactive/streamable.rb +6 -3
- data/lib/phlex/reactive/version.rb +1 -1
- 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
|
|
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="${
|
|
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"?}'
|
|
642
|
-
// <img>: the owned field's value (its first `take` characters, or
|
|
643
|
-
// is a key into `pool`, whose value is a SELECTOR for an <img>
|
|
644
|
-
// The core's copyMedia copies that element's src, srcset and
|
|
645
|
-
// value is never assigned to anything. A key, selector or element
|
|
646
|
-
// clears the preview; `label` gets the alt (or `blank`) via
|
|
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 (
|
|
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
|
|
666
|
-
const
|
|
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
|
|
669
|
-
|
|
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
|
-
|
|
702
|
-
|
|
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))
|
|
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
|
|
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=
|
|
3
|
+
//# debugId=43EEE1B3CF9BBE3564756E2164756E21
|
|
4
4
|
//# sourceMappingURL=bindings.min.js.map
|