phlex-reactive 0.16.3 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,10 +90,16 @@ function enqueue(event, el, descs, win, shortcut) {
90
90
  // never prevents a window binding — but a shortcut (issue #357), the chord
91
91
  // the page claims (mod+s must not open the save dialog before connect), is
92
92
  // always prevented: the keepsToggle exemption is for element-bound clicks
93
- // only. Everything else is prevented now, as the controller would.
93
+ // only. Everything else is prevented now, as the controller would. A
94
+ // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):
95
+ // a connected root's shortcut on the same chord still runs, as a shortcut
96
+ // only skips a key some other handler prevented.
94
97
  const keepsToggle =
95
98
  /^(checkbox|radio)$/.test(el.type) && /"checked":"keep"/.test(el.dataset.reactiveOptimisticParam)
96
- if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === "runOps")) event.preventDefault()
99
+ if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === "runOps")) {
100
+ if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)
101
+ event.preventDefault()
102
+ }
97
103
  // Every firing is queued — a `:once` repeat included: the replay skips a
98
104
  // descriptor already consumed (reactive_controller.js #replayEarly).
99
105
  const { queue } = state
@@ -1,4 +1,4 @@
1
- var g=Symbol.for("phlex-reactive.early");var p=globalThis[g]??={queue:[],connected:new WeakSet};function O(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function h(e,t){if(!e)return!0;let o=e.split("+"),r=o.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((a)=>o.includes(a)===t[`${a}Key`])}var w=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(O),y=(e,t,o)=>w(e).filter((r)=>r.win===o&&r.type===t.type&&h(r.filter,t));function b(e,t,o,r,s){let a=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!a||p.connected.has(a))return;let n=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam);if(r?s:!n||o.some((d)=>d.method==="runOps"))e.preventDefault();let{queue:l}=p;if(l.push({event:e,el:t,root:a,descs:o,win:r,at:performance.now()})>50)l.shift();let{reactiveDormant:u,controller:f=""}=a.dataset;if(u)a.dataset.controller=`${f} ${u}`.trim(),delete a.dataset.reactiveDormant}function E(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let o=y(t,e,!1);if(o.length)b(e,t,o,!1)}}var i=new Set,S=i;function k(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of i){if(!t.isConnected){i.delete(t);continue}if(p.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let o=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(o)&&o),{target:s}=e,a=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),n=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),l=/mod\+/.test(r)&&(n==="meta"?"ctrl":"meta"),u=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],f=(c)=>!c.filter&&u[0]&&c.type==="keydown",d=y(t,e,!0).filter((c)=>(f(c)?!a&&u.some((m)=>h(m.slice(m.lastIndexOf('"')+1).replace("mod",n),e)):!(c.filter&&(a||l&&c.filter.includes(l))))&&(c.method==="runOps"?/"window":true(?!,"outside")/.test(o):t.dataset.reactiveOutsideParam!=="true"));if(d.length)b(e,t,d,!0,r&&d.some((c)=>c.filter||f(c)))}}function T(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:a,win:n}of w(s)){if(n)i.add(s);(n?e.defaultView:e).addEventListener(a,n?k:E,!0)}},o=()=>{for(let r of i)if(!r.isConnected)i.delete(r)};o(),t(e.documentElement),new MutationObserver((r)=>{o();for(let s of r)s.type==="attributes"?t(s.target):s.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)T();export{S as __windowBoundForTest,T as startEarly};
1
+ var g=Symbol.for("phlex-reactive.early");var p=globalThis[g]??={queue:[],connected:new WeakSet};function O(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function h(e,t){if(!e)return!0;let a=e.split("+"),r=a.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((o)=>a.includes(o)===t[`${o}Key`])}var w=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(O),y=(e,t,a)=>w(e).filter((r)=>r.win===a&&r.type===t.type&&h(r.filter,t));function b(e,t,a,r,s){let o=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!o||p.connected.has(o))return;let n=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam);if(r?s:!n||a.some((d)=>d.method==="runOps")){if(r&&!e.defaultPrevented)(p.prevented??=new WeakSet).add(e);e.preventDefault()}let{queue:l}=p;if(l.push({event:e,el:t,root:o,descs:a,win:r,at:performance.now()})>50)l.shift();let{reactiveDormant:u,controller:f=""}=o.dataset;if(u)o.dataset.controller=`${f} ${u}`.trim(),delete o.dataset.reactiveDormant}function E(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let a=y(t,e,!1);if(a.length)b(e,t,a,!1)}}var i=new Set,S=i;function k(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of i){if(!t.isConnected){i.delete(t);continue}if(p.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let a=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(a)&&a),{target:s}=e,o=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),n=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),l=/mod\+/.test(r)&&(n==="meta"?"ctrl":"meta"),u=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],f=(c)=>!c.filter&&u[0]&&c.type==="keydown",d=y(t,e,!0).filter((c)=>(f(c)?!o&&u.some((m)=>h(m.slice(m.lastIndexOf('"')+1).replace("mod",n),e)):!(c.filter&&(o||l&&c.filter.includes(l))))&&(c.method==="runOps"?/"window":true(?!,"outside")/.test(a):t.dataset.reactiveOutsideParam!=="true"));if(d.length)b(e,t,d,!0,r&&d.some((c)=>c.filter||f(c)))}}function T(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:o,win:n}of w(s)){if(n)i.add(s);(n?e.defaultView:e).addEventListener(o,n?k:E,!0)}},a=()=>{for(let r of i)if(!r.isConnected)i.delete(r)};a(),t(e.documentElement),new MutationObserver((r)=>{a();for(let s of r)s.type==="attributes"?t(s.target):s.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)T();export{S as __windowBoundForTest,T as startEarly};
2
2
 
3
- //# debugId=E672E1A717FD883764756E2164756E21
3
+ //# debugId=1F67C61CEC0FBB4264756E2164756E21
4
4
  //# sourceMappingURL=early.min.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["early.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === \"runOps\")) event.preventDefault()\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false)\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
5
+ "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would. A\n // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):\n // a connected root's shortcut on the same chord still runs, as a shortcut\n // only skips a key some other handler prevented.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n if (win ? shortcut : !keepsToggle || descs.some((d) => d.method === \"runOps\")) {\n if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)\n event.preventDefault()\n }\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false)\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "E672E1A717FD883764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "1F67C61CEC0FBB4264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -7,10 +7,10 @@
7
7
  //
8
8
  // data-reactive-frame the coordinate space: its padding box
9
9
  // data-reactive-box='{"x":"crop[x]",…}' a box and the names of its fields
10
- // -min / -ratio / -aspect / -action / -busy / -messages
10
+ // -min / -ratio / -aspect / -scale / -action / -busy / -messages
11
11
  // data-reactive-box-handle="se" an edge or corner (inside the box)
12
12
  // data-reactive-point='{"x":…,"y":…}' a point (its centre)
13
- // -within / -action / -busy / -messages
13
+ // -within / -scale / -action / -busy / -messages
14
14
  //
15
15
  // and what it sets while a pointer drag is on, for the app's CSS:
16
16
  //
@@ -20,12 +20,14 @@
20
20
  // Where an element is lives in its own inline style — left/top/width/height
21
21
  // in percent, rendered by the server and written here — so a morph that
22
22
  // renders new values moves it, and nothing is cached between drags. Each
23
- // field's .value is the same number as a fraction, rounded to 4 decimals and
24
- // written as set_value writes (core.setValue: a bubbling `input` when it
25
- // moved), at most once a frame while a pointer drags; one `change` follows on
26
- // release. A box's x/y/width/height are fractions of the frame; a point's x/y
27
- // of the element it sits in that matches its -within selector, else of the
28
- // frame.
23
+ // field's .value is the same number as a fraction, rounded to 4 decimals —
24
+ // or, with -scale (issue #428), that fraction times the scale (100: percent,
25
+ // 2 decimals) — written as set_value writes (core.setValue: a bubbling
26
+ // `input` when it moved), at most once a frame while a pointer drags; one
27
+ // `change` follows on release. A box's x/y/width/height are fractions of the
28
+ // frame; a point's x/y of the element it sits in that matches its -within
29
+ // selector, else of the frame. Everything else — the style, the action's
30
+ // params, the words — stays in fractions.
29
31
  //
30
32
  // Input is pointer events: mouse, pen and touch behave the same (the helpers
31
33
  // render touch-action: none, so a finger drag does not scroll), and a drag
@@ -98,6 +100,16 @@ const clamp = (value, low, high) => Math.min(Math.max(value, low), high)
98
100
 
99
101
  export const round = (value) => Math.round(value * 1e4) / 1e4
100
102
 
103
+ // What a field on `scale` holds for a fraction: its 4 decimals times the
104
+ // scale, to 15 significant digits — every digit it has, without the float
105
+ // noise: 0.1235 is "0.1235", "12.35" on 100 (not 12.350000000000001), "3.0875"
106
+ // on 25. (A product that rounds up past the largest number stays as it is.)
107
+ export function scaled(value, scale = 1) {
108
+ const exact = round(value) * scale
109
+ const clean = Number(exact.toPrecision(15))
110
+ return String(Number.isFinite(clean) ? clean : exact)
111
+ }
112
+
101
113
  // A position as the fields will hold it: each number rounded to 4 decimals,
102
114
  // a box's x/y then pulled in so it still ends inside the frame (rounded
103
115
  // apart, 0.45545 + 0.54455 is 0.4555 + 0.5446 = 1.0001).
@@ -179,6 +191,7 @@ const json = (value, fallback) => {
179
191
  }
180
192
  }
181
193
  const kind = (el) => (el.matches(POINT) ? "point" : "box")
194
+ const scaleOf = (el) => Number(attr(el, `${kind(el)}-scale`)) || 1
182
195
  const keysOf = (el) => (kind(el) === "point" ? ["x", "y"] : ["x", "y", "width", "height"])
183
196
 
184
197
  function read(el) {
@@ -256,16 +269,26 @@ function begin(core, root, grip, keyboard) {
256
269
  value,
257
270
  ratio: point ? 0 : ratioOf(el, frame, root, core),
258
271
  min: Number(attr(el, "box-min")) || 0,
272
+ scale: scaleOf(el),
259
273
  fields,
260
274
  before: Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, field.value])),
261
275
  }
262
276
  }
263
277
 
278
+ // Write a field the fraction on its scale, as set_value writes — unless it
279
+ // already holds that number, within half a step of the fraction's 4th
280
+ // decimal on its scale ("50.0", the 50 a server rendered for 0.5 × 100, is
281
+ // not a move: no `input`, and no `change` on release).
282
+ function put(core, field, value, scale) {
283
+ const text = scaled(value, scale)
284
+ if (field.value === "" || !(Math.abs(field.value - text) < scale / 2e4)) core.setValue(field, text)
285
+ }
286
+
264
287
  // Write the fields now (set_value's write: `input` on each that moved).
265
288
  function flush(record) {
266
289
  cancelAnimationFrame(record.writing)
267
290
  record.writing = 0
268
- for (const [key, field] of Object.entries(record.fields)) record.core.setValue(field, String(round(record.value[key])))
291
+ for (const [key, field] of Object.entries(record.fields)) put(record.core, field, record.value[key], record.scale)
269
292
  }
270
293
 
271
294
  // --- Pointer --------------------------------------------------------------------------
@@ -515,8 +538,7 @@ function holdMorphs() {
515
538
  const core = cores.get(root)
516
539
  if (!core?.owns(el)) return
517
540
  const value = read(el)
518
- for (const [key, field] of Object.entries(fieldsOf(el, root, core))) {
519
- if (round(Number(field.value)) !== round(value[key])) core.setValue(field, String(round(value[key])))
520
- }
541
+ const scale = scaleOf(el)
542
+ for (const [key, field] of Object.entries(fieldsOf(el, root, core))) put(core, field, value[key], scale)
521
543
  })
522
544
  }
@@ -1,4 +1,4 @@
1
- var j={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]},q={x:"left",y:"top",width:"width",height:"height"},I=new WeakMap,G=new WeakMap,i=null,y,H,B=!1;function ft(t,e){let n=t.element;G.set(n,e),Q(),lt();let c=[["pointerdown",(o)=>st(o,e,n)],["keydown",(o)=>ut(o,e,n)],["keyup",(o)=>i?.keyboard&&o.target===i.grip&&j[o.key]&&N()],["focusout",(o)=>i?.keyboard&&o.target===i.grip&&N()],["input",(o)=>X(o,e,n)],["change",(o)=>X(o,e,n)]];for(let[o,s]of c)n.addEventListener(o,s);I.set(t,()=>{for(let[o,s]of c)n.removeEventListener(o,s)})}function ht(t){if(I.get(t)?.(),I.delete(t),G.delete(t.element),i?.root===t.element)A(i)}var m=(t,e,n)=>Math.min(Math.max(t,e),n),d=(t)=>Math.round(t*1e4)/1e4;function S(t){let e={x:d(t.x),y:d(t.y)};if(t.width!=null)e.width=d(t.width),e.height=d(t.height),e.x=Math.min(e.x,d(1-e.width)),e.y=Math.min(e.y,d(1-e.height));return e}function V(t,e,n){return{x:m(t.x+e,0,1-t.width),y:m(t.y+n,0,1-t.height),width:t.width,height:t.height}}function W(t,e,n){return{x:m(t.x+e,0,1),y:m(t.y+n,0,1)}}function Y(t,e,n,c,o,s){let[r,a,l,f]=["n","e","s","w"].map((R)=>e.includes(R)),x=t.x+t.width,k=t.y+t.height,O=t.x+t.width/2,E=t.y+t.height/2,v=a?1-t.x:f?x:s?2*Math.min(O,1-O):1,u=l?1-t.y:r?k:s?2*Math.min(E,1-E):1,h=t.width+(a?n:f?-n:0),g=t.height+(l?c:r?-c:0);if(s){let R=(a||f)&&(!(r||l)||Math.abs(h-t.width)>=Math.abs(g-t.height)*s),P=Math.min(v,u*s);h=m(R?h:g*s,Math.min(Math.max(o,o*s),P),P),g=h/s}else{if(a||f)h=m(h,Math.min(o,v),v);if(r||l)g=m(g,Math.min(o,u),u)}return{x:f?x-h:a?t.x:O-h/2,y:r?k-g:l?t.y:E-g/2,width:h,height:g}}function et(t,e,n){let c=Math.min(1,e),o=m(Math.sqrt(t.width*t.height*e),Math.min(Math.max(n,n*e),c),c),s=o/e;return{x:m(t.x+t.width/2-o/2,0,1-o),y:m(t.y+t.height/2-s/2,0,1-s),width:o,height:s}}function nt(t){let[e,n=1,c]=String(t??"").split(":").map(Number),o=c===void 0?e/n:0;return o>0&&Number.isFinite(o)?o:0}var p=(t,e)=>t?.getAttribute?.(`data-reactive-${e}`),T=(t,e)=>{try{return t==null?e:JSON.parse(t)}catch{return e}},w=(t)=>t.matches("[data-reactive-point]")?"point":"box",b=(t)=>w(t)==="point"?["x","y"]:["x","y","width","height"];function z(t){let e={};for(let n of b(t))e[n]=(parseFloat(t.style[q[n]])||0)/100;return e}function D(t,e){for(let n of b(t))t.style[q[n]]=`${Math.round(e[n]*1e4)/100}%`}function F(t){let e=t.getBoundingClientRect();return{left:e.left+(t.clientLeft||0),top:e.top+(t.clientTop||0),width:t.clientWidth||e.width,height:t.clientHeight||e.height}}function ot(t,e){try{return t.parentElement.closest(p(t,"point-within")||"[data-reactive-frame]")??e}catch{return e}}function J(t,e,n){return[...t.querySelectorAll("[name]")].filter((c)=>c.name===n&&e.owns(c))}function K(t,e,n){let c=T(p(t,w(t)),{}),o={};for(let s of b(t)){let[r]=J(e,n,c[s]);if(r)o[s]=r}return o}function it(t,e,n,c){let o=p(t,"box-aspect"),s=o&&J(n,c,o).find((f)=>!/^(radio|checkbox)$/.test(f.type)||f.checked),r=o?nt(s?.value):Number(p(t,"box-ratio"))||0,{width:a,height:l}=F(e);return r&&a&&l?r*l/a:0}function C(t,e,n,c){let o=n.matches("[data-reactive-box-handle]")?n.closest("[data-reactive-box]"):n,s=o?.closest("[data-reactive-frame]");if(!s)return null;let r=z(o),a=K(o,e,t),l=w(o)==="point";return{core:t,root:e,grip:n,el:o,frame:s,keyboard:c,bounds:l?ot(o,s):s,edge:p(n,"box-handle"),start:r,value:r,ratio:l?0:it(o,s,e,t),min:Number(p(o,"box-min"))||0,fields:a,before:Object.fromEntries(Object.entries(a).map(([f,x])=>[f,x.value]))}}function M(t){cancelAnimationFrame(t.writing),t.writing=0;for(let[e,n]of Object.entries(t.fields))t.core.setValue(n,String(d(t.value[e])))}function st(t,e,n){if(U(),i||t.button>0||t.isPrimary===!1||t.target.closest?.("input,textarea,select,[contenteditable]"))return;let c=t.target.closest?.("[data-reactive-box-handle],[data-reactive-point],[data-reactive-box]"),o=c&&e.owns(c)&&C(e,n,c,!1);if(!o)return;let{clientX:s,clientY:r}=t,a=F(o.bounds);o.p0=[(s-a.left)/a.width,(r-a.top)/a.height];let l=(u)=>u.pointerId===t.pointerId,f=(u)=>{if(!l(u)||o.over)return;if(i)return L(u.clientX,u.clientY);if(Math.abs(u.clientX-s)<=4&&Math.abs(u.clientY-r)<=4)return;rt(o),L(u.clientX,u.clientY)},x=(u)=>{if(!l(u))return;if(v(),i)ct(),N()},k=(u)=>{if(u.type==="pointercancel"&&!l(u))return;if(v(),i)tt()},E=[["pointermove",f],["pointerup",x],["pointercancel",k],["keydown",(u)=>u.key==="Escape"&&i&&(u.preventDefault(),k(u))]];for(let[u,h]of E)document.addEventListener(u,h);function v(){for(let[u,h]of E)document.removeEventListener(u,h)}}function ct(){let t=(e)=>(e.preventDefault(),e.stopPropagation());window.addEventListener("click",t,{capture:!0,once:!0}),setTimeout(()=>window.removeEventListener("click",t,{capture:!0}))}function rt(t){i=t,t.grip.setAttribute("data-reactive-dragging",""),t.frame.setAttribute("data-reactive-frame-active",""),t.select=document.documentElement.style.userSelect,document.documentElement.style.userSelect="none"}function L(t,e){let n=i;if(!n.el.isConnected)return A(n);n.x=t,n.y=e;let c=F(n.bounds),o=(t-c.left)/c.width-n.p0[0],s=(e-c.top)/c.height-n.p0[1],{start:r,edge:a}=n;n.value=S(w(n.el)==="point"?W(r,o,s):a?Y(r,a,o,s,n.min,n.ratio):V(r,o,s)),D(n.el,n.value),n.writing||=requestAnimationFrame(()=>M(n)),at(e)}function at(t){if(i.wy=t<40?-12:t>window.innerHeight-40?12:0,i.wy&&!i.raf){let e=()=>{if(!i?.wy)return i&&(i.raf=0);if(window.scrollBy(0,i.wy),L(i.x,i.y),i)i.raf=requestAnimationFrame(e)};i.raf=requestAnimationFrame(e)}}function U(){if(i&&!i.el.isConnected)A(i)}function ut(t,e,n){U();let c=t.target;if(!c?.matches?.("[data-reactive-box-handle],[data-reactive-point],[data-reactive-box]")||!e.owns(c)||i&&(!i.keyboard||i.grip!==c))return;if(t.key==="Escape")return i&&(t.preventDefault(),tt());let o=j[t.key];if(!o)return;if(t.preventDefault(),i??=C(e,n,c,!0),!i)return;let[s,r]=o.map((f)=>f*(t.shiftKey?0.1:0.01)),a=t.altKey&&w(i.el)==="box",l=i.value;i.value=S(w(i.el)==="point"?W(l,s,r):a?Y(l,i.edge??"se",s,r,i.min,i.ratio):V(l,s,r)),D(i.el,i.value),M(i),Z(i,a?"resized":"moved")}function Q(){if(!y?.isConnected)y=document.createElement("div"),y.setAttribute("aria-live","polite"),y.setAttribute("data-reactive-drag-live",""),y.style.cssText="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap",document.body.append(y);return y}function Z(t,e){let n=T(p(t.el,`${w(t.el)}-messages`),{})[e]??"";for(let o of b(t.el))n=n.replaceAll(`{${o}}`,Math.round(t.value[o]*100));let c=Q();c.textContent="",cancelAnimationFrame(H),H=requestAnimationFrame(()=>c.textContent=n)}function X(t,e,n){let c=t.target?.name;if(!c)return;for(let o of n.querySelectorAll("[data-reactive-box-aspect]")){if(p(o,"box-aspect")!==c||o===i?.el||!e.owns(o))continue;let s=C(e,n,o,!0),{ratio:r,start:a}=s??{};if(!r||Math.abs(a.width/a.height/r-1)<0.005)continue;s.value=S(et(a,r,s.min)),D(o,s.value),M(s),_(s)}}function N(){let t=i;if(!t.el.isConnected)return A(t);M(t),A(t),_(t)}function _(t){for(let[o,s]of Object.entries(t.fields))if(s.value!==t.before[o])s.dispatchEvent(new Event("change",{bubbles:!0}));let e=w(t.el),n=p(t.el,`${e}-action`),c={};for(let o of b(t.el))c[o]=d(t.value[o]);if(!n||b(t.el).every((o)=>c[o]===d(t.start[o])))return;t.core.proceed(t.el,n,JSON.stringify(c),T(p(t.el,`${e}-busy`)))}function tt(){let t=i;if(t.value=t.start,D(t.el,t.start),M(t),t.keyboard)Z(t,"cancelled");A(t)}function A(t){if(i=null,t.over=!0,cancelAnimationFrame(t.raf),cancelAnimationFrame(t.writing),t.grip.removeAttribute("data-reactive-dragging"),t.frame.removeAttribute("data-reactive-frame-active"),!t.keyboard)document.documentElement.style.userSelect=t.select??""}function lt(){if(B)return;B=!0,document.addEventListener("turbo:before-morph-attribute",(t)=>{let e=t.target,n=t.detail?.attributeName;if(!i)return;if(e===i.el&&n==="style"||e===i.grip&&n==="data-reactive-dragging"||e===i.frame&&n==="data-reactive-frame-active"||n==="value"&&Object.values(i.fields).includes(e))t.preventDefault()}),document.addEventListener("turbo:morph-element",(t)=>{let e=t.target;if(e===i?.el||!e.matches?.("[data-reactive-box],[data-reactive-point]"))return;let n=e.closest('[data-controller~="reactive"]'),c=G.get(n);if(!c?.owns(e))return;let o=z(e);for(let[s,r]of Object.entries(K(e,n,c)))if(d(Number(r.value))!==d(o[s]))c.setValue(r,String(d(o[s])))})}export{ft as connect,ht as disconnect,et as fitBox,V as moveBox,W as movePoint,nt as parseRatio,Y as resizeBox,d as round,S as snap};
1
+ var j={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]},q={x:"left",y:"top",width:"width",height:"height"},N=new WeakMap,G=new WeakMap,i=null,y,H,B=!1;function mt(t,e){let n=t.element;G.set(n,e),_(),dt();let s=[["pointerdown",(o)=>at(o,e,n)],["keydown",(o)=>ht(o,e,n)],["keyup",(o)=>i?.keyboard&&o.target===i.grip&&j[o.key]&&L()],["focusout",(o)=>i?.keyboard&&o.target===i.grip&&L()],["input",(o)=>X(o,e,n)],["change",(o)=>X(o,e,n)]];for(let[o,c]of s)n.addEventListener(o,c);N.set(t,()=>{for(let[o,c]of s)n.removeEventListener(o,c)})}function pt(t){if(N.get(t)?.(),N.delete(t),G.delete(t.element),i?.root===t.element)A(i)}var m=(t,e,n)=>Math.min(Math.max(t,e),n),p=(t)=>Math.round(t*1e4)/1e4;function ot(t,e=1){let n=p(t)*e,s=Number(n.toPrecision(15));return String(Number.isFinite(s)?s:n)}function S(t){let e={x:p(t.x),y:p(t.y)};if(t.width!=null)e.width=p(t.width),e.height=p(t.height),e.x=Math.min(e.x,p(1-e.width)),e.y=Math.min(e.y,p(1-e.height));return e}function W(t,e,n){return{x:m(t.x+e,0,1-t.width),y:m(t.y+n,0,1-t.height),width:t.width,height:t.height}}function V(t,e,n){return{x:m(t.x+e,0,1),y:m(t.y+n,0,1)}}function Y(t,e,n,s,o,c){let[r,a,l,f]=["n","e","s","w"].map((R)=>e.includes(R)),x=t.x+t.width,M=t.y+t.height,O=t.x+t.width/2,v=t.y+t.height/2,E=a?1-t.x:f?x:c?2*Math.min(O,1-O):1,u=l?1-t.y:r?M:c?2*Math.min(v,1-v):1,h=t.width+(a?n:f?-n:0),w=t.height+(l?s:r?-s:0);if(c){let R=(a||f)&&(!(r||l)||Math.abs(h-t.width)>=Math.abs(w-t.height)*c),C=Math.min(E,u*c);h=m(R?h:w*c,Math.min(Math.max(o,o*c),C),C),w=h/c}else{if(a||f)h=m(h,Math.min(o,E),E);if(r||l)w=m(w,Math.min(o,u),u)}return{x:f?x-h:a?t.x:O-h/2,y:r?M-w:l?t.y:v-w/2,width:h,height:w}}function it(t,e,n){let s=Math.min(1,e),o=m(Math.sqrt(t.width*t.height*e),Math.min(Math.max(n,n*e),s),s),c=o/e;return{x:m(t.x+t.width/2-o/2,0,1-o),y:m(t.y+t.height/2-c/2,0,1-c),width:o,height:c}}function st(t){let[e,n=1,s]=String(t??"").split(":").map(Number),o=s===void 0?e/n:0;return o>0&&Number.isFinite(o)?o:0}var d=(t,e)=>t?.getAttribute?.(`data-reactive-${e}`),T=(t,e)=>{try{return t==null?e:JSON.parse(t)}catch{return e}},g=(t)=>t.matches("[data-reactive-point]")?"point":"box",z=(t)=>Number(d(t,`${g(t)}-scale`))||1,b=(t)=>g(t)==="point"?["x","y"]:["x","y","width","height"];function J(t){let e={};for(let n of b(t))e[n]=(parseFloat(t.style[q[n]])||0)/100;return e}function D(t,e){for(let n of b(t))t.style[q[n]]=`${Math.round(e[n]*1e4)/100}%`}function F(t){let e=t.getBoundingClientRect();return{left:e.left+(t.clientLeft||0),top:e.top+(t.clientTop||0),width:t.clientWidth||e.width,height:t.clientHeight||e.height}}function ct(t,e){try{return t.parentElement.closest(d(t,"point-within")||"[data-reactive-frame]")??e}catch{return e}}function K(t,e,n){return[...t.querySelectorAll("[name]")].filter((s)=>s.name===n&&e.owns(s))}function U(t,e,n){let s=T(d(t,g(t)),{}),o={};for(let c of b(t)){let[r]=K(e,n,s[c]);if(r)o[c]=r}return o}function rt(t,e,n,s){let o=d(t,"box-aspect"),c=o&&K(n,s,o).find((f)=>!/^(radio|checkbox)$/.test(f.type)||f.checked),r=o?st(c?.value):Number(d(t,"box-ratio"))||0,{width:a,height:l}=F(e);return r&&a&&l?r*l/a:0}function P(t,e,n,s){let o=n.matches("[data-reactive-box-handle]")?n.closest("[data-reactive-box]"):n,c=o?.closest("[data-reactive-frame]");if(!c)return null;let r=J(o),a=U(o,e,t),l=g(o)==="point";return{core:t,root:e,grip:n,el:o,frame:c,keyboard:s,bounds:l?ct(o,c):c,edge:d(n,"box-handle"),start:r,value:r,ratio:l?0:rt(o,c,e,t),min:Number(d(o,"box-min"))||0,scale:z(o),fields:a,before:Object.fromEntries(Object.entries(a).map(([f,x])=>[f,x.value]))}}function Q(t,e,n,s){let o=ot(n,s);if(e.value===""||!(Math.abs(e.value-o)<s/20000))t.setValue(e,o)}function k(t){cancelAnimationFrame(t.writing),t.writing=0;for(let[e,n]of Object.entries(t.fields))Q(t.core,n,t.value[e],t.scale)}function at(t,e,n){if(Z(),i||t.button>0||t.isPrimary===!1||t.target.closest?.("input,textarea,select,[contenteditable]"))return;let s=t.target.closest?.("[data-reactive-box-handle],[data-reactive-point],[data-reactive-box]"),o=s&&e.owns(s)&&P(e,n,s,!1);if(!o)return;let{clientX:c,clientY:r}=t,a=F(o.bounds);o.p0=[(c-a.left)/a.width,(r-a.top)/a.height];let l=(u)=>u.pointerId===t.pointerId,f=(u)=>{if(!l(u)||o.over)return;if(i)return I(u.clientX,u.clientY);if(Math.abs(u.clientX-c)<=4&&Math.abs(u.clientY-r)<=4)return;lt(o),I(u.clientX,u.clientY)},x=(u)=>{if(!l(u))return;if(E(),i)ut(),L()},M=(u)=>{if(u.type==="pointercancel"&&!l(u))return;if(E(),i)nt()},v=[["pointermove",f],["pointerup",x],["pointercancel",M],["keydown",(u)=>u.key==="Escape"&&i&&(u.preventDefault(),M(u))]];for(let[u,h]of v)document.addEventListener(u,h);function E(){for(let[u,h]of v)document.removeEventListener(u,h)}}function ut(){let t=(e)=>(e.preventDefault(),e.stopPropagation());window.addEventListener("click",t,{capture:!0,once:!0}),setTimeout(()=>window.removeEventListener("click",t,{capture:!0}))}function lt(t){i=t,t.grip.setAttribute("data-reactive-dragging",""),t.frame.setAttribute("data-reactive-frame-active",""),t.select=document.documentElement.style.userSelect,document.documentElement.style.userSelect="none"}function I(t,e){let n=i;if(!n.el.isConnected)return A(n);n.x=t,n.y=e;let s=F(n.bounds),o=(t-s.left)/s.width-n.p0[0],c=(e-s.top)/s.height-n.p0[1],{start:r,edge:a}=n;n.value=S(g(n.el)==="point"?V(r,o,c):a?Y(r,a,o,c,n.min,n.ratio):W(r,o,c)),D(n.el,n.value),n.writing||=requestAnimationFrame(()=>k(n)),ft(e)}function ft(t){if(i.wy=t<40?-12:t>window.innerHeight-40?12:0,i.wy&&!i.raf){let e=()=>{if(!i?.wy)return i&&(i.raf=0);if(window.scrollBy(0,i.wy),I(i.x,i.y),i)i.raf=requestAnimationFrame(e)};i.raf=requestAnimationFrame(e)}}function Z(){if(i&&!i.el.isConnected)A(i)}function ht(t,e,n){Z();let s=t.target;if(!s?.matches?.("[data-reactive-box-handle],[data-reactive-point],[data-reactive-box]")||!e.owns(s)||i&&(!i.keyboard||i.grip!==s))return;if(t.key==="Escape")return i&&(t.preventDefault(),nt());let o=j[t.key];if(!o)return;if(t.preventDefault(),i??=P(e,n,s,!0),!i)return;let[c,r]=o.map((f)=>f*(t.shiftKey?0.1:0.01)),a=t.altKey&&g(i.el)==="box",l=i.value;i.value=S(g(i.el)==="point"?V(l,c,r):a?Y(l,i.edge??"se",c,r,i.min,i.ratio):W(l,c,r)),D(i.el,i.value),k(i),tt(i,a?"resized":"moved")}function _(){if(!y?.isConnected)y=document.createElement("div"),y.setAttribute("aria-live","polite"),y.setAttribute("data-reactive-drag-live",""),y.style.cssText="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap",document.body.append(y);return y}function tt(t,e){let n=T(d(t.el,`${g(t.el)}-messages`),{})[e]??"";for(let o of b(t.el))n=n.replaceAll(`{${o}}`,Math.round(t.value[o]*100));let s=_();s.textContent="",cancelAnimationFrame(H),H=requestAnimationFrame(()=>s.textContent=n)}function X(t,e,n){let s=t.target?.name;if(!s)return;for(let o of n.querySelectorAll("[data-reactive-box-aspect]")){if(d(o,"box-aspect")!==s||o===i?.el||!e.owns(o))continue;let c=P(e,n,o,!0),{ratio:r,start:a}=c??{};if(!r||Math.abs(a.width/a.height/r-1)<0.005)continue;c.value=S(it(a,r,c.min)),D(o,c.value),k(c),et(c)}}function L(){let t=i;if(!t.el.isConnected)return A(t);k(t),A(t),et(t)}function et(t){for(let[o,c]of Object.entries(t.fields))if(c.value!==t.before[o])c.dispatchEvent(new Event("change",{bubbles:!0}));let e=g(t.el),n=d(t.el,`${e}-action`),s={};for(let o of b(t.el))s[o]=p(t.value[o]);if(!n||b(t.el).every((o)=>s[o]===p(t.start[o])))return;t.core.proceed(t.el,n,JSON.stringify(s),T(d(t.el,`${e}-busy`)))}function nt(){let t=i;if(t.value=t.start,D(t.el,t.start),k(t),t.keyboard)tt(t,"cancelled");A(t)}function A(t){if(i=null,t.over=!0,cancelAnimationFrame(t.raf),cancelAnimationFrame(t.writing),t.grip.removeAttribute("data-reactive-dragging"),t.frame.removeAttribute("data-reactive-frame-active"),!t.keyboard)document.documentElement.style.userSelect=t.select??""}function dt(){if(B)return;B=!0,document.addEventListener("turbo:before-morph-attribute",(t)=>{let e=t.target,n=t.detail?.attributeName;if(!i)return;if(e===i.el&&n==="style"||e===i.grip&&n==="data-reactive-dragging"||e===i.frame&&n==="data-reactive-frame-active"||n==="value"&&Object.values(i.fields).includes(e))t.preventDefault()}),document.addEventListener("turbo:morph-element",(t)=>{let e=t.target;if(e===i?.el||!e.matches?.("[data-reactive-box],[data-reactive-point]"))return;let n=e.closest('[data-controller~="reactive"]'),s=G.get(n);if(!s?.owns(e))return;let o=J(e),c=z(e);for(let[r,a]of Object.entries(U(e,n,s)))Q(s,a,o[r],c)})}export{mt as connect,pt as disconnect,it as fitBox,W as moveBox,V as movePoint,st as parseRatio,Y as resizeBox,p as round,ot as scaled,S as snap};
2
2
 
3
- //# debugId=94F6177CA2EF4E8764756E2164756E21
3
+ //# debugId=0D638B03EC2311D064756E2164756E21
4
4
  //# sourceMappingURL=drag.min.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["features/drag.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/features/drag — move and resize a box, or move a point,\n// inside a frame, with the result in fields as fractions of the frame (issue\n// #416). One of the feature modules the core imports on demand (issue #275);\n// in the default client it is part of the one file.\n//\n// The markup is the Ruby helpers' (Phlex::Reactive::Component::Drag):\n//\n// data-reactive-frame the coordinate space: its padding box\n// data-reactive-box='{\"x\":\"crop[x]\",…}' a box and the names of its fields\n// -min / -ratio / -aspect / -action / -busy / -messages\n// data-reactive-box-handle=\"se\" an edge or corner (inside the box)\n// data-reactive-point='{\"x\":…,\"y\":…}' a point (its centre)\n// -within / -action / -busy / -messages\n//\n// and what it sets while a pointer drag is on, for the app's CSS:\n//\n// data-reactive-frame-active on the frame\n// data-reactive-dragging on the pressed box, handle or point\n//\n// Where an element is lives in its own inline style — left/top/width/height\n// in percent, rendered by the server and written here — so a morph that\n// renders new values moves it, and nothing is cached between drags. Each\n// field's .value is the same number as a fraction, rounded to 4 decimals and\n// written as set_value writes (core.setValue: a bubbling `input` when it\n// moved), at most once a frame while a pointer drags; one `change` follows on\n// release. A box's x/y/width/height are fractions of the frame; a point's x/y\n// of the element it sits in that matches its -within selector, else of the\n// frame.\n//\n// Input is pointer events: mouse, pen and touch behave the same (the helpers\n// render touch-action: none, so a finger drag does not scroll), and a drag\n// starts after a 4 px move. The keyboard moves the focused box or point\n// directly — arrows 1%, Shift 10%, Alt resizes — and the key's release is\n// the release. A declared action is sent once per release through the\n// root's own pipeline (core.proceed).\n//\n// This module never imports the core: it reaches a controller through the\n// `core` handle (core.owns, core.proceed, core.setValue).\n\nconst FRAME = \"[data-reactive-frame]\"\nconst BOX = \"[data-reactive-box]\"\nconst POINT = \"[data-reactive-point]\"\nconst HANDLE = \"[data-reactive-box-handle]\"\nconst GRIP = `${HANDLE},${POINT},${BOX}`\nconst ROOT = '[data-controller~=\"reactive\"]'\nconst FIELD = \"input,textarea,select,[contenteditable]\"\nconst DRAGGING = \"data-reactive-dragging\"\nconst ACTIVE = \"data-reactive-frame-active\"\nconst THRESHOLD = 4 // px a press must move before it is a drag\nconst EDGE = 40 // px from the window's top or bottom where auto-scroll starts\nconst SPEED = 12 // px auto-scroll moves per frame\nconst ARROWS = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }\nconst STYLE = { x: \"left\", y: \"top\", width: \"width\", height: \"height\" }\n\n// controller -> the removals of what connect() added.\nconst wired = new WeakMap()\n// reactive root element -> its core handle, for a morph of a box in it.\nconst cores = new WeakMap()\n// The one drag on the page, by pointer or keyboard, or null.\nlet drag = null\n// The page's live region (re-created after a Turbo visit replaced <body>),\n// and the frame that will write its next words.\nlet live\nlet speaking\nlet morphs = false\n\nexport function connect(controller, core) {\n const root = controller.element\n cores.set(root, core)\n // Empty on the page before anything is announced (a screen reader often\n // ignores a region that arrives already holding words).\n liveRegion()\n holdMorphs()\n const on = [\n [\"pointerdown\", (event) => pointerDown(event, core, root)],\n [\"keydown\", (event) => keyDown(event, core, root)],\n [\"keyup\", (event) => drag?.keyboard && event.target === drag.grip && ARROWS[event.key] && release()],\n [\"focusout\", (event) => drag?.keyboard && event.target === drag.grip && release()],\n [\"input\", (event) => aspectChanged(event, core, root)],\n [\"change\", (event) => aspectChanged(event, core, root)],\n ]\n for (const [type, fn] of on) root.addEventListener(type, fn)\n wired.set(controller, () => {\n for (const [type, fn] of on) root.removeEventListener(type, fn)\n })\n}\n\nexport function disconnect(controller) {\n wired.get(controller)?.()\n wired.delete(controller)\n cores.delete(controller.element)\n // A root that leaves mid-drag (a replace, a navigation) ends its drag.\n if (drag?.root === controller.element) settle(drag)\n}\n\n// --- Geometry: fractions, 0..1 on each axis --------------------------------------\nconst clamp = (value, low, high) => Math.min(Math.max(value, low), high)\n\nexport const round = (value) => Math.round(value * 1e4) / 1e4\n\n// A position as the fields will hold it: each number rounded to 4 decimals,\n// a box's x/y then pulled in so it still ends inside the frame (rounded\n// apart, 0.45545 + 0.54455 is 0.4555 + 0.5446 = 1.0001).\nexport function snap(value) {\n const out = { x: round(value.x), y: round(value.y) }\n if (value.width != null) {\n out.width = round(value.width)\n out.height = round(value.height)\n out.x = Math.min(out.x, round(1 - out.width))\n out.y = Math.min(out.y, round(1 - out.height))\n }\n return out\n}\n\nexport function moveBox(b, dx, dy) {\n return { x: clamp(b.x + dx, 0, 1 - b.width), y: clamp(b.y + dy, 0, 1 - b.height), width: b.width, height: b.height }\n}\n\nexport function movePoint(p, dx, dy) {\n return { x: clamp(p.x + dx, 0, 1), y: clamp(p.y + dy, 0, 1) }\n}\n\n// Resize from `edge` (a handle: n, se, …) by (dx, dy), the opposite side\n// staying put, inside the frame and never below `min`. `ratio` is the locked\n// width over height IN FRACTIONS (0: free): on a corner the axis the pointer\n// moved more drives, on an edge the other axis grows around the box's centre.\nexport function resizeBox(b, edge, dx, dy, min, ratio) {\n const [n, e, s, w] = [\"n\", \"e\", \"s\", \"w\"].map((side) => edge.includes(side))\n const right = b.x + b.width\n const bottom = b.y + b.height\n const cx = b.x + b.width / 2\n const cy = b.y + b.height / 2\n // From the side that stays to the frame's edge (an axis no handle drives,\n // with a ratio: around its centre).\n const roomW = e ? 1 - b.x : w ? right : ratio ? 2 * Math.min(cx, 1 - cx) : 1\n const roomH = s ? 1 - b.y : n ? bottom : ratio ? 2 * Math.min(cy, 1 - cy) : 1\n let width = b.width + (e ? dx : w ? -dx : 0)\n let height = b.height + (s ? dy : n ? -dy : 0)\n if (ratio) {\n const byWidth = (e || w) && (!(n || s) || Math.abs(width - b.width) >= Math.abs(height - b.height) * ratio)\n const high = Math.min(roomW, roomH * ratio)\n width = clamp(byWidth ? width : height * ratio, Math.min(Math.max(min, min * ratio), high), high)\n height = width / ratio\n } else {\n if (e || w) width = clamp(width, Math.min(min, roomW), roomW)\n if (n || s) height = clamp(height, Math.min(min, roomH), roomH)\n }\n return { x: w ? right - width : e ? b.x : cx - width / 2, y: n ? bottom - height : s ? b.y : cy - height / 2, width, height }\n}\n\n// The box at a new `ratio` (fractions): the same area, centred where it was,\n// inside the frame and no smaller than `min`.\nexport function fitBox(b, ratio, min) {\n const high = Math.min(1, ratio)\n const width = clamp(Math.sqrt(b.width * b.height * ratio), Math.min(Math.max(min, min * ratio), high), high)\n const height = width / ratio\n return {\n x: clamp(b.x + b.width / 2 - width / 2, 0, 1 - width),\n y: clamp(b.y + b.height / 2 - height / 2, 0, 1 - height),\n width,\n height,\n }\n}\n\n// \"16:9\", \"1.5\" or \"\" (free): width over height, or 0 for free.\nexport function parseRatio(text) {\n const [width, height = 1, extra] = String(text ?? \"\").split(\":\").map(Number)\n const ratio = extra === undefined ? width / height : 0\n return ratio > 0 && Number.isFinite(ratio) ? ratio : 0\n}\n\n// --- Reading and writing an element ------------------------------------------------\nconst attr = (el, name) => el?.getAttribute?.(`data-reactive-${name}`)\nconst json = (value, fallback) => {\n try {\n return value == null ? fallback : JSON.parse(value)\n } catch {\n return fallback\n }\n}\nconst kind = (el) => (el.matches(POINT) ? \"point\" : \"box\")\nconst keysOf = (el) => (kind(el) === \"point\" ? [\"x\", \"y\"] : [\"x\", \"y\", \"width\", \"height\"])\n\nfunction read(el) {\n const value = {}\n for (const key of keysOf(el)) value[key] = (parseFloat(el.style[STYLE[key]]) || 0) / 100\n return value\n}\n\nfunction write(el, value) {\n for (const key of keysOf(el)) el.style[STYLE[key]] = `${Math.round(value[key] * 1e4) / 100}%`\n}\n\n// An element's padding box in viewport px: what CSS % on a child resolves against.\nfunction paddingBox(el) {\n const rect = el.getBoundingClientRect()\n return {\n left: rect.left + (el.clientLeft || 0),\n top: rect.top + (el.clientTop || 0),\n width: el.clientWidth || rect.width,\n height: el.clientHeight || rect.height,\n }\n}\n\n// A point's bounds: the element it sits in that matches -within, else its frame.\nfunction boundsOf(el, frame) {\n try {\n return el.parentElement.closest(attr(el, \"point-within\") || FRAME) ?? frame\n } catch {\n return frame // an invalid selector\n }\n}\n\n// The root's own fields of these names (a nested root's are its own, issue #15).\nfunction named(root, core, name) {\n return [...root.querySelectorAll(\"[name]\")].filter((field) => field.name === name && core.owns(field))\n}\n\nfunction fieldsOf(el, root, core) {\n const names = json(attr(el, kind(el)), {})\n const fields = {}\n for (const key of keysOf(el)) {\n const [field] = named(root, core, names[key])\n if (field) fields[key] = field\n }\n return fields\n}\n\n// The locked ratio in fractions (the pixel ratio over the frame's), or 0.\nfunction ratioOf(box, frame, root, core) {\n const name = attr(box, \"box-aspect\")\n const field = name && named(root, core, name).find((f) => !/^(radio|checkbox)$/.test(f.type) || f.checked)\n const ratio = name ? parseRatio(field?.value) : Number(attr(box, \"box-ratio\")) || 0\n const { width, height } = paddingBox(frame)\n return ratio && width && height ? (ratio * height) / width : 0\n}\n\n// What a drag (or a key, or a re-fit) works on, read off the element now.\nfunction begin(core, root, grip, keyboard) {\n const el = grip.matches(HANDLE) ? grip.closest(BOX) : grip\n const frame = el?.closest(FRAME)\n if (!frame) return null\n const value = read(el)\n const fields = fieldsOf(el, root, core)\n const point = kind(el) === \"point\"\n return {\n core,\n root,\n grip,\n el,\n frame,\n keyboard,\n bounds: point ? boundsOf(el, frame) : frame,\n edge: attr(grip, \"box-handle\"),\n start: value,\n value,\n ratio: point ? 0 : ratioOf(el, frame, root, core),\n min: Number(attr(el, \"box-min\")) || 0,\n fields,\n before: Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, field.value])),\n }\n}\n\n// Write the fields now (set_value's write: `input` on each that moved).\nfunction flush(record) {\n cancelAnimationFrame(record.writing)\n record.writing = 0\n for (const [key, field] of Object.entries(record.fields)) record.core.setValue(field, String(round(record.value[key])))\n}\n\n// --- Pointer --------------------------------------------------------------------------\nfunction pointerDown(event, core, root) {\n forgetRemoved()\n if (drag || event.button > 0 || event.isPrimary === false || event.target.closest?.(FIELD)) return\n const grip = event.target.closest?.(GRIP)\n const record = grip && core.owns(grip) && begin(core, root, grip, false)\n if (!record) return\n const x0 = event.clientX\n const y0 = event.clientY\n const bounds = paddingBox(record.bounds)\n record.p0 = [(x0 - bounds.left) / bounds.width, (y0 - bounds.top) / bounds.height]\n // Only the pointer that pressed drives the drag: a second finger is not it.\n const mine = (e) => e.pointerId === event.pointerId\n const move = (e) => {\n // (A drag that ended before the release — its element left — stays over.)\n if (!mine(e) || record.over) return\n if (drag) return follow(e.clientX, e.clientY)\n if (Math.abs(e.clientX - x0) <= THRESHOLD && Math.abs(e.clientY - y0) <= THRESHOLD) return\n start(record)\n follow(e.clientX, e.clientY)\n }\n const up = (e) => {\n if (!mine(e)) return\n end()\n if (drag) suppressClick(), release()\n }\n const abort = (e) => {\n if (e.type === \"pointercancel\" && !mine(e)) return\n end()\n if (drag) cancel()\n }\n const escape = (e) => e.key === \"Escape\" && drag && (e.preventDefault(), abort(e))\n const on = [\n [\"pointermove\", move],\n [\"pointerup\", up],\n [\"pointercancel\", abort],\n [\"keydown\", escape],\n ]\n for (const [type, fn] of on) document.addEventListener(type, fn)\n function end() {\n for (const [type, fn] of on) document.removeEventListener(type, fn)\n }\n}\n\n// The click a pointer drag ends with is not one.\nfunction suppressClick() {\n const stop = (event) => (event.preventDefault(), event.stopPropagation())\n window.addEventListener(\"click\", stop, { capture: true, once: true })\n setTimeout(() => window.removeEventListener(\"click\", stop, { capture: true }))\n}\n\nfunction start(record) {\n drag = record\n record.grip.setAttribute(DRAGGING, \"\")\n record.frame.setAttribute(ACTIVE, \"\")\n record.select = document.documentElement.style.userSelect\n document.documentElement.style.userSelect = \"none\"\n}\n\n// The pointer is at (x, y): the element goes where the pointer's move since\n// the press puts it, measured in its bounds as they are NOW (a scroll moved them).\nfunction follow(x, y) {\n const record = drag\n // A morph or a stream took the element away: the drag is over.\n if (!record.el.isConnected) return settle(record)\n record.x = x\n record.y = y\n const bounds = paddingBox(record.bounds)\n const dx = (x - bounds.left) / bounds.width - record.p0[0]\n const dy = (y - bounds.top) / bounds.height - record.p0[1]\n const { start: from, edge } = record\n record.value = snap(\n kind(record.el) === \"point\"\n ? movePoint(from, dx, dy)\n : edge\n ? resizeBox(from, edge, dx, dy, record.min, record.ratio)\n : moveBox(from, dx, dy),\n )\n write(record.el, record.value)\n record.writing ||= requestAnimationFrame(() => flush(record))\n scroll(y)\n}\n\n// Near the window's top or bottom edge, scroll one step a frame until the\n// pointer leaves the edge (a frame taller than the window).\nfunction scroll(y) {\n drag.wy = y < EDGE ? -SPEED : y > window.innerHeight - EDGE ? SPEED : 0\n if (drag.wy && !drag.raf) {\n const step = () => {\n if (!drag?.wy) return drag && (drag.raf = 0)\n window.scrollBy(0, drag.wy)\n follow(drag.x, drag.y)\n if (drag) drag.raf = requestAnimationFrame(step)\n }\n drag.raf = requestAnimationFrame(step)\n }\n}\n\n// A drag whose element left the page (a stream replaced it while an arrow\n// was held: no keyup or focusout reaches it) is over, not in the way.\nfunction forgetRemoved() {\n if (drag && !drag.el.isConnected) settle(drag)\n}\n\n// --- Keyboard --------------------------------------------------------------------------\n// A focused box, handle or point: the arrows move it (Shift: ten times as\n// far), Alt+arrows resize a box from the focused handle or the bottom-right\n// corner, Escape puts it back. The key's release ends the step (`change`, the\n// action); each step is announced in the element's own words.\nfunction keyDown(event, core, root) {\n forgetRemoved()\n const grip = event.target\n // (A pointer drag, or a key held on another element, owns the drag.)\n if (!grip?.matches?.(GRIP) || !core.owns(grip) || (drag && (!drag.keyboard || drag.grip !== grip))) return\n if (event.key === \"Escape\") return drag && (event.preventDefault(), cancel())\n const arrow = ARROWS[event.key]\n if (!arrow) return\n event.preventDefault()\n drag ??= begin(core, root, grip, true)\n if (!drag) return\n const [dx, dy] = arrow.map((d) => d * (event.shiftKey ? 0.1 : 0.01))\n const resize = event.altKey && kind(drag.el) === \"box\"\n const now = drag.value\n drag.value = snap(\n kind(drag.el) === \"point\"\n ? movePoint(now, dx, dy)\n : resize\n ? resizeBox(now, drag.edge ?? \"se\", dx, dy, drag.min, drag.ratio)\n : moveBox(now, dx, dy),\n )\n write(drag.el, drag.value)\n flush(drag)\n announce(drag, resize ? \"resized\" : \"moved\")\n}\n\nfunction liveRegion() {\n if (!live?.isConnected) {\n live = document.createElement(\"div\")\n live.setAttribute(\"aria-live\", \"polite\")\n live.setAttribute(\"data-reactive-drag-live\", \"\")\n live.style.cssText = \"position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap\"\n document.body.append(live)\n }\n return live\n}\n\n// Clear the region now and write the words on the next frame, so a change is\n// heard even when the words are the same as the last ones.\nfunction announce(record, name) {\n let words = json(attr(record.el, `${kind(record.el)}-messages`), {})[name] ?? \"\"\n for (const key of keysOf(record.el)) words = words.replaceAll(`{${key}}`, Math.round(record.value[key] * 100))\n const region = liveRegion()\n region.textContent = \"\"\n cancelAnimationFrame(speaking)\n speaking = requestAnimationFrame(() => (region.textContent = words))\n}\n\n// --- Aspect ---------------------------------------------------------------------------\n// The field a box reads its ratio from changed (a preset radio was picked):\n// the box re-fits to it around its centre, inside the frame — a committed\n// change, like a release. A box already at that ratio stays as it is.\nfunction aspectChanged(event, core, root) {\n const name = event.target?.name\n if (!name) return\n for (const box of root.querySelectorAll(\"[data-reactive-box-aspect]\")) {\n if (attr(box, \"box-aspect\") !== name || box === drag?.el || !core.owns(box)) continue\n const record = begin(core, root, box, true)\n const { ratio, start: b } = record ?? {}\n if (!ratio || Math.abs(b.width / b.height / ratio - 1) < 0.005) continue\n record.value = snap(fitBox(b, ratio, record.min))\n write(box, record.value)\n flush(record)\n commit(record)\n }\n}\n\n// --- Release / cancel ---------------------------------------------------------------\nfunction release() {\n const record = drag\n // A morph took the element away with no move since: what it rendered stands.\n if (!record.el.isConnected) return settle(record)\n flush(record)\n settle(record)\n commit(record)\n}\n\n// One `change` on each field the drag moved, and the action, once, when the\n// element ended somewhere new.\nfunction commit(record) {\n for (const [key, field] of Object.entries(record.fields)) {\n if (field.value !== record.before[key]) field.dispatchEvent(new Event(\"change\", { bubbles: true }))\n }\n const name = kind(record.el)\n const action = attr(record.el, `${name}-action`)\n const params = {}\n for (const key of keysOf(record.el)) params[key] = round(record.value[key])\n if (!action || keysOf(record.el).every((key) => params[key] === round(record.start[key]))) return\n record.core.proceed(record.el, action, JSON.stringify(params), json(attr(record.el, `${name}-busy`)))\n}\n\nfunction cancel() {\n const record = drag\n record.value = record.start\n write(record.el, record.start)\n flush(record)\n if (record.keyboard) announce(record, \"cancelled\")\n settle(record)\n}\n\n// Undo everything the drag set: the markers, the frame loops, the text selection.\nfunction settle(record) {\n drag = null\n record.over = true\n cancelAnimationFrame(record.raf)\n cancelAnimationFrame(record.writing)\n record.grip.removeAttribute(DRAGGING)\n record.frame.removeAttribute(ACTIVE)\n if (!record.keyboard) document.documentElement.style.userSelect = record.select ?? \"\"\n}\n\n// --- Morphs ----------------------------------------------------------------------------\n// A morph that arrives mid-drag (a reply, a broadcast) does not move the\n// dragged element, write its fields or drop the markers: the drag wins until\n// release. A morph that moved a box or point outside a drag brings its fields\n// along — they may sit outside what the morph rendered.\nfunction holdMorphs() {\n if (morphs) return\n morphs = true\n document.addEventListener(\"turbo:before-morph-attribute\", (event) => {\n const el = event.target\n const name = event.detail?.attributeName\n if (!drag) return\n const held =\n (el === drag.el && name === \"style\") ||\n (el === drag.grip && name === DRAGGING) ||\n (el === drag.frame && name === ACTIVE) ||\n (name === \"value\" && Object.values(drag.fields).includes(el))\n if (held) event.preventDefault()\n })\n // (Every morphed element fires this: the cheap check first.)\n document.addEventListener(\"turbo:morph-element\", (event) => {\n const el = event.target\n if (el === drag?.el || !el.matches?.(`${BOX},${POINT}`)) return\n const root = el.closest(ROOT)\n const core = cores.get(root)\n if (!core?.owns(el)) return\n const value = read(el)\n for (const [key, field] of Object.entries(fieldsOf(el, root, core))) {\n if (round(Number(field.value)) !== round(value[key])) core.setValue(field, String(round(value[key])))\n }\n })\n}\n"
5
+ "// phlex/reactive/features/drag — move and resize a box, or move a point,\n// inside a frame, with the result in fields as fractions of the frame (issue\n// #416). One of the feature modules the core imports on demand (issue #275);\n// in the default client it is part of the one file.\n//\n// The markup is the Ruby helpers' (Phlex::Reactive::Component::Drag):\n//\n// data-reactive-frame the coordinate space: its padding box\n// data-reactive-box='{\"x\":\"crop[x]\",…}' a box and the names of its fields\n// -min / -ratio / -aspect / -scale / -action / -busy / -messages\n// data-reactive-box-handle=\"se\" an edge or corner (inside the box)\n// data-reactive-point='{\"x\":…,\"y\":…}' a point (its centre)\n// -within / -scale / -action / -busy / -messages\n//\n// and what it sets while a pointer drag is on, for the app's CSS:\n//\n// data-reactive-frame-active on the frame\n// data-reactive-dragging on the pressed box, handle or point\n//\n// Where an element is lives in its own inline style — left/top/width/height\n// in percent, rendered by the server and written here — so a morph that\n// renders new values moves it, and nothing is cached between drags. Each\n// field's .value is the same number as a fraction, rounded to 4 decimals —\n// or, with -scale (issue #428), that fraction times the scale (100: percent,\n// 2 decimals) — written as set_value writes (core.setValue: a bubbling\n// `input` when it moved), at most once a frame while a pointer drags; one\n// `change` follows on release. A box's x/y/width/height are fractions of the\n// frame; a point's x/y of the element it sits in that matches its -within\n// selector, else of the frame. Everything else — the style, the action's\n// params, the words — stays in fractions.\n//\n// Input is pointer events: mouse, pen and touch behave the same (the helpers\n// render touch-action: none, so a finger drag does not scroll), and a drag\n// starts after a 4 px move. The keyboard moves the focused box or point\n// directly — arrows 1%, Shift 10%, Alt resizes — and the key's release is\n// the release. A declared action is sent once per release through the\n// root's own pipeline (core.proceed).\n//\n// This module never imports the core: it reaches a controller through the\n// `core` handle (core.owns, core.proceed, core.setValue).\n\nconst FRAME = \"[data-reactive-frame]\"\nconst BOX = \"[data-reactive-box]\"\nconst POINT = \"[data-reactive-point]\"\nconst HANDLE = \"[data-reactive-box-handle]\"\nconst GRIP = `${HANDLE},${POINT},${BOX}`\nconst ROOT = '[data-controller~=\"reactive\"]'\nconst FIELD = \"input,textarea,select,[contenteditable]\"\nconst DRAGGING = \"data-reactive-dragging\"\nconst ACTIVE = \"data-reactive-frame-active\"\nconst THRESHOLD = 4 // px a press must move before it is a drag\nconst EDGE = 40 // px from the window's top or bottom where auto-scroll starts\nconst SPEED = 12 // px auto-scroll moves per frame\nconst ARROWS = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }\nconst STYLE = { x: \"left\", y: \"top\", width: \"width\", height: \"height\" }\n\n// controller -> the removals of what connect() added.\nconst wired = new WeakMap()\n// reactive root element -> its core handle, for a morph of a box in it.\nconst cores = new WeakMap()\n// The one drag on the page, by pointer or keyboard, or null.\nlet drag = null\n// The page's live region (re-created after a Turbo visit replaced <body>),\n// and the frame that will write its next words.\nlet live\nlet speaking\nlet morphs = false\n\nexport function connect(controller, core) {\n const root = controller.element\n cores.set(root, core)\n // Empty on the page before anything is announced (a screen reader often\n // ignores a region that arrives already holding words).\n liveRegion()\n holdMorphs()\n const on = [\n [\"pointerdown\", (event) => pointerDown(event, core, root)],\n [\"keydown\", (event) => keyDown(event, core, root)],\n [\"keyup\", (event) => drag?.keyboard && event.target === drag.grip && ARROWS[event.key] && release()],\n [\"focusout\", (event) => drag?.keyboard && event.target === drag.grip && release()],\n [\"input\", (event) => aspectChanged(event, core, root)],\n [\"change\", (event) => aspectChanged(event, core, root)],\n ]\n for (const [type, fn] of on) root.addEventListener(type, fn)\n wired.set(controller, () => {\n for (const [type, fn] of on) root.removeEventListener(type, fn)\n })\n}\n\nexport function disconnect(controller) {\n wired.get(controller)?.()\n wired.delete(controller)\n cores.delete(controller.element)\n // A root that leaves mid-drag (a replace, a navigation) ends its drag.\n if (drag?.root === controller.element) settle(drag)\n}\n\n// --- Geometry: fractions, 0..1 on each axis --------------------------------------\nconst clamp = (value, low, high) => Math.min(Math.max(value, low), high)\n\nexport const round = (value) => Math.round(value * 1e4) / 1e4\n\n// What a field on `scale` holds for a fraction: its 4 decimals times the\n// scale, to 15 significant digits — every digit it has, without the float\n// noise: 0.1235 is \"0.1235\", \"12.35\" on 100 (not 12.350000000000001), \"3.0875\"\n// on 25. (A product that rounds up past the largest number stays as it is.)\nexport function scaled(value, scale = 1) {\n const exact = round(value) * scale\n const clean = Number(exact.toPrecision(15))\n return String(Number.isFinite(clean) ? clean : exact)\n}\n\n// A position as the fields will hold it: each number rounded to 4 decimals,\n// a box's x/y then pulled in so it still ends inside the frame (rounded\n// apart, 0.45545 + 0.54455 is 0.4555 + 0.5446 = 1.0001).\nexport function snap(value) {\n const out = { x: round(value.x), y: round(value.y) }\n if (value.width != null) {\n out.width = round(value.width)\n out.height = round(value.height)\n out.x = Math.min(out.x, round(1 - out.width))\n out.y = Math.min(out.y, round(1 - out.height))\n }\n return out\n}\n\nexport function moveBox(b, dx, dy) {\n return { x: clamp(b.x + dx, 0, 1 - b.width), y: clamp(b.y + dy, 0, 1 - b.height), width: b.width, height: b.height }\n}\n\nexport function movePoint(p, dx, dy) {\n return { x: clamp(p.x + dx, 0, 1), y: clamp(p.y + dy, 0, 1) }\n}\n\n// Resize from `edge` (a handle: n, se, …) by (dx, dy), the opposite side\n// staying put, inside the frame and never below `min`. `ratio` is the locked\n// width over height IN FRACTIONS (0: free): on a corner the axis the pointer\n// moved more drives, on an edge the other axis grows around the box's centre.\nexport function resizeBox(b, edge, dx, dy, min, ratio) {\n const [n, e, s, w] = [\"n\", \"e\", \"s\", \"w\"].map((side) => edge.includes(side))\n const right = b.x + b.width\n const bottom = b.y + b.height\n const cx = b.x + b.width / 2\n const cy = b.y + b.height / 2\n // From the side that stays to the frame's edge (an axis no handle drives,\n // with a ratio: around its centre).\n const roomW = e ? 1 - b.x : w ? right : ratio ? 2 * Math.min(cx, 1 - cx) : 1\n const roomH = s ? 1 - b.y : n ? bottom : ratio ? 2 * Math.min(cy, 1 - cy) : 1\n let width = b.width + (e ? dx : w ? -dx : 0)\n let height = b.height + (s ? dy : n ? -dy : 0)\n if (ratio) {\n const byWidth = (e || w) && (!(n || s) || Math.abs(width - b.width) >= Math.abs(height - b.height) * ratio)\n const high = Math.min(roomW, roomH * ratio)\n width = clamp(byWidth ? width : height * ratio, Math.min(Math.max(min, min * ratio), high), high)\n height = width / ratio\n } else {\n if (e || w) width = clamp(width, Math.min(min, roomW), roomW)\n if (n || s) height = clamp(height, Math.min(min, roomH), roomH)\n }\n return { x: w ? right - width : e ? b.x : cx - width / 2, y: n ? bottom - height : s ? b.y : cy - height / 2, width, height }\n}\n\n// The box at a new `ratio` (fractions): the same area, centred where it was,\n// inside the frame and no smaller than `min`.\nexport function fitBox(b, ratio, min) {\n const high = Math.min(1, ratio)\n const width = clamp(Math.sqrt(b.width * b.height * ratio), Math.min(Math.max(min, min * ratio), high), high)\n const height = width / ratio\n return {\n x: clamp(b.x + b.width / 2 - width / 2, 0, 1 - width),\n y: clamp(b.y + b.height / 2 - height / 2, 0, 1 - height),\n width,\n height,\n }\n}\n\n// \"16:9\", \"1.5\" or \"\" (free): width over height, or 0 for free.\nexport function parseRatio(text) {\n const [width, height = 1, extra] = String(text ?? \"\").split(\":\").map(Number)\n const ratio = extra === undefined ? width / height : 0\n return ratio > 0 && Number.isFinite(ratio) ? ratio : 0\n}\n\n// --- Reading and writing an element ------------------------------------------------\nconst attr = (el, name) => el?.getAttribute?.(`data-reactive-${name}`)\nconst json = (value, fallback) => {\n try {\n return value == null ? fallback : JSON.parse(value)\n } catch {\n return fallback\n }\n}\nconst kind = (el) => (el.matches(POINT) ? \"point\" : \"box\")\nconst scaleOf = (el) => Number(attr(el, `${kind(el)}-scale`)) || 1\nconst keysOf = (el) => (kind(el) === \"point\" ? [\"x\", \"y\"] : [\"x\", \"y\", \"width\", \"height\"])\n\nfunction read(el) {\n const value = {}\n for (const key of keysOf(el)) value[key] = (parseFloat(el.style[STYLE[key]]) || 0) / 100\n return value\n}\n\nfunction write(el, value) {\n for (const key of keysOf(el)) el.style[STYLE[key]] = `${Math.round(value[key] * 1e4) / 100}%`\n}\n\n// An element's padding box in viewport px: what CSS % on a child resolves against.\nfunction paddingBox(el) {\n const rect = el.getBoundingClientRect()\n return {\n left: rect.left + (el.clientLeft || 0),\n top: rect.top + (el.clientTop || 0),\n width: el.clientWidth || rect.width,\n height: el.clientHeight || rect.height,\n }\n}\n\n// A point's bounds: the element it sits in that matches -within, else its frame.\nfunction boundsOf(el, frame) {\n try {\n return el.parentElement.closest(attr(el, \"point-within\") || FRAME) ?? frame\n } catch {\n return frame // an invalid selector\n }\n}\n\n// The root's own fields of these names (a nested root's are its own, issue #15).\nfunction named(root, core, name) {\n return [...root.querySelectorAll(\"[name]\")].filter((field) => field.name === name && core.owns(field))\n}\n\nfunction fieldsOf(el, root, core) {\n const names = json(attr(el, kind(el)), {})\n const fields = {}\n for (const key of keysOf(el)) {\n const [field] = named(root, core, names[key])\n if (field) fields[key] = field\n }\n return fields\n}\n\n// The locked ratio in fractions (the pixel ratio over the frame's), or 0.\nfunction ratioOf(box, frame, root, core) {\n const name = attr(box, \"box-aspect\")\n const field = name && named(root, core, name).find((f) => !/^(radio|checkbox)$/.test(f.type) || f.checked)\n const ratio = name ? parseRatio(field?.value) : Number(attr(box, \"box-ratio\")) || 0\n const { width, height } = paddingBox(frame)\n return ratio && width && height ? (ratio * height) / width : 0\n}\n\n// What a drag (or a key, or a re-fit) works on, read off the element now.\nfunction begin(core, root, grip, keyboard) {\n const el = grip.matches(HANDLE) ? grip.closest(BOX) : grip\n const frame = el?.closest(FRAME)\n if (!frame) return null\n const value = read(el)\n const fields = fieldsOf(el, root, core)\n const point = kind(el) === \"point\"\n return {\n core,\n root,\n grip,\n el,\n frame,\n keyboard,\n bounds: point ? boundsOf(el, frame) : frame,\n edge: attr(grip, \"box-handle\"),\n start: value,\n value,\n ratio: point ? 0 : ratioOf(el, frame, root, core),\n min: Number(attr(el, \"box-min\")) || 0,\n scale: scaleOf(el),\n fields,\n before: Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, field.value])),\n }\n}\n\n// Write a field the fraction on its scale, as set_value writes — unless it\n// already holds that number, within half a step of the fraction's 4th\n// decimal on its scale (\"50.0\", the 50 a server rendered for 0.5 × 100, is\n// not a move: no `input`, and no `change` on release).\nfunction put(core, field, value, scale) {\n const text = scaled(value, scale)\n if (field.value === \"\" || !(Math.abs(field.value - text) < scale / 2e4)) core.setValue(field, text)\n}\n\n// Write the fields now (set_value's write: `input` on each that moved).\nfunction flush(record) {\n cancelAnimationFrame(record.writing)\n record.writing = 0\n for (const [key, field] of Object.entries(record.fields)) put(record.core, field, record.value[key], record.scale)\n}\n\n// --- Pointer --------------------------------------------------------------------------\nfunction pointerDown(event, core, root) {\n forgetRemoved()\n if (drag || event.button > 0 || event.isPrimary === false || event.target.closest?.(FIELD)) return\n const grip = event.target.closest?.(GRIP)\n const record = grip && core.owns(grip) && begin(core, root, grip, false)\n if (!record) return\n const x0 = event.clientX\n const y0 = event.clientY\n const bounds = paddingBox(record.bounds)\n record.p0 = [(x0 - bounds.left) / bounds.width, (y0 - bounds.top) / bounds.height]\n // Only the pointer that pressed drives the drag: a second finger is not it.\n const mine = (e) => e.pointerId === event.pointerId\n const move = (e) => {\n // (A drag that ended before the release — its element left — stays over.)\n if (!mine(e) || record.over) return\n if (drag) return follow(e.clientX, e.clientY)\n if (Math.abs(e.clientX - x0) <= THRESHOLD && Math.abs(e.clientY - y0) <= THRESHOLD) return\n start(record)\n follow(e.clientX, e.clientY)\n }\n const up = (e) => {\n if (!mine(e)) return\n end()\n if (drag) suppressClick(), release()\n }\n const abort = (e) => {\n if (e.type === \"pointercancel\" && !mine(e)) return\n end()\n if (drag) cancel()\n }\n const escape = (e) => e.key === \"Escape\" && drag && (e.preventDefault(), abort(e))\n const on = [\n [\"pointermove\", move],\n [\"pointerup\", up],\n [\"pointercancel\", abort],\n [\"keydown\", escape],\n ]\n for (const [type, fn] of on) document.addEventListener(type, fn)\n function end() {\n for (const [type, fn] of on) document.removeEventListener(type, fn)\n }\n}\n\n// The click a pointer drag ends with is not one.\nfunction suppressClick() {\n const stop = (event) => (event.preventDefault(), event.stopPropagation())\n window.addEventListener(\"click\", stop, { capture: true, once: true })\n setTimeout(() => window.removeEventListener(\"click\", stop, { capture: true }))\n}\n\nfunction start(record) {\n drag = record\n record.grip.setAttribute(DRAGGING, \"\")\n record.frame.setAttribute(ACTIVE, \"\")\n record.select = document.documentElement.style.userSelect\n document.documentElement.style.userSelect = \"none\"\n}\n\n// The pointer is at (x, y): the element goes where the pointer's move since\n// the press puts it, measured in its bounds as they are NOW (a scroll moved them).\nfunction follow(x, y) {\n const record = drag\n // A morph or a stream took the element away: the drag is over.\n if (!record.el.isConnected) return settle(record)\n record.x = x\n record.y = y\n const bounds = paddingBox(record.bounds)\n const dx = (x - bounds.left) / bounds.width - record.p0[0]\n const dy = (y - bounds.top) / bounds.height - record.p0[1]\n const { start: from, edge } = record\n record.value = snap(\n kind(record.el) === \"point\"\n ? movePoint(from, dx, dy)\n : edge\n ? resizeBox(from, edge, dx, dy, record.min, record.ratio)\n : moveBox(from, dx, dy),\n )\n write(record.el, record.value)\n record.writing ||= requestAnimationFrame(() => flush(record))\n scroll(y)\n}\n\n// Near the window's top or bottom edge, scroll one step a frame until the\n// pointer leaves the edge (a frame taller than the window).\nfunction scroll(y) {\n drag.wy = y < EDGE ? -SPEED : y > window.innerHeight - EDGE ? SPEED : 0\n if (drag.wy && !drag.raf) {\n const step = () => {\n if (!drag?.wy) return drag && (drag.raf = 0)\n window.scrollBy(0, drag.wy)\n follow(drag.x, drag.y)\n if (drag) drag.raf = requestAnimationFrame(step)\n }\n drag.raf = requestAnimationFrame(step)\n }\n}\n\n// A drag whose element left the page (a stream replaced it while an arrow\n// was held: no keyup or focusout reaches it) is over, not in the way.\nfunction forgetRemoved() {\n if (drag && !drag.el.isConnected) settle(drag)\n}\n\n// --- Keyboard --------------------------------------------------------------------------\n// A focused box, handle or point: the arrows move it (Shift: ten times as\n// far), Alt+arrows resize a box from the focused handle or the bottom-right\n// corner, Escape puts it back. The key's release ends the step (`change`, the\n// action); each step is announced in the element's own words.\nfunction keyDown(event, core, root) {\n forgetRemoved()\n const grip = event.target\n // (A pointer drag, or a key held on another element, owns the drag.)\n if (!grip?.matches?.(GRIP) || !core.owns(grip) || (drag && (!drag.keyboard || drag.grip !== grip))) return\n if (event.key === \"Escape\") return drag && (event.preventDefault(), cancel())\n const arrow = ARROWS[event.key]\n if (!arrow) return\n event.preventDefault()\n drag ??= begin(core, root, grip, true)\n if (!drag) return\n const [dx, dy] = arrow.map((d) => d * (event.shiftKey ? 0.1 : 0.01))\n const resize = event.altKey && kind(drag.el) === \"box\"\n const now = drag.value\n drag.value = snap(\n kind(drag.el) === \"point\"\n ? movePoint(now, dx, dy)\n : resize\n ? resizeBox(now, drag.edge ?? \"se\", dx, dy, drag.min, drag.ratio)\n : moveBox(now, dx, dy),\n )\n write(drag.el, drag.value)\n flush(drag)\n announce(drag, resize ? \"resized\" : \"moved\")\n}\n\nfunction liveRegion() {\n if (!live?.isConnected) {\n live = document.createElement(\"div\")\n live.setAttribute(\"aria-live\", \"polite\")\n live.setAttribute(\"data-reactive-drag-live\", \"\")\n live.style.cssText = \"position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap\"\n document.body.append(live)\n }\n return live\n}\n\n// Clear the region now and write the words on the next frame, so a change is\n// heard even when the words are the same as the last ones.\nfunction announce(record, name) {\n let words = json(attr(record.el, `${kind(record.el)}-messages`), {})[name] ?? \"\"\n for (const key of keysOf(record.el)) words = words.replaceAll(`{${key}}`, Math.round(record.value[key] * 100))\n const region = liveRegion()\n region.textContent = \"\"\n cancelAnimationFrame(speaking)\n speaking = requestAnimationFrame(() => (region.textContent = words))\n}\n\n// --- Aspect ---------------------------------------------------------------------------\n// The field a box reads its ratio from changed (a preset radio was picked):\n// the box re-fits to it around its centre, inside the frame — a committed\n// change, like a release. A box already at that ratio stays as it is.\nfunction aspectChanged(event, core, root) {\n const name = event.target?.name\n if (!name) return\n for (const box of root.querySelectorAll(\"[data-reactive-box-aspect]\")) {\n if (attr(box, \"box-aspect\") !== name || box === drag?.el || !core.owns(box)) continue\n const record = begin(core, root, box, true)\n const { ratio, start: b } = record ?? {}\n if (!ratio || Math.abs(b.width / b.height / ratio - 1) < 0.005) continue\n record.value = snap(fitBox(b, ratio, record.min))\n write(box, record.value)\n flush(record)\n commit(record)\n }\n}\n\n// --- Release / cancel ---------------------------------------------------------------\nfunction release() {\n const record = drag\n // A morph took the element away with no move since: what it rendered stands.\n if (!record.el.isConnected) return settle(record)\n flush(record)\n settle(record)\n commit(record)\n}\n\n// One `change` on each field the drag moved, and the action, once, when the\n// element ended somewhere new.\nfunction commit(record) {\n for (const [key, field] of Object.entries(record.fields)) {\n if (field.value !== record.before[key]) field.dispatchEvent(new Event(\"change\", { bubbles: true }))\n }\n const name = kind(record.el)\n const action = attr(record.el, `${name}-action`)\n const params = {}\n for (const key of keysOf(record.el)) params[key] = round(record.value[key])\n if (!action || keysOf(record.el).every((key) => params[key] === round(record.start[key]))) return\n record.core.proceed(record.el, action, JSON.stringify(params), json(attr(record.el, `${name}-busy`)))\n}\n\nfunction cancel() {\n const record = drag\n record.value = record.start\n write(record.el, record.start)\n flush(record)\n if (record.keyboard) announce(record, \"cancelled\")\n settle(record)\n}\n\n// Undo everything the drag set: the markers, the frame loops, the text selection.\nfunction settle(record) {\n drag = null\n record.over = true\n cancelAnimationFrame(record.raf)\n cancelAnimationFrame(record.writing)\n record.grip.removeAttribute(DRAGGING)\n record.frame.removeAttribute(ACTIVE)\n if (!record.keyboard) document.documentElement.style.userSelect = record.select ?? \"\"\n}\n\n// --- Morphs ----------------------------------------------------------------------------\n// A morph that arrives mid-drag (a reply, a broadcast) does not move the\n// dragged element, write its fields or drop the markers: the drag wins until\n// release. A morph that moved a box or point outside a drag brings its fields\n// along — they may sit outside what the morph rendered.\nfunction holdMorphs() {\n if (morphs) return\n morphs = true\n document.addEventListener(\"turbo:before-morph-attribute\", (event) => {\n const el = event.target\n const name = event.detail?.attributeName\n if (!drag) return\n const held =\n (el === drag.el && name === \"style\") ||\n (el === drag.grip && name === DRAGGING) ||\n (el === drag.frame && name === ACTIVE) ||\n (name === \"value\" && Object.values(drag.fields).includes(el))\n if (held) event.preventDefault()\n })\n // (Every morphed element fires this: the cheap check first.)\n document.addEventListener(\"turbo:morph-element\", (event) => {\n const el = event.target\n if (el === drag?.el || !el.matches?.(`${BOX},${POINT}`)) return\n const root = el.closest(ROOT)\n const core = cores.get(root)\n if (!core?.owns(el)) return\n const value = read(el)\n const scale = scaleOf(el)\n for (const [key, field] of Object.entries(fieldsOf(el, root, core))) put(core, field, value[key], scale)\n })\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "94F6177CA2EF4E8764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "0D638B03EC2311D064756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -177,7 +177,7 @@ function ownedClientOps(root, allRoots) {
177
177
  const records = (raw ?? "").split(" ").map(parseJson).filter((r) => r && typeof r === "object" && !Array.isArray(r))
178
178
  if (records.length === 0) return [{ event, ops: raw }]
179
179
  // A shortcut record (issue #357) reads as "keydown@window shortcut=mod+z".
180
- const shortcutOf = (s) => (typeof s === "object" ? `${s.keys}${s.inEditable ? " editable" : ""}` : s)
180
+ const shortcutOf = (s) => (typeof s === "object" ? `${s.keys}${s.inEditable ? " editable" : ""}${s.handled ? " handled" : ""}` : s)
181
181
  const summary = (r) => `${r.on}${r.window ? "@window" : ""}${r.shortcut == null ? "" : ` shortcut=${shortcutOf(r.shortcut)}`}`
182
182
  return records.map((r) => ({ event: r.on ? summary(r) : event, ops: r.ops }))
183
183
  })
@@ -1,4 +1,4 @@
1
- function m(t=document){let r=Array.from(t.querySelectorAll('[data-controller~="reactive"]'));return r.map((e)=>b(e,r))}function T(t=document){let r=m(t),e=globalThis.console;if(e&&typeof e.log==="function")e.log(`[phlex-reactive] ${r.length} reactive root(s)`);if(e&&typeof e.table==="function")e.table(r.map((a)=>({id:a.id,component:a.component??(a.opaque?"(opaque token)":null),actions:a.triggers.map((n)=>n.action).join(", "),fields:a.fields.join(", "),computes:a.computes.join(", ")})));return r}function b(t,r){let e=t.getAttribute("data-reactive-token-value"),a=g(e);return{id:t.id||null,component:a.opaque?null:a.c??null,gid:a.gid??null,stateKeys:a.s?Object.keys(a.s):[],tokenVersion:a.v??null,opaque:!!a.opaque,status:v(t),triggers:h(t,r),clientOps:w(t,r),computes:q(t,r),fields:O(t,r),show:x(t,r),text:j(t,r),filter:S(t)}}function g(t){if(!t)return{opaque:!0};try{let r=t.split("--")[0],e=l(r),a=JSON.parse(e),n=a&&a._rails&&a._rails.data?a._rails.data:a;if(!n||typeof n!=="object")return{opaque:!0};return n}catch{return{opaque:!0}}}function l(t){return(globalThis.atob||((e)=>Buffer.from(e,"base64").toString("binary")))(t)}function v(t){return{error:t.getAttribute("data-reactive-error"),busy:t.getAttribute("aria-busy")==="true"||t.hasAttribute("data-reactive-busy"),dirty:t.hasAttribute("data-reactive-dirty")}}var A='[data-controller~="reactive"]';function y(t,r){return t.closest(A)===r}function o(t,r,e){return Array.from(t.querySelectorAll(r)).filter((a)=>y(a,t))}function h(t,r){return o(t,"[data-reactive-action-param]",r).map((e)=>({action:e.getAttribute("data-reactive-action-param"),event:d(e.getAttribute("data-action")),params:e.getAttribute("data-reactive-params-param"),debounce:e.getAttribute("data-reactive-debounce-param"),throttle:e.getAttribute("data-reactive-throttle-param"),confirm:e.getAttribute("data-reactive-confirm-param"),optimistic:e.getAttribute("data-reactive-optimistic-param"),loading:e.getAttribute("data-reactive-loading-param"),shortcut:e.getAttribute("data-reactive-shortcut-param")}))}function d(t){if(!t)return null;let r=t.indexOf("->");return r===-1?null:t.slice(0,r)}function w(t,r){return o(t,"[data-reactive-ops-param]",r).flatMap((e)=>{let a=e.getAttribute("data-reactive-ops-param"),n=d(e.getAttribute("data-action")),u=s(a);if(Array.isArray(u))return[{event:n,ops:u}];let c=(a??"").split(" ").map(s).filter((i)=>i&&typeof i==="object"&&!Array.isArray(i));if(c.length===0)return[{event:n,ops:a}];let p=(i)=>typeof i==="object"?`${i.keys}${i.inEditable?" editable":""}`:i,f=(i)=>`${i.on}${i.window?"@window":""}${i.shortcut==null?"":` shortcut=${p(i.shortcut)}`}`;return c.map((i)=>({event:i.on?f(i):n,ops:i.ops}))})}function s(t){try{return JSON.parse(t)}catch{return}}function q(t,r){return o(t,"[data-reactive-compute-reducer-param]",r).map((e)=>e.getAttribute("data-reactive-compute-reducer-param"))}function O(t,r){return o(t,"input[name], select[name], textarea[name]",r).map((e)=>e.getAttribute("name"))}function x(t,r){return o(t,"[data-reactive-show-field]",r).map((e)=>({field:e.getAttribute("data-reactive-show-field"),equals:e.getAttribute("data-reactive-show-equals"),not:e.getAttribute("data-reactive-show-not"),in:e.getAttribute("data-reactive-show-in")}))}function j(t,r){return o(t,"[data-reactive-text]",r).map((e)=>e.getAttribute("data-reactive-text"))}function S(t){let r=t.getAttribute("data-reactive-filter-input");if(!r)return null;return{input:r,option:t.getAttribute("data-reactive-filter-option"),group:t.getAttribute("data-reactive-filter-group"),empty:t.getAttribute("data-reactive-filter-empty"),match:t.getAttribute("data-reactive-filter-match")??"substring",fold:t.hasAttribute("data-reactive-filter-fold")}}export{T as report,m as scan};
1
+ function m(t=document){let r=Array.from(t.querySelectorAll('[data-controller~="reactive"]'));return r.map((e)=>b(e,r))}function T(t=document){let r=m(t),e=globalThis.console;if(e&&typeof e.log==="function")e.log(`[phlex-reactive] ${r.length} reactive root(s)`);if(e&&typeof e.table==="function")e.table(r.map((a)=>({id:a.id,component:a.component??(a.opaque?"(opaque token)":null),actions:a.triggers.map((i)=>i.action).join(", "),fields:a.fields.join(", "),computes:a.computes.join(", ")})));return r}function b(t,r){let e=t.getAttribute("data-reactive-token-value"),a=l(e);return{id:t.id||null,component:a.opaque?null:a.c??null,gid:a.gid??null,stateKeys:a.s?Object.keys(a.s):[],tokenVersion:a.v??null,opaque:!!a.opaque,status:v(t),triggers:h(t,r),clientOps:w(t,r),computes:q(t,r),fields:O(t,r),show:x(t,r),text:j(t,r),filter:S(t)}}function l(t){if(!t)return{opaque:!0};try{let r=t.split("--")[0],e=g(r),a=JSON.parse(e),i=a&&a._rails&&a._rails.data?a._rails.data:a;if(!i||typeof i!=="object")return{opaque:!0};return i}catch{return{opaque:!0}}}function g(t){return(globalThis.atob||((e)=>Buffer.from(e,"base64").toString("binary")))(t)}function v(t){return{error:t.getAttribute("data-reactive-error"),busy:t.getAttribute("aria-busy")==="true"||t.hasAttribute("data-reactive-busy"),dirty:t.hasAttribute("data-reactive-dirty")}}var A='[data-controller~="reactive"]';function y(t,r){return t.closest(A)===r}function o(t,r,e){return Array.from(t.querySelectorAll(r)).filter((a)=>y(a,t))}function h(t,r){return o(t,"[data-reactive-action-param]",r).map((e)=>({action:e.getAttribute("data-reactive-action-param"),event:d(e.getAttribute("data-action")),params:e.getAttribute("data-reactive-params-param"),debounce:e.getAttribute("data-reactive-debounce-param"),throttle:e.getAttribute("data-reactive-throttle-param"),confirm:e.getAttribute("data-reactive-confirm-param"),optimistic:e.getAttribute("data-reactive-optimistic-param"),loading:e.getAttribute("data-reactive-loading-param"),shortcut:e.getAttribute("data-reactive-shortcut-param")}))}function d(t){if(!t)return null;let r=t.indexOf("->");return r===-1?null:t.slice(0,r)}function w(t,r){return o(t,"[data-reactive-ops-param]",r).flatMap((e)=>{let a=e.getAttribute("data-reactive-ops-param"),i=d(e.getAttribute("data-action")),u=s(a);if(Array.isArray(u))return[{event:i,ops:u}];let c=(a??"").split(" ").map(s).filter((n)=>n&&typeof n==="object"&&!Array.isArray(n));if(c.length===0)return[{event:i,ops:a}];let p=(n)=>typeof n==="object"?`${n.keys}${n.inEditable?" editable":""}${n.handled?" handled":""}`:n,f=(n)=>`${n.on}${n.window?"@window":""}${n.shortcut==null?"":` shortcut=${p(n.shortcut)}`}`;return c.map((n)=>({event:n.on?f(n):i,ops:n.ops}))})}function s(t){try{return JSON.parse(t)}catch{return}}function q(t,r){return o(t,"[data-reactive-compute-reducer-param]",r).map((e)=>e.getAttribute("data-reactive-compute-reducer-param"))}function O(t,r){return o(t,"input[name], select[name], textarea[name]",r).map((e)=>e.getAttribute("name"))}function x(t,r){return o(t,"[data-reactive-show-field]",r).map((e)=>({field:e.getAttribute("data-reactive-show-field"),equals:e.getAttribute("data-reactive-show-equals"),not:e.getAttribute("data-reactive-show-not"),in:e.getAttribute("data-reactive-show-in")}))}function j(t,r){return o(t,"[data-reactive-text]",r).map((e)=>e.getAttribute("data-reactive-text"))}function S(t){let r=t.getAttribute("data-reactive-filter-input");if(!r)return null;return{input:r,option:t.getAttribute("data-reactive-filter-option"),group:t.getAttribute("data-reactive-filter-group"),empty:t.getAttribute("data-reactive-filter-empty"),match:t.getAttribute("data-reactive-filter-match")??"substring",fold:t.hasAttribute("data-reactive-filter-fold")}}export{T as report,m as scan};
2
2
 
3
- //# debugId=072DC7AC80C1193464756E2164756E21
3
+ //# debugId=0503DAE97DCB3C7564756E2164756E21
4
4
  //# sourceMappingURL=inspect.min.js.map