phlex-reactive 0.16.2 → 0.16.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.
@@ -90,10 +90,16 @@ function enqueue(event, el, descs, win, shortcut) {
90
90
  // never prevents a window binding — but a shortcut (issue #357), the chord
91
91
  // the page claims (mod+s must not open the save dialog before connect), is
92
92
  // always prevented: the keepsToggle exemption is for element-bound clicks
93
- // only. Everything else is prevented now, as the controller would.
93
+ // only. Everything else is prevented now, as the controller would. A
94
+ // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):
95
+ // a connected root's shortcut on the same chord still runs, as a shortcut
96
+ // only skips a key some other handler prevented.
94
97
  const keepsToggle =
95
98
  /^(checkbox|radio)$/.test(el.type) && /"checked":"keep"/.test(el.dataset.reactiveOptimisticParam)
96
- if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === "runOps")) event.preventDefault()
99
+ if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === "runOps")) {
100
+ if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)
101
+ event.preventDefault()
102
+ }
97
103
  // Every firing is queued — a `:once` repeat included: the replay skips a
98
104
  // descriptor already consumed (reactive_controller.js #replayEarly).
99
105
  const { queue } = state
@@ -1,4 +1,4 @@
1
- var g=Symbol.for("phlex-reactive.early");var p=globalThis[g]??={queue:[],connected:new WeakSet};function O(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function h(e,t){if(!e)return!0;let o=e.split("+"),r=o.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((a)=>o.includes(a)===t[`${a}Key`])}var w=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(O),y=(e,t,o)=>w(e).filter((r)=>r.win===o&&r.type===t.type&&h(r.filter,t));function b(e,t,o,r,s){let a=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!a||p.connected.has(a))return;let n=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam);if(r?s:!n||o.some((d)=>d.method==="runOps"))e.preventDefault();let{queue:l}=p;if(l.push({event:e,el:t,root:a,descs:o,win:r,at:performance.now()})>50)l.shift();let{reactiveDormant:u,controller:f=""}=a.dataset;if(u)a.dataset.controller=`${f} ${u}`.trim(),delete a.dataset.reactiveDormant}function E(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let o=y(t,e,!1);if(o.length)b(e,t,o,!1)}}var i=new Set,S=i;function k(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of i){if(!t.isConnected){i.delete(t);continue}if(p.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let o=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(o)&&o),{target:s}=e,a=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),n=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),l=/mod\+/.test(r)&&(n==="meta"?"ctrl":"meta"),u=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],f=(c)=>!c.filter&&u[0]&&c.type==="keydown",d=y(t,e,!0).filter((c)=>(f(c)?!a&&u.some((m)=>h(m.slice(m.lastIndexOf('"')+1).replace("mod",n),e)):!(c.filter&&(a||l&&c.filter.includes(l))))&&(c.method==="runOps"?/"window":true(?!,"outside")/.test(o):t.dataset.reactiveOutsideParam!=="true"));if(d.length)b(e,t,d,!0,r&&d.some((c)=>c.filter||f(c)))}}function T(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:a,win:n}of w(s)){if(n)i.add(s);(n?e.defaultView:e).addEventListener(a,n?k:E,!0)}},o=()=>{for(let r of i)if(!r.isConnected)i.delete(r)};o(),t(e.documentElement),new MutationObserver((r)=>{o();for(let s of r)s.type==="attributes"?t(s.target):s.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)T();export{S as __windowBoundForTest,T as startEarly};
1
+ var g=Symbol.for("phlex-reactive.early");var p=globalThis[g]??={queue:[],connected:new WeakSet};function O(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function h(e,t){if(!e)return!0;let a=e.split("+"),r=a.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((o)=>a.includes(o)===t[`${o}Key`])}var w=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(O),y=(e,t,a)=>w(e).filter((r)=>r.win===a&&r.type===t.type&&h(r.filter,t));function b(e,t,a,r,s){let o=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!o||p.connected.has(o))return;let n=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam);if(r?s:!n||a.some((d)=>d.method==="runOps")){if(r&&!e.defaultPrevented)(p.prevented??=new WeakSet).add(e);e.preventDefault()}let{queue:l}=p;if(l.push({event:e,el:t,root:o,descs:a,win:r,at:performance.now()})>50)l.shift();let{reactiveDormant:u,controller:f=""}=o.dataset;if(u)o.dataset.controller=`${f} ${u}`.trim(),delete o.dataset.reactiveDormant}function E(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let a=y(t,e,!1);if(a.length)b(e,t,a,!1)}}var i=new Set,S=i;function k(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of i){if(!t.isConnected){i.delete(t);continue}if(p.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let a=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(a)&&a),{target:s}=e,o=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),n=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),l=/mod\+/.test(r)&&(n==="meta"?"ctrl":"meta"),u=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],f=(c)=>!c.filter&&u[0]&&c.type==="keydown",d=y(t,e,!0).filter((c)=>(f(c)?!o&&u.some((m)=>h(m.slice(m.lastIndexOf('"')+1).replace("mod",n),e)):!(c.filter&&(o||l&&c.filter.includes(l))))&&(c.method==="runOps"?/"window":true(?!,"outside")/.test(a):t.dataset.reactiveOutsideParam!=="true"));if(d.length)b(e,t,d,!0,r&&d.some((c)=>c.filter||f(c)))}}function T(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:o,win:n}of w(s)){if(n)i.add(s);(n?e.defaultView:e).addEventListener(o,n?k:E,!0)}},a=()=>{for(let r of i)if(!r.isConnected)i.delete(r)};a(),t(e.documentElement),new MutationObserver((r)=>{a();for(let s of r)s.type==="attributes"?t(s.target):s.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)T();export{S as __windowBoundForTest,T as startEarly};
2
2
 
3
- //# debugId=E672E1A717FD883764756E2164756E21
3
+ //# debugId=1F67C61CEC0FBB4264756E2164756E21
4
4
  //# sourceMappingURL=early.min.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["early.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === \"runOps\")) event.preventDefault()\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false)\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
5
+ "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would. A\n // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):\n // a connected root's shortcut on the same chord still runs, as a shortcut\n // only skips a key some other handler prevented.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === \"runOps\")) {\n if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)\n event.preventDefault()\n }\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false)\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "E672E1A717FD883764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "1F67C61CEC0FBB4264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1021,11 +1021,30 @@ function filterInputEvent(c, event) {
1021
1021
  return !!selector && typeof event.target?.matches === "function" && event.target.matches(selector)
1022
1022
  }
1023
1023
 
1024
+ // The characters a word_start query may follow (issue #425): whitespace and
1025
+ // - ' ( ) , . /, so "ivo" finds Côte d'Ivoire and "bis" Guinea-Bissau.
1026
+ const FILTER_WORD_BREAK = /[\s\-'(),./]/
1027
+
1028
+ // Whether a normalized haystack keeps its option for a normalized, non-empty
1029
+ // query (issue #425): "prefix" at its start, "word_start" at its start or
1030
+ // right after a FILTER_WORD_BREAK, anything else (the default "substring", or
1031
+ // a mode this client does not know) anywhere in it.
1032
+ function filterMatches(mode, haystack, query) {
1033
+ if (mode === "prefix") return haystack.startsWith(query)
1034
+ if (mode !== "word_start") return haystack.includes(query)
1035
+ for (let i = haystack.indexOf(query); i !== -1; i = haystack.indexOf(query, i + 1)) {
1036
+ if (i === 0 || FILTER_WORD_BREAK.test(haystack[i - 1])) return true
1037
+ }
1038
+ return false
1039
+ }
1040
+
1024
1041
  // Re-apply the filter in one pass (issue #163): lowercase the named input's
1025
- // current value, toggle `hidden` on every OWNED option by a substring match
1026
- // against its haystack (data-reactive-filter-text, falling back to the
1027
- // option's own text), collapse any group whose every contained option is
1028
- // hidden, and reveal the empty target at 0 visible. A filtered-out option
1042
+ // current value, toggle `hidden` on every OWNED option by matching it against
1043
+ // its haystack (data-reactive-filter-text, falling back to the option's own
1044
+ // text) in the root's data-reactive-filter-match mode (issue #425; substring
1045
+ // when absent), with diacritics folded on both sides under
1046
+ // data-reactive-filter-fold, collapse any group whose every contained option
1047
+ // is hidden, and reveal the empty target at 0 visible. A filtered-out option
1029
1048
  // also loses its listnav highlight so Enter can never pick an invisible row.
1030
1049
  // No owned input → leave visibility ALONE — a binding that can't resolve
1031
1050
  // must never break or blank the page (client-side default-deny). All
@@ -1041,16 +1060,27 @@ function syncFilter(c) {
1041
1060
  const input = [...c.root.querySelectorAll(inputSelector)].find(owns)
1042
1061
  if (!input) return
1043
1062
 
1044
- const query = (input.value ?? "").trim().toLowerCase()
1063
+ const mode = c.root.getAttribute("data-reactive-filter-match")
1064
+ const fold = c.root.hasAttribute?.("data-reactive-filter-fold")
1065
+ // Both sides go through the same normalization: lowercased and trimmed
1066
+ // (markup indentation in a textContent haystack must not defeat a prefix),
1067
+ // then, under fold, decomposed with the combining marks stripped — so
1068
+ // "zur" and "zür" both find Zürich.
1069
+ const norm = (s) => {
1070
+ const text = (s ?? "").toLowerCase().trim()
1071
+ return fold ? text.normalize("NFD").replace(/\p{M}/gu, "") : text
1072
+ }
1073
+ const query = norm(input.value)
1045
1074
  let visible = 0
1046
1075
  for (const el of c.root.querySelectorAll(optionSelector)) {
1047
1076
  if (!owns(el)) continue // a nested root's option is its own controller's job
1048
- const haystack = (el.getAttribute("data-reactive-filter-text") ?? el.textContent ?? "").toLowerCase()
1049
1077
  // An option whose tag is already selected (reactive_tags, issue #203)
1050
1078
  // stays hidden through every re-filter — clearing the query must not
1051
1079
  // resurface an already-added tag.
1052
1080
  const hidden =
1053
- el.hasAttribute?.("data-reactive-tags-selected") || (query !== "" && !haystack.includes(query))
1081
+ el.hasAttribute?.("data-reactive-tags-selected") ||
1082
+ (query !== "" &&
1083
+ !filterMatches(mode, norm(el.getAttribute("data-reactive-filter-text") ?? el.textContent), query))
1054
1084
  el.hidden = hidden
1055
1085
  if (hidden) el.removeAttribute("data-reactive-highlighted")
1056
1086
  else visible++
@@ -1,4 +1,4 @@
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};
1
+ import{confirmPredicate as de}from"phlex/reactive/confirm_predicate";var v=new WeakMap,k=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)k.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 b(e,r){g(e,"turbo:morph-element",r),e.seeds.push(r),r()}function ot(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")Re(t,s.target);else t.groups=Ee(t),F(t);L(t)},g(t,"input change",t.show),g(t,"focusout",(s)=>{let c=s.target?.getAttribute?.("name");if(!c||t.touched?.has(c)||!t.core.owns(s.target))return;if((t.touched??=new Set).add(c),t.root.querySelector?.("[data-reactive-show-after]"))L(t)}),b(t,t.show);if(!t.arm&&me(t))g(t,"input change",(s)=>D(t,s)),b(t,t.arm=()=>{D(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=x(t)))g(t,"input",(s)=>Te(t,s)&&A(t)),b(t,()=>A(t));if(!i&&(i=N(t)))b(t,()=>te(t));if(!a&&(a=De(t)))g(t,"input change",(s)=>ie(t,s)),b(t,()=>T(t))})(),g(t,"keydown",(s)=>et(t,s)),g(t,"paste drop",(s)=>tt(t,s)),g(t,"input",(s)=>rt(t,s))}function it(e){let r=v.get(e);if(!r)return;v.delete(e),k.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function st(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 at(e,r,t){return ie(h(e,r),t)}function ct(e,r,t){return ue(h(e,r),t)}var pe=/^#[A-Za-z_][\w-]*$/,K="[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 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 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 w(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 S(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?.(K)??[];for(let t of r)if(e.core.owns(t))return!0;for(let t of e.root.querySelectorAll?.(_)??[])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 D(e,r){let t=be(e);if(!t.length)return;let n=Y(e,e.core.ownership());t.forEach((o,i)=>{let a=w(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 L(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(K)){if(!r(i))continue;let a=i.getAttribute("data-reactive-show");if(a!==null){let u=V(H(a),n);if(u===null)continue;let d=i.getAttribute("data-reactive-show-after"),f=u&&(d==null||!!e.touched?.has(E(d,t)));W(i,f,r,t);let p=(e.shown??=new WeakMap).get(i);if(e.shown.set(i,f),Ae.some((fe)=>i.hasAttribute(`data-reactive-show-${fe}`))&&p!==void 0&&p!==f)o.push(i);continue}let s=i.getAttribute("data-reactive-show-field");if(!s)continue;let c=n(s);if(c===null)continue;let l=ge(i,c);if(l===null)continue;W(i,l,r,t)}if(e.groups)je(e,n,r,t);Ne(e,n,r),ke(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 c=new Set((Array.isArray(s)?s:[]).map((l)=>E(String(l),n)));for(let l of c.size?e.root.querySelectorAll("[name]"):[]){if(!c.has(l.getAttribute("name"))||!t(l)||l.type==="file")continue;if(l.type==="checkbox"||l.type==="radio"){if(l.checked)l.checked=!1,y(l)}else e.core.setValue(l,"")}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 W(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,c)=>c?r(n,!0):i;for(let[s,c]of Object.entries(o)){if(!S(s))continue;let l;if(Array.isArray(c)){if(c.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${s} — skipped`);continue}l=c.every((u)=>J(u,a))}else{let u=le(c,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${s} — skipped`);continue}l=u}for(let u of document.querySelectorAll(s))u.hidden=!l}}}function xe(e,r,t){if(!S(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Qe(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((a)=>t(a)===null)&&!Xe(n))return;let i=w(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(q(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 q(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?R(e,o,r,t).filter((s)=>s.checked).length:_e(e,o,r,t));return n.get(a)}}var U="[data-reactive-media]";function Ne(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 c=o.take>0?[...s]:null,l=c?c.slice(0,o.take).join(""):s,u=c?.length<o.take||i?.test(l)===!1||a?.test(s)===!1;if(u&&o.pending==="keep"){P(n,"pending");continue}let d=!u&&Object.hasOwn(o.pool,l)?G(e,o.pool[l])[0]:null,f=e.core.copyMedia(n,d??null);if(f===null)continue;if(P(n,u?"pending":f===!1?"miss":"hit"),typeof o.label!=="string")continue;let p=u?"":f===!1?String(o.blank??""):f;for(let m of G(e,o.label))if(m.textContent!==p)m.textContent=p}}function P(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,Oe=["","px","%","deg","turn","ms","s","em","rem","fr"];function ke(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((c)=>{let l={to:c,global:a.global===!0},u=[];try{u=e.core.opTargets(l)}catch{}if(!u.length)e.core.diagnose("reactive_css_vars",l);return u});for(let[c,l]of Object.entries(a.vars)){let u=l?.unit??"";if(!Ce.test(c)||!Oe.includes(u)||typeof l?.field!=="string"){console.warn(`[phlex-reactive] refused reactive_css_vars entry ${JSON.stringify(c)} — skipped`);continue}let d=qe(e,r,l.field,t,n);if(d===void 0)continue;let f=d===null?"":`${d}${u}`;for(let p of s){if(p.style.getPropertyValue(c)===f)continue;if(f)p.style.setProperty(c,f);else p.style.removeProperty(c)}}}}function qe(e,r,t,n,o){let i=r(t);if(i===null)return;if((i==="true"||i==="false")&&[...e.root.querySelectorAll(q(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 _="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function Ee(e){return!!e.root.querySelector?.(_)}function R(e,r,t,n,o){return[...e.root.querySelectorAll(q(r,n))].filter((i)=>i.type==="checkbox"&&t(i)&&(!o||$e(i.getAttribute("data-reactive-group"),o)))}function $e(e,r){return e===r||!!e?.startsWith(`${r}.`)}function Re(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 lt(e,r,t){if(!r)return;let n=k.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=R(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 c of["input","change"])s.dispatchEvent?.(new Event(c,{bubbles:!0}))}finally{e.flipping=!1}}function je(e,r,t,n){for(let o of e.root.querySelectorAll(_)){if(!t(o))continue;let i=o.getAttribute("data-reactive-enable");if(i!==null){let c=V(H(i),r);if(c!==null)o.disabled=!c}let a=o.getAttribute("data-reactive-count");if(a){let c=String(r(a,!0));if(o.textContent!==c)o.textContent=c}let s=o.getAttribute("data-reactive-select-all");if(s){let c=o.getAttribute("data-reactive-select-all-under"),l=R(e,s,t,n,c).filter((d)=>d!==o),u=l.filter((d)=>d.checked).length;o.checked=u>0&&u===l.length,o.indeterminate=u>0&&u<l.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(!S(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(!S(a,"group"))continue;let c=w(s?.any,r);if(c===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${a} — skipped`);continue}for(let l of document.querySelectorAll(a))l.disabled=!c}}}function 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?.(`${_}, [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 x(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)}var Je=/[\s\-'(),./]/;function Ie(e,r,t){if(e==="prefix")return r.startsWith(t);if(e!=="word_start")return r.includes(t);for(let n=r.indexOf(t);n!==-1;n=r.indexOf(t,n+1))if(n===0||Je.test(r[n-1]))return!0;return!1}function A(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=e.root.getAttribute("data-reactive-filter-match"),a=e.root.hasAttribute?.("data-reactive-filter-fold"),s=(f)=>{let p=(f??"").toLowerCase().trim();return a?p.normalize("NFD").replace(/\p{M}/gu,""):p},c=s(o.value),l=0;for(let f of e.root.querySelectorAll(t)){if(!n(f))continue;let p=f.hasAttribute?.("data-reactive-tags-selected")||c!==""&&!Ie(i,s(f.getAttribute("data-reactive-filter-text")??f.textContent),c);if(f.hidden=p,p)f.removeAttribute("data-reactive-highlighted");else l++}let u=e.root.getAttribute("data-reactive-filter-group");if(u)for(let f of e.root.querySelectorAll(u)){if(!n(f))continue;let p=[...f.querySelectorAll(t)].filter(n);if(p.length===0)continue;f.hidden=p.every((m)=>m.hidden)}let d=e.root.getAttribute("data-reactive-filter-empty");if(d){for(let f of e.root.querySelectorAll(d))if(n(f))f.hidden=l>0}}function N(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function De(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 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=j(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 Le(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=j(e);if(!r)return;let t=M(r),n=e.core.ownership();We(e,t,n),Pe(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 a of r){let s=i.cloneNode(!0);s.setAttribute?.("data-reactive-tag",a);let c=s.matches?.("[data-reactive-tag-text]")?s:(s.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(c)c.textContent=a;let l=[...s.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(s.matches?.('[data-action*="reactive#tagsRemove"]'))l.push(s);for(let u of l)u.setAttribute?.("data-reactive-tag-param",a);n.appendChild(s)}}function Pe(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"),!x(e))o.hidden=!1}}if(x(e))A(e)}function Ge(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Fe(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 Be(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 Ke(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[c,l]of Object.entries(o)){let u=[...e.root.querySelectorAll?.(l)??[]].find(i);if(!u)continue;let d=a.find((f)=>ne(f.getAttribute?.("name"))===c);if(!d)continue;He(d,u),s.push(u)}if(n)for(let c of s)Ve(c);return s[0]??null}function He(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:C(r)!=="";else e.value=C(r);y(e)}function Ve(e){if(e.type==="checkbox")e.checked=!1;else e.value="";y(e)}function ze(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 Ye(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=Ye(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 ut(e,r,t){let n=h(e,r);if(!N(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="",x(n))A(n)}function ft(e,r,t){let n=h(e,r);if(!N(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=Le(n);if(!a)return;a.value="",A(n),a.focus?.()}function dt(e,r,t){let n=h(e,r);if(!N(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let a=j(n);if(!a)return;let s=M(a),c=s.filter((l)=>l.toLowerCase()!==i.toLowerCase());if(c.length===s.length)return;ee(n,a,c)}function pt(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),l=[...n.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(a)?.content?.firstElementChild;if(!s||!l){Be(n,i);return}let u=l.cloneNode(!0);Fe(u,Ge(n)),s.appendChild(u);let d=o?.getAttribute?.("data-reactive-nested-from-param"),f=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",p=Ke(n,u,d,f);if(d)p?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(s.getAttribute?.("data-reactive-nested-json")===i)oe(n,i)}function gt(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"),c=a||(s?ue(n,s):null);if(!c)return B(n,i);let l=re(n,i),u=ze(c,l);return n.core.confirm(u,{el:o,row:i,fields:l}).then((d)=>{if(d)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"],Ue=["len_eq","len_gte","len_gt","len_lte","len_lt"],ae=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"],Ze=["digits_eq","digits_gte","digits_lt","digits_lte"];function O(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 Ue)if(t in e)return O(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 O(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,(c)=>String.fromCharCode(c.charCodeAt(0)+32)):s};return i(e.field)===i(e.same)!==(e.negate===!0)}let n=Ze.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)=>O(s,e[s],i.length))&&i.startsWith(e.prefix??""))!==(e.negate===!0)}let o=r(e.field)??"";return le(e,o)===!0}function w(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 Qe(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 Xe(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=w(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 et(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 tt(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 rt(e,r){let t=I(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{lt as checkGroup,ct as confirmMessage,ot as connect,it as disconnect,pt as nestedAdd,gt as nestedRemove,st as reseed,at as syncNestedJson,ut as tagsAdd,ft as tagsPick,dt as tagsRemove};
2
2
 
3
- //# debugId=67F94BC6D50AEEBC64756E2164756E21
3
+ //# debugId=B45AD8F5762693AC64756E2164756E21
4
4
  //# sourceMappingURL=bindings.min.js.map