phlex-reactive 0.16.0 → 0.16.1

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.
@@ -2,8 +2,8 @@
2
2
  // re-syncs after a morph, all of it client-only and all of it reading the
3
3
  // root's own controls: show bindings and cross-root show targets (issues
4
4
  // #161, #164, #176, #180, #209), completion bindings (#226), option filtering
5
- // (#163), the tag-chip input (#203) and draft nested-attribute rows with their
6
- // JSON mode (#208). One of the feature modules the opt-in client imports on
5
+ // (#163), the tag-chip input (#203), draft nested-attribute rows with their
6
+ // JSON mode (#208) and fields fed into CSS custom properties (#417). One of the feature modules the opt-in client imports on
7
7
  // demand (issue #275); in the default client it is part of the one file.
8
8
  //
9
9
  // Grouped because they share a shape — a marker check at connect, one
@@ -386,8 +386,10 @@ function showSyncEnabled(c) {
386
386
  const nodes = c.root.querySelectorAll?.(SHOW_BINDING_SELECTOR) ?? []
387
387
  for (const el of nodes) if (c.core.owns(el)) return true
388
388
  for (const el of c.root.querySelectorAll?.(GROUP_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true
389
- for (const el of c.root.querySelectorAll?.(MEDIA_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true
390
- return false
389
+ for (const el of c.root.querySelectorAll?.(`${MEDIA_BINDING_SELECTOR}, ${CSS_VARS_SELECTOR}`) ?? []) {
390
+ if (c.core.owns(el)) return true
391
+ }
392
+ return c.root.hasAttribute?.("data-reactive-css-vars") ?? false
391
393
  }
392
394
 
393
395
  // The connect() gate for completion bindings (issue #226) — one attribute read.
@@ -493,6 +495,7 @@ function syncShow(c) {
493
495
  // share the same owned-field memo, so a field driving several reads once.
494
496
  if (c.groups) syncGroups(c, fieldValue, owns, scope)
495
497
  syncMedia(c, fieldValue, owns)
498
+ syncCssVars(c, fieldValue, owns, scope)
496
499
  syncShowTargets(c, fieldValue)
497
500
  syncGroupTargets(c, fieldValue)
498
501
  for (const el of moved) showMoved(c, el, owns, scope)
@@ -777,6 +780,80 @@ function mediaMatches(c, selector) {
777
780
  }
778
781
  }
779
782
 
783
+ // --- CSS custom properties (issue #417) --------------------------------------
784
+ // data-reactive-css-vars='{"to":[selector|"@root",…],"global"?:true,
785
+ // "vars":{"--name":{"field","unit"?}}}' on the root or an element it owns.
786
+ // Each property reads its owned field (a checkbox is 1 or 0, a radio group its
787
+ // checked value) as a finite number and writes number + unit on every `to:`
788
+ // target, resolved from the root like a client op's. Anything else removes the
789
+ // property, so the author's var() fallback applies: no string from a field ever
790
+ // reaches CSS. A name or unit off the Ruby allowlist is refused here too (a
791
+ // hand-built attr must not widen it); a field no owned control carries leaves
792
+ // the property alone (the show default-deny). A value is written only when it
793
+ // changes, since the pass runs on every owned field's edit.
794
+ const CSS_VARS_SELECTOR = "[data-reactive-css-vars]"
795
+ const CSS_VAR_NAME = /^--[a-z0-9-]+$/i
796
+ const CSS_VAR_UNITS = ["", "px", "%", "deg", "turn", "ms", "s", "em", "rem", "fr"]
797
+
798
+ function syncCssVars(c, fieldValue, owns, scope) {
799
+ const nodes = [...c.root.querySelectorAll(CSS_VARS_SELECTOR)].filter(owns)
800
+ if (c.root.hasAttribute("data-reactive-css-vars")) nodes.unshift(c.root)
801
+ for (const el of nodes) {
802
+ let wire
803
+ try {
804
+ wire = JSON.parse(el.getAttribute("data-reactive-css-vars"))
805
+ } catch {}
806
+ if (!Array.isArray(wire?.to) || !wire.vars || typeof wire.vars !== "object") {
807
+ // Most likely two calls mixed onto one element: their JSON space-joins.
808
+ console.warn("[phlex-reactive] malformed data-reactive-css-vars — skipped (two reactive_css_vars on one element?)")
809
+ continue
810
+ }
811
+ // A selector that does not parse matches nothing: it must not throw out
812
+ // of the show pass and stop the root's other bindings.
813
+ const targets = wire.to.flatMap((to) => {
814
+ const args = { to, global: wire.global === true }
815
+ let found = []
816
+ try {
817
+ found = c.core.opTargets(args)
818
+ } catch {}
819
+ if (!found.length) c.core.diagnose("reactive_css_vars", args)
820
+ return found
821
+ })
822
+ for (const [name, spec] of Object.entries(wire.vars)) {
823
+ const unit = spec?.unit ?? ""
824
+ if (!CSS_VAR_NAME.test(name) || !CSS_VAR_UNITS.includes(unit) || typeof spec?.field !== "string") {
825
+ console.warn(`[phlex-reactive] refused reactive_css_vars entry ${JSON.stringify(name)} — skipped`)
826
+ continue
827
+ }
828
+ const number = cssVarNumber(c, fieldValue, spec.field, owns, scope)
829
+ if (number === undefined) continue
830
+ const value = number === null ? "" : `${number}${unit}`
831
+ for (const target of targets) {
832
+ if (target.style.getPropertyValue(name) === value) continue
833
+ if (value) target.style.setProperty(name, value)
834
+ else target.style.removeProperty(name)
835
+ }
836
+ }
837
+ }
838
+ }
839
+
840
+ // The number an owned field holds: 1 or 0 for a checkbox (decided by its
841
+ // type, so a text field reading "true" is no number), else its value through
842
+ // Number() — null when that is not finite or the value is blank (Number("")
843
+ // is 0), undefined when no owned field has the name.
844
+ function cssVarNumber(c, fieldValue, name, owns, scope) {
845
+ const value = fieldValue(name)
846
+ if (value === null) return undefined
847
+ if (
848
+ (value === "true" || value === "false") &&
849
+ [...c.root.querySelectorAll(namedSelector(name, scope))].some((el) => owns(el) && el.type === "checkbox")
850
+ ) {
851
+ return value === "true" ? 1 : 0
852
+ }
853
+ const number = value.trim() ? Number(value) : NaN
854
+ return Number.isFinite(number) ? number : null
855
+ }
856
+
780
857
  // --- Bulk selection (issue #319) ---------------------------------------------
781
858
  // A checkbox GROUP is the owned boxes sharing one name. Three bindings read it:
782
859
  // the select-all header (data-reactive-select-all), the ticked count
@@ -1,4 +1,4 @@
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};
1
+ import{confirmPredicate as de}from"phlex/reactive/confirm_predicate";var v=new WeakMap,q=new WeakMap;function h(e,r){let t=v.get(e);if(!t){if(v.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)q.set(t.root,t)}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 m(e,r){g(e,"turbo:morph-element",r),e.seeds.push(r),r()}function rt(e,r){let t=h(e,r);t.offs=[],t.seeds=[];let n,o,i,a;(t.wire=()=>{if(!t.show&&ve(t))t.show=(s)=>{if(t.flipping)return;if(s?.type==="input"||s?.type==="change")je(t,s.target);else t.groups=$e(t),F(t);D(t)},g(t,"input change",t.show),g(t,"focusout",(s)=>{let l=s.target?.getAttribute?.("name");if(!l||t.touched?.has(l)||!t.core.owns(s.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&&me(t))g(t,"input change",(s)=>L(t,s)),m(t,t.arm=()=>{L(t,null),F(t)});if(!n&&(n=t.show||t.arm))g(t,"reset",()=>setTimeout(()=>{if(v.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!o&&(o=S(t)))g(t,"input",(s)=>Te(t,s)&&b(t)),m(t,()=>b(t));if(!i&&(i=_(t)))m(t,()=>te(t));if(!a&&(a=Je(t)))g(t,"input change",(s)=>ie(t,s)),m(t,()=>T(t))})(),g(t,"keydown",(s)=>Qe(t,s)),g(t,"paste drop",(s)=>Xe(t,s)),g(t,"input",(s)=>et(t,s))}function nt(e){let r=v.get(e);if(!r)return;v.delete(e),q.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function ot(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 it(e,r,t){return ie(h(e,r),t)}function st(e,r,t){return ue(h(e,r),t)}var pe=/^#[A-Za-z_][\w-]*$/,H="[data-reactive-show-field], [data-reactive-show]";function ge(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 se){let a=e.getAttribute(`data-reactive-show-${i}`);if(a!==null)return ce(i,a,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function K(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 he(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 V(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 ye(e,r)}function ye(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"&&pe.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function ve(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?.(H)??[];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?.(`${U}, ${Z}`)??[])if(e.core.owns(t))return!0;return e.root.hasAttribute?.("data-reactive-css-vars")??!1}function me(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function be(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:he(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function L(e,r){let t=be(e);if(!t.length)return;let n=Y(e,e.core.ownership());t.forEach((o,i)=>{let a=A(o.any,n);if(a===null)return;let s=a&&!e.onCompleteStates[i]&&Boolean(r);if(e.onCompleteStates[i]=a,s)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=Y(e,r),o=[];for(let i of e.root.querySelectorAll(H)){if(!r(i))continue;let a=i.getAttribute("data-reactive-show");if(a!==null){let u=V(K(a),n);if(u===null)continue;let f=i.getAttribute("data-reactive-show-after"),d=u&&(f==null||!!e.touched?.has(E(f,t)));P(i,d,r,t);let p=(e.shown??=new WeakMap).get(i);if(e.shown.set(i,d),Ae.some((fe)=>i.hasAttribute(`data-reactive-show-${fe}`))&&p!==void 0&&p!==d)o.push(i);continue}let s=i.getAttribute("data-reactive-show-field");if(!s)continue;let l=n(s);if(l===null)continue;let c=ge(i,l);if(c===null)continue;P(i,c,r,t)}if(e.groups)Re(e,n,r,t);ke(e,n,r),qe(e,n,r,t),Se(e,n),Me(e,n);for(let i of o)we(e,i,r,t)}var Ae=["clear","focus","scroll"];function we(e,r,t,n){if(r.hidden){let s=[];try{s=JSON.parse(r.getAttribute("data-reactive-show-clear")??"[]")}catch{}let l=new Set((Array.isArray(s)?s:[]).map((c)=>E(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"),a=null;try{a=i?e.root.querySelector(i):null}catch{}a?.focus?.(o?{preventScroll:!0}:void 0)}function P(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 Se(e,r){let t=z(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){xe(n,o,r);continue}if(!o||typeof o!=="object"||Array.isArray(o))continue;let i=r(n);if(i===null)continue;let a=(s,l)=>l?r(n,!0):i;for(let[s,l]of Object.entries(o)){if(!w(s))continue;let c;if(Array.isArray(l)){if(l.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${s} — skipped`);continue}c=l.every((u)=>J(u,a))}else{let u=le(l,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${s} — skipped`);continue}c=u}for(let u of document.querySelectorAll(s))u.hidden=!c}}}function xe(e,r,t){if(!w(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Ue(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((a)=>t(a)===null)&&!Ze(n))return;let i=A(n,t);if(i===null)return;for(let a of document.querySelectorAll(e))a.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 _e(e,r,t,n){let o=!1,i=null;for(let a of e.root.querySelectorAll(O(r,n))){if(!t(a))continue;if(a.type==="checkbox")return a.checked?"true":"false";if(a.type==="radio"){if(a.checked)return a.value??"";o=!0;continue}i??=a}if(i)return i.value??"";return o?"":null}function O(e,r){return`[name="${E(e,r)}"]`}function E(e,r){return r&&!e.includes("[")?`${r}[${e}]`:e}function Y(e,r){let t=e.root.getAttribute?.("data-reactive-scope")||null,n=new Map;return(o,i)=>{let a=i?`#${o}`:o;if(!n.has(a))n.set(a,i?j(e,o,r,t).filter((s)=>s.checked).length:_e(e,o,r,t));return n.get(a)}}var U="[data-reactive-media]";function ke(e,r,t){for(let n of e.root.querySelectorAll(U)){if(!t(n))continue;let o,i,a;try{if(o=JSON.parse(n.getAttribute("data-reactive-media")),o?.match!=null)i=new RegExp(`^(?:${o.match})$`);if(o?.when!=null)a=new RegExp(`^(?:${o.when})$`)}catch{}if(typeof o?.field!=="string"||!o.pool||typeof o.pool!=="object"||o.match!=null&&!i||o.when!=null&&!a){console.warn("[phlex-reactive] malformed data-reactive-media — skipped");continue}let s=r(o.field);if(s===null)continue;let l=o.take>0?[...s]:null,c=l?l.slice(0,o.take).join(""):s,u=l?.length<o.take||i?.test(c)===!1||a?.test(s)===!1;if(u&&o.pending==="keep"){W(n,"pending");continue}let f=!u&&Object.hasOwn(o.pool,c)?G(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 p=u?"":d===!1?String(o.blank??""):d;for(let k of G(e,o.label))if(k.textContent!==p)k.textContent=p}}function W(e,r){if(e.getAttribute("data-reactive-media-state")!==r)e.setAttribute("data-reactive-media-state",r)}function G(e,r){try{return r==="@root"?[]:e.core.opTargets({to:r})}catch{return[]}}var Z="[data-reactive-css-vars]",Ce=/^--[a-z0-9-]+$/i,Ne=["","px","%","deg","turn","ms","s","em","rem","fr"];function qe(e,r,t,n){let o=[...e.root.querySelectorAll(Z)].filter(t);if(e.root.hasAttribute("data-reactive-css-vars"))o.unshift(e.root);for(let i of o){let a;try{a=JSON.parse(i.getAttribute("data-reactive-css-vars"))}catch{}if(!Array.isArray(a?.to)||!a.vars||typeof a.vars!=="object"){console.warn("[phlex-reactive] malformed data-reactive-css-vars — skipped (two reactive_css_vars on one element?)");continue}let s=a.to.flatMap((l)=>{let c={to:l,global:a.global===!0},u=[];try{u=e.core.opTargets(c)}catch{}if(!u.length)e.core.diagnose("reactive_css_vars",c);return u});for(let[l,c]of Object.entries(a.vars)){let u=c?.unit??"";if(!Ce.test(l)||!Ne.includes(u)||typeof c?.field!=="string"){console.warn(`[phlex-reactive] refused reactive_css_vars entry ${JSON.stringify(l)} — skipped`);continue}let f=Oe(e,r,c.field,t,n);if(f===void 0)continue;let d=f===null?"":`${f}${u}`;for(let p of s){if(p.style.getPropertyValue(l)===d)continue;if(d)p.style.setProperty(l,d);else p.style.removeProperty(l)}}}}function Oe(e,r,t,n,o){let i=r(t);if(i===null)return;if((i==="true"||i==="false")&&[...e.root.querySelectorAll(O(t,o))].some((s)=>n(s)&&s.type==="checkbox"))return i==="true"?1:0;let a=i.trim()?Number(i):NaN;return Number.isFinite(a)?a:null}var x="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function $e(e){return!!e.root.querySelector?.(x)}function j(e,r,t,n,o){return[...e.root.querySelectorAll(O(r,n))].filter((i)=>i.type==="checkbox"&&t(i)&&(!o||Ee(i.getAttribute("data-reactive-group"),o)))}function Ee(e,r){return e===r||!!e?.startsWith(`${r}.`)}function je(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 at(e,r,t){if(!r)return;let n=q.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,a=j(e,r,e.core.ownership(),i,o).filter((s)=>s!==n&&s.checked!==t);for(let s of a)s.checked=t;e.flipping=!0;try{for(let s of a)for(let l of["input","change"])s.dispatchEvent?.(new Event(l,{bubbles:!0}))}finally{e.flipping=!1}}function Re(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=V(K(i),r);if(l!==null)o.disabled=!l}let a=o.getAttribute("data-reactive-count");if(a){let l=String(r(a,!0));if(o.textContent!==l)o.textContent=l}let s=o.getAttribute("data-reactive-select-all");if(s){let l=o.getAttribute("data-reactive-select-all-under"),c=j(e,s,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 Me(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 a of Array.isArray(n.count)?n.count:[]){if(!w(a,"group"))continue;for(let s of document.querySelectorAll(a))if(s.textContent!==o)s.textContent=o}let i=n.enable&&typeof n.enable==="object"?n.enable:{};for(let[a,s]of Object.entries(i)){if(!w(a,"group"))continue;let l=A(s?.any,r);if(l===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${a} — skipped`);continue}for(let c of document.querySelectorAll(a))c.disabled=!l}}}function F(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 Te(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(),a=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 a++}let s=e.root.getAttribute("data-reactive-filter-group");if(s)for(let c of e.root.querySelectorAll(s)){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=a>0}}function _(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function Je(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function R(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 M(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 X(e,r){let t=R(e);if(!t)return!1;let n=M(t),o=new Set(n.map((a)=>a.toLowerCase())),i=!1;for(let a of r){let s=String(a??"").trim();if(s===""||o.has(s.toLowerCase()))continue;o.add(s.toLowerCase()),n.push(s),i=!0}if(i)ee(e,t,n);return i}function ee(e,r,t){r.value=t.join(","),y(r),te(e)}function Ie(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 te(e){let r=R(e);if(!r)return;let t=M(r),n=e.core.ownership();Le(e,t,n),De(e,t,n)}function Le(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 a of r){let s=i.cloneNode(!0);s.setAttribute?.("data-reactive-tag",a);let l=s.matches?.("[data-reactive-tag-text]")?s:(s.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(l)l.textContent=a;let c=[...s.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(s.matches?.('[data-action*="reactive#tagsRemove"]'))c.push(s);for(let u of c)u.setAttribute?.("data-reactive-tag-param",a);n.appendChild(s)}}function De(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 Pe(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function We(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 Ge(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 Fe(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(),a=[...r.querySelectorAll?.("input, select, textarea")??[]],s=[];for(let[l,c]of Object.entries(o)){let u=[...e.root.querySelectorAll?.(c)??[]].find(i);if(!u)continue;let f=a.find((d)=>ne(d.getAttribute?.("name"))===l);if(!f)continue;Be(f,u),s.push(u)}if(n)for(let l of s)He(l);return s[0]??null}function Be(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:C(r)!=="";else e.value=C(r);y(e)}function He(e){if(e.type==="checkbox")e.checked=!1;else e.value="";y(e)}function Ke(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);T(e)}function T(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))oe(e,t.getAttribute("data-reactive-nested-json"))}function Ve(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 re(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=ne(o.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=C(o)}return t}function ne(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 oe(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=Ve(e,n);if(!o)return;let i=[];for(let s of[...n.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!t(s)||s.hidden)continue;i.push(re(e,s))}let a=JSON.stringify(i);if(o.value===a)return;o.value=a,y(o)}function ct(e,r,t){let n=h(e,r);if(!_(n))return;if(t?.defaultPrevented)return;if(n.core.listnavOptions(t).some((a)=>a.hasAttribute?.("data-reactive-highlighted")))return;t?.preventDefault?.();let o=t?.currentTarget??t?.target;if(!o)return;if(!X(n,String(o.value??"").split(",")))return;if(o.value="",S(n))b(n)}function lt(e,r,t){let n=h(e,r);if(!_(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!X(n,[i]))return;let a=Ie(n);if(!a)return;a.value="",b(n),a.focus?.()}function ut(e,r,t){let n=h(e,r);if(!_(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let a=R(n);if(!a)return;let s=M(a),l=s.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===s.length)return;ee(n,a,l)}function ft(e,r,t){let n=h(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 a=n.core.ownership(),s=[...n.root.querySelectorAll(`[data-reactive-nested-list="${i}"]`)].find(a),c=[...n.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(a)?.content?.firstElementChild;if(!s||!c){Ge(n,i);return}let u=c.cloneNode(!0);We(u,Pe(n)),s.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),d=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",p=Fe(n,u,f,d);if(f)p?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(s.getAttribute?.("data-reactive-nested-json")===i)oe(n,i)}function dt(e,r,t){let n=h(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 a=o?.getAttribute?.("data-reactive-confirm-param"),s=o?.getAttribute?.("data-reactive-confirm-when-param"),l=a||(s?ue(n,s):null);if(!l)return B(n,i);let c=re(n,i),u=Ke(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)B(n,i)})}function ie(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;T(e)}var se=["gte","gt","lte","lt"],ze=["len_eq","len_gte","len_gt","len_lte","len_lt"],ae=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"],Ye=["digits_eq","digits_gte","digits_lt","digits_lte"];function N(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 ce(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 le(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 se)if(t in e)return ce(t,e[t],r);for(let t of ze)if(t in e)return N(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 N(t,e[t],Array.isArray(i)?i.length:Number(i)||0)===!0}if(typeof e.same==="string"){let i=(a)=>{let s=String(r(a)??"");return e.fold===!0?s.replace(/[A-Z]/g,(l)=>String.fromCharCode(l.charCodeAt(0)+32)):s};return i(e.field)===i(e.same)!==(e.negate===!0)}let n=Ye.filter((i)=>(i in e));if(n.length){let i=String(r(e.field)??"");if(!n.every((s)=>Number.isInteger(e[s]))||e.prefix!=null&&typeof e.prefix!=="string")return!1;return(/^[0-9]*$/.test(i)&&n.every((s)=>N(s,e[s],i.length))&&i.startsWith(e.prefix??""))!==(e.negate===!0)}let o=r(e.field)??"";return le(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 Ue(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 Ze(e){return e.flat().every((r)=>ae.some((t)=>(t in Object(r))))}function ue(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=(a)=>n[a],i;if(typeof t.predicate==="string"){let a=de(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(n)}else i=A(t.groups?.any,o)===!0;return i?t.message:null}function I(e,r){let t=r.target?.closest?.("[data-reactive-editable]");return t&&e.core.owns(t)?t:null}function Qe(e,r){let t=I(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 Xe(e,r){let t=I(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 et(e,r){let t=I(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{at as checkGroup,st as confirmMessage,rt as connect,nt as disconnect,ft as nestedAdd,dt as nestedRemove,ot as reseed,it as syncNestedJson,ct as tagsAdd,lt as tagsPick,ut as tagsRemove};
2
2
 
3
- //# debugId=43EEE1B3CF9BBE3564756E2164756E21
3
+ //# debugId=67F94BC6D50AEEBC64756E2164756E21
4
4
  //# sourceMappingURL=bindings.min.js.map