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