phlex-reactive 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/README.md +73 -5
- data/app/javascript/phlex/reactive/core.js +7 -1
- data/app/javascript/phlex/reactive/core.min.js +6 -2
- data/app/javascript/phlex/reactive/core.min.js.map +4 -4
- data/app/javascript/phlex/reactive/features/bindings.js +106 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/form.js +12 -5
- data/app/javascript/phlex/reactive/features/form.min.js +2 -2
- data/app/javascript/phlex/reactive/features/form.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/persist.js +12 -3
- data/app/javascript/phlex/reactive/features/persist.min.js +2 -2
- data/app/javascript/phlex/reactive/features/persist.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/sortable.js +463 -0
- data/app/javascript/phlex/reactive/features/sortable.min.js +4 -0
- data/app/javascript/phlex/reactive/features/sortable.min.js.map +10 -0
- data/app/javascript/phlex/reactive/reactive_controller.js +2 -0
- data/app/javascript/phlex/reactive/reactive_controller.min.js +6 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +9 -8
- data/app/javascript/phlex/reactive/runtime.js +117 -10
- data/lib/phlex/reactive/component/editable.rb +78 -0
- data/lib/phlex/reactive/component/helpers.rb +2 -0
- data/lib/phlex/reactive/component/sortable.rb +212 -0
- data/lib/phlex/reactive/engine.rb +4 -4
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +6 -1
|
@@ -174,6 +174,17 @@ export function connect(controller, core) {
|
|
|
174
174
|
listen(c, "input change", (event) => syncNestedJson(c, event))
|
|
175
175
|
seed(c, () => syncAllNestedJson(c))
|
|
176
176
|
}
|
|
177
|
+
|
|
178
|
+
// Editable text (issue #351). Delegated keys, paste and drop on the root,
|
|
179
|
+
// whether or not it owns an editable yet: a stream can append the first one
|
|
180
|
+
// into a root whose bindings connected for another marker (each handler
|
|
181
|
+
// returns at once for an event that is not an owned editable's). The seed
|
|
182
|
+
// downgrades plaintext-only where the engine lacks it, again after a morph
|
|
183
|
+
// (which writes the server's attribute back).
|
|
184
|
+
seed(c, () => downgradeEditables(c))
|
|
185
|
+
listen(c, "keydown", (event) => editableKey(c, event))
|
|
186
|
+
listen(c, "paste drop", (event) => editablePaste(c, event))
|
|
187
|
+
listen(c, "input", (event) => editableEmptied(c, event))
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
// Remove every listener connect() added, so a stray event after a Turbo
|
|
@@ -524,7 +535,9 @@ function syncShowTargets(c, fieldValue) {
|
|
|
524
535
|
// in-root reactive_show — each term reads its OWN owned field, a missing
|
|
525
536
|
// owned field reads as blank (fail-closed, the shared-fixture contract). A
|
|
526
537
|
// target whose referenced fields are ALL unowned is left alone — the
|
|
527
|
-
// single-field skip generalized (this root has nothing to evaluate with)
|
|
538
|
+
// single-field skip generalized (this root has nothing to evaluate with) —
|
|
539
|
+
// unless every term is a checked_* count (issue #348): a group with no owned
|
|
540
|
+
// boxes counts 0, so that target is always decidable, as in-root. A
|
|
528
541
|
// malformed payload warn-skips its one target while siblings still apply;
|
|
529
542
|
// the selector guard is the same id-only allowlist as every cross-root arm.
|
|
530
543
|
function applyConditionsTarget(selector, payload, fieldValue) {
|
|
@@ -535,7 +548,7 @@ function applyConditionsTarget(selector, payload, fieldValue) {
|
|
|
535
548
|
console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${selector} — skipped`)
|
|
536
549
|
return
|
|
537
550
|
}
|
|
538
|
-
if (fields.every((name) => fieldValue(name) === null)) return // no owned field — leave it be
|
|
551
|
+
if (fields.every((name) => fieldValue(name) === null) && !countsOnly(groups)) return // no owned field — leave it be
|
|
539
552
|
const match = anyOfAllsMatches(groups, fieldValue)
|
|
540
553
|
if (match === null) return // unreachable after dnfGroupFields, kept fail-closed
|
|
541
554
|
for (const node of document.querySelectorAll(selector)) node.hidden = !match
|
|
@@ -1535,6 +1548,12 @@ function dnfGroupFields(groups) {
|
|
|
1535
1548
|
return fields.size > 0 ? [...fields] : null
|
|
1536
1549
|
}
|
|
1537
1550
|
|
|
1551
|
+
// Whether every term of a DNF payload is a checked_* count (issue #348) —
|
|
1552
|
+
// the payload a group with zero owned boxes still decides (count 0).
|
|
1553
|
+
function countsOnly(groups) {
|
|
1554
|
+
return groups.flat().every((term) => SHOW_CHECKED_KEYS.some((key) => key in Object(term)))
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1538
1557
|
// Resolve the effective confirm message (issue #179). A plain string is the
|
|
1539
1558
|
// static #52 form (always shown). A confirmWhen JSON payload is the CONDITIONAL
|
|
1540
1559
|
// form — evaluated over the SAME collected fields reactive_compute reads — and
|
|
@@ -1580,3 +1599,88 @@ function conditionalConfirmMessage(c, confirmWhen) {
|
|
|
1580
1599
|
|
|
1581
1600
|
return fires ? payload.message : null
|
|
1582
1601
|
}
|
|
1602
|
+
|
|
1603
|
+
// --- Editable text (issue #351) ----------------------------------------------
|
|
1604
|
+
// reactive_editable renders contenteditable="plaintext-only": the browser
|
|
1605
|
+
// itself then pastes plain text and inserts "\n" for Enter. An engine without
|
|
1606
|
+
// it reads the value as invalid — NOT editable — so the seed downgrades to
|
|
1607
|
+
// "true" there and the paste handler below does the plain-text part. Detected
|
|
1608
|
+
// once per page.
|
|
1609
|
+
let plaintextOnly
|
|
1610
|
+
|
|
1611
|
+
export function __setPlaintextOnlyForTest(value) {
|
|
1612
|
+
plaintextOnly = value
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
function plaintextSupported() {
|
|
1616
|
+
if (plaintextOnly === undefined) {
|
|
1617
|
+
try {
|
|
1618
|
+
const probe = document.createElement("div")
|
|
1619
|
+
probe.contentEditable = "plaintext-only"
|
|
1620
|
+
plaintextOnly = probe.contentEditable === "plaintext-only"
|
|
1621
|
+
} catch {
|
|
1622
|
+
plaintextOnly = false
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
return plaintextOnly
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
// The owned editable an event came from, or null — a nested reactive root's
|
|
1629
|
+
// editables are its own (#15).
|
|
1630
|
+
function ownedEditable(c, event) {
|
|
1631
|
+
const el = event.target?.closest?.("[data-reactive-editable]")
|
|
1632
|
+
return el && c.core.owns(el) ? el : null
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
function downgradeEditables(c) {
|
|
1636
|
+
if (plaintextSupported()) return
|
|
1637
|
+
for (const el of c.root.querySelectorAll("[data-reactive-editable][contenteditable=plaintext-only]")) {
|
|
1638
|
+
el.setAttribute("contenteditable", "true")
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// Enter: a single-line field swallows it; on_enter runs its ops (root-scoped,
|
|
1643
|
+
// as on_client) or submits the field's form — in a multiline field only a
|
|
1644
|
+
// plain Enter, so Shift+Enter still breaks the line. Never while an IME is
|
|
1645
|
+
// composing (its Enter commits the composition). Escape with on_escape:
|
|
1646
|
+
// :revert restores the last server-rendered text, announces it as an edit
|
|
1647
|
+
// (dirty, show, an input-bound save) and blurs.
|
|
1648
|
+
function editableKey(c, event) {
|
|
1649
|
+
const el = ownedEditable(c, event)
|
|
1650
|
+
if (!el || event.isComposing || event.keyCode === 229) return
|
|
1651
|
+
if (event.key === "Enter") {
|
|
1652
|
+
const enter = el.getAttribute("data-reactive-editable-enter")
|
|
1653
|
+
const multi = el.getAttribute("data-reactive-editable") === "multi"
|
|
1654
|
+
if (multi && (enter == null || event.shiftKey)) return
|
|
1655
|
+
event.preventDefault()
|
|
1656
|
+
if (enter === "submit") el.closest("form")?.requestSubmit()
|
|
1657
|
+
else if (enter) c.core.applyOps(JSON.parse(enter), "@root")
|
|
1658
|
+
} else if (event.key === "Escape" && el.hasAttribute("data-reactive-editable-escape")) {
|
|
1659
|
+
event.preventDefault()
|
|
1660
|
+
el.textContent = c.core.served(el)
|
|
1661
|
+
fireInput(el)
|
|
1662
|
+
el.blur()
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// Paste and drop insert the clipboard's PLAIN text — handled here where the
|
|
1667
|
+
// engine has no plaintext-only, and for a single-line field everywhere (its
|
|
1668
|
+
// line breaks fold to spaces). insertText keeps the browser's undo stack. A
|
|
1669
|
+
// drop lands at the caret, not under the pointer.
|
|
1670
|
+
function editablePaste(c, event) {
|
|
1671
|
+
const el = ownedEditable(c, event)
|
|
1672
|
+
const single = el?.getAttribute("data-reactive-editable") !== "multi"
|
|
1673
|
+
let text = (event.clipboardData ?? event.dataTransfer)?.getData("text/plain")
|
|
1674
|
+
if (!el || text == null || (plaintextSupported() && !(single && /[\r\n]/.test(text)))) return
|
|
1675
|
+
event.preventDefault()
|
|
1676
|
+
if (single) text = text.replace(/\r?\n|\r/g, " ")
|
|
1677
|
+
el.focus()
|
|
1678
|
+
document.execCommand("insertText", false, text)
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// A field the user emptied keeps the <br> browsers leave behind, so
|
|
1682
|
+
// :empty::before (the placeholder) would never match: drop it.
|
|
1683
|
+
function editableEmptied(c, event) {
|
|
1684
|
+
const el = ownedEditable(c, event)
|
|
1685
|
+
if (el?.firstChild && c.core.text(el) === "") el.replaceChildren()
|
|
1686
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
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};
|
|
1
|
+
import{confirmPredicate as ae}from"phlex/reactive/confirm_predicate";var h=new WeakMap,C=new WeakMap;function p(e,r){let t=h.get(e);if(!t){if(h.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)C.set(t.root,t)}return t}function d(e,r,t){for(let n of r.split(" "))e.root.addEventListener?.(n,t),e.offs.push(()=>e.root.removeEventListener?.(n,t))}function g(e,r){d(e,"turbo:morph-element",r),e.seeds.push(r),r()}function Fe(e,r){let t=p(e,r);if(t.offs=[],t.seeds=[],fe(t))t.show=(n)=>{if(t.flipping)return;if(n?.type==="input"||n?.type==="change")be(t,n.target);else t.groups=ve(t),L(t);ge(t)},d(t,"input change",t.show),g(t,t.show);if(de(t))d(t,"input change",(n)=>J(t,n)),g(t,t.arm=()=>{J(t,null),L(t)});if(t.show||t.arm)d(t,"reset",()=>setTimeout(()=>{if(h.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(w(t))d(t,"input",(n)=>Se(t,n)&&v(t)),g(t,()=>v(t));if(x(t))g(t,()=>Y(t));if(xe(t))d(t,"input change",(n)=>Z(t,n)),g(t,()=>E(t));g(t,()=>De(t)),d(t,"keydown",(n)=>Ie(t,n)),d(t,"paste drop",(n)=>Pe(t,n)),d(t,"input",(n)=>We(t,n))}function Be(e){let r=h.get(e);if(!r)return;h.delete(e),C.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function Ke(e){for(let r of h.get(e)?.seeds??[])r()}function y(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function He(e,r,t){return Z(p(e,r),t)}function Ye(e,r,t){return ne(p(e,r),t)}var se=/^#[A-Za-z_][\w-]*$/,D="[data-reactive-show-field], [data-reactive-show]";function ce(e,r){let t=e.getAttribute("data-reactive-show-equals");if(t!==null)return r===t;let 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 V){let a=e.getAttribute(`data-reactive-show-${i}`);if(a!==null)return te(i,a,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function I(e){try{let r=JSON.parse(e);if(r&&typeof r==="object"&&!Array.isArray(r))return r}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(e)} — skipped`),null}function le(e){try{let r=JSON.parse(e);if(Array.isArray(r)&&r.every((t)=>t&&typeof t==="object"&&Array.isArray(t.any)&&Array.isArray(t.ops)))return r}catch{}return console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(e)} — skipped`),[]}function P(e,r){if(!e||typeof e!=="object")return null;let t=e.any;if(Array.isArray(t)&&(t.length===0||Array.isArray(t[0])))return b(t,r);return ue(e,r)}function ue(e,r){let t=Array.isArray(e.all)?"all":Array.isArray(e.any)?"any":null;if(!t)return null;let 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 A(e,r="show"){if(typeof e==="string"&&se.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function fe(e){if(e.root.getAttribute?.("data-reactive-show-targets"))return!0;if(e.root.getAttribute?.("data-reactive-group-targets"))return!0;let r=e.root.querySelectorAll?.(D)??[];for(let t of r)if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(S)??[])if(e.core.owns(t))return!0;return!1}function de(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function pe(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:le(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function J(e,r){let t=pe(e);if(!t.length)return;let n=F(e,e.core.ownership());t.forEach((o,i)=>{let a=b(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 ge(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.root.getAttribute?.("data-reactive-scope")||null,n=F(e,r);for(let o of e.root.querySelectorAll(D)){if(!r(o))continue;let i=o.getAttribute("data-reactive-show");if(i!==null){let c=P(I(i),n);if(c!==null)T(o,c,r,t);continue}let a=o.getAttribute("data-reactive-show-field");if(!a)continue;let s=n(a);if(s===null)continue;let l=ce(o,s);if(l===null)continue;T(o,l,r,t)}if(e.groups)Ae(e,n,r,t);he(e,n),we(e,n)}function T(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 he(e,r){let t=W(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){ye(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(!A(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=re(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 ye(e,r,t){if(!A(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Le(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((a)=>t(a)===null)&&!Me(n))return;let i=b(n,t);if(i===null)return;for(let a of document.querySelectorAll(e))a.hidden=!i}function W(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 me(e,r,t,n){let o=!1,i=null;for(let a of e.root.querySelectorAll(G(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 G(e,r){return`[name="${r&&!e.includes("[")?`${r}[${e}]`:e}"]`}function F(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?N(e,o,r,t).filter((s)=>s.checked).length:me(e,o,r,t));return n.get(a)}}var S="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function ve(e){return!!e.root.querySelector?.(S)}function N(e,r,t,n){return[...e.root.querySelectorAll(G(r,n))].filter((o)=>o.type==="checkbox"&&t(o))}function be(e,r){let t=r?.getAttribute?.("data-reactive-select-all");if(t&&e.core.owns(r))B(e,t,r.checked,r)}function ze(e,r,t){if(!r)return;let n=C.get(e)??{root:e,core:{ownership:()=>(o)=>o.closest('[data-controller~="reactive"]')===e}};B(n,r,t!==!1),n.show?.()}function B(e,r,t,n){let o=e.root.getAttribute?.("data-reactive-scope")||null,i=N(e,r,e.core.ownership(),o).filter((a)=>a!==n&&a.checked!==t);for(let a of i)a.checked=t;e.flipping=!0;try{for(let a of i)for(let s of["input","change"])a.dispatchEvent?.(new Event(s,{bubbles:!0}))}finally{e.flipping=!1}}function Ae(e,r,t,n){for(let o of e.root.querySelectorAll(S)){if(!t(o))continue;let i=o.getAttribute("data-reactive-enable");if(i!==null){let l=P(I(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=N(e,s,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 we(e,r){for(let[t,n]of Object.entries(W(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(!A(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(!A(a,"group"))continue;let l=b(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 L(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?.(`${S}, [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 w(e){return!!(e.root.getAttribute?.("data-reactive-filter-input")&&e.root.getAttribute?.("data-reactive-filter-option"))}function Se(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}function v(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 x(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function xe(e){if(typeof e.root?.querySelector!=="function")return!1;return!!e.root.querySelector("[data-reactive-nested-json]")}function O(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 K(e,r){let t=O(e);if(!t)return!1;let n=k(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)H(e,t,n);return i}function H(e,r,t){r.value=t.join(","),y(r),Y(e)}function _e(e){if(typeof e.root?.querySelectorAll!=="function")return null;let r=e.root.getAttribute("data-reactive-filter-input");if(!r)return null;let t=e.core.ownership();return[...e.root.querySelectorAll(r)].find(t)??null}function Y(e){let r=O(e);if(!r)return;let t=k(r),n=e.core.ownership();qe(e,t,n),Ce(e,t,n)}function qe(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 Ce(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"),!w(e))o.hidden=!1}}if(w(e))v(e)}function Ne(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Oe(e,r){let t=[e,...e.querySelectorAll?.("*")??[]];for(let 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 ke(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 Ee(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((_)=>U(_.getAttribute?.("name"))===l);if(!f)continue;$e(f,u),s.push(u)}if(n)for(let l of s)je(l);return s[0]??null}function $e(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:q(r)!=="";else e.value=q(r);y(e)}function je(e){if(e.type==="checkbox")e.checked=!1;else e.value="";y(e)}function Re(e,r){if(!e.includes("%{"))return e;return e.replace(/%\{(\w+)\}/g,(t,n)=>Object.prototype.hasOwnProperty.call(r,n)?r[n]:t)}function M(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);E(e)}function E(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))Q(e,t.getAttribute("data-reactive-nested-json"))}function Je(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 z(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=U(o.getAttribute?.("name"));if(i===null||i==="_destroy")continue;t[i]=q(o)}return t}function U(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function q(e){if(e.type==="checkbox")return e.checked?e.value||"on":"";return e.value??""}function Q(e,r){let t=e.core.ownership(),n=[...e.root.querySelectorAll(`[data-reactive-nested-list="${r}"]`)].find(t);if(!n)return;let o=Je(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(z(e,s))}let a=JSON.stringify(i);if(o.value===a)return;o.value=a,y(o)}function Ue(e,r,t){let n=p(e,r);if(!x(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(!K(n,String(o.value??"").split(",")))return;if(o.value="",w(n))v(n)}function Qe(e,r,t){let n=p(e,r);if(!x(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!K(n,[i]))return;let a=_e(n);if(!a)return;a.value="",v(n),a.focus?.()}function Ze(e,r,t){let n=p(e,r);if(!x(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let a=O(n);if(!a)return;let s=k(a),l=s.filter((c)=>c.toLowerCase()!==i.toLowerCase());if(l.length===s.length)return;H(n,a,l)}function Ve(e,r,t){let n=p(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){ke(n,i);return}let u=c.cloneNode(!0);Oe(u,Ne(n)),s.appendChild(u);let f=o?.getAttribute?.("data-reactive-nested-from-param"),_=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",ie=Ee(n,u,f,_);if(f)ie?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(s.getAttribute?.("data-reactive-nested-json")===i)Q(n,i)}function Xe(e,r,t){let n=p(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?ne(n,s):null);if(!l)return M(n,i);let c=z(n,i),u=Re(l,c);return n.core.confirm(u,{el:o,row:i,fields:c}).then((f)=>{if(f)M(n,i)})}function Z(e,r){let t=r?.target;if(!t||!e.core.owns(t))return;E(e)}var V=["gte","gt","lte","lt"],Te=["len_eq","len_gte","len_gt","len_lte","len_lt"],X=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"];function ee(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 te(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 re(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 V)if(t in e)return te(t,e[t],r);for(let t of Te)if(t in e)return ee(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=X.find((o)=>(o in e));if(t){let o=r(e.field,!0);return ee(t,e[t],Array.isArray(o)?o.length:Number(o)||0)===!0}let n=r(e.field)??"";return re(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)=>j(n,r)))}function Le(e){if(!Array.isArray(e)||e.length===0)return null;let r=new Set;for(let t of e){if(!Array.isArray(t))continue;for(let n of t)if(n&&typeof n==="object"&&typeof n.field==="string")r.add(n.field)}return r.size>0?[...r]:null}function Me(e){return e.flat().every((r)=>X.some((t)=>(t in Object(r))))}function ne(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=ae(t.predicate);if(!a)return console.warn(`[phlex-reactive] confirm predicate "${t.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;i=!!a(n)}else i=b(t.groups?.any,o)===!0;return i?t.message:null}var m;function et(e){m=e}function oe(){if(m===void 0)try{let e=document.createElement("div");e.contentEditable="plaintext-only",m=e.contentEditable==="plaintext-only"}catch{m=!1}return m}function R(e,r){let t=r.target?.closest?.("[data-reactive-editable]");return t&&e.core.owns(t)?t:null}function De(e){if(oe())return;for(let r of e.root.querySelectorAll("[data-reactive-editable][contenteditable=plaintext-only]"))r.setAttribute("contenteditable","true")}function Ie(e,r){let t=R(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 Pe(e,r){let t=R(e,r),n=t?.getAttribute("data-reactive-editable")!=="multi",o=(r.clipboardData??r.dataTransfer)?.getData("text/plain");if(!t||o==null||oe()&&!(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 We(e,r){let t=R(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{et as __setPlaintextOnlyForTest,ze as checkGroup,Ye as confirmMessage,Fe as connect,Be as disconnect,Ve as nestedAdd,Xe as nestedRemove,Ke as reseed,He as syncNestedJson,Ue as tagsAdd,Qe as tagsPick,Ze as tagsRemove};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=1B8B852C90C8A7F964756E2164756E21
|
|
4
4
|
//# sourceMappingURL=bindings.min.js.map
|