phlex-reactive 0.16.7 → 0.16.9
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 +94 -1
- data/README.md +106 -10
- data/app/javascript/phlex/reactive/core.min.js +6 -6
- data/app/javascript/phlex/reactive/core.min.js.map +3 -3
- data/app/javascript/phlex/reactive/early.js +28 -1
- data/app/javascript/phlex/reactive/early.min.js +2 -2
- data/app/javascript/phlex/reactive/early.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/bindings.js +52 -17
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/compute.js +32 -4
- data/app/javascript/phlex/reactive/features/compute.min.js +2 -2
- data/app/javascript/phlex/reactive/features/compute.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/devtools.js +15 -5
- data/app/javascript/phlex/reactive/features/devtools.min.js +2 -2
- data/app/javascript/phlex/reactive/features/devtools.min.js.map +3 -3
- data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -4
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +6 -6
- data/app/javascript/phlex/reactive/runtime.js +146 -22
- data/lib/phlex/reactive/component/dsl.rb +2 -0
- data/lib/phlex/reactive/component/helpers.rb +50 -4
- data/lib/phlex/reactive/js.rb +211 -41
- data/lib/phlex/reactive/response.rb +4 -0
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
|
@@ -132,10 +132,37 @@ function enqueue(event, el, descs, win, shortcut) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
// Issue #442: an on_client descriptor whose every record on its `on` is a
|
|
136
|
+
// within: delegation the event's target falls outside of (no matching element
|
|
137
|
+
// strictly under `el`: its parent is not inside `el`) is a miss — runOps
|
|
138
|
+
// would run nothing and prevent nothing — so it is neither queued nor
|
|
139
|
+
// prevented: a link in a delegated list's footer still navigates before
|
|
140
|
+
// connect. A record without within:, or an attr that does not parse (a legacy
|
|
141
|
+
// list), keeps the descriptor; a within: that does not parse is a miss, as
|
|
142
|
+
// it is for runOps. (A row of a nested root is still queued; the replay's
|
|
143
|
+
// ownership check makes it a miss.)
|
|
144
|
+
const outside = (el, target, within) => {
|
|
145
|
+
try {
|
|
146
|
+
return !el.contains(target.closest(within)?.parentElement ?? null)
|
|
147
|
+
} catch {
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function delegatedMiss(el, d, target) {
|
|
153
|
+
let mine
|
|
154
|
+
try {
|
|
155
|
+
mine = el.dataset.reactiveOpsParam.split(" ").map(JSON.parse).filter((r) => r.on === d.token.split("->")[0])
|
|
156
|
+
} catch {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
return mine.length > 0 && mine.every((r) => r.within != null && outside(el, target, r.within))
|
|
160
|
+
}
|
|
161
|
+
|
|
135
162
|
function record(event) {
|
|
136
163
|
for (let el = event.target; el?.closest; el = el.parentElement) {
|
|
137
164
|
if (!event.bubbles && el !== event.target) break
|
|
138
|
-
const descs = matching(el, event, false)
|
|
165
|
+
const descs = matching(el, event, false).filter((d) => d.method !== "runOps" || !delegatedMiss(el, d, event.target))
|
|
139
166
|
if (descs.length) enqueue(event, el, descs, false)
|
|
140
167
|
}
|
|
141
168
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var b=Symbol.for("phlex-reactive.early");var m=globalThis[b]??={queue:[],connected:new WeakSet};function k(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 w(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((n)=>a.includes(n)===t[`${n}Key`])}var y=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(k),g=(e,t,a)=>y(e).filter((r)=>r.win===a&&r.type===t.type&&w(r.filter,t));function O(e,t,a,r,s){let n=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!n||m.connected.has(n))return;let c=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam),i=` ${t.dataset.reactiveOpsParam??""}`,p=(f)=>{let h=` {"on":${JSON.stringify(f.token.split("->")[0])}`;return f.method==="runOps"?i.includes(h)&&i.split(h).length===i.split(`${h},"prevent":false`).length:c||t.dataset.reactivePreventParam==="false"};if(r?s:!a.every(p)){if(r&&!e.defaultPrevented)(m.prevented??=new WeakSet).add(e);e.preventDefault()}let{queue:d}=m;if(d.push({event:e,el:t,root:n,descs:a,win:r,at:performance.now()})>50)d.shift();let{reactiveDormant:l,controller:o=""}=n.dataset;if(l)n.dataset.controller=`${o} ${l}`.trim(),delete n.dataset.reactiveDormant}var E=(e,t,a)=>{try{return!e.contains(t.closest(a)?.parentElement??null)}catch{return!0}};function v(e,t,a){let r;try{r=e.dataset.reactiveOpsParam.split(" ").map(JSON.parse).filter((s)=>s.on===t.token.split("->")[0])}catch{return!1}return r.length>0&&r.every((s)=>s.within!=null&&E(e,a,s.within))}function T(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let a=g(t,e,!1).filter((r)=>r.method!=="runOps"||!v(t,r,e.target));if(a.length)O(e,t,a,!1)}}var u=new Set,x=u;function S(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of u){if(!t.isConnected){u.delete(t);continue}if(m.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,n=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),c=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),i=/mod\+/.test(r)&&(c==="meta"?"ctrl":"meta"),p=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],d=(o)=>!o.filter&&p[0]&&o.type==="keydown",l=g(t,e,!0).filter((o)=>(d(o)?!n&&p.some((f)=>w(f.slice(f.lastIndexOf('"')+1).replace("mod",c),e)):!(o.filter&&(n||i&&o.filter.includes(i))))&&(o.method==="runOps"?/"window":true(?!,"outside")/.test(a):t.dataset.reactiveOutsideParam!=="true"));if(l.length)O(e,t,l,!0,r&&l.some((o)=>o.filter||d(o)))}}function P(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:n,win:c}of y(s)){if(c)u.add(s);(c?e.defaultView:e).addEventListener(n,c?S:T,!0)}},a=()=>{for(let r of u)if(!r.isConnected)u.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)P();export{x as __windowBoundForTest,P as startEarly};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=B1FCA9A2D1DB67F064756E2164756E21
|
|
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. 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 //\n // `prevent: false` (issue #433) keeps the default when EVERY descriptor that\n // fired opts out: on() through its element-wide param, an on_client\n // descriptor when every record on its `on` carries `\"prevent\":false`,\n // written right after `on` (a legacy attr has no `on`, so it is prevented).\n // A record starts the attribute or follows the space mix joins records with\n // (a record writes its own spaces as \\u0020), so ` {\"on\":\"…\"` is a record's\n // start and never an op's argument.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n const ops = ` ${el.dataset.reactiveOpsParam ?? \"\"}`\n const keeps = (d) => {\n const on = ` {\"on\":${JSON.stringify(d.token.split(\"->\")[0])}`\n return d.method === \"runOps\"\n ? ops.includes(on) && ops.split(on).length === ops.split(`${on},\"prevent\":false`).length\n : keepsToggle || el.dataset.reactivePreventParam === \"false\"\n }\n if (win ? shortcut : !descs.every(keeps)) {\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"
|
|
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 //\n // `prevent: false` (issue #433) keeps the default when EVERY descriptor that\n // fired opts out: on() through its element-wide param, an on_client\n // descriptor when every record on its `on` carries `\"prevent\":false`,\n // written right after `on` (a legacy attr has no `on`, so it is prevented).\n // A record starts the attribute or follows the space mix joins records with\n // (a record writes its own spaces as \\u0020), so ` {\"on\":\"…\"` is a record's\n // start and never an op's argument.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n const ops = ` ${el.dataset.reactiveOpsParam ?? \"\"}`\n const keeps = (d) => {\n const on = ` {\"on\":${JSON.stringify(d.token.split(\"->\")[0])}`\n return d.method === \"runOps\"\n ? ops.includes(on) && ops.split(on).length === ops.split(`${on},\"prevent\":false`).length\n : keepsToggle || el.dataset.reactivePreventParam === \"false\"\n }\n if (win ? shortcut : !descs.every(keeps)) {\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\n// Issue #442: an on_client descriptor whose every record on its `on` is a\n// within: delegation the event's target falls outside of (no matching element\n// strictly under `el`: its parent is not inside `el`) is a miss — runOps\n// would run nothing and prevent nothing — so it is neither queued nor\n// prevented: a link in a delegated list's footer still navigates before\n// connect. A record without within:, or an attr that does not parse (a legacy\n// list), keeps the descriptor; a within: that does not parse is a miss, as\n// it is for runOps. (A row of a nested root is still queued; the replay's\n// ownership check makes it a miss.)\nconst outside = (el, target, within) => {\n try {\n return !el.contains(target.closest(within)?.parentElement ?? null)\n } catch {\n return true\n }\n}\n\nfunction delegatedMiss(el, d, target) {\n let mine\n try {\n mine = el.dataset.reactiveOpsParam.split(\" \").map(JSON.parse).filter((r) => r.on === d.token.split(\"->\")[0])\n } catch {\n return false\n }\n return mine.length > 0 && mine.every((r) => r.within != null && outside(el, target, r.within))\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).filter((d) => d.method !== \"runOps\" || !delegatedMiss(el, d, event.target))\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": "AA6CA,IAAM,EAAM,OAAO,IAAI,sBAAsB,EAQ7C,IAAM,EAAS,WAAW,KAAS,CAAE,MAAO,CAAC,EAAG,UAAW,IAAI,OAAU,EAOzE,SAAS,CAAe,CAAC,EAAO,CAC9B,IAAM,EAAQ,qFAAqF,KAAK,CAAK,EAC7G,OAAO,EAAQ,CAAC,CAAE,QAAO,KAAM,EAAM,IAAM,EAAM,GAAI,OAAQ,EAAM,GAAI,IAAK,CAAC,CAAC,EAAM,GAAI,OAAQ,EAAM,EAAG,CAAC,EAAI,CAAC,EASjH,SAAS,CAAU,CAAC,EAAQ,EAAO,CACjC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAQ,EAAO,MAAM,GAAG,EACxB,EAAM,EAAM,IAAI,EAEtB,OADa,IAAQ,MAAQ,SAAW,IAAQ,QAAU,IAAM,EAAI,QAAQ,0BAA2B,OAAO,EAAE,QAAQ,IAAK,EAAE,KAC/G,EAAM,KAAK,YAAY,GAAK,CAAC,OAAQ,OAAQ,MAAO,OAAO,EAAE,MAAM,CAAC,IAAQ,EAAM,SAAS,CAAG,IAAM,EAAM,GAAG,OAAS,EAGxI,IAAM,EAAc,CAAC,KAAQ,EAAG,QAAQ,QAAU,IAAI,MAAM,KAAK,EAAE,QAAQ,CAAe,EAEpF,EAAW,CAAC,EAAI,EAAO,IAC3B,EAAY,CAAE,EAAE,OAAO,CAAC,IAAM,EAAE,MAAQ,GAAO,EAAE,OAAS,EAAM,MAAQ,EAAW,EAAE,OAAQ,CAAK,CAAC,EAGrG,SAAS,CAAO,CAAC,EAAO,EAAI,EAAO,EAAK,EAAU,CAChD,IAAM,EAAO,EAAG,QAnCI,mEAmCiB,EACrC,GAAI,CAAC,GAAQ,EAAM,UAAU,IAAI,CAAI,EAAG,OAiBxC,IAAM,EACJ,qBAAqB,KAAK,EAAG,IAAI,GAAK,mBAAmB,KAAK,EAAG,QAAQ,uBAAuB,EAC5F,EAAM,IAAI,EAAG,QAAQ,kBAAoB,KACzC,EAAQ,CAAC,IAAM,CACnB,IAAM,EAAK,UAAU,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,IAC1D,OAAO,EAAE,SAAW,SAChB,EAAI,SAAS,CAAE,GAAK,EAAI,MAAM,CAAE,EAAE,SAAW,EAAI,MAAM,GAAG,mBAAoB,EAAE,OAChF,GAAe,EAAG,QAAQ,uBAAyB,SAEzD,GAAI,EAAM,EAAW,CAAC,EAAM,MAAM,CAAK,EAAG,CACxC,GAAI,GAAO,CAAC,EAAM,kBAAmB,EAAM,YAAc,IAAI,SAAW,IAAI,CAAK,EACjF,EAAM,eAAe,EAIvB,IAAQ,SAAU,EAIlB,GAAI,EAAM,KAAK,CAAE,QAAO,KAAI,OAAM,QAAO,MAAK,GAAI,YAAY,IAAI,CAAE,CAAC,EAAI,GAAI,EAAM,MAAM,EAIzF,IAAQ,kBAAiB,aAAa,IAAO,EAAK,QAClD,GAAI,EACF,EAAK,QAAQ,WAAa,GAAG,KAAc,IAAkB,KAAK,EAClE,OAAO,EAAK,QAAQ,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "AA6CA,IAAM,EAAM,OAAO,IAAI,sBAAsB,EAQ7C,IAAM,EAAS,WAAW,KAAS,CAAE,MAAO,CAAC,EAAG,UAAW,IAAI,OAAU,EAOzE,SAAS,CAAe,CAAC,EAAO,CAC9B,IAAM,EAAQ,qFAAqF,KAAK,CAAK,EAC7G,OAAO,EAAQ,CAAC,CAAE,QAAO,KAAM,EAAM,IAAM,EAAM,GAAI,OAAQ,EAAM,GAAI,IAAK,CAAC,CAAC,EAAM,GAAI,OAAQ,EAAM,EAAG,CAAC,EAAI,CAAC,EASjH,SAAS,CAAU,CAAC,EAAQ,EAAO,CACjC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAQ,EAAO,MAAM,GAAG,EACxB,EAAM,EAAM,IAAI,EAEtB,OADa,IAAQ,MAAQ,SAAW,IAAQ,QAAU,IAAM,EAAI,QAAQ,0BAA2B,OAAO,EAAE,QAAQ,IAAK,EAAE,KAC/G,EAAM,KAAK,YAAY,GAAK,CAAC,OAAQ,OAAQ,MAAO,OAAO,EAAE,MAAM,CAAC,IAAQ,EAAM,SAAS,CAAG,IAAM,EAAM,GAAG,OAAS,EAGxI,IAAM,EAAc,CAAC,KAAQ,EAAG,QAAQ,QAAU,IAAI,MAAM,KAAK,EAAE,QAAQ,CAAe,EAEpF,EAAW,CAAC,EAAI,EAAO,IAC3B,EAAY,CAAE,EAAE,OAAO,CAAC,IAAM,EAAE,MAAQ,GAAO,EAAE,OAAS,EAAM,MAAQ,EAAW,EAAE,OAAQ,CAAK,CAAC,EAGrG,SAAS,CAAO,CAAC,EAAO,EAAI,EAAO,EAAK,EAAU,CAChD,IAAM,EAAO,EAAG,QAnCI,mEAmCiB,EACrC,GAAI,CAAC,GAAQ,EAAM,UAAU,IAAI,CAAI,EAAG,OAiBxC,IAAM,EACJ,qBAAqB,KAAK,EAAG,IAAI,GAAK,mBAAmB,KAAK,EAAG,QAAQ,uBAAuB,EAC5F,EAAM,IAAI,EAAG,QAAQ,kBAAoB,KACzC,EAAQ,CAAC,IAAM,CACnB,IAAM,EAAK,UAAU,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,IAC1D,OAAO,EAAE,SAAW,SAChB,EAAI,SAAS,CAAE,GAAK,EAAI,MAAM,CAAE,EAAE,SAAW,EAAI,MAAM,GAAG,mBAAoB,EAAE,OAChF,GAAe,EAAG,QAAQ,uBAAyB,SAEzD,GAAI,EAAM,EAAW,CAAC,EAAM,MAAM,CAAK,EAAG,CACxC,GAAI,GAAO,CAAC,EAAM,kBAAmB,EAAM,YAAc,IAAI,SAAW,IAAI,CAAK,EACjF,EAAM,eAAe,EAIvB,IAAQ,SAAU,EAIlB,GAAI,EAAM,KAAK,CAAE,QAAO,KAAI,OAAM,QAAO,MAAK,GAAI,YAAY,IAAI,CAAE,CAAC,EAAI,GAAI,EAAM,MAAM,EAIzF,IAAQ,kBAAiB,aAAa,IAAO,EAAK,QAClD,GAAI,EACF,EAAK,QAAQ,WAAa,GAAG,KAAc,IAAkB,KAAK,EAClE,OAAO,EAAK,QAAQ,gBAaxB,IAAM,EAAU,CAAC,EAAI,EAAQ,IAAW,CACtC,GAAI,CACF,MAAO,CAAC,EAAG,SAAS,EAAO,QAAQ,CAAM,GAAG,eAAiB,IAAI,EACjE,KAAM,CACN,MAAO,KAIX,SAAS,CAAa,CAAC,EAAI,EAAG,EAAQ,CACpC,IAAI,EACJ,GAAI,CACF,EAAO,EAAG,QAAQ,iBAAiB,MAAM,GAAG,EAAE,IAAI,KAAK,KAAK,EAAE,OAAO,CAAC,IAAM,EAAE,KAAO,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,EAC3G,KAAM,CACN,MAAO,GAET,OAAO,EAAK,OAAS,GAAK,EAAK,MAAM,CAAC,IAAM,EAAE,QAAU,MAAQ,EAAQ,EAAI,EAAQ,EAAE,MAAM,CAAC,EAG/F,SAAS,CAAM,CAAC,EAAO,CACrB,QAAS,EAAK,EAAM,OAAQ,GAAI,QAAS,EAAK,EAAG,cAAe,CAC9D,GAAI,CAAC,EAAM,SAAW,IAAO,EAAM,OAAQ,MAC3C,IAAM,EAAQ,EAAS,EAAI,EAAO,EAAK,EAAE,OAAO,CAAC,IAAM,EAAE,SAAW,UAAY,CAAC,EAAc,EAAI,EAAG,EAAM,MAAM,CAAC,EACnH,GAAI,EAAM,OAAQ,EAAQ,EAAO,EAAI,EAAO,EAAK,GAQrD,IAAM,EAAc,IAAI,IACX,EAAuB,EAEpC,SAAS,CAAY,CAAC,EAAO,CAG3B,GAAI,CAAC,EAAM,SAAW,EAAM,SAAW,EAAM,cAAe,OAC5D,QAAW,KAAM,EAAa,CAC5B,GAAI,CAAC,EAAG,YAAa,CACnB,EAAY,OAAO,CAAE,EACrB,SAGF,GAAI,EAAM,UAAU,IAAI,EAAG,QAvIT,mEAuI8B,CAAC,EAAG,SAQpD,IAAM,EAAM,EAAG,QAAQ,kBAAoB,GACrC,EAAW,EAAG,QAAQ,wBAA0B,cAAc,KAAK,CAAG,GAAK,IACzE,UAAW,EACb,EACJ,GAAY,CAAC,mBAAmB,KAAK,CAAQ,IAAM,EAAO,mBAAqB,4BAA4B,KAAK,EAAO,SAAS,GAG5H,EAAM,IAAa,YAAY,KAAK,UAAU,eAAe,UAAY,UAAU,QAAQ,EAAI,OAAS,QACxG,EAAO,QAAQ,KAAK,CAAQ,IAAM,IAAQ,OAAS,OAAS,QAG5D,EAAU,GAAY,EAAS,MAAM,yDAAyD,GAAM,CAAC,EACrG,EAAO,CAAC,IAAM,CAAC,EAAE,QAAU,EAAO,IAAM,EAAE,OAAS,UAInD,EAAQ,EAAS,EAAI,EAAO,EAAI,EAAE,OACtC,CAAC,KACE,EAAK,CAAC,EACH,CAAC,GAAU,EAAO,KAAK,CAAC,IAAU,EAAW,EAAM,MAAM,EAAM,YAAY,GAAG,EAAI,CAAC,EAAE,QAAQ,MAAO,CAAG,EAAG,CAAK,CAAC,EAChH,EAAE,EAAE,SAAW,GAAW,GAAQ,EAAE,OAAO,SAAS,CAAI,OAC3D,EAAE,SAAW,SAAW,8BAA8B,KAAK,CAAG,EAAI,EAAG,QAAQ,uBAAyB,OAC3G,EACA,GAAI,EAAM,OAAQ,EAAQ,EAAO,EAAI,EAAO,GAAM,GAAY,EAAM,KAAK,CAAC,IAAM,EAAE,QAAU,EAAK,CAAC,CAAC,CAAC,GASjG,SAAS,CAAU,CAAC,EAAM,SAAU,CACzC,IAAM,EAAO,CAAC,IAAS,CACrB,GAAI,EAAK,WAAa,EAAG,OACzB,QAAW,IAAM,CAAC,EAAM,GAAG,EAAK,iBAAiB,4BAA4B,CAAC,EAC5E,QAAa,OAAM,SAAS,EAAY,CAAE,EAAG,CAC3C,GAAI,EAAK,EAAY,IAAI,CAAE,GACzB,EAAM,EAAI,YAAc,GAAK,iBAAiB,EAAM,EAAM,EAAe,EAAQ,EAAI,IAMvF,EAAQ,IAAM,CAClB,QAAW,KAAM,EAAa,GAAI,CAAC,EAAG,YAAa,EAAY,OAAO,CAAE,GAE1E,EAAM,EACN,EAAK,EAAI,eAAe,EACxB,IAAI,iBAAiB,CAAC,IAAc,CAClC,EAAM,EACN,QAAW,KAAK,EAAW,EAAE,OAAS,aAAe,EAAK,EAAE,MAAM,EAAI,EAAE,WAAW,QAAQ,CAAI,EAChG,EAAE,QAAQ,EAAI,gBAAiB,CAAE,UAAW,GAAM,QAAS,GAAM,gBAAiB,CAAC,aAAa,CAAE,CAAC,EAGtG,GAAI,WAAW,SAAU,EAAW",
|
|
8
|
+
"debugId": "B1FCA9A2D1DB67F064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -68,6 +68,7 @@ export function connect(controller, core) {
|
|
|
68
68
|
// here, and again from reseed() — a stream can render a root's first show
|
|
69
69
|
// target, filter or nested list into it after connect (issue #356).
|
|
70
70
|
let resetting
|
|
71
|
+
let sharing
|
|
71
72
|
let filtering
|
|
72
73
|
let tagging
|
|
73
74
|
let serializing
|
|
@@ -105,10 +106,11 @@ export function connect(controller, core) {
|
|
|
105
106
|
listen(c, "input change", c.show)
|
|
106
107
|
// The after: gate (issue #396): an owned field's first blur marks its
|
|
107
108
|
// name touched for this controller (a morph keeps it, a new one starts
|
|
108
|
-
// without), and re-syncs once if a binding waits on a blur.
|
|
109
|
+
// without), and re-syncs once if a binding waits on a blur. A shared
|
|
110
|
+
// field just below counts as owned here (issue #440).
|
|
109
111
|
listen(c, "focusout", (event) => {
|
|
110
112
|
const name = event.target?.getAttribute?.("name")
|
|
111
|
-
if (!name || c.touched?.has(name) || !c.core.
|
|
113
|
+
if (!name || c.touched?.has(name) || !c.core.bindingOwns(event.target)) return
|
|
112
114
|
;(c.touched ??= new Set()).add(name)
|
|
113
115
|
if (c.root.querySelector?.("[data-reactive-show-after]")) syncShow(c)
|
|
114
116
|
})
|
|
@@ -151,6 +153,21 @@ export function connect(controller, core) {
|
|
|
151
153
|
)
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
// Issue #440: a nested root that comes back through a plain replace (a
|
|
157
|
+
// morph already bubbles turbo:morph-element here) brings a fresh shared
|
|
158
|
+
// field: a hidden disable: true group must disable it again, and the
|
|
159
|
+
// bindings read its new value. So does one whose draft restore wrote its
|
|
160
|
+
// fields without an input event. Each announces itself with a bubbling
|
|
161
|
+
// event; only a root holding a shared field this root reads (one level,
|
|
162
|
+
// core.bindingOwns) re-syncs it.
|
|
163
|
+
if (!sharing && (sharing = c.show || c.arm)) {
|
|
164
|
+
listen(c, "reactive:connect reactive:persist-restored", (event) => {
|
|
165
|
+
if (event.target === c.root || !sharedWith(c, event.target)) return
|
|
166
|
+
c.show?.()
|
|
167
|
+
c.arm?.()
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
154
171
|
// Option filtering (issue #163) — ONLY when the root declares the binding
|
|
155
172
|
// (reactive_filter emits both attrs together), so a component without one
|
|
156
173
|
// pays two attribute reads. ONE delegated input listener on the root — the
|
|
@@ -201,6 +218,11 @@ export function connect(controller, core) {
|
|
|
201
218
|
listen(c, "input", (event) => editableEmptied(c, event))
|
|
202
219
|
}
|
|
203
220
|
|
|
221
|
+
// Whether `root` holds a shared field this root's bindings read (issue #440).
|
|
222
|
+
function sharedWith(c, root) {
|
|
223
|
+
return [...(root.querySelectorAll?.("[data-reactive-shared-field]") ?? [])].some(c.core.bindingOwns)
|
|
224
|
+
}
|
|
225
|
+
|
|
204
226
|
// Remove every listener connect() added, so a stray event after a Turbo
|
|
205
227
|
// morph/navigation never re-evaluates against a detached root.
|
|
206
228
|
export function disconnect(controller) {
|
|
@@ -423,7 +445,7 @@ function syncOnComplete(c, event) {
|
|
|
423
445
|
const bindings = onCompleteBindings(c)
|
|
424
446
|
if (!bindings.length) return
|
|
425
447
|
|
|
426
|
-
const fieldValue = fieldResolver(c, c.core.
|
|
448
|
+
const fieldValue = fieldResolver(c, c.core.bindingOwnership())
|
|
427
449
|
bindings.forEach((binding, i) => {
|
|
428
450
|
const matches = anyOfAllsMatches(binding.any, fieldValue)
|
|
429
451
|
if (matches === null) return
|
|
@@ -443,15 +465,21 @@ function syncOnComplete(c, event) {
|
|
|
443
465
|
// pass). A binding whose field can't be resolved, or whose predicate is
|
|
444
466
|
// malformed, leaves visibility ALONE — a bad binding must never break or
|
|
445
467
|
// blank the page (client-side default-deny).
|
|
468
|
+
//
|
|
469
|
+
// Two predicates (issue #440): `owns` picks the binding ELEMENTS this root
|
|
470
|
+
// evaluates (a nested root's are its own); `reads` picks the FIELDS they read,
|
|
471
|
+
// and the controls disable:/clear: may write: this root's own, plus a control
|
|
472
|
+
// marked data-reactive-shared-field just below (core.bindingOwnership).
|
|
446
473
|
function syncShow(c) {
|
|
447
474
|
if (typeof c.root?.querySelectorAll !== "function") return
|
|
448
475
|
|
|
449
476
|
const owns = c.core.ownership()
|
|
477
|
+
const reads = c.core.bindingOwnership()
|
|
450
478
|
const scope = c.root.getAttribute?.("data-reactive-scope") || null
|
|
451
479
|
// A memoized resolver shared by every binding in this pass — a field driving
|
|
452
480
|
// several bindings (and several DNF terms) reads exactly once. Scope-aware:
|
|
453
481
|
// a bare field `director` resolves as `[name="scope[director]"]` (issue #180).
|
|
454
|
-
const fieldValue = fieldResolver(c,
|
|
482
|
+
const fieldValue = fieldResolver(c, reads)
|
|
455
483
|
// Bindings whose visibility flipped this pass and that declare transition
|
|
456
484
|
// work (issue #394) — run after the pass, so the `input` a clear dispatches
|
|
457
485
|
// re-syncs from fresh values instead of this pass's memo.
|
|
@@ -469,7 +497,7 @@ function syncShow(c) {
|
|
|
469
497
|
// after: (issue #396) keeps it hidden until its field has blurred once.
|
|
470
498
|
const after = el.getAttribute("data-reactive-show-after")
|
|
471
499
|
const visible = match && (after == null || !!c.touched?.has(wireName(after, scope)))
|
|
472
|
-
applyShowVisibility(el, visible,
|
|
500
|
+
applyShowVisibility(el, visible, reads)
|
|
473
501
|
// The previous visibility lives on this controller (absent on connect,
|
|
474
502
|
// kept by a morph), so first paint never counts as a change.
|
|
475
503
|
// Tracked for every binding, so a morph that drops the transition attrs
|
|
@@ -488,17 +516,17 @@ function syncShow(c) {
|
|
|
488
516
|
if (value === null) continue // no owned field with that name — leave it be
|
|
489
517
|
const match = showBindingMatches(el, value)
|
|
490
518
|
if (match === null) continue // malformed predicate — warned + skipped
|
|
491
|
-
applyShowVisibility(el, match,
|
|
519
|
+
applyShowVisibility(el, match, reads)
|
|
492
520
|
}
|
|
493
521
|
|
|
494
522
|
// The group bindings (issue #319) and the cross-root pass (issue #164)
|
|
495
523
|
// share the same owned-field memo, so a field driving several reads once.
|
|
496
524
|
if (c.groups) syncGroups(c, fieldValue, owns, scope)
|
|
497
525
|
syncMedia(c, fieldValue, owns)
|
|
498
|
-
syncCssVars(c, fieldValue, owns, scope)
|
|
526
|
+
syncCssVars(c, fieldValue, owns, scope, reads)
|
|
499
527
|
syncShowTargets(c, fieldValue)
|
|
500
528
|
syncGroupTargets(c, fieldValue)
|
|
501
|
-
for (const el of moved) showMoved(c, el,
|
|
529
|
+
for (const el of moved) showMoved(c, el, reads, scope)
|
|
502
530
|
}
|
|
503
531
|
|
|
504
532
|
// The transition attrs a show binding may declare (issue #394).
|
|
@@ -542,8 +570,9 @@ function showMoved(c, el, owns, scope) {
|
|
|
542
570
|
// the `disabled` of every owned named control inside it) from a match result
|
|
543
571
|
// (issue #180). Disabling a hidden section's controls stops them submitting —
|
|
544
572
|
// the stale-value fix. A visible section re-enables them. Controls a nested
|
|
545
|
-
// reactive root owns are left alone (#15 ownership)
|
|
546
|
-
|
|
573
|
+
// reactive root owns are left alone (#15 ownership), except one marked
|
|
574
|
+
// data-reactive-shared-field: it declared itself the host's too (issue #440).
|
|
575
|
+
function applyShowVisibility(el, match, owns) {
|
|
547
576
|
el.hidden = !match
|
|
548
577
|
if (el.getAttribute("data-reactive-show-disable") !== "true") return
|
|
549
578
|
if (typeof el.querySelectorAll !== "function") return
|
|
@@ -556,8 +585,9 @@ function applyShowVisibility(el, match, owns, scope) {
|
|
|
556
585
|
|
|
557
586
|
// Apply the declared cross-root show targets (issue #164) — the visibility
|
|
558
587
|
// parallel of #applyComputeMirrors. For each declared field: read the OWNED
|
|
559
|
-
// field's current value (never a nested root's
|
|
560
|
-
//
|
|
588
|
+
// field's current value (never a nested root's, except a shared field just
|
|
589
|
+
// below, issue #440 — you can only drive outside visibility from a field this
|
|
590
|
+
// root reads), then for each "#id" → predicate
|
|
561
591
|
// entry: guard the selector id-only (warn-and-skip; the Ruby helper raised
|
|
562
592
|
// at declare time — two-sided default-deny), resolve it DOCUMENT-WIDE, and
|
|
563
593
|
// toggle `hidden`. A target id not on the page is silently skipped (an
|
|
@@ -694,7 +724,9 @@ function wireName(name, scope) {
|
|
|
694
724
|
// The per-pass field resolver every DNF fold reads (show, enable, on-complete).
|
|
695
725
|
// fieldValue(name) is the field's string value; fieldValue(name, true) is how
|
|
696
726
|
// many owned boxes of that checkbox group are ticked (the checked_* terms,
|
|
697
|
-
// issue #319).
|
|
727
|
+
// issue #319). `owns` is what the bindings read: core.bindingOwnership, so a
|
|
728
|
+
// shared field just below counts as owned (issue #440). Memoized, so a field
|
|
729
|
+
// driving several bindings reads once.
|
|
698
730
|
function fieldResolver(c, owns) {
|
|
699
731
|
const scope = c.root.getAttribute?.("data-reactive-scope") || null
|
|
700
732
|
const values = new Map()
|
|
@@ -795,7 +827,7 @@ const CSS_VARS_SELECTOR = "[data-reactive-css-vars]"
|
|
|
795
827
|
const CSS_VAR_NAME = /^--[a-z0-9-]+$/i
|
|
796
828
|
const CSS_VAR_UNITS = ["", "px", "%", "deg", "turn", "ms", "s", "em", "rem", "fr"]
|
|
797
829
|
|
|
798
|
-
function syncCssVars(c, fieldValue, owns, scope) {
|
|
830
|
+
function syncCssVars(c, fieldValue, owns, scope, reads) {
|
|
799
831
|
const nodes = [...c.root.querySelectorAll(CSS_VARS_SELECTOR)].filter(owns)
|
|
800
832
|
if (c.root.hasAttribute("data-reactive-css-vars")) nodes.unshift(c.root)
|
|
801
833
|
for (const el of nodes) {
|
|
@@ -825,7 +857,7 @@ function syncCssVars(c, fieldValue, owns, scope) {
|
|
|
825
857
|
console.warn(`[phlex-reactive] refused reactive_css_vars entry ${JSON.stringify(name)} — skipped`)
|
|
826
858
|
continue
|
|
827
859
|
}
|
|
828
|
-
const number = cssVarNumber(c, fieldValue, spec.field,
|
|
860
|
+
const number = cssVarNumber(c, fieldValue, spec.field, reads, scope)
|
|
829
861
|
if (number === undefined) continue
|
|
830
862
|
const value = number === null ? "" : `${number}${unit}`
|
|
831
863
|
for (const target of targets) {
|
|
@@ -872,7 +904,9 @@ function hasGroups(c) {
|
|
|
872
904
|
}
|
|
873
905
|
|
|
874
906
|
// The owned checkboxes of a group (a nested root's boxes are its own, #15).
|
|
875
|
-
//
|
|
907
|
+
// `owns` decides which: strict for a select-all header and the boxes it
|
|
908
|
+
// ticks; core.bindingOwnership for a count, which takes a shared box just
|
|
909
|
+
// below too (issue #440). With `under` (a nested header, issue #398) only the boxes whose
|
|
876
910
|
// data-reactive-group is that path or below it, on whole segments.
|
|
877
911
|
function groupBoxes(c, name, owns, scope, under) {
|
|
878
912
|
return [...c.root.querySelectorAll(namedSelector(name, scope))].filter(
|
|
@@ -957,7 +991,8 @@ function syncGroups(c, fieldValue, owns, scope) {
|
|
|
957
991
|
// The declared cross-root group targets (issue #343) — the count and enable
|
|
958
992
|
// parallel of syncShowTargets, declared on the root that OWNS the group:
|
|
959
993
|
// { group: { count: ["#id", …], enable: { "#id": { any: … } } } }. The count
|
|
960
|
-
// is this root's owned ticked boxes (a nested root's are its own, #15
|
|
994
|
+
// is this root's owned ticked boxes (a nested root's are its own, #15, except
|
|
995
|
+
// a shared box just below, issue #440), the
|
|
961
996
|
// enable folds its DNF payload with the same owned-field memo as every other
|
|
962
997
|
// binding. Targets are id-only (warn-skip — the Ruby helper raised at
|
|
963
998
|
// declare time) and resolved document-wide; a missing one is skipped.
|
|
@@ -1,4 +1,4 @@
|
|
|
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};
|
|
1
|
+
import{confirmPredicate as de}from"phlex/reactive/confirm_predicate";var m=new WeakMap,k=new WeakMap;function y(e,r){let t=m.get(e);if(!t){if(m.set(e,t={root:e.element,core:r,nestedIndex:0}),t.root)k.set(t.root,t)}return t}function h(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){h(e,"turbo:morph-element",r),e.seeds.push(r),r()}function st(e,r){let t=y(e,r);t.offs=[],t.seeds=[];let n,o,i,s,a;(t.wire=()=>{if(!t.show&&me(t))t.show=(c)=>{if(t.flipping)return;if(c?.type==="input"||c?.type==="change")je(t,c.target);else t.groups=$e(t),F(t);W(t)},h(t,"input change",t.show),h(t,"focusout",(c)=>{let l=c.target?.getAttribute?.("name");if(!l||t.touched?.has(l)||!t.core.bindingOwns(c.target))return;if((t.touched??=new Set).add(l),t.root.querySelector?.("[data-reactive-show-after]"))W(t)}),b(t,t.show);if(!t.arm&&be(t))h(t,"input change",(c)=>D(t,c)),b(t,t.arm=()=>{D(t,null),F(t)});if(!n&&(n=t.show||t.arm))h(t,"reset",()=>setTimeout(()=>{if(m.get(e)!==t)return;t.show?.(),t.arm?.()},0));if(!o&&(o=t.show||t.arm))h(t,"reactive:connect reactive:persist-restored",(c)=>{if(c.target===t.root||!pe(t,c.target))return;t.show?.(),t.arm?.()});if(!i&&(i=x(t)))h(t,"input",(c)=>Je(t,c)&&A(t)),b(t,()=>A(t));if(!s&&(s=O(t)))b(t,()=>te(t));if(!a&&(a=We(t)))h(t,"input change",(c)=>ie(t,c)),b(t,()=>T(t))})(),h(t,"keydown",(c)=>tt(t,c)),h(t,"paste drop",(c)=>rt(t,c)),h(t,"input",(c)=>nt(t,c))}function pe(e,r){return[...r.querySelectorAll?.("[data-reactive-shared-field]")??[]].some(e.core.bindingOwns)}function at(e){let r=m.get(e);if(!r)return;m.delete(e),k.delete(r.root);for(let t of r.offs??[])t();r.groupObserver?.disconnect()}function ct(e){let r=m.get(e);if(!r)return;for(let t of r.seeds)t();r.wire()}function v(e){if(typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}))}function lt(e,r,t){return ie(y(e,r),t)}function ut(e,r,t){return ue(y(e,r),t)}var ge=/^#[A-Za-z_][\w-]*$/,K="[data-reactive-show-field], [data-reactive-show]";function he(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 s=e.getAttribute(`data-reactive-show-${i}`);if(s!==null)return ce(i,s,r)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}function H(e){try{let r=JSON.parse(e);if(r&&typeof r==="object"&&!Array.isArray(r))return r}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(e)} — skipped`),null}function ye(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 ve(e,r)}function ve(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"&&ge.test(e))return!0;return console.warn(`[phlex-reactive] refused cross-root ${r} target ${JSON.stringify(e)} — skipped`),!1}function me(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 be(e){return!!e.root.getAttribute?.("data-reactive-on-complete")}function Ae(e){let r=e.root.getAttribute?.("data-reactive-on-complete")??null;if(r!==e.onCompleteRaw)e.onCompleteRaw=r,e.onCompleteParsed=r==null?[]:ye(r),e.onCompleteStates=e.onCompleteParsed.map(()=>!1);return e.onCompleteParsed}function D(e,r){let t=Ae(e);if(!t.length)return;let n=Y(e,e.core.bindingOwnership());t.forEach((o,i)=>{let s=w(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 W(e){if(typeof e.root?.querySelectorAll!=="function")return;let r=e.core.ownership(),t=e.core.bindingOwnership(),n=e.root.getAttribute?.("data-reactive-scope")||null,o=Y(e,t),i=[];for(let s of e.root.querySelectorAll(K)){if(!r(s))continue;let a=s.getAttribute("data-reactive-show");if(a!==null){let d=V(H(a),o);if(d===null)continue;let f=s.getAttribute("data-reactive-show-after"),p=d&&(f==null||!!e.touched?.has(E(f,n)));L(s,p,t);let g=(e.shown??=new WeakMap).get(s);if(e.shown.set(s,p),we.some((fe)=>s.hasAttribute(`data-reactive-show-${fe}`))&&g!==void 0&&g!==p)i.push(s);continue}let c=s.getAttribute("data-reactive-show-field");if(!c)continue;let l=o(c);if(l===null)continue;let u=he(s,l);if(u===null)continue;L(s,u,t)}if(e.groups)Me(e,o,r,n);Ne(e,o,r),qe(e,o,r,n,t),xe(e,o),Te(e,o);for(let s of i)Se(e,s,t,n)}var we=["clear","focus","scroll"];function Se(e,r,t,n){if(r.hidden){let a=[];try{a=JSON.parse(r.getAttribute("data-reactive-show-clear")??"[]")}catch{}let c=new Set((Array.isArray(a)?a:[]).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,v(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"),s=null;try{s=i?e.root.querySelector(i):null}catch{}s?.focus?.(o?{preventScroll:!0}:void 0)}function L(e,r,t){if(e.hidden=!r,e.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof e.querySelectorAll!=="function")return;for(let n of e.querySelectorAll("input[name], select[name], textarea[name]"))if(t(n))n.disabled=!r;if(e.name&&t(e))e.disabled=!r}function xe(e,r){let t=z(e);for(let[n,o]of Object.entries(t)){if(n.startsWith("#")){_e(n,o,r);continue}if(!o||typeof o!=="object"||Array.isArray(o))continue;let i=r(n);if(i===null)continue;let s=(a,c)=>c?r(n,!0):i;for(let[a,c]of Object.entries(o)){if(!S(a))continue;let l;if(Array.isArray(c)){if(c.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${a} — skipped`);continue}l=c.every((u)=>J(u,s))}else{let u=le(c,i);if(u===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${a} — skipped`);continue}l=u}for(let u of document.querySelectorAll(a))u.hidden=!l}}}function _e(e,r,t){if(!S(e))return;let n=r&&typeof r==="object"&&!Array.isArray(r)?r.any:null,o=Xe(n);if(o===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${e} — skipped`);return}if(o.every((s)=>t(s)===null)&&!et(n))return;let i=w(n,t);if(i===null)return;for(let s of document.querySelectorAll(e))s.hidden=!i}function z(e,r="show",t="mode: { ... }, kind: { ... }"){let n=e.root.getAttribute?.(`data-reactive-${r}-targets`);if(!n)return{};try{let o=JSON.parse(n);if(o&&typeof o==="object"&&!Array.isArray(o))return o}catch{}return console.warn(`[phlex-reactive] malformed data-reactive-${r}-targets — ignored. `+`Did two reactive_${r}_targets calls collide on one root? Declare every entry in ONE call: reactive_${r}_targets(${t})`),{}}function Oe(e,r,t,n){let o=!1,i=null;for(let s of e.root.querySelectorAll(q(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 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 s=i?`#${o}`:o;if(!n.has(s))n.set(s,i?R(e,o,r,t).filter((a)=>a.checked).length:Oe(e,o,r,t));return n.get(s)}}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,s;try{if(o=JSON.parse(n.getAttribute("data-reactive-media")),o?.match!=null)i=new RegExp(`^(?:${o.match})$`);if(o?.when!=null)s=new RegExp(`^(?:${o.when})$`)}catch{}if(typeof o?.field!=="string"||!o.pool||typeof o.pool!=="object"||o.match!=null&&!i||o.when!=null&&!s){console.warn("[phlex-reactive] malformed data-reactive-media — skipped");continue}let a=r(o.field);if(a===null)continue;let c=o.take>0?[...a]:null,l=c?c.slice(0,o.take).join(""):a,u=c?.length<o.take||i?.test(l)===!1||s?.test(a)===!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 g of G(e,o.label))if(g.textContent!==p)g.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,ke=["","px","%","deg","turn","ms","s","em","rem","fr"];function qe(e,r,t,n,o){let i=[...e.root.querySelectorAll(Z)].filter(t);if(e.root.hasAttribute("data-reactive-css-vars"))i.unshift(e.root);for(let s of i){let a;try{a=JSON.parse(s.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 c=a.to.flatMap((l)=>{let u={to:l,global:a.global===!0},d=[];try{d=e.core.opTargets(u)}catch{}if(!d.length)e.core.diagnose("reactive_css_vars",u);return d});for(let[l,u]of Object.entries(a.vars)){let d=u?.unit??"";if(!Ce.test(l)||!ke.includes(d)||typeof u?.field!=="string"){console.warn(`[phlex-reactive] refused reactive_css_vars entry ${JSON.stringify(l)} — skipped`);continue}let f=Ee(e,r,u.field,o,n);if(f===void 0)continue;let p=f===null?"":`${f}${d}`;for(let g of c){if(g.style.getPropertyValue(l)===p)continue;if(p)g.style.setProperty(l,p);else g.style.removeProperty(l)}}}}function Ee(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((a)=>n(a)&&a.type==="checkbox"))return i==="true"?1:0;let s=i.trim()?Number(i):NaN;return Number.isFinite(s)?s:null}var _="[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]";function $e(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||Re(i.getAttribute("data-reactive-group"),o)))}function Re(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 ft(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,s=R(e,r,e.core.ownership(),i,o).filter((a)=>a!==n&&a.checked!==t);for(let a of s)a.checked=t;e.flipping=!0;try{for(let a of s)for(let c of["input","change"])a.dispatchEvent?.(new Event(c,{bubbles:!0}))}finally{e.flipping=!1}}function Me(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 s=o.getAttribute("data-reactive-count");if(s){let c=String(r(s,!0));if(o.textContent!==c)o.textContent=c}let a=o.getAttribute("data-reactive-select-all");if(a){let c=o.getAttribute("data-reactive-select-all-under"),l=R(e,a,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 Te(e,r){for(let[t,n]of Object.entries(z(e,"group",'"ids[]" => { ... }, "tags[]" => { ... }'))){if(!n||typeof n!=="object"||Array.isArray(n))continue;let o=String(r(t,!0));for(let s of Array.isArray(n.count)?n.count:[]){if(!S(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(!S(s,"group"))continue;let c=w(a?.any,r);if(c===null){console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${s} — skipped`);continue}for(let l of document.querySelectorAll(s))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 Je(e,r){let t=e.root.getAttribute("data-reactive-filter-input");return!!t&&typeof r.target?.matches==="function"&&r.target.matches(t)}var Ie=/[\s\-'(),./]/;function De(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||Ie.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"),s=e.root.hasAttribute?.("data-reactive-filter-fold"),a=(f)=>{let p=(f??"").toLowerCase().trim();return s?p.normalize("NFD").replace(/\p{M}/gu,""):p},c=a(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!==""&&!De(i,a(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((g)=>g.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 O(e){return!!e.root.getAttribute?.("data-reactive-tags-field")}function We(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((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)ee(e,t,n);return i}function ee(e,r,t){r.value=t.join(","),v(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();Pe(e,t,n),Ge(e,t,n)}function Pe(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 c=a.matches?.("[data-reactive-tag-text]")?a:(a.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(c)c.textContent=s;let l=[...a.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(a.matches?.('[data-action*="reactive#tagsRemove"]'))l.push(a);for(let u of l)u.setAttribute?.("data-reactive-tag-param",s);n.appendChild(a)}}function Ge(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 Fe(e){return e.nestedIndex=Math.max(e.nestedIndex+1,Date.now()),e.nestedIndex}function Be(e,r){let t=[e,...e.querySelectorAll?.("*")??[]];for(let n of t)for(let o of["name","id","for"]){let i=n.getAttribute?.(o);if(i&&i.includes("NEW_ROW"))n.setAttribute?.(o,i.replaceAll("NEW_ROW",String(r)))}}function 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 He(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[c,l]of Object.entries(o)){let u=[...e.root.querySelectorAll?.(l)??[]].find(i);if(!u)continue;let d=s.find((f)=>ne(f.getAttribute?.("name"))===c);if(!d)continue;Ve(d,u),a.push(u)}if(n)for(let c of a)ze(c);return a[0]??null}function Ve(e,r){if(e.type==="checkbox")e.checked=r.type==="checkbox"?!!r.checked:N(r)!=="";else e.value=N(r);v(e)}function ze(e){if(e.type==="checkbox")e.checked=!1;else e.value="";v(e)}function Ye(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",v(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 Ue(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]=N(o)}return t}function ne(e){if(!e)return null;let r=e.match(/\[([^\][]+)\]$/);return r?r[1]:e}function N(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=Ue(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(re(e,a))}let s=JSON.stringify(i);if(o.value===s)return;o.value=s,v(o)}function dt(e,r,t){let n=y(e,r);if(!O(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(!X(n,String(o.value??"").split(",")))return;if(o.value="",x(n))A(n)}function pt(e,r,t){let n=y(e,r);if(!O(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;if(!X(n,[i]))return;let s=Le(n);if(!s)return;s.value="",A(n),s.focus?.()}function gt(e,r,t){let n=y(e,r);if(!O(n))return;t?.preventDefault?.();let i=(t?.currentTarget??t?.target)?.getAttribute?.("data-reactive-tag-param");if(!i)return;let s=j(n);if(!s)return;let a=M(s),c=a.filter((l)=>l.toLowerCase()!==i.toLowerCase());if(c.length===a.length)return;ee(n,s,c)}function ht(e,r,t){let n=y(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),l=[...n.root.querySelectorAll(`[data-reactive-nested-template="${i}"]`)].find(s)?.content?.firstElementChild;if(!a||!l){Ke(n,i);return}let u=l.cloneNode(!0);Be(u,Fe(n)),a.appendChild(u);let d=o?.getAttribute?.("data-reactive-nested-from-param"),f=o?.getAttribute?.("data-reactive-nested-clear-param")==="true",p=He(n,u,d,f);if(d)p?.focus?.();else[...u.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(a.getAttribute?.("data-reactive-nested-json")===i)oe(n,i)}function yt(e,r,t){let n=y(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"),c=s||(a?ue(n,a):null);if(!c)return B(n,i);let l=re(n,i),u=Ye(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"],Ze=["len_eq","len_gte","len_gt","len_lte","len_lt"],ae=["checked_eq","checked_gte","checked_gt","checked_lte","checked_lt"],Qe=["digits_eq","digits_gte","digits_lt","digits_lte"];function C(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 C(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 C(t,e[t],Array.isArray(i)?i.length:Number(i)||0)===!0}if(typeof e.same==="string"){let i=(s)=>{let a=String(r(s)??"");return e.fold===!0?a.replace(/[A-Z]/g,(c)=>String.fromCharCode(c.charCodeAt(0)+32)):a};return i(e.field)===i(e.same)!==(e.negate===!0)}let n=Qe.filter((i)=>(i in e));if(n.length){let i=String(r(e.field)??"");if(!n.every((a)=>Number.isInteger(e[a]))||e.prefix!=null&&typeof e.prefix!=="string")return!1;return(/^[0-9]*$/.test(i)&&n.every((a)=>C(a,e[a],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 Xe(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 et(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=(s)=>n[s],i;if(typeof t.predicate==="string"){let s=de(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=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 tt(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),v(t),t.blur()}function rt(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 nt(e,r){let t=I(e,r);if(t?.firstChild&&e.core.text(t)==="")t.replaceChildren()}export{ft as checkGroup,ut as confirmMessage,st as connect,at as disconnect,ht as nestedAdd,yt as nestedRemove,ct as reseed,lt as syncNestedJson,dt as tagsAdd,pt as tagsPick,gt as tagsRemove};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=0E6A93F98751F98F64756E2164756E21
|
|
4
4
|
//# sourceMappingURL=bindings.min.js.map
|